]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Bug 452802 Handle lld 9+ split RW PT_LOAD segments correctly
authorPaul Floyd <pjfloyd@wanadoo.fr>
Thu, 9 Jun 2022 20:03:04 +0000 (22:03 +0200)
committerPaul Floyd <pjfloyd@wanadoo.fr>
Thu, 9 Jun 2022 20:03:04 +0000 (22:03 +0200)
Many changes mostly related to modifying VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
so that instead of triggering debuginfo reading after seeing one RX PT_LOAD and 1 RW PT_LOAD it
can handle either 1 or 2 RW PT_LOADs.

NEWS
coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/image.c
coregrind/m_debuginfo/priv_image.h
coregrind/m_debuginfo/priv_readelf.h
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readelf.c
coregrind/m_debuginfo/readpdb.c
coregrind/m_debuginfo/storage.c
memcheck/tests/Makefile.am
memcheck/tests/varinfo5.stderr.exp-freebsd [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index a0cf73eafffad6afd42a988bde5ee8273491523d..1f92c25e3dcc45cf7063b4e3561bad5603932c94 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,7 @@ are not entered into bugzilla tend to get forgotten about or ignored.
 452779  Valgrind fails to build on FreeBSD 13.0 with llvm-devel (15.0.0)
 453055  shared_timed_mutex drd test fails with "Lock shared failed" message
 453602  Missing command line option to enable/disable debuginfod
+452802  Handle lld 9+ split RW PT_LOAD segments correctly
 
 To see details of a given bug, visit
   https://bugs.kde.org/show_bug.cgi?id=XXXXXX
index 60f9ea195d1329eba1f90e87e95da33971608cd5..34a2ea8ccbd81351d8e1eafb7660f20605e26bea 100644 (file)
@@ -665,7 +665,7 @@ static void check_CFSI_related_invariants ( const DebugInfo* di )
       been successfully read.  And that shouldn't happen until we have
       both a r-x and rw- mapping for the object.  Hence: */
    vg_assert(di->fsm.have_rx_map);
-   vg_assert(di->fsm.have_rw_map);
+   vg_assert(di->fsm.rw_map_count);
    for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) {
       const DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i);
       /* We are interested in r-x mappings only */
@@ -1024,16 +1024,96 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di )
 
 
 /* Notify the debuginfo system about a new mapping.  This is the way
-   new debug information gets loaded.  If allow_SkFileV is True, it
-   will try load debug info if the mapping at 'a' belongs to Valgrind;
-   whereas normally (False) it will not do that.  This allows us to
-   carefully control when the thing will read symbols from the
-   Valgrind executable itself.
+   new debug information gets loaded.
+
+   redelf -e will output something like
+
+   readelf -e says
+
+   Program Headers:
+  Type           Offset             VirtAddr           PhysAddr
+                 FileSiz            MemSiz              Flg    Align
+  PHDR           0x0000000000000040 0x0000000000200040 0x0000000000200040
+                 0x0000000000000268 0x0000000000000268  R      0x8
+  INTERP         0x00000000000002a8 0x00000000002002a8 0x00000000002002a8
+                 0x0000000000000015 0x0000000000000015  R      0x1
+      [Requesting program interpreter: /libexec/ld-elf.so.1]
+  LOAD           0x0000000000000000 0x0000000000200000 0x0000000000200000
+                 0x0000000000002acc 0x0000000000002acc  R      0x1000
+  LOAD           0x0000000000002ad0 0x0000000000203ad0 0x0000000000203ad0
+                 0x0000000000004a70 0x0000000000004a70  R E    0x1000
+  LOAD           0x0000000000007540 0x0000000000209540 0x0000000000209540
+                 0x00000000000001d8 0x00000000000001d8  RW     0x1000
+  LOAD           0x0000000000007720 0x000000000020a720 0x000000000020a720
+                 0x00000000000002b8 0x00000000000005a0  RW     0x1000
+  DYNAMIC        0x0000000000007570 0x0000000000209570 0x0000000000209570
+                 0x00000000000001a0 0x00000000000001a0  RW     0x8
+  GNU_RELRO      0x0000000000007540 0x0000000000209540 0x0000000000209540
+                 0x00000000000001d8 0x00000000000001d8  R      0x1
+  GNU_EH_FRAME   0x0000000000002334 0x0000000000202334 0x0000000000202334
+                 0x000000000000012c 0x000000000000012c  R      0x4
+  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
+                 0x0000000000000000 0x0000000000000000  RW     0
+  NOTE           0x00000000000002c0 0x00000000002002c0 0x00000000002002c0
+                 0x0000000000000048 0x0000000000000048  R      0x4
+
+   This function will be called for the "LOAD" segments above.
+
+   This function gets called from 2 contexts
+
+   "HOST TRIGGERED"
+
+   1a. For the tool exe and tool/core shared libs. These are already
+       mmap'd when the host starts so we look at something like the
+       /proc filesystem to get the mapping after the event and build
+       up the NSegments from that.
+
+   1b. Then the host loads ld.so and the guest exe. This is done in
+       the sequence
+          load_client -> VG_(do_exec) -> VG_(do_exec_inner) ->
+          exe_handlers->load_fn ( == VG_(load_ELF) ).
+
+       This does the mmap'ing and creats the associated NSegments.
+
+       The NSegments may get merged, (see maybe_merge_nsegments)
+       so there could be more PT_LOADs than there are NSegments.
+       VG_(di_notify_mmap) is called by iterating over the
+       NSegments
+
+   "GUEST TRIGGERED"
+
+   2.  When the guest loads any further shared libs (libc,
+       other dependencies, dlopens) using mmap.
+
+       There are a few variations for syswraps/platforms.
+
+       In this case the NSegment could possibly be merged,
+       but that is irrelevant because di_notify_mmap is being
+       called directy on the mmap result.
+
+   If allow_SkFileV is True, it will try load debug info if the
+   mapping at 'a' belongs to Valgrind; whereas normally (False)
+   it will not do that.  This allows us to carefully control when
+   the thing will read symbols from the Valgrind executable itself.
 
    If use_fd is not -1, that is used instead of the filename; this
    avoids perturbing fcntl locks, which are released by simply
    re-opening and closing the same file (even via different fd!).
 
+   Read-only mappings will be ignored.
+   There may be 1 or 2 RW mappings.
+   There will also be 1 RX mapping.
+
+   If there is no RX or no RW mapping then we will not attempt to
+   read debuginfo for the file.
+
+   In order to know whether there are 1 or 2 RW mappings we
+   need to check the ELF headers. And in the case that we
+   detect 2 RW mappings we need to double check that they
+   aren't contiguous in memory resulting in merged NSegemnts.
+
+   This does not apply to Darwin which just checks the Mach-O header
+
    If a call to VG_(di_notify_mmap) causes debug info to be read, then
    the returned ULong is an abstract handle which can later be used to
    refer to the debuginfo read as a result of this specific mapping,
@@ -1044,12 +1124,19 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di )
 ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
 {
    NSegment const * seg;
+   Int rw_load_count;
    const HChar* filename;
    Bool       is_rx_map, is_rw_map, is_ro_map;
+
    DebugInfo* di;
    Int        actual_fd, oflags;
+#if defined(VGO_darwin)
    SysRes     preadres;
    HChar      buf1k[1024];
+#else
+   Bool       elf_ok;
+#endif
+
    const Bool       debug = VG_(debugLog_getLevel)() >= 3;
    SysRes     statres;
    struct vg_stat statbuf;
@@ -1211,9 +1298,12 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
       return 0;
 #endif
 
+#if defined(VGO_darwin)
    /* Peer at the first few bytes of the file, to see if it is an ELF */
    /* object file. Ignore the file if we do not have read permission. */
    VG_(memset)(buf1k, 0, sizeof(buf1k));
