Update Valgrind and TSan.

Rebased local changes on:
http://valgrind-variant.googlecode.com/svn/trunk@125
http://data-race-test.googlecode.com/svn/trunk@3717

Change-Id: Ic8636da312685c604941d6d2c937c3e8eadc1e53
diff --git a/main/coregrind/m_debuginfo/readelf.c b/main/coregrind/m_debuginfo/readelf.c
index 9387dc7..60ca91b 100644
--- a/main/coregrind/m_debuginfo/readelf.c
+++ b/main/coregrind/m_debuginfo/readelf.c
@@ -8,7 +8,7 @@
    This file is part of Valgrind, a dynamic binary instrumentation
    framework.
 
-   Copyright (C) 2000-2010 Julian Seward 
+   Copyright (C) 2000-2011 Julian Seward 
       jseward@acm.org
 
    This program is free software; you can redistribute it and/or
@@ -40,6 +40,7 @@
 #include "pub_core_libcfile.h"
 #include "pub_core_aspacemgr.h"    /* for mmaping debuginfo files */
 #include "pub_core_machine.h"      /* VG_ELF_CLASS */
+#include "pub_core_mallocfree.h"
 #include "pub_core_options.h"
 #include "pub_core_oset.h"
 #include "pub_core_tooliface.h"    /* VG_(needs) */
@@ -360,14 +361,35 @@
    if (!plausible)
       return False;
 
