Blob Blame History Raw
From 60f7e89ba32b54d73b9e36d49e28d0f559ade0b9 Mon Sep 17 00:00:00 2001
From: Aaron Merey <amerey@redhat.com>
Date: Fri, 30 Jun 2023 18:31:42 -0400
Subject: [PATCH] Support lazy reading and downloading of DWARF debuginfo

Currently valgrind attempts to read DWARF .debug_* sections as well
as separate debuginfo files for ELF binaries as soon as a shared library
is loaded.  This might also result in the downloading of separate debuginfo
files via debuginfod.

This is inefficient when some of this debuginfo never ends up being used
by valgrind while running the client process.

This patch adds support for lazy reading and downloading of DWARF
debuginfo.  When an ELF shared library is loaded, the reading of .debug_*
sections as well as separate or alternate debuginfo is deferred until
valgrind handles an instruction pointer corresponding to a text segment
of the shared library.  At this point the deferred sections and separate
debug files are loaded.

This feature is only supported on ELF platforms.

https://bugs.kde.org/show_bug.cgi?id=471807

ChangeLog
	* debuginfo.c (di_notify_ACHIEVE_ACCEPT_STATE): Replace
	read_elf_debug_info with read_elf_object.
	(addr_load_di): New function.  Attempts to load deferred debuginfo
	associated with a given address.
	(load_di): New function.  Attempts to load a given deferred
	debuginfo associated with a given address.
	(describe_IP): Add calls to load_di and addr_load_di.
	(find_DiCfSI): Add call to load_di.

	* priv_readelf.h (read_elf_object): New declaration.
	(read_elf_debug): Ditto.

	* priv_storage.h (struct _DebugInfo): New field 'bool deferred'.

	* readelf.c (read_elf_debug_info): Split into read_elf_object and
	read_elf_debug.
	(read_elf_object): Read non .debug_* section from an ELF binary.
	(read_elf_debug): Read .debug_* sections from an ELF binary as
	as well any separate/alternate debuginfo files.

	* storage.c (canonicaliseSymtab): Remove assert in order to support
	canonicalization of deferred _DebugInfo.
	(finish_CFSI_arrays): Add early return if _DebugInfo is
	deferred in order to avoid freeing memory that will be needed
	when reading debuginfo at a later time.
	(canonicaliseTables): Ditto.

	* pub_core_debuginfo.h (addr_load_di): New declaration.
	(load_di): New declaration.
---
 NEWS                                 |   1 +
 coregrind/m_debuginfo/debuginfo.c    |  57 +++-
 coregrind/m_debuginfo/priv_readelf.h |  24 +-
 coregrind/m_debuginfo/priv_storage.h |   7 +
 coregrind/m_debuginfo/readelf.c      | 437 ++++++++++++++++++---------
 coregrind/m_debuginfo/storage.c      |  13 +-
 coregrind/pub_core_debuginfo.h       |   4 +
 7 files changed, 379 insertions(+), 164 deletions(-)

diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c
index 22b41def2..8d1fdc696 100644
--- a/coregrind/m_debuginfo/debuginfo.c
+++ b/coregrind/m_debuginfo/debuginfo.c
@@ -959,14 +959,16 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di )
    discard_DebugInfos_which_overlap_with( di );
 
    /* The DebugInfoMappings that now exist in the FSM may involve
-      overlaps.  This confuses ML_(read_elf_debug_info), and may cause
+      overlaps.  This confuses ML_(read_elf_*), and may cause
       it to compute wrong biases.  So de-overlap them now.
       See http://bugzilla.mozilla.org/show_bug.cgi?id=788974 */
    truncate_DebugInfoMapping_overlaps( di, di->fsm.maps );
 
    /* And acquire new info. */
 #  if defined(VGO_linux) || defined(VGO_solaris) || defined(VGO_freebsd)
-   ok = ML_(read_elf_debug_info)( di );
+   ok = ML_(read_elf_object)( di );
+   if (ok)
+      di->deferred = True;
 #  elif defined(VGO_darwin)
    ok = ML_(read_macho_debug_info)( di );
 #  else
@@ -1443,6 +1445,50 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd )
    }
 }
 