+#endif
+
    oflags = VKI_O_RDONLY;
 #  if defined(VKI_O_LARGEFILE)
    oflags |= VKI_O_LARGEFILE;
@@ -1237,6 +1327,7 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
       actual_fd = use_fd;
    }
 
+#if defined(VGO_darwin)
    preadres = VG_(pread)( actual_fd, buf1k, sizeof(buf1k), 0 );
    if (use_fd == -1) {
       VG_(close)( actual_fd );
@@ -1246,20 +1337,33 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
       DebugInfo fake_di;
       VG_(memset)(&fake_di, 0, sizeof(fake_di));
       fake_di.fsm.filename = ML_(dinfo_strdup)("di.debuginfo.nmm", filename);
-      ML_(symerr)(&fake_di, True, "can't read file to inspect ELF header");
+      ML_(symerr)(&fake_di, True, "can't read file to inspect Mach-O headers");
       return 0;
    }
    if (sr_Res(preadres) == 0)
       return 0;
    vg_assert(sr_Res(preadres) > 0 && sr_Res(preadres) <= sizeof(buf1k) );
+#endif
 
    /* We're only interested in mappings of object files. */
 #  if defined(VGO_linux) || defined(VGO_solaris) || defined(VGO_freebsd)
-   if (!ML_(is_elf_object_file)( buf1k, (SizeT)sr_Res(preadres), False ))
+
+   rw_load_count = 0;
+
+   elf_ok = ML_(check_elf_and_get_rw_loads) ( actual_fd, filename, &rw_load_count );
+
+   if (use_fd == -1) {
+      VG_(close)( actual_fd );
+   }
+
+   if (!elf_ok) {
       return 0;
+   }
+
 #  elif defined(VGO_darwin)
    if (!ML_(is_macho_object_file)( buf1k, (SizeT)sr_Res(preadres) ))
       return 0;
+   rw_load_count = 1;
 #  else
 #    error "unknown OS"
 #  endif
@@ -1311,15 +1415,20 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
 
    /* Update flags about what kind of mappings we've already seen. */
    di->fsm.have_rx_map |= is_rx_map;
-   di->fsm.have_rw_map |= is_rw_map;
+   /* This is a bit of a hack, using a Bool as a counter */
+   if (is_rw_map)
+     ++di->fsm.rw_map_count;
    di->fsm.have_ro_map |= is_ro_map;
 
    /* So, finally, are we in an accept state? */
    vg_assert(!di->have_dinfo);