-   /* Ignore if nameless, or zero-sized. */
-   if (sym->st_name == (ElfXX_Word)0
+   /* Ignore if nameless. */
+   if (sym_name == (ElfXX_Word)0
        || /* VG_(strlen)(sym_name) == 0 */
           /* equivalent but cheaper ... */
-          sym_name[0] == 0
-       || sym->st_size == 0) {
+          sym_name[0] == 0) {
+      TRACE_SYMTAB("    ignore -- nameless: %s\n", sym_name);
+      return False;
+   }
+
+   /* Ignore if zero-sized.  Except on Android:
+
+      On Android 2.3.5, some of the symbols that Memcheck needs to
+      intercept (for noise reduction purposes) have zero size, due to
+      lack of .size directives in handwritten assembly sources.  So we
+      can't reject them out of hand -- instead give them a bogusly
+      large size and let canonicaliseSymtab trim them so they don't
+      overlap any following symbols.  At least the following symbols
+      are known to be affected:
+
+      in /system/lib/libc.so: strlen strcmp strcpy memcmp memcpy
+      in /system/bin/linker:  __dl_strcmp __dl_strlen
+   */
+   if (sym->st_size == 0) {
+#     if defined(VGPV_arm_linux_android)
+      *sym_size_out = 1024;
+#     else
       TRACE_SYMTAB("    ignore -- size=0: %s\n", sym_name);
       return False;
+#     endif
    }
 
    /* This seems to significantly reduce the number of junk
@@ -532,9 +554,10 @@
          "Comment_Regarding_Text_Range_Checks" in storage.c for
          background. */
       Bool in_rx;
-      vg_assert(di->have_rx_map);
-      in_rx = (!(*sym_avma_out + *sym_size_out <= di->rx_map_avma
-                 || *sym_avma_out >= di->rx_map_avma + di->rx_map_size));
+      vg_assert(di->fsm.have_rx_map);
+      in_rx = (!(*sym_avma_out + *sym_size_out <= di->fsm.rx_map_avma
+                 || *sym_avma_out >= di->fsm.rx_map_avma
+                                     + di->fsm.rx_map_size));
       if (in_text)
          vg_assert(in_rx);
       if (!in_rx) {
@@ -548,7 +571,8 @@
    } else {
      if (!(in_data || in_sdata || in_rodata || in_bss || in_sbss)) {
          TRACE_SYMTAB(
-            "ignore -- %#lx .. %#lx outside .data / .sdata / .rodata / .bss / .sbss svma ranges\n",
+            "ignore -- %#lx .. %#lx outside .data / .sdata / .rodata "
+            "/ .bss / .sbss svma ranges\n",
             *sym_avma_out, *sym_avma_out + *sym_size_out);
          return False;
       }
@@ -588,7 +612,7 @@
    Int        sym_size;
    Addr       sym_tocptr;
    Bool       from_opd, is_text, is_ifunc;
-   DiSym      risym;
+   DiSym      disym;
    ElfXX_Sym *sym;
 
    if (strtab_img == NULL || symtab_img == NULL) {
@@ -621,24 +645,25 @@
                               &sym_tocptr,
                               &from_opd, &is_text, &is_ifunc)) {
 
-         risym.addr    = sym_avma_really;
-         risym.size    = sym_size;
-         risym.name    = ML_(addStr) ( di, sym_name_really, -1 );
-         risym.tocptr  = sym_tocptr;
-         risym.isText  = is_text;
-         risym.isIFunc = is_ifunc;
-         vg_assert(risym.name != NULL);
-         vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */
-         ML_(addSym) ( di, &risym );
+         disym.addr      = sym_avma_really;
+         disym.tocptr    = sym_tocptr;
+         disym.pri_name  = ML_(addStr) ( di, sym_name_really, -1 );
+         disym.sec_names = NULL;
+         disym.size      = sym_size;
+         disym.isText    = is_text;
+         disym.isIFunc   = is_ifunc;
+         vg_assert(disym.pri_name);
+         vg_assert(disym.tocptr == 0); /* has no role except on ppc64-linux */
+         ML_(addSym) ( di, &disym );
 
          if (di->trace_symtab) {
             VG_(printf)("    rec(%c) [%4ld]:          "
                         "  val %#010lx, sz %4d  %s\n",
                         is_text ? 't' : 'd',
                         i,
-                        risym.addr,
-                        (Int)risym.size,
-                        (HChar*)risym.name
+                        disym.addr,
+                        (Int)disym.size,
+                        (HChar*)disym.pri_name
             );
          }
 
@@ -691,7 +716,7 @@
    Int         sym_size;
    Addr        sym_tocptr;
    Bool        from_opd, modify_size, modify_tocptr, is_text, is_ifunc;
-   DiSym       risym;
+   DiSym       disym;
    ElfXX_Sym  *sym;
    OSet       *oset;
    TempSymKey  key;
@@ -828,24 +853,25 @@
    VG_(OSetGen_ResetIter)( oset );
 
    while ( (elem = VG_(OSetGen_Next)(oset)) ) {
-      risym.addr    = elem->key.addr;
-      risym.size    = elem->size;
-      risym.name    = ML_(addStr) ( di, elem->key.name, -1 );
-      risym.tocptr  = elem->tocptr;
-      risym.isText  = elem->is_text;
-      risym.isIFunc = elem->is_ifunc;
-      vg_assert(risym.name != NULL);
+      disym.addr      = elem->key.addr;
+      disym.tocptr    = elem->tocptr;
+      disym.pri_name  = ML_(addStr) ( di, elem->key.name, -1 );
+      disym.sec_names = NULL;
+      disym.size      = elem->size;
+      disym.isText    = elem->is_text;
+      disym.isIFunc   = elem->is_ifunc;
+      vg_assert(disym.pri_name != NULL);
 
-      ML_(addSym) ( di, &risym );
+      ML_(addSym) ( di, &disym );
       if (di->trace_symtab) {
          VG_(printf)("    rec(%c) [%4ld]:          "
                      "   val %#010lx, toc %#010lx, sz %4d  %s\n",
-                     risym.isText ? 't' : 'd',
+                     disym.isText ? 't' : 'd',
                      i,
-                     risym.addr,
-                     risym.tocptr,
-                     (Int)   risym.size,
-                     (HChar*)risym.name
+                     disym.addr,
+                     disym.tocptr,
+                     (Int)   disym.size,
+                     (HChar*)disym.pri_name
                );
       }
       i++;
@@ -865,6 +891,7 @@
 Char *find_buildid(Addr image, UWord n_image)
 {
    Char* buildid = NULL;
+   __attribute__((unused)) /* on Android, at least */
    ElfXX_Ehdr* ehdr = (ElfXX_Ehdr*)image;
 
 #ifdef NT_GNU_BUILD_ID
@@ -873,7 +900,8 @@
       Word i;
 
       for (i = 0; i < ehdr->e_phnum; i++) {
-         ElfXX_Phdr* phdr = (ElfXX_Phdr*)(image + ehdr->e_phoff + i * ehdr->e_phentsize);
+         ElfXX_Phdr* phdr
+            = (ElfXX_Phdr*)(image + ehdr->e_phoff + i * ehdr->e_phentsize);
 
          if (phdr->p_type == PT_NOTE) {
             ElfXX_Off offset =  phdr->p_offset;
@@ -886,10 +914,12 @@
 
                if (VG_(strcmp)(name, ELF_NOTE_GNU) == 0 &&
                    note->n_type == NT_GNU_BUILD_ID) {
-                  buildid = ML_(dinfo_zalloc)("di.fbi.1", note->n_descsz * 2 + 1);
+                  buildid = ML_(dinfo_zalloc)("di.fbi.1",
+                                              note->n_descsz * 2 + 1);
                   
                   for (j = 0; j < note->n_descsz; j++) {
-                     VG_(sprintf)(buildid + VG_(strlen)(buildid), "%02x", desc[j]);
+                     VG_(sprintf)(buildid + VG_(strlen)(buildid), 
+                                  "%02x", desc[j]);
                   }
                }
 
@@ -1015,7 +1045,8 @@
          vg_assert(!sr_isError(res));
          if (VG_(clo_verbosity) > 1)
             VG_(message)(Vg_DebugMsg, 
-               "  .. build-id mismatch (found %s wanted %s)\n", debug_buildid, buildid);
+               "  .. build-id mismatch (found %s wanted %s)\n", 
+               debug_buildid, buildid);
          ML_(dinfo_free)(debug_buildid);
          return 0;
       }
@@ -1041,17 +1072,101 @@
    return sr_Res(sres);
 }
 
-/*
- * Try to find a separate debug file for a given object file.
- */
+
+/* Try to find and map in a debuginfo file by some totally ad-hoc
+   scheme.  If successful, set *dimage and *n_dimage to point to the
+   image, and return True, else return False.  A temporary hack for
+   Android; does nothing on any other platform. */
 static
-Addr find_debug_file( struct _DebugInfo* di,
+Bool find_ad_hoc_debug_image( struct _DebugInfo* di, 
+                              Char* filename,
+                              /*OUT*/Addr* dimage,
+                              /*OUT*/SizeT* n_dimage )
+{
+   vg_assert(*dimage == 0 && *n_dimage == 0);
+
+#  if !defined(VGPV_arm_linux_android)
+   return False; /* we don't know narfink */
+
+#  else /* android specific hacks; look away now. */
+
+   /* The deal is: if we're looking for for a debuginfo file for some
+      object /system/blah (where blah can be any path), see if we can
+      find the file /sdcard/symbols/system/blah.  So for example it
+      produces the following mappings, both of which are important for
+      Memcheck:
+
+      /system/bin/linker  --> /sdcard/symbols/system/bin/linker
+      /system/lib/libc.so --> /sdcard/symbols/system/lib/libc.so
+
+      These /symbols files come from the AOSP build tree for your
+      device, for example out/target/product/crespo/symbols/system
+      (for a Nexus S), so one simple thing you can do is take the tree
+      rooted at out/target/product/crespo/symbols/system on the host
+      and park it at /sdcard/symbols/system on the device.  Then,
+      assuming it matches what's actually running on the device,
+      you'll have full debuginfo for all the libraries on the device.
+  
+      But beware: there is no checking that the debuginfo file, if
+      found, matches the main file in any way.
+   */
+   if (0 != VG_(strncmp)(filename, "/system/", 8))
+      return False;
+
+   HChar* nm = ML_(dinfo_zalloc)("di.fahdi.1", 
+                                 50 + VG_(strlen)(filename));
+   VG_(sprintf)(nm, "/sdcard/symbols%s", filename);
+
+   SysRes fd = VG_(open)(nm, VKI_O_RDONLY, 0);
+   if (sr_isError(fd)) goto fail;
+
+   struct vg_stat stat_buf;
+   if (VG_(fstat)(sr_Res(fd), &stat_buf) != 0) {
+      VG_(close)(sr_Res(fd));
+      goto fail;
+   }
+
+   *n_dimage = stat_buf.size;
+
+   SysRes sres = VG_(am_mmap_file_float_valgrind)
+                    ( *n_dimage, VKI_PROT_READ, sr_Res(fd), 0 );
+
+   VG_(close)(sr_Res(fd));
+   if (sr_isError(sres))
+     goto fail;
+
+   *dimage = sr_Res(sres);
+
+   if (VG_(clo_verbosity) > 1)
+      VG_(dmsg)("  Using debuginfo from %s\n", nm);
+
+   ML_(dinfo_free)(nm);
+   return True;
+
+  fail:
+   if (nm) ML_(dinfo_free)(nm);
+   return False;
+
+#  endif
+}
+
+
+/* Try to find a separate debug file for a given object file.  If
+   found, it will be mapped in and the address and size returned in
+   *dimage and *n_dimage.  If not, *dimage and *n_dimage will be
+   unchanged.  The caller should set them to zero before the call. */
+static
+void find_debug_file( struct _DebugInfo* di,
                       Char* objpath, Char* buildid,
                       Char* debugname, UInt crc,
-                      /*OUT*/UWord* size )
+                      /*OUT*/Addr*  dimage,
+                      /*OUT*/SizeT* n_dimage )
 {
-   Char *debugpath = NULL;
-   Addr addr = 0;
+   Char* debugpath = NULL;
+   Addr  addr = 0;
+   UWord size = 0;
+
+   vg_assert(*dimage == 0 && *n_dimage == 0);
 
    if (buildid != NULL) {
       debugpath = ML_(dinfo_zalloc)(
@@ -1061,7 +1176,7 @@
       VG_(sprintf)(debugpath, "/usr/lib/debug/.build-id/%c%c/%s.debug",
                    buildid[0], buildid[1], buildid + 2);
 
-      if ((addr = open_debug_file(debugpath, buildid, 0, size)) == 0) {
+      if ((addr = open_debug_file(debugpath, buildid, 0, &size)) == 0) {
          ML_(dinfo_free)(debugpath);
          debugpath = NULL;
       }
@@ -1080,30 +1195,31 @@
 
       VG_(sprintf)(debugpath, "%s/%s", objdir, debugname);
 
-      if ((addr = open_debug_file(debugpath, NULL, crc, size)) == 0) {
+      if ((addr = open_debug_file(debugpath, NULL, crc, &size)) == 0) {
          VG_(sprintf)(debugpath, "%s/.debug/%s", objdir, debugname);
-         if ((addr = open_debug_file(debugpath, NULL, crc, size)) == 0) {
+         if ((addr = open_debug_file(debugpath, NULL, crc, &size)) == 0) {
             VG_(sprintf)(debugpath, "/usr/lib/debug%s/%s", objdir, debugname);
-            if ((addr = open_debug_file(debugpath, NULL, crc, size)) == 0) {
+            if ((addr = open_debug_file(debugpath, NULL, crc, &size)) == 0) {
 #ifdef ANDROID
-	      VG_(sprintf)(debugpath, "/data/local/symbols%s/%s", objdir, debugname);
-	      addr = open_debug_file(debugpath, NULL, crc, size);
+               VG_(sprintf)(debugpath, "/data/local/symbols%s/%s", objdir, debugname);
+               addr = open_debug_file(debugpath, NULL, crc, &size);
 #endif
-	    }
+           }
+
          }
       }
 
       ML_(dinfo_free)(objdir);
    }
 
-   if (addr) {
+   if (addr > 0 && size > 0) {
       TRACE_SYMTAB("\n");
       TRACE_SYMTAB("------ Found a debuginfo file: %s\n", debugpath);
+      *dimage = addr;
+      *n_dimage = size;
    }
 
    ML_(dinfo_free)(debugpath);
-
-   return addr;
 }
 
 
@@ -1149,14 +1265,29 @@
    return 0;
 }
 
+
 /* The central function for reading ELF debug info.  For the
    object/exe specified by the DebugInfo, find ELF sections, then read
    the symbols, line number info, file name info, CFA (stack-unwind
    info) and anything else we want, into the tables within the
    supplied DebugInfo.
 */
+
+/* Temporarily holds information copied out of PT_LOAD entries
+   in ML_(read_elf_debug_info. */
+typedef
+   struct { Addr svma_base; Addr svma_limit; PtrdiffT bias; }
+   RangeAndBias;
+
 Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 {
+   /* This function is long and complex.  That, and the presence of
+      nested scopes, means it's not always easy to see which parts are
+      in loops/conditionals and which aren't.  To make it easier to
+      follow, points executed exactly once -- that is, those which are
+      the top level of the function -- are marked TOPLEVEL.
+   */
+   /* TOPLEVEL */
    Bool          res, ok;
    SysRes        fd, sres;
    Word          i;
@@ -1187,25 +1318,31 @@
    UWord       shdr_ent_szB    = 0;
    UChar*      shdr_strtab_img = NULL;
 
-   /* SVMAs covered by rx and rw segments and corresponding bias. */
-   Addr     rx_svma_base = 0;
-   Addr     rx_svma_limit = 0;
-   PtrdiffT rx_bias = 0;
-   Addr     rw_svma_base = 0;
-   Addr     rw_svma_limit = 0;
-   PtrdiffT rw_bias = 0;
+   /* SVMAs covered by rx and rw segments and corresponding biases.
+      We keep separate lists of rx and rw areas.  Each can have up to
+      N_RX_RW_AREAS entries.  Normally each object would provide just
+      one rx and one rw area, but Mike Hommey's elfhack creates
+      objects with two rx PT_LOAD entries, hence the generality. */
+   const Int N_RX_RW_AREAS = 2;
+
+   RangeAndBias rx[N_RX_RW_AREAS];
+   RangeAndBias rw[N_RX_RW_AREAS];
+   Word n_rx = 0; /* 0 .. N_RX_RW_AREAS */
+   Word n_rw = 0; /* 0 .. N_RX_RW_AREAS */
+   /* Pointless paranoia: */
+   VG_(memset)( rx, 0, sizeof(rx) );
+   VG_(memset)( rw, 0, sizeof(rw) );
 
    /* Build ID */
    Char* buildid = NULL;
 
    vg_assert(di);
-   vg_assert(di->have_rx_map == True);
-   vg_assert(di->have_rw_map == True);
-   vg_assert(di->rx_map_size > 0);
-   vg_assert(di->rw_map_size > 0);
+   vg_assert(di->fsm.have_rx_map == True);
+   vg_assert(di->fsm.have_rw_map == True);
+   vg_assert(di->fsm.rx_map_size > 0);
+   vg_assert(di->fsm.rw_map_size > 0);
    vg_assert(di->have_dinfo == False);
-   vg_assert(di->filename);
-   vg_assert(!di->memname);
+   vg_assert(di->fsm.filename);
    vg_assert(!di->symtab);
    vg_assert(!di->loctab);
    vg_assert(!di->cfsi);
@@ -1216,8 +1353,8 @@
    /* If these don't hold true, it means that m_syswrap/m_aspacemgr
       managed to do a mapping where the start isn't page aligned.
       Which sounds pretty bogus to me. */
-   vg_assert(VG_IS_PAGE_ALIGNED(di->rx_map_avma));
-   vg_assert(VG_IS_PAGE_ALIGNED(di->rw_map_avma));
+   vg_assert(VG_IS_PAGE_ALIGNED(di->fsm.rx_map_avma));
+   vg_assert(VG_IS_PAGE_ALIGNED(di->fsm.rw_map_avma));
 
    /* ----------------------------------------------------------
       At this point, there is very little information in the
@@ -1227,8 +1364,8 @@
       di->*rw_map* fields.  First we examine the file's ELF Program
       Header, and, by comparing that against the di->*r{w,x}_map*
       info, try to figure out the AVMAs for the sections we care
-      about, that should have been mapped: text, data, sdata, bss got,
-      plt, and toc.
+      about, that should have been mapped: text, data, sdata, bss,
+      got, plt, and toc.
       ---------------------------------------------------------- */
 
    res = False;
@@ -1236,13 +1373,13 @@
    oimage = (Addr)NULL;
    if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
       VG_(message)(Vg_DebugMsg, "Reading syms from %s (%#lx)\n",
-                                di->filename, di->rx_map_avma );
+                                di->fsm.filename, di->fsm.rx_map_avma );
 
    /* mmap the object image aboard, so that we can read symbols and
       line number info out of it.  It will be munmapped immediately
       thereafter; it is only aboard transiently. */
 
-   fd = VG_(open)(di->filename, VKI_O_RDONLY, 0);
+   fd = VG_(open)(di->fsm.filename, VKI_O_RDONLY, 0);
    if (sr_isError(fd)) {
       ML_(symerr)(di, True, "Can't open .so/.exe to read symbols?!");
       return False;
@@ -1263,7 +1400,8 @@
    VG_(close)(sr_Res(fd));
 
    if (sr_isError(sres)) {
-      VG_(message)(Vg_UserMsg, "warning: mmap failed on %s\n", di->filename );
+      VG_(message)(Vg_UserMsg, "warning: mmap failed on %s\n",
+                               di->fsm.filename );
       VG_(message)(Vg_UserMsg, "         no symbols or debug info loaded\n" );
       return False;
    }
@@ -1304,12 +1442,16 @@
    shdr_ent_szB = ehdr_img->e_shentsize;
 
    TRACE_SYMTAB("------ Basic facts about the object ------\n");
-   TRACE_SYMTAB("object: img %p n_oimage %ld\n",
+   TRACE_SYMTAB("object:  img %p n_oimage %ld\n",
                (void*)oimage, n_oimage);
-   TRACE_SYMTAB("phdr:   img %p nent %ld ent_szB %ld\n",
+   TRACE_SYMTAB("phdr:    img %p nent %ld ent_szB %ld\n",
                phdr_img, phdr_nent, phdr_ent_szB);
-   TRACE_SYMTAB("shdr:   img %p nent %ld ent_szB %ld\n",
+   TRACE_SYMTAB("shdr:    img %p nent %ld ent_szB %ld\n",
                shdr_img, shdr_nent, shdr_ent_szB);
+   TRACE_SYMTAB("rx_map:  avma %#lx  size %lu  foff %lu\n",
+                di->fsm.rx_map_avma, di->fsm.rx_map_size, di->fsm.rx_map_foff);
+   TRACE_SYMTAB("rw_map:  avma %#lx  size %lu  foff %lu\n",
+                di->fsm.rw_map_avma, di->fsm.rw_map_size, di->fsm.rw_map_foff);
 
    if (phdr_nent == 0
        || !contained_within(
@@ -1341,48 +1483,82 @@
       goto out;
    }
 
-   TRACE_SYMTAB("shdr:   string table at %p\n", shdr_strtab_img );
+   TRACE_SYMTAB("shdr:    string table at %p\n", shdr_strtab_img );
 
-   /* Do another amazingly tedious thing: find out the .soname for
-      this object.  Apparently requires looking through the program
-      header table. */
+   /* TOPLEVEL */
+   /* Look through the program header table, and:
+      - copy information from suitable PT_LOAD entries into rx[] or
+        rw[]
+      - find (or fake up) the .soname for this object.
+   */
    TRACE_SYMTAB("\n");
-   TRACE_SYMTAB("------ Looking for the soname ------\n");
+   TRACE_SYMTAB("------ Examining the program headers ------\n");
    vg_assert(di->soname == NULL);
    {
+      /* TOPLEVEL */
       ElfXX_Addr prev_svma = 0;
 
       for (i = 0; i < phdr_nent; i++) {
          ElfXX_Phdr* phdr = INDEX_BIS( phdr_img, i, phdr_ent_szB );
 
-         /* Make sure the PT_LOADable entries are in order */
+         /* Make sure the PT_LOADable entries are in order and
+            non-overlapping.  This in turn means the address ranges
+            slurped into rx[] and rw[] are in order and
+            non-overlapping. */
+         vg_assert(n_rx >= 0 && n_rx <= N_RX_RW_AREAS);
+         vg_assert(n_rw >= 0 && n_rw <= N_RX_RW_AREAS);
+
          if (phdr->p_type == PT_LOAD) {
-            TRACE_SYMTAB("PT_LOAD in order?: %#lx %#lx\n",
-                         prev_svma + 0UL,
-                         phdr->p_vaddr + 0UL);
+            TRACE_SYMTAB("PT_LOAD[%ld]: p_vaddr %#lx (prev %#lx)\n",
+                         i, (UWord)phdr->p_vaddr, (UWord)prev_svma);
+            TRACE_SYMTAB("PT_LOAD[%ld]:   p_offset %lu, p_filesz %lu,"
+                         " perms %c%c%c\n",
+                         i, (UWord)phdr->p_offset, (UWord)phdr->p_filesz,
+                         phdr->p_flags & PF_R ? 'r' : '-',
+                         phdr->p_flags & PF_W ? 'w' : '-',
+                         phdr->p_flags & PF_X ? 'x' : '-');
             if (phdr->p_vaddr < prev_svma) {
                ML_(symerr)(di, True,
                            "ELF Program Headers are not in ascending order");
                goto out;
             }
             prev_svma = phdr->p_vaddr;
-            if (rx_svma_limit == 0
-                && phdr->p_offset >= di->rx_map_foff
-                && phdr->p_offset < di->rx_map_foff + di->rx_map_size
-                && phdr->p_offset + phdr->p_filesz <= di->rx_map_foff + di->rx_map_size
-                && (phdr->p_flags&(PF_R|PF_W|PF_X)) == (PF_R|PF_X)) {
-               rx_svma_base = phdr->p_vaddr;
-               rx_svma_limit = phdr->p_vaddr + phdr->p_memsz;
-               rx_bias = di->rx_map_avma - di->rx_map_foff + phdr->p_offset - phdr->p_vaddr;
+            if (phdr->p_offset >= di->fsm.rx_map_foff
+                && phdr->p_offset < di->fsm.rx_map_foff + di->fsm.rx_map_size
+                && phdr->p_offset + phdr->p_filesz
+                   <= di->fsm.rx_map_foff + di->fsm.rx_map_size
+                && (phdr->p_flags & (PF_R | PF_W | PF_X)) == (PF_R | PF_X)) {
+               if (n_rx == N_RX_RW_AREAS) {
+                  ML_(symerr)(di, True,
+                              "N_RX_RW_AREAS is too low; "
+                              "increase and recompile.");
+                  goto out;
+               }
+               rx[n_rx].svma_base  = phdr->p_vaddr;
+               rx[n_rx].svma_limit = phdr->p_vaddr + phdr->p_memsz;
+               rx[n_rx].bias       = di->fsm.rx_map_avma - di->fsm.rx_map_foff
+                                     + phdr->p_offset - phdr->p_vaddr;
+               n_rx++;
+               TRACE_SYMTAB("PT_LOAD[%ld]:   acquired as rx\n", i);
             }
-            else if (rw_svma_limit == 0
-                     && phdr->p_offset >= di->rw_map_foff
-                     && phdr->p_offset < di->rw_map_foff + di->rw_map_size
-                     && phdr->p_offset + phdr->p_filesz <= di->rw_map_foff + di->rw_map_size
-                     && (phdr->p_flags&(PF_R|PF_W|PF_X)) == (PF_R|PF_W)) {
-               rw_svma_base = phdr->p_vaddr;
-               rw_svma_limit = phdr->p_vaddr + phdr->p_memsz;
-               rw_bias = di->rw_map_avma - di->rw_map_foff + phdr->p_offset - phdr->p_vaddr;
+            else
+            if (phdr->p_offset >= di->fsm.rw_map_foff
+                && phdr->p_offset < di->fsm.rw_map_foff + di->fsm.rw_map_size
+                && phdr->p_offset + phdr->p_filesz 
+                   <= di->fsm.rw_map_foff + di->fsm.rw_map_size
+                && (phdr->p_flags & (PF_R | PF_W | PF_X)) == (PF_R | PF_W)) {
+               if (n_rw == N_RX_RW_AREAS) {
+                  ML_(symerr)(di, True,
+                              "N_RX_RW_AREAS is too low; "
+                              "increase and recompile.");
+                  goto out;
+               }
+               rw[n_rw].svma_base  = phdr->p_vaddr;
+               rw[n_rw].svma_limit = phdr->p_vaddr + phdr->p_memsz;
+               rw[n_rw].bias       = di->fsm.rw_map_avma - di->fsm.rw_map_foff
+                                     + phdr->p_offset - phdr->p_vaddr;
+               n_rw++;
+               TRACE_SYMTAB("PT_LOAD[%ld]:   acquired as rw\n", i);
             }
          }
 
@@ -1426,7 +1602,11 @@
             }
          }
       } /* for (i = 0; i < phdr_nent; i++) ... */
-   } /* look for the soname */
+      /* TOPLEVEL */
+
+   } /* examine the program headers (local scope) */
+
+   /* TOPLEVEL */
 
    /* If, after looking at all the program headers, we still didn't 
       find a soname, add a fake one. */
@@ -1435,24 +1615,47 @@
       di->soname = "NONE";
    }
 
-   vg_assert(rx_svma_limit != 0);
-   vg_assert(rw_svma_limit != 0);
+   // NaCl nexe
+   if (ehdr_img->e_ident[EI_OSABI] == 0x7b) {
+     HChar* s = VG_(arena_malloc)(VG_AR_DINFO, "di.redi.1", 6 + VG_(strlen)(di->soname));
+     VG_(strcpy)(s, "NaCl_");
+     VG_(strcpy)(s + 5, di->soname);
+     di->soname = s;
+     TRACE_SYMTAB("Fixed soname = %s\n", di->soname);
+   }
+
+
+   vg_assert(n_rx >= 0 && n_rx <= N_RX_RW_AREAS);
+   vg_assert(n_rw >= 0 && n_rw <= N_RX_RW_AREAS);
+   for (i = 0; i < n_rx; i++) {
+      vg_assert(rx[i].svma_limit != 0);
+   }
+   for (i = 0; i < n_rw; i++) {
+      vg_assert(rw[i].svma_limit != 0);
+   }
 
    /* Now read the section table. */
    TRACE_SYMTAB("\n");
-   TRACE_SYMTAB("------ Examining the section headers "
-                "and program headers ------\n");
+   TRACE_SYMTAB("------ Examining the section headers ------\n");
    TRACE_SYMTAB("rx: at %#lx are mapped foffsets %ld .. %ld\n",
-               di->rx_map_avma,
-               di->rx_map_foff, di->rx_map_foff + di->rx_map_size - 1 );
-   TRACE_SYMTAB("rx: contains svmas %#lx .. %#lx with bias %#lx\n",
-                rx_svma_base, rx_svma_limit - 1, rx_bias );
+                di->fsm.rx_map_avma,
+                di->fsm.rx_map_foff,
+                di->fsm.rx_map_foff + di->fsm.rx_map_size - 1 );
+   for (i = 0; i < n_rx; i++) {
+      TRACE_SYMTAB("rx[%ld]: contains svmas %#lx .. %#lx with bias %#lx\n",
+                   i, rx[i].svma_base, rx[i].svma_limit - 1, rx[i].bias );
+   }
    TRACE_SYMTAB("rw: at %#lx are mapped foffsets %ld .. %ld\n",
-               di->rw_map_avma,
-               di->rw_map_foff, di->rw_map_foff + di->rw_map_size - 1 );
-   TRACE_SYMTAB("rw: contains svmas %#lx .. %#lx with bias %#lx\n",
-                rw_svma_base, rw_svma_limit - 1, rw_bias );
+                di->fsm.rw_map_avma,
+                di->fsm.rw_map_foff, 
+                di->fsm.rw_map_foff + di->fsm.rw_map_size - 1 );
+   for (i = 0; i < n_rw; i++) {
+      TRACE_SYMTAB("rw[%ld]: contains svmas %#lx .. %#lx with bias %#lx\n",
+                   i, rw[i].svma_base, rw[i].svma_limit - 1, rw[i].bias );
+   }
 
+   /* TOPLEVEL */
+   /* Iterate over section headers */
    for (i = 0; i < shdr_nent; i++) {
       ElfXX_Shdr* shdr = INDEX_BIS( shdr_img, i, shdr_ent_szB );
       UChar* name = shdr_strtab_img + shdr->sh_name;
@@ -1461,8 +1664,26 @@
       UWord  size = shdr->sh_size;
       UInt   alyn = shdr->sh_addralign;
       Bool   bits = !(shdr->sh_type == SHT_NOBITS);
-      Bool   inrx = svma >= rx_svma_base && svma < rx_svma_limit;
-      Bool   inrw = svma >= rw_svma_base && svma < rw_svma_limit;
+      /* Look through our collection of info obtained from the PT_LOAD
+         headers, and make 'inrx' and 'inrw' 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;
+      { Word j;
+        for (j = 0; j < n_rx; j++) {
+           if (svma >= rx[j].svma_base && svma < rx[j].svma_limit) {
+             inrx = &rx[j];
+             break;
+           }
+        }
+        for (j = 0; j < n_rw; j++) {
+           if (svma >= rw[j].svma_base && svma < rw[j].svma_limit) {
+             inrw = &rw[j];
+             break;
+           }
+        }
+      }
 
       TRACE_SYMTAB(" [sec %2ld]  %s %s  al%2u  foff %6ld .. %6ld  "
                   "  svma %p  name \"%s\"\n", 
@@ -1471,7 +1692,8 @@
 
       /* Check for sane-sized segments.  SHT_NOBITS sections have zero
          size in the file. */
-      if ((foff >= n_oimage) || (foff + (bits ? size : 0) > n_oimage)) {
+      if (bits &&
+          ((foff >= n_oimage) || (foff + (bits ? size : 0) > n_oimage))) {
          ML_(symerr)(di, True, "ELF Section extends beyond image end");
          goto out;
       }
@@ -1487,6 +1709,10 @@
          do { ML_(symerr)(di, True,                        \
                           "Can't make sense of " _secname  \
                           " section mapping");             \
+              /* make sure we don't assert if we find */   \
+              /* ourselves back in this routine later, */  \
+              /* with the same di */                       \
+              di->soname = NULL;                           \
               goto out;                                    \
          } while (0)
 
@@ -1498,11 +1724,11 @@
          if (inrx && size >= 0 && !di->text_present) {
             di->text_present = True;
             di->text_svma = svma;
-            di->text_avma = svma + rx_bias;
+            di->text_avma = svma + inrx->bias;
             di->text_size = size;
-            di->text_bias = rx_bias;
+            di->text_bias = inrx->bias;
             di->text_debug_svma = svma;
-            di->text_debug_bias = rx_bias;
+            di->text_debug_bias = inrx->bias;
             TRACE_SYMTAB("acquiring .text svma = %#lx .. %#lx\n",
                          di->text_svma, 
                          di->text_svma + di->text_size - 1);
@@ -1520,11 +1746,11 @@
          if (inrw && size >= 0 && !di->data_present) {
             di->data_present = True;
             di->data_svma = svma;
-            di->data_avma = svma + rw_bias;
+            di->data_avma = svma + inrw->bias;
             di->data_size = size;
-            di->data_bias = rw_bias;
+            di->data_bias = inrw->bias;
             di->data_debug_svma = svma;
-            di->data_debug_bias = rw_bias;
+            di->data_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .data svma = %#lx .. %#lx\n",
                          di->data_svma,
                          di->data_svma + di->data_size - 1);
@@ -1542,11 +1768,11 @@
          if (inrw && size > 0 && !di->sdata_present) {
             di->sdata_present = True;
             di->sdata_svma = svma;
-            di->sdata_avma = svma + rw_bias;
+            di->sdata_avma = svma + inrw->bias;
             di->sdata_size = size;
-            di->sdata_bias = rw_bias;
+            di->sdata_bias = inrw->bias;
             di->sdata_debug_svma = svma;
-            di->sdata_debug_bias = rw_bias;
+            di->sdata_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .sdata svma = %#lx .. %#lx\n",
                          di->sdata_svma,
                          di->sdata_svma + di->sdata_size - 1);
@@ -1561,14 +1787,15 @@
 
       /* Accept .rodata where mapped as rx (data), even if zero-sized */
       if (0 == VG_(strcmp)(name, ".rodata")) {
-         if (/*inrx && */size >= 0 && !di->rodata_present) {
+         if (inrx && size >= 0 && !di->rodata_present) {
             di->rodata_present = True;
             di->rodata_svma = svma;
-            di->rodata_avma = svma + rx_bias;
+            di->rodata_avma = svma + inrx->bias;
             di->rodata_size = size;
-            di->rodata_bias = rx_bias;
+            di->rodata_bias = inrx->bias;
             di->rodata_debug_svma = svma;
-            di->rodata_debug_bias = rw_bias;
+            di->rodata_debug_bias = inrx->bias;
+                                    /* NB was 'inrw' prior to r11794 */
             TRACE_SYMTAB("acquiring .rodata svma = %#lx .. %#lx\n",
                          di->rodata_svma,
                          di->rodata_svma + di->rodata_size - 1);
@@ -1586,11 +1813,11 @@
             dynbss_present = True;
             di->bss_present = True;
             di->bss_svma = svma;
-            di->bss_avma = svma + rw_bias;
+            di->bss_avma = svma + inrw->bias;
             di->bss_size = size;
-            di->bss_bias = rw_bias;
+            di->bss_bias = inrw->bias;
             di->bss_debug_svma = svma;
-            di->bss_debug_bias = rw_bias;
+            di->bss_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .dynbss svma = %#lx .. %#lx\n",
                          di->bss_svma,
                          di->bss_svma + di->bss_size - 1);
@@ -1611,18 +1838,18 @@
             TRACE_SYMTAB("acquiring .bss svma = %#lx .. %#lx\n",
                          svma, svma + size - 1);
             TRACE_SYMTAB("acquiring .bss avma = %#lx .. %#lx\n",
-                         svma + rw_bias, svma + rw_bias + size - 1);
+                         svma + inrw->bias, svma + inrw->bias + size - 1);
             TRACE_SYMTAB("acquiring .bss bias = %#lx\n", di->bss_bias);
          } else
 
          if (inrw && size >= 0 && !di->bss_present) {
             di->bss_present = True;
             di->bss_svma = svma;
-            di->bss_avma = svma + rw_bias;
+            di->bss_avma = svma + inrw->bias;
             di->bss_size = size;
-            di->bss_bias = rw_bias;
+            di->bss_bias = inrw->bias;
             di->bss_debug_svma = svma;
-            di->bss_debug_bias = rw_bias;
+            di->bss_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .bss svma = %#lx .. %#lx\n",
                          di->bss_svma,
                          di->bss_svma + di->bss_size - 1);
@@ -1648,8 +1875,8 @@
                VG_(message)(Vg_UserMsg,
                             "Warning: the following file's .bss is "
                             "mapped r-x only - ignoring .bss syms\n");
-               VG_(message)(Vg_UserMsg,   " %s\n", di->filename 
-                                                      ? di->filename
+               VG_(message)(Vg_UserMsg,   " %s\n", di->fsm.filename 
+                                                      ? di->fsm.filename
                                                       : (UChar*)"(null?!)" );
             }
          } else
@@ -1671,11 +1898,11 @@
             sdynbss_present = True;
             di->sbss_present = True;
             di->sbss_svma = svma;
-            di->sbss_avma = svma + rw_bias;
+            di->sbss_avma = svma + inrw->bias;
             di->sbss_size = size;
-            di->sbss_bias = rw_bias;
+            di->sbss_bias = inrw->bias;
             di->sbss_debug_svma = svma;
-            di->sbss_debug_bias = rw_bias;
+            di->sbss_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .sdynbss svma = %#lx .. %#lx\n",
                          di->sbss_svma,
                          di->sbss_svma + di->sbss_size - 1);
@@ -1696,18 +1923,18 @@
             TRACE_SYMTAB("acquiring .sbss svma = %#lx .. %#lx\n",
                          svma, svma + size - 1);
             TRACE_SYMTAB("acquiring .sbss avma = %#lx .. %#lx\n",
-                         svma + rw_bias, svma + rw_bias + size - 1);
+                         svma + inrw->bias, svma + inrw->bias + size - 1);
             TRACE_SYMTAB("acquiring .sbss bias = %#lx\n", di->sbss_bias);
          } else
 
          if (inrw && size > 0 && !di->sbss_present) {
             di->sbss_present = True;
             di->sbss_svma = svma;
-            di->sbss_avma = svma + rw_bias;
+            di->sbss_avma = svma + inrw->bias;
             di->sbss_size = size;
-            di->sbss_bias = rw_bias;
+            di->sbss_bias = inrw->bias;
             di->sbss_debug_svma = svma;
-            di->sbss_debug_bias = rw_bias;
+            di->sbss_debug_bias = inrw->bias;
             TRACE_SYMTAB("acquiring .sbss svma = %#lx .. %#lx\n",
                          di->sbss_svma,
                          di->sbss_svma + di->sbss_size - 1);
@@ -1724,7 +1951,7 @@
       if (0 == VG_(strcmp)(name, ".got")) {
          if (inrw && size > 0 && !di->got_present) {
             di->got_present = True;
-            di->got_avma = svma + rw_bias;
+            di->got_avma = svma + inrw->bias;
             di->got_size = size;
             TRACE_SYMTAB("acquiring .got avma = %#lx\n", di->got_avma);
          } else {
@@ -1736,27 +1963,22 @@
       if (0 == VG_(strcmp)(name, ".got.plt")) {
          if (inrw && size > 0 && !di->gotplt_present) {
             di->gotplt_present = True;
-            di->gotplt_avma = svma + rw_bias;
+            di->gotplt_avma = svma + inrw->bias;
             di->gotplt_size = size;
             TRACE_SYMTAB("acquiring .got.plt avma = %#lx\n", di->gotplt_avma);
          } else if (size != 0) {
-           if (!di->gotplt_present) {
-             VG_(printf)("WARNING: ignoring non-empty .got.plt outside of RW segment!\n");
-             VG_(printf)("Filename %s, section size %d\n", di->filename, size);
-           } else {
-             BAD(".got.plt");
-           }
+            BAD(".got.plt");
          }
       }
 
       /* PLT is different on different platforms, it seems. */
 #     if defined(VGP_x86_linux) || defined(VGP_amd64_linux) \
-         || defined(VGP_arm_linux)
+         || defined(VGP_arm_linux) || defined (VGP_s390x_linux)
       /* Accept .plt where mapped as rx (code) */
       if (0 == VG_(strcmp)(name, ".plt")) {
          if (inrx && size > 0 && !di->plt_present) {
             di->plt_present = True;
-            di->plt_avma = svma + rx_bias;
+            di->plt_avma = svma + inrx->bias;
             di->plt_size = size;
             TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma);
          } else {
@@ -1768,7 +1990,7 @@
       if (0 == VG_(strcmp)(name, ".plt")) {
          if (inrw && size > 0 && !di->plt_present) {
             di->plt_present = True;
-            di->plt_avma = svma + rw_bias;
+            di->plt_avma = svma + inrw->bias;
             di->plt_size = size;
             TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma);
          } else {
@@ -1780,7 +2002,7 @@
       if (0 == VG_(strcmp)(name, ".plt")) {
          if (inrw && size > 0 && !di->plt_present) {
             di->plt_present = True;
-            di->plt_avma = svma + rw_bias;
+            di->plt_avma = svma + inrw->bias;
             di->plt_size = size;
             TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma);
          } else 
@@ -1803,7 +2025,7 @@
       if (0 == VG_(strcmp)(name, ".opd")) {
          if (inrw && size > 0 && !di->opd_present) {
             di->opd_present = True;
-            di->opd_avma = svma + rw_bias;
+            di->opd_avma = svma + inrw->bias;
             di->opd_size = size;
             TRACE_SYMTAB("acquiring .opd avma = %#lx\n", di->opd_avma);
          } else {
@@ -1813,19 +2035,22 @@
 
       /* Accept .eh_frame where mapped as rx (code).  This seems to be
          the common case.  However, if that doesn't pan out, try for
-         rw (data) instead. */
+         rw (data) instead.  We can handle up to N_EHFRAME_SECTS per
+         ELF object. */
       if (0 == VG_(strcmp)(name, ".eh_frame")) {
-         if (/*inrx && */size > 0 && !di->ehframe_present) {
-            di->ehframe_present = True;
-            di->ehframe_avma = svma + rx_bias;
-            di->ehframe_size = size;
-            TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n", di->ehframe_avma);
+         if (inrx && size > 0 && di->n_ehframe < N_EHFRAME_SECTS) {
+            di->ehframe_avma[di->n_ehframe] = svma + inrx->bias;
+            di->ehframe_size[di->n_ehframe] = size;
+            TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n",
+                         di->ehframe_avma[di->n_ehframe]);
+            di->n_ehframe++;
          } else
-         if (inrw && size > 0 && !di->ehframe_present) {
-            di->ehframe_present = True;
-            di->ehframe_avma = svma + rw_bias;
-            di->ehframe_size = size;
-            TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n", di->ehframe_avma);
+         if (inrw && size > 0 && di->n_ehframe < N_EHFRAME_SECTS) {
+            di->ehframe_avma[di->n_ehframe] = svma + inrw->bias;
+            di->ehframe_size[di->n_ehframe] = size;
+            TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n",
+                         di->ehframe_avma[di->n_ehframe]);
+            di->n_ehframe++;
          } else {
             BAD(".eh_frame");
          }
@@ -1833,8 +2058,9 @@
 
 #    undef BAD
 
-   }
+   } /* iterate over the section headers */
 
+   /* TOPLEVEL */
    if (0) VG_(printf)("YYYY text_: avma %#lx  size %ld  bias %#lx\n",
                       di->text_avma, di->text_size, di->text_bias);
 
@@ -1847,12 +2073,14 @@
    TRACE_SYMTAB("------ Finding image addresses "
                 "for debug-info sections ------\n");
 
+   /* TOPLEVEL */
    /* Find interesting sections, read the symbol table(s), read any debug
       information */
    {
       /* IMAGE addresses: pointers to start of sections in the
          transiently loaded oimage, not in the fragments of the file
          mapped in by the guest's dynamic linker. */
+      /* TOPLEVEL */
       UChar*     strtab_img       = NULL; /* .strtab */
       ElfXX_Sym* symtab_img       = NULL; /* .symtab */
       UChar*     dynstr_img       = NULL; /* .dynstr */
@@ -1869,9 +2097,10 @@
       UChar*     debug_frame_img  = NULL; /* .debug_frame  (dwarf2) */
       UChar*     dwarf1d_img      = NULL; /* .debug        (dwarf1) */
       UChar*     dwarf1l_img      = NULL; /* .line         (dwarf1) */
-      UChar*     ehframe_img      = NULL; /* .eh_frame     (dwarf2) */
       UChar*     opd_img          = NULL; /* .opd (dwarf2,
                                                    ppc64-linux) */
+      UChar*     ehframe_img[N_EHFRAME_SECTS]; /* .eh_frame (dwarf2) */
+
       /* Section sizes, in bytes */
       SizeT      strtab_sz       = 0;
       SizeT      symtab_sz       = 0;
@@ -1889,44 +2118,61 @@
       SizeT      debug_frame_sz  = 0;
       SizeT      dwarf1d_sz      = 0;
       SizeT      dwarf1l_sz      = 0;
-      SizeT      ehframe_sz      = 0;
       SizeT      opd_sz_unused   = 0;
+      SizeT      ehframe_sz[N_EHFRAME_SECTS];
+
+      for (i = 0; i < N_EHFRAME_SECTS; i++) {
+         ehframe_img[i] = NULL;
+         ehframe_sz[i]  = 0;
+      }
 
       /* Find all interesting sections */
 
-      /* What FIND does: it finds the section called SEC_NAME.  The
-         size of it is assigned to SEC_SIZE.  The address of the
-         section in the transiently loaded oimage is assigned to
-         SEC_FILEA.  Even for sections which are marked loadable, the
-         client's ld.so may not have loaded them yet, so there is no
-         guarantee that we can safely prod around in any such area).
-         Because the entire object file is transiently mapped aboard
-         for inspection, it's always safe to inspect that area. */
+      UInt ehframe_ix = 0;
 
+      /* What FIND does: it finds the section called _SEC_NAME.  The
+         size of it is assigned to _SEC_SIZE.  The address of the
+         section in the transiently loaded oimage is assigned to
+         _SEC_IMG.  If the section is found, _POST_FX is executed
+         after _SEC_NAME and _SEC_SIZE have been assigned to.
+
+         Even for sections which are marked loadable, the client's
+         ld.so may not have loaded them yet, so there is no guarantee
+         that we can safely prod around in any such area).  Because
+         the entire object file is transiently mapped aboard for
+         inspection, it's always safe to inspect that area. */
+
+      /* TOPLEVEL */
+      /* Iterate over section headers (again) */
       for (i = 0; i < ehdr_img->e_shnum; i++) {
 
-#        define FIND(sec_name, sec_size, sec_img) \
+#        define FINDX(_sec_name, _sec_size, _sec_img, _post_fx)     \
          do { ElfXX_Shdr* shdr \
                  = INDEX_BIS( shdr_img, i, shdr_ent_szB ); \
-            if (0 == VG_(strcmp)(sec_name, shdr_strtab_img \
-                                           + shdr->sh_name)) { \
+            if (0 == VG_(strcmp)(_sec_name, shdr_strtab_img \
+                                            + shdr->sh_name)) { \
                Bool nobits; \
-               sec_img  = (void*)(oimage + shdr->sh_offset); \
-               sec_size = shdr->sh_size; \
-               nobits   = shdr->sh_type == SHT_NOBITS; \
+               _sec_img  = (void*)(oimage + shdr->sh_offset); \
+               _sec_size = shdr->sh_size; \
+               nobits    = shdr->sh_type == SHT_NOBITS; \
                TRACE_SYMTAB( "%18s:  img %p .. %p\n", \
-                             sec_name, (UChar*)sec_img, \
-                             ((UChar*)sec_img) + sec_size - 1); \
+                             _sec_name, (UChar*)_sec_img, \
+                             ((UChar*)_sec_img) + _sec_size - 1); \
                /* SHT_NOBITS sections have zero size in the file. */ \
                if ( shdr->sh_offset \
-                    + (nobits ? 0 : sec_size) > n_oimage ) { \
+                    + (nobits ? 0 : _sec_size) > n_oimage ) { \
                   ML_(symerr)(di, True, \
                               "   section beyond image end?!"); \
                   goto out; \
                } \
+               _post_fx; \
             } \
          } while (0);
 
+         /* Version with no post-effects */
+#        define FIND(_sec_name, _sec_size, _sec_img) \
+            FINDX(_sec_name, _sec_size, _sec_img, /**/)
+
          /*   NAME              SIZE             IMAGE addr */
          FIND(".dynsym",        dynsym_sz,       dynsym_img)
          FIND(".dynstr",        dynstr_sz,       dynstr_img)
@@ -1948,12 +2194,34 @@
 
          FIND(".debug",         dwarf1d_sz,      dwarf1d_img)
          FIND(".line",          dwarf1l_sz,      dwarf1l_img)
-         FIND(".eh_frame",      ehframe_sz,      ehframe_img)
 
          FIND(".opd",           opd_sz_unused,   opd_img)
 
+         FINDX(".eh_frame",     ehframe_sz[ehframe_ix],
+                                                 ehframe_img[ehframe_ix], 
+               do { ehframe_ix++; vg_assert(ehframe_ix <= N_EHFRAME_SECTS); }
+                    while (0)
+         )
+         /* Comment_on_EH_FRAME_MULTIPLE_INSTANCES: w.r.t. .eh_frame
+            multi-instance kludgery, how are we assured that the order
+            in which we fill in ehframe_sz[] and ehframe_img[] is
+            consistent with the order in which we previously filled in
+            di->ehframe_avma[] and di->ehframe_size[] ?  By the fact
+            that in both cases, these arrays were filled in by
+            iterating over the section headers top-to-bottom.  So both
+            loops (this one and the previous one) encounter the
+            .eh_frame entries in the same order and so fill in these
+            arrays in a consistent order.
+         */
+
+#        undef FINDX
 #        undef FIND
-      }
+      } /* Iterate over section headers (again) */
+
+      /* TOPLEVEL */
+      /* Now, see if we can find a debuginfo object, and if so map it in, and
+         put the mapping address and size in dimage and n_dimage. */
+      vg_assert(dimage == 0 && n_dimage == 0);
 
       /* Look for a build-id */
       buildid = find_buildid(oimage, n_oimage);
@@ -1968,196 +2236,228 @@
             vg_assert(crc_offset + sizeof(UInt) <= debuglink_sz);
 
             /* Extract the CRC from the debuglink section */
-            crc = *(UInt *)(debuglink_img + crc_offset);
+            crc = ML_(read_UInt)(debuglink_img + crc_offset);
 
             /* See if we can find a matching debug file */
-            dimage = find_debug_file( di, di->filename, buildid,
-                                      debuglink_img, crc, &n_dimage );
+            find_debug_file( di, di->fsm.filename, buildid,
+                             debuglink_img, crc, &dimage, &n_dimage );
          } else {
             /* See if we can find a matching debug file */
-            dimage = find_debug_file( di, di->filename, buildid, NULL, 0, &n_dimage );
-         }
-
-         ML_(dinfo_free)(buildid);
-
-         if (dimage != 0 
-             && n_dimage >= sizeof(ElfXX_Ehdr)
-             && ML_(is_elf_object_file)((void*)dimage, n_dimage)) {
-
-            /* Pull out and validate program header and section header info */
-            ElfXX_Ehdr* ehdr_dimg     = (ElfXX_Ehdr*)dimage;
-            ElfXX_Phdr* phdr_dimg     = (ElfXX_Phdr*)( ((UChar*)ehdr_dimg)
-                                                       + ehdr_dimg->e_phoff );
-            UWord       phdr_dnent    = ehdr_dimg->e_phnum;
-            UWord       phdr_dent_szB = ehdr_dimg->e_phentsize;
-            ElfXX_Shdr* shdr_dimg     = (ElfXX_Shdr*)( ((UChar*)ehdr_dimg)
-                                                       + ehdr_dimg->e_shoff );
-            UWord       shdr_dnent       = ehdr_dimg->e_shnum;
-            UWord       shdr_dent_szB    = ehdr_dimg->e_shentsize;
-            UChar*      shdr_strtab_dimg = NULL;
-
-            /* SVMAs covered by rx and rw segments and corresponding bias. */
-            Addr     rx_dsvma_base = 0;
-            Addr     rx_dsvma_limit = 0;
-            PtrdiffT rx_dbias = 0;
-            Addr     rw_dsvma_base = 0;
-            Addr     rw_dsvma_limit = 0;
-            PtrdiffT rw_dbias = 0;
-
-            Bool need_symtab, need_stabs, need_dwarf2, need_dwarf1;
-
-            if (phdr_dnent == 0
-                || !contained_within(
-                       dimage, n_dimage,
-                       (Addr)phdr_dimg, phdr_dnent * phdr_dent_szB)) {
-               ML_(symerr)(di, True,
-                           "Missing or invalid ELF Program Header Table"
-                           " (debuginfo file)");
-               goto out;
-            }
-
-            if (shdr_dnent == 0
-                || !contained_within(
-                       dimage, n_dimage,
-                       (Addr)shdr_dimg, shdr_dnent * shdr_dent_szB)) {
-               ML_(symerr)(di, True,
-                           "Missing or invalid ELF Section Header Table"
-                           " (debuginfo file)");
-               goto out;
-            }
-
-            /* Also find the section header's string table, and validate. */
-            /* checked previously by is_elf_object_file: */
-            vg_assert( ehdr_dimg->e_shstrndx != SHN_UNDEF );
-
-            shdr_strtab_dimg
-               = (UChar*)( ((UChar*)ehdr_dimg)
-                           + shdr_dimg[ehdr_dimg->e_shstrndx].sh_offset);
-            if (!contained_within( 
-                    dimage, n_dimage,
-                    (Addr)shdr_strtab_dimg,
-                    1/*bogus, but we don't know the real size*/ )) {
-               ML_(symerr)(di, True, 
-                           "Invalid ELF Section Header String Table"
-                           " (debuginfo file)");
-               goto out;
-            }
-
-            need_symtab = (NULL == symtab_img);
-            need_stabs  = (NULL == stab_img);
-            need_dwarf2 = (NULL == debug_info_img);
-            need_dwarf1 = (NULL == dwarf1d_img);
-
-            for (i = 0; i < ehdr_dimg->e_phnum; i++) {
-               ElfXX_Phdr* phdr 
-                  = INDEX_BIS( (void*)(dimage + ehdr_dimg->e_phoff), 
-                                          i, phdr_ent_szB );
-               if (phdr->p_type == PT_LOAD) {
-                  if (rx_dsvma_limit == 0
-                      && phdr->p_offset >= di->rx_map_foff
-                      && phdr->p_offset < di->rx_map_foff + di->rx_map_size
-                      && phdr->p_offset + phdr->p_filesz <= di->rx_map_foff + di->rx_map_size) {
-                     rx_dsvma_base = phdr->p_vaddr;
-                     rx_dsvma_limit = phdr->p_vaddr + phdr->p_memsz;
-                     rx_dbias = di->rx_map_avma - di->rx_map_foff + phdr->p_offset - phdr->p_vaddr;
-                  }
-                  else if (rw_dsvma_limit == 0
-                           && phdr->p_offset >= di->rw_map_foff
-                           && phdr->p_offset < di->rw_map_foff + di->rw_map_size
-                           && phdr->p_offset + phdr->p_filesz <= di->rw_map_foff + di->rw_map_size) {
-                     rw_dsvma_base = phdr->p_vaddr;
-                     rw_dsvma_limit = phdr->p_vaddr + phdr->p_memsz;
-                     rw_dbias = di->rw_map_avma - di->rw_map_foff + phdr->p_offset - phdr->p_vaddr;
-                  }
-               }
-            }
-
-            /* Find all interesting sections */
-            for (i = 0; i < ehdr_dimg->e_shnum; i++) {
-
-               /* Find debug svma and bias information for sections
-                  we found in the main file. */ 
-
-#              define FIND(sec, seg) \
-               do { ElfXX_Shdr* shdr \
-                       = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \
-                  if (di->sec##_present \
-                      && 0 == VG_(strcmp)("." #sec, \
-                                          shdr_strtab_dimg + shdr->sh_name)) { \
-                     vg_assert(di->sec##_size == shdr->sh_size); \
-                     vg_assert(di->sec##_avma +  shdr->sh_addr + seg##_dbias); \
-                     di->sec##_debug_svma = shdr->sh_addr; \
-                     di->sec##_debug_bias = seg##_dbias; \
-                     TRACE_SYMTAB("acquiring ." #sec " debug svma = %#lx .. %#lx\n", \
-                                  di->sec##_debug_svma, \
-                                  di->sec##_debug_svma + di->sec##_size - 1); \
-                     TRACE_SYMTAB("acquiring ." #sec " debug bias = %#lx\n", \
-                                  di->sec##_debug_bias); \
-                  } \
-               } while (0);
-
-               /* SECTION   SEGMENT */
-               FIND(text,   rx)
-               FIND(data,   rw)
-               FIND(sdata,  rw)
-               FIND(rodata, rw)
-               FIND(bss,    rw)
-               FIND(sbss,   rw)
-
-#              undef FIND
-
-               /* Same deal as previous FIND, except only do it for those
-                  sections for which we didn't find anything useful in
-                  the main file. */
-
-#              define FIND(condition, sec_name, sec_size, sec_img) \
-               do { ElfXX_Shdr* shdr \
-                       = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \
-                  if (condition \
-                      && 0 == VG_(strcmp)(sec_name, \
-                                          shdr_strtab_dimg + shdr->sh_name)) { \
-                     Bool nobits; \
-                     if (0 != sec_img) \
-                        VG_(core_panic)("repeated section!\n"); \
-                     sec_img  = (void*)(dimage + shdr->sh_offset); \
-                     sec_size = shdr->sh_size; \
-                     nobits   = shdr->sh_type == SHT_NOBITS; \
-                     TRACE_SYMTAB( "%18s: dimg %p .. %p\n", \
-                                   sec_name, \
-                                   (UChar*)sec_img, \
-                                   ((UChar*)sec_img) + sec_size - 1); \
-                     /* SHT_NOBITS sections have zero size in the file. */ \
-                     if ( shdr->sh_offset \
-                          + (nobits ? 0 : sec_size) > n_dimage ) { \
-                        ML_(symerr)(di, True, \
-                                    "   section beyond image end?!"); \
-                        goto out; \
-                     } \
-                  } \
-               } while (0);
-
-               /* NEEDED?        NAME             SIZE           IMAGE addr */
-               FIND(need_symtab, ".symtab",       symtab_sz,     symtab_img)
-               FIND(need_symtab, ".strtab",       strtab_sz,     strtab_img)
-               FIND(need_stabs,  ".stab",         stab_sz,       stab_img)
-               FIND(need_stabs,  ".stabstr",      stabstr_sz,    stabstr_img)
-               FIND(need_dwarf2, ".debug_line",   debug_line_sz, debug_line_img)
-               FIND(need_dwarf2, ".debug_info",   debug_info_sz, debug_info_img)
-               FIND(need_dwarf2, ".debug_abbrev", debug_abbv_sz, debug_abbv_img)
-               FIND(need_dwarf2, ".debug_str",    debug_str_sz,  debug_str_img)
-               FIND(need_dwarf2, ".debug_ranges", debug_ranges_sz, 
-                                                               debug_ranges_img)
-               FIND(need_dwarf2, ".debug_loc",    debug_loc_sz,  debug_loc_img)
-               FIND(need_dwarf2, ".debug_frame",  debug_frame_sz,
-                                                               debug_frame_img)
-               FIND(need_dwarf1, ".debug",        dwarf1d_sz,    dwarf1d_img)
-               FIND(need_dwarf1, ".line",         dwarf1l_sz,    dwarf1l_img)
-
-#              undef FIND
-            }
+            find_debug_file( di, di->fsm.filename, buildid,
+                             NULL, 0, &dimage, &n_dimage );
          }
       }
 
+      if (buildid) {
+         ML_(dinfo_free)(buildid);
+         buildid = NULL; /* paranoia */
+      }
+
+      /* Still no luck?  Let's have one last roll of the dice. */
+      if (dimage == 0) {
+         vg_assert(n_dimage == 0);
+         Bool found = find_ad_hoc_debug_image( di, di->fsm.filename,
+                                               &dimage, &n_dimage );
+         if (found)
+            vg_assert(dimage != 0);
+      }
+
+      /* TOPLEVEL */
+      /* If we were successful in finding a debug image, pull various
+         SVMA/bias/size and image addresses out of it. */
+      if (dimage != 0 
+          && n_dimage >= sizeof(ElfXX_Ehdr)
+          && ML_(is_elf_object_file)((void*)dimage, n_dimage)) {
+
+         /* Pull out and validate program header and section header info */
+         ElfXX_Ehdr* ehdr_dimg     = (ElfXX_Ehdr*)dimage;
+         ElfXX_Phdr* phdr_dimg     = (ElfXX_Phdr*)( ((UChar*)ehdr_dimg)
+                                                       + ehdr_dimg->e_phoff );
+         UWord       phdr_dnent    = ehdr_dimg->e_phnum;
+         UWord       phdr_dent_szB = ehdr_dimg->e_phentsize;
+         ElfXX_Shdr* shdr_dimg     = (ElfXX_Shdr*)( ((UChar*)ehdr_dimg)
+                                                       + ehdr_dimg->e_shoff );
+         UWord       shdr_dnent       = ehdr_dimg->e_shnum;
+         UWord       shdr_dent_szB    = ehdr_dimg->e_shentsize;
+         UChar*      shdr_strtab_dimg = NULL;
+
+         /* SVMAs covered by rx and rw segments and corresponding bias. */
+         /* Addr     rx_dsvma_base = 0; */ /* UNUSED */
+         Addr     rx_dsvma_limit = 0;
+         PtrdiffT rx_dbias = 0;
+         /* Addr     rw_dsvma_base = 0; */ /* UNUSED */
+         Addr     rw_dsvma_limit = 0;
+         PtrdiffT rw_dbias = 0;
+
+         Bool need_symtab, need_stabs, need_dwarf2, need_dwarf1;
+
+         if (phdr_dnent == 0
+             || !contained_within(
+                    dimage, n_dimage,
+                    (Addr)phdr_dimg, phdr_dnent * phdr_dent_szB)) {
+            ML_(symerr)(di, True,
+                        "Missing or invalid ELF Program Header Table"
+                        " (debuginfo file)");
+            goto out;
+         }
+
+         if (shdr_dnent == 0
+             || !contained_within(
+                    dimage, n_dimage,
+                    (Addr)shdr_dimg, shdr_dnent * shdr_dent_szB)) {
+            ML_(symerr)(di, True,
+                        "Missing or invalid ELF Section Header Table"
+                        " (debuginfo file)");
+            goto out;
+         }
+
+         /* Also find the section header's string table, and validate. */
+         /* checked previously by is_elf_object_file: */
+         vg_assert( ehdr_dimg->e_shstrndx != SHN_UNDEF );
+
+         shdr_strtab_dimg
+            = (UChar*)( ((UChar*)ehdr_dimg)
+                        + shdr_dimg[ehdr_dimg->e_shstrndx].sh_offset);
+         if (!contained_within( 
+                 dimage, n_dimage,
+                 (Addr)shdr_strtab_dimg,
+                 1/*bogus, but we don't know the real size*/ )) {
+            ML_(symerr)(di, True, 
+                        "Invalid ELF Section Header String Table"
+                        " (debuginfo file)");
+            goto out;
+         }
+
+         need_symtab = (NULL == symtab_img);
+         need_stabs  = (NULL == stab_img);
+         need_dwarf2 = (NULL == debug_info_img);
+         need_dwarf1 = (NULL == dwarf1d_img);
+
+         for (i = 0; i < ehdr_dimg->e_phnum; i++) {
+            ElfXX_Phdr* phdr 
+               = INDEX_BIS( (void*)(dimage + ehdr_dimg->e_phoff), 
+                                       i, phdr_ent_szB );
+            if (phdr->p_type == PT_LOAD) {
+               if (rx_dsvma_limit == 0
+                   && phdr->p_offset >= di->fsm.rx_map_foff
+                   && phdr->p_offset
+                      < di->fsm.rx_map_foff + di->fsm.rx_map_size
+                   && phdr->p_offset + phdr->p_filesz
+                      <= di->fsm.rx_map_foff + di->fsm.rx_map_size) {
+                  /* rx_dsvma_base = phdr->p_vaddr; */ /* UNUSED */
+                  rx_dsvma_limit = phdr->p_vaddr + phdr->p_memsz;
+                  rx_dbias = di->fsm.rx_map_avma - di->fsm.rx_map_foff 
+                             + phdr->p_offset - phdr->p_vaddr;
+               }
+               else
+               if (rw_dsvma_limit == 0
+                   && phdr->p_offset >= di->fsm.rw_map_foff
+                   && phdr->p_offset
+                      < di->fsm.rw_map_foff + di->fsm.rw_map_size
+                   && phdr->p_offset + phdr->p_filesz
+                      <= di->fsm.rw_map_foff + di->fsm.rw_map_size) {
+                  /* rw_dsvma_base = phdr->p_vaddr; */ /* UNUSED */
+                  rw_dsvma_limit = phdr->p_vaddr + phdr->p_memsz;
+                  rw_dbias = di->fsm.rw_map_avma - di->fsm.rw_map_foff
+                             + phdr->p_offset - phdr->p_vaddr;
+               }
+            }
+         }
+
+         /* Find all interesting sections */
+         for (i = 0; i < ehdr_dimg->e_shnum; i++) {
+
+            /* Find debug svma and bias information for sections
+               we found in the main file. */ 
+
+#           define FIND(sec, seg) \
+            do { ElfXX_Shdr* shdr \
+                    = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \
+               if (di->sec##_present \
+                   && 0 == VG_(strcmp)("." #sec, \
+                                       shdr_strtab_dimg + shdr->sh_name)) { \
+                  vg_assert(di->sec##_size == shdr->sh_size); \
+                  vg_assert(di->sec##_avma +  shdr->sh_addr + seg##_dbias); \
+                  /* Assume we have a correct value for the main */ \
+                  /* object's bias.  Use that to derive the debuginfo */ \
+                  /* object's bias, by adding the difference in SVMAs */ \
+                  /* for the corresponding sections in the two files. */ \
+                  /* That should take care of all prelinking effects. */ \
+                  di->sec##_debug_svma = shdr->sh_addr; \
+                  di->sec##_debug_bias \
+                     = di->sec##_bias + \
+                       di->sec##_svma - di->sec##_debug_svma; \
+                  TRACE_SYMTAB("acquiring ." #sec \
+                               " debug svma = %#lx .. %#lx\n",       \
+                               di->sec##_debug_svma, \
+                               di->sec##_debug_svma + di->sec##_size - 1); \
+                  TRACE_SYMTAB("acquiring ." #sec " debug bias = %#lx\n", \
+                               di->sec##_debug_bias); \
+               } \
+            } while (0);
+
+            /* SECTION   SEGMENT */
+            FIND(text,   rx)
+            FIND(data,   rw)
+            FIND(sdata,  rw)
+            FIND(rodata, rw)
+            FIND(bss,    rw)
+            FIND(sbss,   rw)
+
+#           undef FIND
+
+            /* Same deal as previous FIND, except only do it for those
+               sections for which we didn't find anything useful in
+               the main file. */
+
+#           define FIND(condition, sec_name, sec_size, sec_img) \
+            do { ElfXX_Shdr* shdr \
+                    = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \
+               if (condition \
+                   && 0 == VG_(strcmp)(sec_name, \
+                                       shdr_strtab_dimg + shdr->sh_name)) { \
+                  Bool nobits; \
+                  if (0 != sec_img) \
+                     VG_(core_panic)("repeated section!\n"); \
+                  sec_img  = (void*)(dimage + shdr->sh_offset); \
+                  sec_size = shdr->sh_size; \
+                  nobits   = shdr->sh_type == SHT_NOBITS; \
+                  TRACE_SYMTAB( "%18s: dimg %p .. %p\n", \
+                                sec_name, \
+                                (UChar*)sec_img, \
+                                ((UChar*)sec_img) + sec_size - 1); \
+                  /* SHT_NOBITS sections have zero size in the file. */ \
+                  if ( shdr->sh_offset \
+                       + (nobits ? 0 : sec_size) > n_dimage ) { \
+                     ML_(symerr)(di, True, \
+                                 "   section beyond image end?!"); \
+                     goto out; \
+                  } \
+               } \
+            } while (0);
+
+            /* NEEDED?        NAME             SIZE           IMAGE addr */
+            FIND(need_symtab, ".symtab",       symtab_sz,     symtab_img)
+            FIND(need_symtab, ".strtab",       strtab_sz,     strtab_img)
+            FIND(need_stabs,  ".stab",         stab_sz,       stab_img)
+            FIND(need_stabs,  ".stabstr",      stabstr_sz,    stabstr_img)
+            FIND(need_dwarf2, ".debug_line",   debug_line_sz, debug_line_img)
+            FIND(need_dwarf2, ".debug_info",   debug_info_sz, debug_info_img)
+            FIND(need_dwarf2, ".debug_abbrev", debug_abbv_sz, debug_abbv_img)
+            FIND(need_dwarf2, ".debug_str",    debug_str_sz,  debug_str_img)
+            FIND(need_dwarf2, ".debug_ranges", debug_ranges_sz, 
+                                                            debug_ranges_img)
+            FIND(need_dwarf2, ".debug_loc",    debug_loc_sz,  debug_loc_img)
+            FIND(need_dwarf2, ".debug_frame",  debug_frame_sz,
+                                                            debug_frame_img)
+            FIND(need_dwarf1, ".debug",        dwarf1d_sz,    dwarf1d_img)
+            FIND(need_dwarf1, ".line",         dwarf1l_sz,    dwarf1l_img)
+
+#           undef FIND
+         } /* Find all interesting sections */
+      } /* do we have a debug image? */
+
+      /* TOPLEVEL */
       /* Check some sizes */
       vg_assert((dynsym_sz % sizeof(ElfXX_Sym)) == 0);
       vg_assert((symtab_sz % sizeof(ElfXX_Sym)) == 0);
@@ -2185,22 +2485,39 @@
                          dynsym_img, dynsym_sz,
                          dynstr_img, dynstr_sz, 
                          False, opd_img);
-      }
+      } /* Read symbols */
 
-      /* Read .eh_frame and .debug_frame (call-frame-info) if any */
-      if (ehframe_img) {
-         vg_assert(ehframe_sz == di->ehframe_size);
-         ML_(read_callframe_info_dwarf3)( di, ehframe_img, ehframe_sz, True );
+      /* TOPLEVEL */
+      /* Read .eh_frame and .debug_frame (call-frame-info) if any.  Do
+         the .eh_frame section(s) first. */
+      vg_assert(di->n_ehframe >= 0 && di->n_ehframe <= N_EHFRAME_SECTS);
+      for (i = 0; i < di->n_ehframe; i++) {
+         /* see Comment_on_EH_FRAME_MULTIPLE_INSTANCES above for why
+            this next assertion should hold. */
+         vg_assert(ehframe_sz[i] == di->ehframe_size[i]);
+         ML_(read_callframe_info_dwarf3)( di,
+                                          ehframe_img[i],
+                                          ehframe_sz[i],
+                                          di->ehframe_avma[i],
+                                          True/*is_ehframe*/ );
       }
       if (debug_frame_sz) {
-         ML_(read_callframe_info_dwarf3)( di, debug_frame_img,
-                                          debug_frame_sz, False );
+         ML_(read_callframe_info_dwarf3)( di,
+                                          debug_frame_img, debug_frame_sz,
+                                          0/*assume zero avma*/,
+                                          False/*!is_ehframe*/ );
       }
 
       /* Read the stabs and/or dwarf2 debug information, if any.  It
          appears reading stabs stuff on amd64-linux doesn't work, so
-         we ignore it. */
-#     if !defined(VGP_amd64_linux)
+         we ignore it.  On s390x stabs also doesnt work and we always
+         have the dwarf info in the eh_frame.  We also segfault on
+         ppc64-linux when reading stabs, so skip that.  ppc32-linux
+         seems OK though.  Also skip on Android. */
+#     if !defined(VGP_amd64_linux) \
+         && !defined(VGP_s390x_linux) \
+         && !defined(VGP_ppc64_linux) \
+         && !defined(VGPV_arm_linux_android)
       if (stab_img && stabstr_img) {
          ML_(read_debuginfo_stabs) ( di, stab_img, stab_sz, 
                                          stabstr_img, stabstr_sz );
@@ -2241,21 +2558,58 @@
          ML_(read_debuginfo_dwarf1) ( di, dwarf1d_img, dwarf1d_sz, 
                                           dwarf1l_img, dwarf1l_sz );
       }
-   }
+      /* TOPLEVEL */
+
+   } /* "Find interesting sections, read the symbol table(s), read any debug
+        information" (a local scope) */
+
+   /* TOPLEVEL */
    res = True;
 
-  out: {
-   SysRes m_res;
-
-   /* Last, but not least, heave the image(s) back overboard. */
-   if (dimage) {
-      m_res = VG_(am_munmap_valgrind) ( dimage, n_dimage );
-      vg_assert(!sr_isError(m_res));
+   /* If reading Dwarf3 variable type/location info, print a line
+      showing the number of variables read for each object.
+      (Currently disabled -- is a sanity-check mechanism for
+      exp-sgcheck.) */
+   if (0 && (VG_(needs).var_info || VG_(clo_read_var_info))) {
+      UWord nVars = 0;
+      Word  j;
+      if (di->varinfo) {
+         for (j = 0; j < VG_(sizeXA)(di->varinfo); j++) {
+            OSet* /* of DiAddrRange */ scope
+               = *(OSet**)VG_(indexXA)(di->varinfo, j);
+            vg_assert(scope);
+            VG_(OSetGen_ResetIter)( scope );
+            while (True) {
+               DiAddrRange* range  = VG_(OSetGen_Next)( scope );
+               if (!range) break;
+               vg_assert(range->vars);
+               Word w = VG_(sizeXA)(range->vars);
+               vg_assert(w >= 0);
+               if (0) VG_(printf)("range %#lx %#lx %ld\n",
+                                  range->aMin, range->aMax, w);
+               nVars += (UWord)w;
+            }
+         }
+      }
+      VG_(umsg)("VARINFO: %7lu vars   %7ld text_size   %s\n",
+                nVars, di->text_size, di->fsm.filename);
    }
-   m_res = VG_(am_munmap_valgrind) ( oimage, n_oimage );
-   vg_assert(!sr_isError(m_res));
-   return res;
-  } 
+   /* TOPLEVEL */
+
+  out: 
+   {
+      SysRes m_res;
+      /* Last, but not least, heave the image(s) back overboard. */
+      if (dimage) {
+         m_res = VG_(am_munmap_valgrind) ( dimage, n_dimage );
+         vg_assert(!sr_isError(m_res));
+      }
+      m_res = VG_(am_munmap_valgrind) ( oimage, n_oimage );
+      vg_assert(!sr_isError(m_res));
+      return res;
+   } /* out: */ 
+
+   /* NOTREACHED */
 }
 
 #endif // defined(VGO_linux)