+/* Load DI if it has a text segment containing A and DI hasn't already
+   been loaded.  */
+
+void VG_(load_di)( DebugInfo *di, Addr a)
+{
+   if (!di->deferred
+       || !di->text_present
+       || di->text_size <= 0
+       || di->text_avma > a
+       || a >= di->text_avma + di->text_size)
+      return;
+
+   di->deferred = False;
+   ML_(read_elf_debug) (di);
+   ML_(canonicaliseTables)( di );
+
+   /* Check invariants listed in
+      Comment_on_IMPORTANT_REPRESENTATIONAL_INVARIANTS in
+      priv_storage.h. */
+   check_CFSI_related_invariants(di);
+   ML_(finish_CFSI_arrays)(di);
+}
+
+/* Attempt to load DebugInfo with a text segment containing A,
+   if such a debuginfo hasn't already been loaded.  */
+
+void VG_(addr_load_di)( Addr a )
+{
+   DebugInfo *di;
+
+   di = VG_(find_DebugInfo)(VG_(current_DiEpoch)(), a);
+   if (di != NULL)
+      if (di->deferred) {
+         di->deferred = False;
+         ML_(read_elf_debug) (di);
+         ML_(canonicaliseTables)( di );
+
+         /* Check invariants listed in
+            Comment_on_IMPORTANT_REPRESENTATIONAL_INVARIANTS in
+            priv_storage.h. */
+         check_CFSI_related_invariants(di);
+         ML_(finish_CFSI_arrays)(di);
+      }
+}
 
 /* Unmap is simpler - throw away any SegInfos intersecting 
    [a, a+len).  */
@@ -2746,6 +2792,11 @@ const HChar* VG_(describe_IP)(DiEpoch ep, Addr eip, const InlIPCursor *iipc)
    Bool  know_objname;
    Bool  know_srcloc;
 