-   if (di->fsm.have_rx_map && di->fsm.have_rw_map) {
+   if (di->fsm.have_rx_map &&
+       rw_load_count >= 1 &&
+       di->fsm.rw_map_count == rw_load_count) {
       /* Ok, so, finally, we found what we need, and we haven't
          already read debuginfo for this object.  So let's do so now.
          Yee-ha! */
+
       if (debug)
          VG_(dmsg)("di_notify_mmap-5: "
                    "achieved accept state for %s\n", filename);
@@ -1416,7 +1525,7 @@ void VG_(di_notify_vm_protect)( Addr a, SizeT len, UInt prot )
          continue; /* need to have a r-- mapping for this object */
       if (di->fsm.have_rx_map)
          continue; /* rx- mapping already exists */
-      if (!di->fsm.have_rw_map)
+      if (!di->fsm.rw_map_count)
          continue; /* need to have a rw- mapping */
       /* Try to find a mapping matching the memory area. */
       for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) {
@@ -1454,7 +1563,7 @@ void VG_(di_notify_vm_protect)( Addr a, SizeT len, UInt prot )
    }
 
    /* Check if we're now in an accept state and read debuginfo.  Finally. */
-   if (di->fsm.have_rx_map && di->fsm.have_rw_map && !di->have_dinfo) {
+   if (di->fsm.have_rx_map && di->fsm.rw_map_count && !di->have_dinfo) {
       if (debug)
          VG_(dmsg)("di_notify_vm_protect-5: "
                      "achieved accept state for %s\n", di->fsm.filename);
@@ -1669,7 +1778,7 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
    { DebugInfo* di = find_or_create_DebugInfo_for(exename);
 
      /* this di must be new, since we just nuked any old stuff in the range */
-     vg_assert(di && !di->fsm.have_rx_map && !di->fsm.have_rw_map);
+     vg_assert(di && !di->fsm.have_rx_map && !di->fsm.rw_map_count);
      vg_assert(!di->have_dinfo);
 
      /* don't set up any of the di-> fields; let
index ebe6dfcfe8f615553597ae2d8d6d63441cf78773..28dfd0b4722a628b99684b7cf0d943c03f3ea90e 100644 (file)
@@ -874,6 +874,49 @@ DiImage* ML_(img_from_local_file)(const HChar* fullpath)
    return img;
 }
 
+/* As above, but uses fd rather than filename */
+DiImage* ML_(img_from_fd)(Int fd, const HChar* fullpath)
+{
+   struct vg_stat stat_buf;
+   DiOffT         size;
+
+   if (VG_(fstat)(fd, &stat_buf) != 0) {
+      return NULL;
+   }
+
+   size = stat_buf.size;
+   if (size == 0 || size == DiOffT_INVALID
+       || /* size is unrepresentable as a SizeT */
+          size != (DiOffT)(SizeT)(size)) {
+      return NULL;
+   }
+
+   DiImage* img = ML_(dinfo_zalloc)("di.image.ML_iflf.1", sizeof(DiImage));
+   img->source.is_local = True;
+   img->source.fd       = fd;
+   img->size            = size;
+   img->real_size       = size;
+   img->ces_used        = 0;
+   img->source.name     = ML_(dinfo_strdup)("di.image.ML_iflf.2", fullpath);
+   img->cslc            = NULL;
+   img->cslc_size       = 0;
+   img->cslc_used       = 0;
+   /* img->ces is already zeroed out */
+   vg_assert(img->source.fd >= 0);
+
+   /* Force the zeroth entry to be the first chunk of the file.
+      That's likely to be the first part that's requested anyway, and
+      loading it at this point forcing img->cent[0] to always be
+      non-empty, thereby saving us an is-it-empty check on the fast
+      path in get(). */
+   UInt entNo = alloc_CEnt(img, CACHE_ENTRY_SIZE, False/*!fromC*/);
+   vg_assert(entNo == 0);
+   set_CEnt(img, 0, 0);
+
+   return img;
+}
+
+
 
 /* Create an image from a file on a remote debuginfo server.  This is
    more complex.  There are lots of ways in which it can fail. */
@@ -1007,20 +1050,9 @@ DiOffT ML_(img_mark_compressed_part)(DiImage* img, DiOffT offset, SizeT szC,
    return ret;
 }
 
-void ML_(img_done)(DiImage* img)
+void ML_(img_free)(DiImage* img)
 {
    vg_assert(img != NULL);
-   if (img->source.is_local) {
-      /* Close the file; nothing else to do. */
-      vg_assert(img->source.session_id == 0);
-      VG_(close)(img->source.fd);
-   } else {
-      /* Close the socket.  The server can detect this and will scrub
-         the connection when it happens, so there's no need to tell it
-         explicitly by sending it a "CLOSE" message, or any such. */
-      vg_assert(img->source.session_id != 0);
-      VG_(close)(img->source.fd);
-   }
 
    /* Free up the cache entries, ultimately |img| itself. */
    UInt i;
@@ -1037,6 +1069,26 @@ void ML_(img_done)(DiImage* img)
    ML_(dinfo_free)(img);
 }
 
+void ML_(img_done)(DiImage* img)
+{
+   vg_assert(img != NULL);
+   if (img->source.is_local) {
+      /* Close the file; nothing else to do. */
+      vg_assert(img->source.session_id == 0);
+      VG_(close)(img->source.fd);
+   } else {
+      /* Close the socket.  The server can detect this and will scrub
+         the connection when it happens, so there's no need to tell it
+         explicitly by sending it a "CLOSE" message, or any such. */
+      vg_assert(img->source.session_id != 0);
+      VG_(close)(img->source.fd);
+   }
+
+   ML_(img_free)(img);
+}
+
+
+
 DiOffT ML_(img_size)(const DiImage* img)
 {
    vg_assert(img != NULL);
index f7413a90842cdd77c8263aaae9c15a30e373ce35..a49846f149a38090851948dffdd492a07b68e21e 100644 (file)
@@ -59,6 +59,8 @@ typedef  ULong  DiOffT;
    if it fails, for whatever reason. */
 DiImage* ML_(img_from_local_file)(const HChar* fullpath);
 
+DiImage* ML_(img_from_fd)(Int fd, const HChar* fullpath);
+
 /* Create an image by connecting to a Valgrind debuginfo server
    (auxprogs/valgrind-di-server.c).  |filename| contains the object
    name to ask for; it must be a plain filename, not absolute, not a
@@ -69,6 +71,9 @@ DiImage* ML_(img_from_local_file)(const HChar* fullpath);
 DiImage* ML_(img_from_di_server)(const HChar* filename,
                                  const HChar* serverAddr);
 
+/* Free memory allocated for image. */
+void ML_(img_free)(DiImage*);
+
 /* Destroy an existing image. */
 void ML_(img_done)(DiImage*);
 
index 2bee615ab54a8348c6c2c8d340c2cdbe6df79a69..57aa0cc3f365edcedf14472fc612a68110263942 100644 (file)
@@ -52,6 +52,8 @@ extern Bool ML_(is_elf_object_file)( const void* image, SizeT n_image,
 */
 extern Bool ML_(read_elf_debug_info) ( DebugInfo* di );
 
+extern Bool ML_(check_elf_and_get_rw_loads) ( Int fd, const HChar* filename, Int * rw_load_count );
+
 
 #endif /* ndef __PRIV_READELF_H */
 
index ae44ca34e539d79db6e8021862e9e236dad222bd..f44ab43ffed9f9097e586b42d7618ab4f2f13504 100644 (file)
@@ -585,7 +585,7 @@ struct _DebugInfoFSM
    HChar*  dbgname;   /* in mallocville (VG_AR_DINFO)               */
    XArray* maps;      /* XArray of DebugInfoMapping structs         */
    Bool  have_rx_map; /* did we see a r?x mapping yet for the file? */
-   Bool  have_rw_map; /* did we see a rw? mapping yet for the file? */
+   Int   rw_map_count; /* count of w? mappings seen (may be > 1 )   */
    Bool  have_ro_map; /* did we see a r-- mapping yet for the file? */
 };
 
index ea9c80415b83734f04ed498d5661a0280cd03854..b4edb4fe854237f053ae6946dd27a4304a2efc96 100644 (file)
@@ -1182,10 +1182,10 @@ void read_and_set_osrel(DiImage* img)
                     SizeT newlen = sizeof(osrel);
                     Int error = VG_(sysctl)(name, 4, NULL, NULL, &osrel, newlen);
                     if (error == -1) {
-                        VG_(message)(Vg_DebugMsg, "Warning: failed to set osrel for current process with value %d\n", osrel);
+                        VG_(message)(Vg_DebugMsg, "Warning: failed to set osrel for current process with value %u\n", osrel);
                     } else {
                         if (VG_(clo_verbosity) > 1) {
-                            VG_(message)(Vg_DebugMsg, "Set osrel for current process with value %d\n", osrel);
+                            VG_(message)(Vg_DebugMsg, "Set osrel for current process with value %u\n", osrel);
                         }
                     }
                 }
@@ -1924,7 +1924,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
    vg_assert(di);
    vg_assert(di->fsm.have_rx_map == True);
-   vg_assert(di->fsm.have_rw_map == True);
+   vg_assert(di->fsm.rw_map_count >= 1);
    vg_assert(di->have_dinfo == False);
    vg_assert(di->fsm.filename);
    vg_assert(!di->symtab);
@@ -2120,11 +2120,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                      item.svma_limit = a_phdr.p_vaddr + a_phdr.p_memsz;
                      item.bias       = map->avma - map->foff
                                        + a_phdr.p_offset - a_phdr.p_vaddr;
-#if (FREEBSD_VERS >= FREEBSD_12_2)
-                     if ((long long int)item.bias < 0LL) {
-                        item.bias = 0;
-                     }
-#endif
                      if (map->rw
                          && (a_phdr.p_flags & (PF_R | PF_W))
                             == (PF_R | PF_W)) {
@@ -2264,9 +2259,10 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
    }
    for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) {
       const DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i);
-      if (map->rw)
+      if (map->rw) {
          TRACE_SYMTAB("rw: at %#lx are mapped foffsets %lld .. %lld\n",
                       map->avma, (Long)map->foff, (Long)(map->foff + map->size - 1) );
+      }
    }
    TRACE_SYMTAB("rw: contains these svma regions:\n");
    for (i = 0; i < VG_(sizeXA)(svma_ranges); i++) {
@@ -2290,28 +2286,34 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       UInt   alyn = a_shdr.sh_addralign;
       Bool   nobits = a_shdr.sh_type == SHT_NOBITS;
       /* Look through our collection of info obtained from the PT_LOAD
-         headers, and make 'inrx' and 'inrw' point to the first entry
+         headers, and make 'inrx' and 'inrw1' point to the first entry
          in each that intersects 'avma'.  If in each case none is found,
          leave the relevant pointer at NULL. */
       RangeAndBias* inrx = NULL;
-      RangeAndBias* inrw = NULL;
+      RangeAndBias* inrw1 = NULL;
+      /* Depending on the link editro there may be two RW PT_LOAD headers
+       * If so this will point to the seond one */
+      RangeAndBias* inrw2 = NULL;
+      /* used to switch between inrw1 and inrw2 */
+      RangeAndBias* inrw;
+
       for (j = 0; j < VG_(sizeXA)(svma_ranges); j++) {
          RangeAndBias* rng = VG_(indexXA)(svma_ranges, j);
          if (svma >= rng->svma_base && svma < rng->svma_limit) {
             if (!inrx && rng->exec) {
                inrx = rng;
-            } else if (!inrw && !rng->exec) {
-               inrw = rng;
+            } else if (!inrw1 && !rng->exec) {
+               inrw1 = rng;
+            }  else if (!inrw2 && !rng->exec) {
+               inrw2 = rng;
             }
-            if (inrx && inrw)
-               break;
          }
       }
 
-      TRACE_SYMTAB(" [sec %2ld]  %s %s  al%4u  foff %6lld .. %6lld  "
+      TRACE_SYMTAB(" [sec %2ld]  %s %s %s  al%4u  foff %6lld .. %6lld  "
                    "  svma %p  name \"%s\"\n", 
-                   i, inrx ? "rx" : "  ", inrw ? "rw" : "  ", alyn,
-                   (Long) foff, (size == 0) ? (Long)foff : (Long)(foff+size-1),
+                   i, inrx ? "rx" : "  ", inrw1 ? "rw" : "  ", inrw2 ? "rw" : "  ",
+                   alyn, (Long) foff, (size == 0) ? (Long)foff : (Long)(foff+size-1),
                    (void *) svma, name);
 
       /* Check for sane-sized segments.  SHT_NOBITS sections have zero
@@ -2350,6 +2352,8 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       /* Find avma-s for: .text .data .sdata .rodata .bss .sbss .plt .got .opd
          and .eh_frame */
 
+      /* In inrw2 is non-NULL then it will be used for .data .got.plt .bss */
+
       /* Accept .text where mapped as rx (code), even if zero-sized */
       if (0 == VG_(strcmp)(name, ".text")) {
          if (inrx && !di->text_present) {
@@ -2380,6 +2384,13 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                          "%#lx .. %#lx\n", svma, svma + size - 1);
          } else
 #        endif /* SOLARIS_PT_SUNDWTRACE_THRP */
+
+        if (inrw2) {
+           inrw = inrw2;
+        } else {
+           inrw = inrw1;
+        }
+
          if (inrw && !di->data_present) {
             di->data_present = True;
             di->data_svma = svma;
@@ -2402,14 +2413,14 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .sdata where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".sdata")) {
-         if (inrw && !di->sdata_present) {
+         if (inrw1 && !di->sdata_present) {
             di->sdata_present = True;
             di->sdata_svma = svma;
-            di->sdata_avma = svma + inrw->bias;
+            di->sdata_avma = svma + inrw1->bias;
             di->sdata_size = size;
-            di->sdata_bias = inrw->bias;
+            di->sdata_bias = inrw1->bias;
             di->sdata_debug_svma = svma;
-            di->sdata_debug_bias = inrw->bias;
+            di->sdata_debug_bias = inrw1->bias;
             TRACE_SYMTAB("acquiring .sdata svma = %#lx .. %#lx\n",
                          di->sdata_svma,
                          di->sdata_svma + di->sdata_size - 1);
@@ -2434,10 +2445,10 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                di->rodata_avma += inrx->bias;
                di->rodata_bias = inrx->bias;
                di->rodata_debug_bias = inrx->bias;
-            } else if (inrw) {
-               di->rodata_avma += inrw->bias;
-               di->rodata_bias = inrw->bias;
-               di->rodata_debug_bias = inrw->bias;
+            } else if (inrw1) {
+               di->rodata_avma += inrw1->bias;
+               di->rodata_bias = inrw1->bias;
+               di->rodata_debug_bias = inrw1->bias;
             } else {
                BAD(".rodata");
             }
@@ -2456,15 +2467,15 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       }
 
       if (0 == VG_(strcmp)(name, ".dynbss")) {
-         if (inrw && !di->bss_present) {
+         if (inrw1 && !di->bss_present) {
             dynbss_present = True;
             di->bss_present = True;
             di->bss_svma = svma;
-            di->bss_avma = svma + inrw->bias;
+            di->bss_avma = svma + inrw1->bias;
             di->bss_size = size;
-            di->bss_bias = inrw->bias;
+            di->bss_bias = inrw1->bias;
             di->bss_debug_svma = svma;
-            di->bss_debug_bias = inrw->bias;
+            di->bss_debug_bias = inrw1->bias;
             TRACE_SYMTAB("acquiring .dynbss svma = %#lx .. %#lx\n",
                          di->bss_svma,
                          di->bss_svma + di->bss_size - 1);
@@ -2478,6 +2489,13 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .bss where mapped as rw (data), even if zero-sized */
       if (0 == VG_(strcmp)(name, ".bss")) {
+
+         if (inrw2) {
+            inrw = inrw2;
+         } else {
+            inrw = inrw1;
+         }
+
          if (inrw && dynbss_present) {
             vg_assert(di->bss_present);
             dynbss_present = False;
@@ -2544,15 +2562,15 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       }
 
       if (0 == VG_(strcmp)(name, ".sdynbss")) {
-         if (inrw && !di->sbss_present) {
+         if (inrw1 && !di->sbss_present) {
             sdynbss_present = True;
             di->sbss_present = True;
             di->sbss_svma = svma;
-            di->sbss_avma = svma + inrw->bias;
+            di->sbss_avma = svma + inrw1->bias;
             di->sbss_size = size;
-            di->sbss_bias = inrw->bias;
+            di->sbss_bias = inrw1->bias;
             di->sbss_debug_svma = svma;
-            di->sbss_debug_bias = inrw->bias;
+            di->sbss_debug_bias = inrw1->bias;
             TRACE_SYMTAB("acquiring .sdynbss svma = %#lx .. %#lx\n",
                          di->sbss_svma,
                          di->sbss_svma + di->sbss_size - 1);
@@ -2566,7 +2584,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .sbss where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".sbss")) {
-         if (inrw && sdynbss_present) {
+         if (inrw1 && sdynbss_present) {
             vg_assert(di->sbss_present);
             sdynbss_present = False;
             vg_assert(di->sbss_svma + di->sbss_size == svma);
@@ -2574,18 +2592,18 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
             TRACE_SYMTAB("acquiring .sbss svma = %#lx .. %#lx\n",
                          svma, svma + size - 1);
             TRACE_SYMTAB("acquiring .sbss avma = %#lx .. %#lx\n",
-                         svma + inrw->bias, svma + inrw->bias + size - 1);
+                         svma + inrw1->bias, svma + inrw1->bias + size - 1);
             TRACE_SYMTAB("acquiring .sbss bias = %#lx\n", (UWord)di->sbss_bias);
          } else
 
-         if (inrw && !di->sbss_present) {
+         if (inrw1 && !di->sbss_present) {
             di->sbss_present = True;
             di->sbss_svma = svma;
-            di->sbss_avma = svma + inrw->bias;
+            di->sbss_avma = svma + inrw1->bias;
             di->sbss_size = size;
-            di->sbss_bias = inrw->bias;
+            di->sbss_bias = inrw1->bias;
             di->sbss_debug_svma = svma;
-            di->sbss_debug_bias = inrw->bias;
+            di->sbss_debug_bias = inrw1->bias;
             TRACE_SYMTAB("acquiring .sbss svma = %#lx .. %#lx\n",
                          di->sbss_svma,
                          di->sbss_svma + di->sbss_size - 1);
@@ -2600,9 +2618,9 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .got where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".got")) {
-         if (inrw && !di->got_present) {
+         if (inrw1 && !di->got_present) {
             di->got_present = True;
-            di->got_avma = svma + inrw->bias;
+            di->got_avma = svma + inrw1->bias;
             di->got_size = size;
             TRACE_SYMTAB("acquiring .got avma = %#lx\n", di->got_avma);
          } else {
@@ -2612,6 +2630,13 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .got.plt where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".got.plt")) {
+
+         if (inrw2) {
+            inrw = inrw2;
+         } else {
+            inrw = inrw1;
+         }
+
          if (inrw && !di->gotplt_present) {
             di->gotplt_present = True;
             di->gotplt_avma = svma + inrw->bias;
@@ -2643,9 +2668,9 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 #     elif defined(VGP_ppc32_linux)
       /* Accept .plt where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".plt")) {
-         if (inrw && !di->plt_present) {
+         if (inrw1 && !di->plt_present) {
             di->plt_present = True;
-            di->plt_avma = svma + inrw->bias;
+            di->plt_avma = svma + inrw1->bias;
             di->plt_size = size;
             TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma);
          } else {
@@ -2655,13 +2680,13 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 #     elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux)
       /* Accept .plt where mapped as rw (data), or unmapped */
       if (0 == VG_(strcmp)(name, ".plt")) {
-         if (inrw && !di->plt_present) {
+         if (inrw1 && !di->plt_present) {
             di->plt_present = True;
-            di->plt_avma = svma + inrw->bias;
+            di->plt_avma = svma + inrw1->bias;
             di->plt_size = size;
             TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma);
          } else 
-         if ((!inrw) && (!inrx) && size > 0 && !di->plt_present) {
+         if ((!inrw1) && (!inrx) && size > 0 && !di->plt_present) {
             /* File contains a .plt, but it didn't get mapped.
                Presumably it is not required on this platform.  At
                least don't reject the situation as invalid. */
@@ -2678,9 +2703,9 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
       /* Accept .opd where mapped as rw (data) */
       if (0 == VG_(strcmp)(name, ".opd")) {
-         if (inrw && !di->opd_present) {
+         if (inrw1 && !di->opd_present) {
             di->opd_present = True;
-            di->opd_avma = svma + inrw->bias;
+            di->opd_avma = svma + inrw1->bias;
             di->opd_size = size;
             TRACE_SYMTAB("acquiring .opd avma = %#lx\n", di->opd_avma);
          } else {
@@ -2700,8 +2725,8 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                          di->ehframe_avma[di->n_ehframe]);
             di->n_ehframe++;
          } else
-         if (inrw && di->n_ehframe < N_EHFRAME_SECTS) {
-            di->ehframe_avma[di->n_ehframe] = svma + inrw->bias;
+         if (inrw1 && di->n_ehframe < N_EHFRAME_SECTS) {
+            di->ehframe_avma[di->n_ehframe] = svma + inrw1->bias;
             di->ehframe_size[di->n_ehframe] = size;
             TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n",
                          di->ehframe_avma[di->n_ehframe]);
@@ -3606,6 +3631,94 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
    /* NOTREACHED */
 }
 
+Bool ML_(check_elf_and_get_rw_loads) ( Int fd, const HChar* filename, Int * rw_load_count )
+{
+   Bool     res, ok;
+   UWord    i;
+   DiImage* mimg = NULL;
+   DiOffT   ehdr_mioff = 0;
+   DiOffT   phdr_mioff    = 0;
+   UWord    phdr_mnent    = 0U;
+   UWord    phdr_ment_szB = 0U;
+
+   res = False;
+
+   mimg = ML_(img_from_fd)(fd, filename);
+   if (mimg == NULL) {
+      VG_(message)(Vg_UserMsg, "warning: connection to image %s failed\n",
+                               filename );
+      VG_(message)(Vg_UserMsg, "         cannot read program headers \n" );
+      return False;
+   }
+
+   ok = is_elf_object_file_by_DiImage(mimg, False);
+   if (!ok) {
+      goto out;
+   }
+
+   ElfXX_Ehdr ehdr_m;
+   Elf64_Word flag_x;
+#if defined(VGA_amd64) || defined(VGA_ppc64be) || defined(VGA_ppc64le) || defined(VGA_arm) || defined(VGA_arm64)
+   flag_x = PF_X;
+#else
+   flag_x = 0;
+#endif
+   vg_assert(ehdr_mioff == 0); // ensured by its initialisation
+   ok = ML_(img_valid)(mimg, ehdr_mioff, sizeof(ehdr_m));
+   vg_assert(ok); // ML_(is_elf_object_file) should ensure this
+   ML_(img_get)(&ehdr_m, mimg, ehdr_mioff, sizeof(ehdr_m));
+
+   phdr_mioff    = ehdr_mioff + ehdr_m.e_phoff;
+   phdr_mnent    = ehdr_m.e_phnum;
+   phdr_ment_szB = ehdr_m.e_phentsize;
+
+   for (i = 0U; i < phdr_mnent; i++) {
+      ElfXX_Phdr a_phdr;
+      ML_(img_get)(&a_phdr, mimg,
+                   INDEX_BIS(phdr_mioff, i, phdr_ment_szB),
+                   sizeof(a_phdr));
+
+      if (a_phdr.p_type == PT_LOAD) {
+         if (a_phdr.p_memsz > 0) {
+            if (((a_phdr.p_flags & (PF_R | PF_W)) == (PF_R | PF_W)) &&
+                ((a_phdr.p_flags & flag_x) == 0)) {
+               ++*rw_load_count;
+            }
+
+            /*
+             * Hold your horses
+             * Just because The ELF file contains 2 RW PT_LOAD segments it
+             * doesn't mean that Valgrind will also make 2 calls to
+             * VG_(di_notify_mmap). If the stars are all aligned
+             * (which usually means that the ELF file is the client
+             * executable with the segment offset for the
+             * second PT_LOAD falls exactly on 0x1000) then the NSegements
+             * will get merged and VG_(di_notify_mmap) only gets called once. */
+            if (*rw_load_count == 2 &&
+                ehdr_m.e_type == ET_EXEC &&
+                a_phdr.p_offset == VG_PGROUNDDN(a_phdr.p_offset) )
+            {
+               *rw_load_count = 1;
+            }
+         }
+      }
+   } /* for (i = 0; i < phdr_Mnent; i++) ... */
+
+   res = True;
+
+  out:
+   {
+      /* Last, but not least, detach from the image(s). */
+      if (mimg) ML_(img_free)(mimg);
+
+      return res;
+   } /* out: */
+
+   /* NOTREACHED */
+}
+
+
+
 #endif // defined(VGO_linux) || defined(VGO_solaris) || defined(VGO_freebsd)
 
 /*--------------------------------------------------------------------*/
index a53cf48c445558cbf12906bdb56c7153950d9de1..f3a3817d8965191bf5b15fdd309ddf59db63d92a 100644 (file)
@@ -2363,7 +2363,7 @@ Bool ML_(read_pdb_debug_info)(
          map.rx   = False;
          map.rw   = True;
          VG_(addToXA)(di->fsm.maps, &map);
-         di->fsm.have_rw_map = True;
+         di->fsm.rw_map_count = 1;
 
          di->data_present = True;
          if (di->data_avma == 0) {
@@ -2385,7 +2385,7 @@ Bool ML_(read_pdb_debug_info)(
       }
    }
 
-   if (di->fsm.have_rx_map && di->fsm.have_rw_map && !di->have_dinfo) {
+   if (di->fsm.have_rx_map && di->fsm.rw_map_count && !di->have_dinfo) {
       vg_assert(di->fsm.filename);
       TRACE_SYMTAB("\n");
       TRACE_SYMTAB("------ start PE OBJECT with PDB INFO "
index 9ba74076c11ef1a2c63668f2324c4424ae6e66a2..6eb932747f9800297997dce0c33a26f6cfa7a4e4 100644 (file)
@@ -616,7 +616,7 @@ void ML_(addLineInfo) ( struct _DebugInfo* di,
    /* Rule out ones which are completely outside the r-x mapped area.
       See "Comment_Regarding_Text_Range_Checks" elsewhere in this file
       for background and rationale. */
-   vg_assert(di->fsm.have_rx_map && di->fsm.have_rw_map);
+   vg_assert(di->fsm.have_rx_map && di->fsm.rw_map_count);
    if (ML_(find_rx_mapping)(di, this, this + size - 1) == NULL) {
        if (0)
           VG_(message)(Vg_DebugMsg, 
@@ -798,7 +798,7 @@ void ML_(addDiCfSI) ( struct _DebugInfo* di,
                    "warning: DiCfSI %#lx .. %#lx is huge; length = %u (%s)\n",
                    base, base + len - 1, len, di->soname);
 
-   vg_assert(di->fsm.have_rx_map && di->fsm.have_rw_map);
+   vg_assert(di->fsm.have_rx_map && di->fsm.rw_map_count);
    /* Find mapping where at least one end of the CFSI falls into. */
    map  = ML_(find_rx_mapping)(di, base, base);
    map2 = ML_(find_rx_mapping)(di, base + len - 1,
@@ -1304,7 +1304,7 @@ void ML_(addVar)( struct _DebugInfo* di,
    /* This is assured us by top level steering logic in debuginfo.c,
       and it is re-checked at the start of
       ML_(read_elf_debug_info). */
-   vg_assert(di->fsm.have_rx_map && di->fsm.have_rw_map);
+   vg_assert(di->fsm.have_rx_map && di->fsm.rw_map_count);
    if (level > 0 && ML_(find_rx_mapping)(di, aMin, aMax) == NULL) {
       if (VG_(clo_verbosity) > 1) {
          VG_(message)(Vg_DebugMsg, 
index 474ef5c4c958fe8b1915398f7dc88d32273b48f6..c8fa5b5cbf129bea72f5f1ef8b0ebd44003b50c3 100644 (file)
@@ -342,6 +342,7 @@ EXTRA_DIST = \
                varinfo4.stderr.exp-freebsd \
        varinfo5.vgtest varinfo5.stdout.exp varinfo5.stderr.exp \
                varinfo5.stderr.exp-ppc64 \
+               varinfo5.stderr.exp-freebsd \
        varinfo6.vgtest varinfo6.stdout.exp varinfo6.stderr.exp \
                varinfo6.stderr.exp-ppc64 \
        varinforestrict.vgtest varinforestrict.stderr.exp \
diff --git a/memcheck/tests/varinfo5.stderr.exp-freebsd b/memcheck/tests/varinfo5.stderr.exp-freebsd
new file mode 100644 (file)
index 0000000..df30c00
--- /dev/null
@@ -0,0 +1,191 @@
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:52)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is 1 bytes inside a block of size 3 alloc'd
+   at 0x........: malloc (vg_replace_malloc.c:...)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:50)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:55)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global var "global_u1"
+ declared at varinfo5so.c:38
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:56)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global var "global_i1"
+ declared at varinfo5so.c:40
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:57)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global_u2[3],
+ a global variable declared at varinfo5so.c:42
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:58)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside global_i2[7],
+ a global variable declared at varinfo5so.c:44
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: varinfo1_main (tests/varinfo5so.c:59)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:154)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside local var "local"
+ declared at varinfo5so.c:49, in frame #X of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo2 (tests/varinfo5so.c:71)
+   by 0x........: varinfo2_main (tests/varinfo5so.c:81)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:155)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside var[7],
+ declared at varinfo5so.c:69, in frame #X of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo2 (tests/varinfo5so.c:73)
+   by 0x........: varinfo2_main (tests/varinfo5so.c:81)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:155)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 2 bytes inside var.bar,
+ declared at varinfo5so.c:72, in frame #X of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo2 (tests/varinfo5so.c:76)
+   by 0x........: varinfo2_main (tests/varinfo5so.c:81)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:155)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is on thread 1's stack
+ in frame #X, created by foo2 (varinfo5so.c:66)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo3 (tests/varinfo5so.c:106)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside static_global_def[1],
+ a global variable declared at varinfo5so.c:87
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo3 (tests/varinfo5so.c:107)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_global_def[2],
+ a global variable declared at varinfo5so.c:88
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo3 (tests/varinfo5so.c:108)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside static_global_undef[3],
+ a global variable declared at varinfo5so.c:89
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: foo3 (tests/varinfo5so.c:109)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside nonstatic_global_undef[4],
+ a global variable declared at varinfo5so.c:90
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: bar3 (tests/varinfo5so.c:94)
+   by 0x........: foo3 (tests/varinfo5so.c:110)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is 5 bytes inside data symbol "foo3.static_local_def"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: bar3 (tests/varinfo5so.c:95)
+   by 0x........: foo3 (tests/varinfo5so.c:110)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is on thread 1's stack
+ in frame #X, created by foo3 (varinfo5so.c:101)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: bar3 (tests/varinfo5so.c:96)
+   by 0x........: foo3 (tests/varinfo5so.c:110)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is 7 bytes inside data symbol "foo3.static_local_undef"
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: bar3 (tests/varinfo5so.c:97)
+   by 0x........: foo3 (tests/varinfo5so.c:110)
+   by 0x........: varinfo3_main (tests/varinfo5so.c:118)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:156)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is on thread 1's stack
+ in frame #X, created by foo3 (varinfo5so.c:101)
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: blah4 (tests/varinfo5so.c:137)
+   by 0x........: varinfo4_main (tests/varinfo5so.c:146)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:157)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c1,
+ declared at varinfo5so.c:135, in frame #X of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: blah4 (tests/varinfo5so.c:138)
+   by 0x........: varinfo4_main (tests/varinfo5so.c:146)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:157)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 0 bytes inside a[5].bong,
+ declared at varinfo5so.c:135, in frame #X of thread 1
+
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: blah4 (tests/varinfo5so.c:139)
+   by 0x........: varinfo4_main (tests/varinfo5so.c:146)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:157)
+   by 0x........: main (tests/varinfo5.c:5)
+ Location 0x........ is 1 byte inside a[3].xyzzy[21].c2[2],
+ declared at varinfo5so.c:135, in frame #X of thread 1
+
+answer is 0
+Uninitialised byte(s) found during client check request
+   at 0x........: croak (tests/varinfo5so.c:29)
+   by 0x........: fun_c (tests/varinfo5so.c:164)
+   by 0x........: fun_b (./varinfo5so.c:168)
+   by 0x........: fun_a (./varinfo5so.c:172)
+   by 0x........: inlinetest (./varinfo5so.c:178)
+   by 0x........: varinfo5_main (tests/varinfo5so.c:158)
+   by 0x........: main (tests/varinfo5.c:5)
+ Address 0x........ is on thread 1's stack
+ in frame #X, created by inlinetest (varinfo5so.c:176)
+