On a big application linking with gtk, using the compilation options
-ffunction-sections -fdata-sections and the linker option
-Wl,--gc-sections, --read-var-info=yes gives the following:

    valgrind: m_debuginfo/d3basics.c:973 (vgModuleLocal_evaluate_GX): Assertion 'aMax == ~(Addr)0' failed.

    host stacktrace:
    ==18521==    at 0x38057C54: show_sched_status_wrk (m_libcassert.c:308)
    ==18521==    by 0x38057F50: report_and_quit (m_libcassert.c:367)
    ==18521==    by 0x38058151: vgPlain_assert_fail (m_libcassert.c:432)
    ==18521==    by 0x3813F084: vgModuleLocal_evaluate_GX (d3basics.c:973)
    ==18521==    by 0x38098300: data_address_is_in_var (debuginfo.c:2769)
    ==18521==    by 0x38099E26: vgPlain_get_data_description (debuginfo.c:3298)

The problem is that -Wl,--gc-sections eliminates the unused functions
but keeps some debug info for the functions or their compilation units.
The dwarf entry has low and high pc, but both are equal to 0.
The dwarf reader of Valgrind is confused by this, as the varstack becomes
empty, while it should not. This then causes local (eliminated) variables
to be put in the global scope, leading afterwards to evaluation errors
when describing any other variables.

The fix is to also push something on the varstack when
a CU that has low and high pc given but with 0 value.
This is similar to the varstack_push done for a CU that has
no low pc, no high pc and no range.
Despite considerable effort to make a small reproducer, the problem
could only be produced with a big executable.
After the fix, everything was working properly.

The wrong behaviour for dwarf entries produce the following trace:
     <2><2ff291a>: Abbrev Number: 23 (DW_TAG_formal_parameter)
         DW_AT_name        : AET	
         DW_AT_decl_file   : 1	
         DW_AT_decl_line   : 243	
         DW_AT_type        : <2ff2811>	
         DW_AT_location    : 18288554	
      Recording this variable, with 1 PC range(s)
    <2ff291a> addVar: level 0: AET :: EdgeTableEntry*
      declared at: gdkpolyreg-generic.c:243
      ACQUIRE for range(s) [0x0,0xffffffff] 

The AET is a formal parameter of a function, but is wrongly added
at level 0, with a PC range covering the full space. It has a Loc GX
which uses non biased program counters (e.g. 0x0,0x8).
This dwarf entry will require a FrB (and registers when evaluating)
but no such things are available (or given) when evaluating a variable
in the global scope.

The fix is to handle compilation units with lo and hi pc == 0x0
similarly to a CU that has no lo and hi pc.
With this fix, valgrind --read-var-info=yes could properly
handle a big application with plenty of eliminated functions.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13941 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 8accb8d..ac02d09 100644
--- a/NEWS
+++ b/NEWS
@@ -44,8 +44,10 @@
   by increasing the value.
   See user manual for details.
-* Support for --read-var-info=yes has been improved
-  to handle Ada and C struct containing VLA.
+* Minor improvements in dwarf handling with --read-var-info=yes
+  - Ada and C struct containing VLA do not cause a bad DIE error anymore
+  - Code compiled with -ffunction-sections -fdata-sections -Wl,--gc-sections
+    does not cause assert errors anymore.
 * ==================== FIXED BUGS ====================
diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c
index 0052d33..de491e9 100644
--- a/coregrind/m_debuginfo/readdwarf3.c
+++ b/coregrind/m_debuginfo/readdwarf3.c
@@ -1749,6 +1749,18 @@
                            unitary_range_list(ip_lo, ip_hi1 - 1),
                            False/*isFunc*/, NULL/*fbGX*/ );
+         else if (ip_lo == 0 && ip_hi1 == 0)
+            /* CU has no code, presumably?
+               Such situations have been encountered for code
+               compiled with -ffunction-sections -fdata-sections
+               and linked with --gc-sections. Completely
+               eliminated CU gives such 0 lo/hi pc. Similarly
+               to a CU which has no lo/hi/range pc, we push
+               an empty range list. */
+            varstack_push( cc, parser, td3,
+                           empty_range_list(),
+                           level,
+                           False/*isFunc*/, NULL/*fbGX*/ );
       } else
       if ((!have_lo) && (!have_hi1) && have_range) {
          varstack_push( cc, parser, td3,