+   if (iipc && iipc->di)
+      VG_(load_di) (iipc->di, eip);
+   else
+      VG_(addr_load_di) (eip);
+
    if (is_bottom(iipc)) {
       // At the bottom (towards main), we describe the fn at eip.
       know_fnname = VG_(clo_sym_offsets)
@@ -3090,6 +3141,8 @@ static void find_DiCfSI ( /*OUT*/DebugInfo** diP,
       if (!is_DI_valid_for_epoch(di, curr_epoch))
          continue;
 
+      VG_(load_di)(di, ip);
+
       /* Use the per-DebugInfo summary address ranges to skip
          inapplicable DebugInfos quickly. */
       if (di->cfsi_used == 0)
diff --git a/coregrind/m_debuginfo/priv_readelf.h b/coregrind/m_debuginfo/priv_readelf.h
index 57aa0cc3f..7e0fa17c9 100644
--- a/coregrind/m_debuginfo/priv_readelf.h
+++ b/coregrind/m_debuginfo/priv_readelf.h
@@ -44,13 +44,23 @@
 extern Bool ML_(is_elf_object_file)( const void* image, SizeT n_image,
                                      Bool rel_ok );
 
-/* The central function for reading ELF debug info.  For the
-   object/exe specified by the SegInfo, 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 SegInfo.
-*/
-extern Bool ML_(read_elf_debug_info) ( DebugInfo* di );
+/* Read the ELF binary specified by DI.  For the object/exe specified
+   by the SegInfo, 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 SegInfo.
+
+   .debug_* sections as well as any separate debuginfo files are not
+   loaded by this function but instead by ML_(read_elf_debug).  This
+   separation facilitates lazy loading of debuginfo.  */
+extern Bool ML_(read_elf_object) ( DebugInfo* di );
+
+/* Read .debug_* sections from the ELF binary specified by DI.  Also
+   attempt to load any separate debuginfo files associated with the
+   object.
+
+   ML_(read_elf_object) should be called on DI before calling this
+   function.  */
+extern Bool ML_(read_elf_debug) ( DebugInfo* di );
 
 extern Bool ML_(check_elf_and_get_rw_loads) ( Int fd, const HChar* filename, Int * rw_load_count );
 
diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h
index a4b90d36b..b959873ab 100644
--- a/coregrind/m_debuginfo/priv_storage.h
+++ b/coregrind/m_debuginfo/priv_storage.h
@@ -678,6 +678,13 @@ struct _DebugInfo {
       invalid and should not be consulted. */
    Bool  have_dinfo; /* initially False */
 
+   /* If true then the reading of .debug_* section has been deferred
+      until it this information is required (such as when printing
+      a stacktrace).  Additionally, if true then the reading of any
+      separate debuginfo files associated with this object has also
+      been deferred. */
+   Bool deferred;
+
    /* All the rest of the fields in this structure are filled in once
       we have committed to reading the symbols and debug info (that
       is, at the point where .have_dinfo is set to True). */
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
index ce7b7998d..f99d3dfd2 100644
--- a/coregrind/m_debuginfo/readelf.c
+++ b/coregrind/m_debuginfo/readelf.c
@@ -1836,6 +1836,44 @@ static HChar* readlink_path (const HChar *path)
   return buf;
 }
 
+#define FINDX_MIMG(_sec_name, _sec_escn, _post_fx) \
+   do { \
+      ElfXX_Shdr a_shdr; \
+      ML_(img_get)(&a_shdr, mimg, \
+                   INDEX_BIS(shdr_mioff, i, shdr_ment_szB), \
+                   sizeof(a_shdr)); \
+      if (0 == ML_(img_strcmp_c)(mimg, shdr_strtab_mioff \
+                                        + a_shdr.sh_name, _sec_name)) { \
+         Bool nobits; \
+         _sec_escn.img  = mimg; \
+         _sec_escn.ioff = (DiOffT)a_shdr.sh_offset; \
+         _sec_escn.szB  = a_shdr.sh_size; \
+         if (!check_compression(&a_shdr, &_sec_escn)) { \
+            ML_(symerr)(di, True, "   Compression type is unsupported"); \
+            goto out; \
+         } \
+         nobits         = a_shdr.sh_type == SHT_NOBITS; \
+         vg_assert(_sec_escn.img  != NULL); \
+         vg_assert(_sec_escn.ioff != DiOffT_INVALID); \
+         TRACE_SYMTAB( "%-18s:  ioff %llu .. %llu\n", \
+                       _sec_name, (ULong)a_shdr.sh_offset, \
+                       ((ULong)a_shdr.sh_offset) + a_shdr.sh_size - 1); \
+         /* SHT_NOBITS sections have zero size in the file. */ \
+         if (!nobits && \
+             a_shdr.sh_offset + \
+                a_shdr.sh_size > ML_(img_real_size)(mimg)) { \
+            ML_(symerr)(di, True, \
+                        "   section beyond image end?!"); \
+            goto out; \
+         } \
+         _post_fx; \
+      } \
+   } while (0);
+
+/* Version with no post-effects */
+#define FIND_MIMG(_sec_name, _sec_escn) \
+   FINDX_MIMG(_sec_name, _sec_escn, /**/)
+
 /* 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
@@ -1843,7 +1881,7 @@ static HChar* readlink_path (const HChar *path)
    supplied DebugInfo.
 */
 
-Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
+Bool ML_(read_elf_object) ( 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
@@ -1874,7 +1912,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 
 
    /* TOPLEVEL */
-   Bool     res, ok;
+   Bool     ok;
    Word     i, j;
    Bool     dynbss_present = False;
    Bool     sdynbss_present = False;
@@ -1882,12 +1920,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
    /* Image for the main ELF file we're working with. */
    DiImage* mimg = NULL;
 
-   /* Ditto for any ELF debuginfo file that we might happen to load. */
-   DiImage* dimg = NULL;
-
-   /* Ditto for alternate ELF debuginfo file that we might happen to load. */
-   DiImage* aimg = NULL;
-
    /* ELF header offset for the main file.  Should be zero since the
       ELF header is at start of file. */
    DiOffT   ehdr_mioff = 0;
@@ -1970,8 +2002,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       got, plt, and toc.
       ---------------------------------------------------------- */
 
-   res = False;
-
    if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
       VG_(message)(Vg_DebugMsg, "Reading syms from %s\n",
                                 di->fsm.filename );
@@ -2056,7 +2086,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
      shdr_strtab_mioff
         = ehdr_mioff /* isn't this always zero? */ + a_shdr.sh_offset;
 
-     if (!ML_(img_valid)(mimg, shdr_strtab_mioff, 
+     if (!ML_(img_valid)(mimg, shdr_strtab_mioff,
                          1/*bogus, but we don't know the real size*/ )) {
         ML_(symerr)(di, True, "Invalid ELF Section Header String Table");
         goto out;
@@ -2798,10 +2828,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                                 di->text_avma - di->text_bias,
                                 di->text_avma );
 
-   TRACE_SYMTAB("\n");
-   TRACE_SYMTAB("------ Finding image addresses "
-                "for debug-info sections ------\n");
-
    /* TOPLEVEL */
    /* Find interesting sections, read the symbol table(s), read any
       debug information.  Each section is located either in the main,
@@ -2821,27 +2847,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
 #     if defined(VGO_solaris)
       DiSlice ldynsym_escn        = DiSlice_INVALID; // .SUNW_ldynsym
 #     endif
-      DiSlice debuglink_escn      = DiSlice_INVALID; // .gnu_debuglink
-      DiSlice debugaltlink_escn   = DiSlice_INVALID; // .gnu_debugaltlink
-      DiSlice debug_line_escn     = DiSlice_INVALID; // .debug_line   (dwarf2)
-      DiSlice debug_info_escn     = DiSlice_INVALID; // .debug_info   (dwarf2)
-      DiSlice debug_types_escn    = DiSlice_INVALID; // .debug_types  (dwarf4)
-      DiSlice debug_abbv_escn     = DiSlice_INVALID; // .debug_abbrev (dwarf2)
-      DiSlice debug_str_escn      = DiSlice_INVALID; // .debug_str    (dwarf2)
-      DiSlice debug_line_str_escn = DiSlice_INVALID; // .debug_line_str(dwarf5)
-      DiSlice debug_ranges_escn   = DiSlice_INVALID; // .debug_ranges (dwarf2)
-      DiSlice debug_rnglists_escn = DiSlice_INVALID; // .debug_rnglists(dwarf5)
-      DiSlice debug_loclists_escn = DiSlice_INVALID; // .debug_loclists(dwarf5)
-      DiSlice debug_addr_escn     = DiSlice_INVALID; // .debug_addr   (dwarf5)
-      DiSlice debug_str_offsets_escn = DiSlice_INVALID; // .debug_str_offsets (dwarf5)
-      DiSlice debug_loc_escn      = DiSlice_INVALID; // .debug_loc    (dwarf2)
-      DiSlice debug_frame_escn    = DiSlice_INVALID; // .debug_frame  (dwarf2)
-      DiSlice debug_line_alt_escn = DiSlice_INVALID; // .debug_line   (alt)
-      DiSlice debug_info_alt_escn = DiSlice_INVALID; // .debug_info   (alt)
-      DiSlice debug_abbv_alt_escn = DiSlice_INVALID; // .debug_abbrev (alt)
-      DiSlice debug_str_alt_escn  = DiSlice_INVALID; // .debug_str    (alt)
-      DiSlice dwarf1d_escn        = DiSlice_INVALID; // .debug        (dwarf1)
-      DiSlice dwarf1l_escn        = DiSlice_INVALID; // .line         (dwarf1)
       DiSlice opd_escn            = DiSlice_INVALID; // .opd (dwarf2, 
                                                      //       ppc64be-linux)
       DiSlice ehframe_escn[N_EHFRAME_SECTS];         // .eh_frame (dwarf2)
@@ -2868,118 +2873,282 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       /* TOPLEVEL */
       /* Iterate over section headers (again) */
       for (i = 0; i < ehdr_m.e_shnum; i++) {
+         /*      NAME                  ElfSec */
+         FIND_MIMG(   ".dynsym",            dynsym_escn)
+         FIND_MIMG(   ".dynstr",            dynstr_escn)
+         FIND_MIMG(   ".symtab",            symtab_escn)
+         FIND_MIMG(   ".strtab",            strtab_escn)
+#        if defined(VGO_solaris)
+         FIND_MIMG(   ".SUNW_ldynsym",      ldynsym_escn)
+#        endif
 
-#        define FINDX(_sec_name, _sec_escn, _post_fx) \
-         do { \
-            ElfXX_Shdr a_shdr; \
-            ML_(img_get)(&a_shdr, mimg, \
-                         INDEX_BIS(shdr_mioff, i, shdr_ment_szB), \
-                         sizeof(a_shdr)); \
-            if (0 == ML_(img_strcmp_c)(mimg, shdr_strtab_mioff \
-                                              + a_shdr.sh_name, _sec_name)) { \
-               Bool nobits; \
-               _sec_escn.img  = mimg; \
-               _sec_escn.ioff = (DiOffT)a_shdr.sh_offset; \
-               _sec_escn.szB  = a_shdr.sh_size; \
-               if (!check_compression(&a_shdr, &_sec_escn)) { \
-                  ML_(symerr)(di, True, "   Compression type is unsupported"); \
-                  goto out; \
-               } \
-               nobits         = a_shdr.sh_type == SHT_NOBITS; \
-               vg_assert(_sec_escn.img  != NULL); \
-               vg_assert(_sec_escn.ioff != DiOffT_INVALID); \
-               TRACE_SYMTAB( "%-18s:  ioff %llu .. %llu\n", \
-                             _sec_name, (ULong)a_shdr.sh_offset, \
-                             ((ULong)a_shdr.sh_offset) + a_shdr.sh_size - 1); \
-               /* SHT_NOBITS sections have zero size in the file. */ \
-               if (!nobits && \
-                   a_shdr.sh_offset + \
-                      a_shdr.sh_size > ML_(img_real_size)(mimg)) { \
-                  ML_(symerr)(di, True, \
-                              "   section beyond image end?!"); \
-                  goto out; \
-               } \
-               _post_fx; \
-            } \
-         } while (0);
+         FINDX_MIMG(  ".eh_frame",          ehframe_escn[ehframe_mix],
+               do { ehframe_mix++; vg_assert(ehframe_mix <= 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_escn[] 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.
+         */
+      } /* Iterate over section headers (again) */
 
-         /* Version with no post-effects */
-#        define FIND(_sec_name, _sec_escn) \
-            FINDX(_sec_name, _sec_escn, /**/)
+      /* Check some sizes */
+      vg_assert((dynsym_escn.szB % sizeof(ElfXX_Sym)) == 0);
+      vg_assert((symtab_escn.szB % sizeof(ElfXX_Sym)) == 0);
+#     if defined(VGO_solaris)
+      vg_assert((ldynsym_escn.szB % sizeof(ElfXX_Sym)) == 0);
+#     endif
 
-         /*      NAME                  ElfSec */
-         FIND(   ".dynsym",            dynsym_escn)
-         FIND(   ".dynstr",            dynstr_escn)
-         FIND(   ".symtab",            symtab_escn)
-         FIND(   ".strtab",            strtab_escn)
+      /* Read symbols */
+      {
+         void (*read_elf_symtab)(struct _DebugInfo*, const HChar*,
+                                 DiSlice*, DiSlice*, DiSlice*, Bool);
+#        if defined(VGP_ppc64be_linux)
+         read_elf_symtab = read_elf_symtab__ppc64be_linux;
+#        else
+         read_elf_symtab = read_elf_symtab__normal;
+#        endif
+         if (symtab_escn.img != NULL)
+            read_elf_symtab(di, "symbol table",
+                            &symtab_escn, &strtab_escn, &opd_escn,
+                            False);
+         read_elf_symtab(di, "dynamic symbol table",
+                         &dynsym_escn, &dynstr_escn, &opd_escn,
+                         False);
 #        if defined(VGO_solaris)
-         FIND(   ".SUNW_ldynsym",      ldynsym_escn)
+         read_elf_symtab(di, "local dynamic symbol table",
+                         &ldynsym_escn, &dynstr_escn, &opd_escn,
+                         False);
 #        endif
+      }
 
-         FIND(   ".gnu_debuglink",     debuglink_escn)
-         FIND(   ".gnu_debugaltlink",  debugaltlink_escn)
+      /* 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(ML_(sli_is_valid)(ehframe_escn[i]));
+         vg_assert(ehframe_escn[i].szB == di->ehframe_size[i]);
+         ML_(read_callframe_info_dwarf3)( di,
+                                          ehframe_escn[i],
+                                          di->ehframe_avma[i],
+                                          True/*is_ehframe*/ );
+      }
+   }
+
+  return True;
+
+  out:
+   {
+      /* Last, but not least, detach from the image. */
+      if (mimg) ML_(img_done)(mimg);
+
+      if (svma_ranges) VG_(deleteXA)(svma_ranges);
 
-         FIND(   ".debug_line",        debug_line_escn)
+      return False;
+   } /* out: */
+
+   /* NOTREACHED */
+}
+
+Bool ML_(read_elf_debug) ( struct _DebugInfo* di )
+{
+   Word     i, j;
+   Bool     res = True;
+   Bool     ok;
+
+   /* Image for the main ELF file we're working with. */
+   DiImage* mimg = NULL;
+
+   /* Ditto for any ELF debuginfo file that we might happen to load. */
+   DiImage* dimg = NULL;
+
+   /* Ditto for alternate ELF debuginfo file that we might happen to load. */
+   DiImage* aimg = NULL;
+
+   /* Section header image addr, # entries, entry size.  Also the
+      associated string table. */
+   DiOffT   shdr_mioff        = 0;
+   UWord    shdr_mnent        = 0;
+   UWord    shdr_ment_szB     = 0;
+   DiOffT   shdr_strtab_mioff = 0;
+
+   DiOffT   ehdr_mioff = 0;
+
+   /* Connect to the primary object image, so that we can read symbols
+      and line number info out of it.  It will be disconnected
+      immediately thereafter; it is only connected transiently. */
+   mimg = ML_(img_from_local_file)(di->fsm.filename);
+   if (mimg == NULL) {
+      VG_(message)(Vg_UserMsg, "warning: connection to image %s failed\n",
+                               di->fsm.filename );
+      VG_(message)(Vg_UserMsg, "         no debug info loaded\n" );
+      return False;
+   }
+
+ /* Ok, the object image is available.  Now verify that it is a
+      valid ELF .so or executable image. */
+   ok = is_elf_object_file_by_DiImage(mimg, False);
+   if (!ok) {
+      ML_(symerr)(di, True, "Invalid ELF Header");
+      goto out;
+   }
+
+   /* Find where the program and section header tables are, and give
+      up if either is missing or outside the image (bogus). */
+   ElfXX_Ehdr ehdr_m;
+   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));
+
+   shdr_mioff    = ehdr_mioff + ehdr_m.e_shoff;
+   shdr_mnent    = ehdr_m.e_shnum;
+   shdr_ment_szB = ehdr_m.e_shentsize;
+
+   if (shdr_mnent == 0
+       || !ML_(img_valid)(mimg, shdr_mioff, shdr_mnent * shdr_ment_szB)) {
+      ML_(symerr)(di, True, "Missing or invalid ELF Section Header Table");
+      goto out;
+   }
+
+   /* Also find the section header's string table, and validate. */
+   /* checked previously by is_elf_object_file: */
+   vg_assert(ehdr_m.e_shstrndx != SHN_UNDEF);
+
+   // shdr_mioff is the offset of the section header table
+   // and we need the ehdr_m.e_shstrndx'th entry
+   { ElfXX_Shdr a_shdr;
+     ML_(img_get)(&a_shdr, mimg,
+                  INDEX_BIS(shdr_mioff, ehdr_m.e_shstrndx, shdr_ment_szB),
+                  sizeof(a_shdr));
+     shdr_strtab_mioff
+        = ehdr_mioff /* isn't this always zero? */ + a_shdr.sh_offset;
+
+     if (!ML_(img_valid)(mimg, shdr_strtab_mioff,
+                         1/*bogus, but we don't know the real size*/ )) {
+        ML_(symerr)(di, True, "Invalid ELF Section Header String Table");
+        goto out;
+     }
+   }
+
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("------ Finding image addresses "
+                "for debug-info sections ------\n");
+   /* TOPLEVEL */
+   /* Find interesting sections, read the symbol table(s), read any
+      debug information.  Each section is located either in the main,
+      debug or alt-debug files, but only in one.  For each section,
+      |section_escn| records which of |mimg|, |dimg| or |aimg| we
+      found it in, along with the section's image offset and its size.
+      The triples (section_img, section_ioff, section_szB) are
+      consistent, in that they are always either (NULL,
+      DiOffT_INVALID, 0), or refer to the same image, and are all
+      assigned together. */
+
+   {
+      /* TOPLEVEL */
+      DiSlice strtab_escn         = DiSlice_INVALID; // .strtab
+      DiSlice symtab_escn         = DiSlice_INVALID; // .symtab
+      DiSlice debuglink_escn      = DiSlice_INVALID; // .gnu_debuglink
+      DiSlice debugaltlink_escn   = DiSlice_INVALID; // .gnu_debugaltlink
+      DiSlice debug_line_escn     = DiSlice_INVALID; // .debug_line   (dwarf2)
+      DiSlice debug_info_escn     = DiSlice_INVALID; // .debug_info   (dwarf2)
+      DiSlice debug_types_escn    = DiSlice_INVALID; // .debug_types  (dwarf4)
+      DiSlice debug_abbv_escn     = DiSlice_INVALID; // .debug_abbrev (dwarf2)
+      DiSlice debug_str_escn      = DiSlice_INVALID; // .debug_str    (dwarf2)
+      DiSlice debug_line_str_escn = DiSlice_INVALID; // .debug_line_str(dwarf5)
+      DiSlice debug_ranges_escn   = DiSlice_INVALID; // .debug_ranges (dwarf2)
+      DiSlice debug_rnglists_escn = DiSlice_INVALID; // .debug_rnglists(dwarf5)
+      DiSlice debug_loclists_escn = DiSlice_INVALID; // .debug_loclists(dwarf5)
+      DiSlice debug_addr_escn     = DiSlice_INVALID; // .debug_addr   (dwarf5)
+      DiSlice debug_str_offsets_escn = DiSlice_INVALID; // .debug_str_offsets (dwarf5)
+      DiSlice debug_loc_escn      = DiSlice_INVALID; // .debug_loc    (dwarf2)
+      DiSlice debug_frame_escn    = DiSlice_INVALID; // .debug_frame  (dwarf2)
+      DiSlice debug_line_alt_escn = DiSlice_INVALID; // .debug_line   (alt)
+      DiSlice debug_info_alt_escn = DiSlice_INVALID; // .debug_info   (alt)
+      DiSlice debug_abbv_alt_escn = DiSlice_INVALID; // .debug_abbrev (alt)
+      DiSlice debug_str_alt_escn  = DiSlice_INVALID; // .debug_str    (alt)
+      DiSlice dwarf1d_escn        = DiSlice_INVALID; // .debug        (dwarf1)
+      DiSlice dwarf1l_escn        = DiSlice_INVALID; // .line         (dwarf1)
+      DiSlice opd_escn            = DiSlice_INVALID; // .opd (dwarf2,
+                                                     //       ppc64be-linux)
+
+      /* TOPLEVEL */
+      /* Iterate over section headers (again) */
+      for (i = 0; i < ehdr_m.e_shnum; i++) {
+
+         /*      NAME                  ElfSec */
+         FIND_MIMG(   ".symtab",            symtab_escn)
+         FIND_MIMG(   ".strtab",            strtab_escn)
+         FIND_MIMG(   ".gnu_debuglink",     debuglink_escn)
+         FIND_MIMG(   ".gnu_debugaltlink",  debugaltlink_escn)
+
+         FIND_MIMG(   ".debug_line",        debug_line_escn)
          if (!ML_(sli_is_valid)(debug_line_escn))
-            FIND(".zdebug_line",       debug_line_escn)
+            FIND_MIMG(".zdebug_line",       debug_line_escn)
 
-         FIND(   ".debug_info",        debug_info_escn)
+         FIND_MIMG(   ".debug_info",        debug_info_escn)
          if (!ML_(sli_is_valid)(debug_info_escn))
-            FIND(".zdebug_info",       debug_info_escn)
+            FIND_MIMG(".zdebug_info",       debug_info_escn)
 
-         FIND(   ".debug_types",       debug_types_escn)
+         FIND_MIMG(   ".debug_types",       debug_types_escn)
          if (!ML_(sli_is_valid)(debug_types_escn))
-            FIND(".zdebug_types",      debug_types_escn)
+            FIND_MIMG(".zdebug_types",      debug_types_escn)
 
-         FIND(   ".debug_abbrev",      debug_abbv_escn)
+         FIND_MIMG(   ".debug_abbrev",      debug_abbv_escn)
          if (!ML_(sli_is_valid)(debug_abbv_escn))
-            FIND(".zdebug_abbrev",     debug_abbv_escn)
+            FIND_MIMG(".zdebug_abbrev",     debug_abbv_escn)
 
-         FIND(   ".debug_str",         debug_str_escn)
+         FIND_MIMG(   ".debug_str",         debug_str_escn)
          if (!ML_(sli_is_valid)(debug_str_escn))
-            FIND(".zdebug_str",        debug_str_escn)
+            FIND_MIMG(".zdebug_str",        debug_str_escn)
 
-         FIND(   ".debug_line_str",    debug_line_str_escn)
+         FIND_MIMG(   ".debug_line_str",    debug_line_str_escn)
          if (!ML_(sli_is_valid)(debug_line_str_escn))
-            FIND(".zdebug_str",        debug_line_str_escn)
+            FIND_MIMG(".zdebug_str",        debug_line_str_escn)
 
-         FIND(   ".debug_ranges",      debug_ranges_escn)
+         FIND_MIMG(   ".debug_ranges",      debug_ranges_escn)
          if (!ML_(sli_is_valid)(debug_ranges_escn))
-            FIND(".zdebug_ranges",     debug_ranges_escn)
+            FIND_MIMG(".zdebug_ranges",     debug_ranges_escn)
 
-         FIND(   ".debug_rnglists",    debug_rnglists_escn)
+         FIND_MIMG(   ".debug_rnglists",    debug_rnglists_escn)
          if (!ML_(sli_is_valid)(debug_rnglists_escn))
-            FIND(".zdebug_rnglists",   debug_rnglists_escn)
+            FIND_MIMG(".zdebug_rnglists",   debug_rnglists_escn)
 
-         FIND(   ".debug_loclists",    debug_loclists_escn)
+         FIND_MIMG(   ".debug_loclists",    debug_loclists_escn)
          if (!ML_(sli_is_valid)(debug_loclists_escn))
-            FIND(".zdebug_loclists",   debug_loclists_escn)
+            FIND_MIMG(".zdebug_loclists",   debug_loclists_escn)
 
-         FIND(   ".debug_loc",         debug_loc_escn)
+         FIND_MIMG(   ".debug_loc",         debug_loc_escn)
          if (!ML_(sli_is_valid)(debug_loc_escn))
-            FIND(".zdebug_loc",    debug_loc_escn)
+            FIND_MIMG(".zdebug_loc",    debug_loc_escn)
 
-         FIND(   ".debug_frame",       debug_frame_escn)
+         FIND_MIMG(   ".debug_frame",       debug_frame_escn)
          if (!ML_(sli_is_valid)(debug_frame_escn))
-            FIND(".zdebug_frame",      debug_frame_escn)
+            FIND_MIMG(".zdebug_frame",      debug_frame_escn)
 
-         FIND(   ".debug_addr",        debug_addr_escn)
+         FIND_MIMG(   ".debug_addr",        debug_addr_escn)
          if (!ML_(sli_is_valid)(debug_addr_escn))
-            FIND(".zdebug_addr",       debug_addr_escn)
+            FIND_MIMG(".zdebug_addr",       debug_addr_escn)
 
-         FIND(   ".debug_str_offsets", debug_str_offsets_escn)
+         FIND_MIMG(   ".debug_str_offsets", debug_str_offsets_escn)
          if (!ML_(sli_is_valid)(debug_str_offsets_escn))
-            FIND(".zdebug_str_offsets", debug_str_offsets_escn)
+            FIND_MIMG(".zdebug_str_offsets", debug_str_offsets_escn)
 
-         FIND(   ".debug",             dwarf1d_escn)
-         FIND(   ".line",              dwarf1l_escn)
+         FIND_MIMG(   ".debug",             dwarf1d_escn)
+         FIND_MIMG(   ".line",              dwarf1l_escn)
 
-         FIND(   ".opd",               opd_escn)
+	 FIND_MIMG(   ".opd",               opd_escn)
 
-         FINDX(  ".eh_frame",          ehframe_escn[ehframe_mix],
-               do { ehframe_mix++; vg_assert(ehframe_mix <= 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_escn[] is consistent with the
@@ -2991,8 +3160,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
             order and so fill in these arrays in a consistent order.
          */
 
-#        undef FINDX
-#        undef FIND
       } /* Iterate over section headers (again) */
 
       /* TOPLEVEL */
@@ -3465,53 +3632,23 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
          } /* Find all interesting sections */
       } /* do we have a debug image? */
 
-
       /* TOPLEVEL */
-      /* Check some sizes */
-      vg_assert((dynsym_escn.szB % sizeof(ElfXX_Sym)) == 0);
       vg_assert((symtab_escn.szB % sizeof(ElfXX_Sym)) == 0);
-#     if defined(VGO_solaris)
-      vg_assert((ldynsym_escn.szB % sizeof(ElfXX_Sym)) == 0);
-#     endif
 
       /* TOPLEVEL */
       /* Read symbols */
       {
          void (*read_elf_symtab)(struct _DebugInfo*, const HChar*,
                                  DiSlice*, DiSlice*, DiSlice*, Bool);
-         Bool symtab_in_debug;
 #        if defined(VGP_ppc64be_linux)
          read_elf_symtab = read_elf_symtab__ppc64be_linux;
 #        else
          read_elf_symtab = read_elf_symtab__normal;
 #        endif
-         symtab_in_debug = symtab_escn.img == dimg;
-         read_elf_symtab(di, "symbol table",
-                         &symtab_escn, &strtab_escn, &opd_escn,
-                         symtab_in_debug);
-         read_elf_symtab(di, "dynamic symbol table",
-                         &dynsym_escn, &dynstr_escn, &opd_escn,
-                         False);
-#        if defined(VGO_solaris)
-         read_elf_symtab(di, "local dynamic symbol table",
-                         &ldynsym_escn, &dynstr_escn, &opd_escn,
-                         False);
-#        endif
-      }
-
-      /* 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(ML_(sli_is_valid)(ehframe_escn[i]));
-         vg_assert(ehframe_escn[i].szB == di->ehframe_size[i]);
-         ML_(read_callframe_info_dwarf3)( di,
-                                          ehframe_escn[i],
-                                          di->ehframe_avma[i],
-                                          True/*is_ehframe*/ );
+         if (symtab_escn.img != NULL)
+            read_elf_symtab(di, "symbol table",
+                            &symtab_escn, &strtab_escn, &opd_escn,
+                            True);
       }
       if (ML_(sli_is_valid)(debug_frame_escn)) {
          ML_(read_callframe_info_dwarf3)( di,
@@ -3643,8 +3780,6 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       if (dimg) ML_(img_done)(dimg);
       if (aimg) ML_(img_done)(aimg);
 
-      if (svma_ranges) VG_(deleteXA)(svma_ranges);
-
       return res;
    } /* out: */ 
 
diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c
index c3fa62e96..3ad114607 100644
--- a/coregrind/m_debuginfo/storage.c
+++ b/coregrind/m_debuginfo/storage.c
@@ -1297,8 +1297,7 @@ void ML_(addVar)( struct _DebugInfo* di,
       that those extra sections have the same bias as .text, but that
       seems a reasonable assumption to me. */
    /* 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). */
+      and it is re-checked at the start of ML_(read_elf_object). */
    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) {
@@ -1725,7 +1724,6 @@ static void canonicaliseSymtab ( struct _DebugInfo* di )
    for (i = 0; i < di->symtab_used; i++) {
       DiSym* sym = &di->symtab[i];
       vg_assert(sym->pri_name);
-      vg_assert(!sym->sec_names);
    }
 
    /* Sort by address. */
@@ -2383,6 +2381,9 @@ void ML_(finish_CFSI_arrays) ( struct _DebugInfo* di )
    vg_assert (f_holes == n_holes);
    vg_assert (pos == new_used);
 
+   if (di->deferred)
+      return;
+
    di->cfsi_used = new_used;
    di->cfsi_size = new_used;
    ML_(dinfo_free) (di->cfsi_rd);
@@ -2398,9 +2399,13 @@ void ML_(canonicaliseTables) ( struct _DebugInfo* di )
    canonicaliseLoctab ( di );
    canonicaliseInltab ( di );
    ML_(canonicaliseCFI) ( di );
+   canonicaliseVarInfo ( di );
+
+   if (di->deferred)
+      return;
+
    if (di->cfsi_m_pool)
       VG_(freezeDedupPA) (di->cfsi_m_pool, ML_(dinfo_shrink_block));
-   canonicaliseVarInfo ( di );
    if (di->strpool)
       VG_(freezeDedupPA) (di->strpool, ML_(dinfo_shrink_block));
    if (di->fndnpool)
diff --git a/coregrind/pub_core_debuginfo.h b/coregrind/pub_core_debuginfo.h
index 938ed00cc..ce7246217 100644
--- a/coregrind/pub_core_debuginfo.h
+++ b/coregrind/pub_core_debuginfo.h
@@ -76,6 +76,10 @@ extern void VG_(di_notify_pdb_debuginfo)( Int fd, Addr avma,
 extern void VG_(di_notify_vm_protect)( Addr a, SizeT len, UInt prot );
 #endif
 
+extern void VG_(addr_load_di)( Addr a );
+
+extern void VG_(load_di)( DebugInfo *di, Addr a );
+
 extern void VG_(di_discard_ALL_debuginfo)( void );
 
 /* Like VG_(get_fnname), but it does not do C++ demangling nor Z-demangling
-- 
2.41.0