]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
elf: Use mmap to map in symbol and relocation tables
authorH.J. Lu <hjl.tools@gmail.com>
Sun, 3 Mar 2024 16:44:01 +0000 (08:44 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Wed, 3 Apr 2024 16:11:03 +0000 (09:11 -0700)
Add _bfd_mmap_read_temporary to mmap in symbol tables and relocations
whose sizes >= 4 * page size.  For the final link, allocate an external
relocation buffer of 4 * page size to avoid using mmap and munmap on
smaller relocation sections.  Since _bfd_mmap_read_temporary allocates
buffer as needed, its callers don't need to.

When mmap is used to map in all ELF sections, data to link the 3.5GB
clang executable in LLVM 17 debug build on Linux/x86-64 with 32GB RAM
is:

stdio mmap improvement
user 84.79 85.27 -0.5%
system 10.95 9.09 17%
total 97.91 94.90 3%
page faults 4837944 4033778 17%

and data to link the 275M cc1plus executable in GCC 14 stage 1 build
is:

user 5.31 5.33 -0.4%
system 0.86 0.76 12%
total 6.19 6.13 1%
page faults 361273 322491 11%

* elf.c (bfd_elf_get_elf_syms): Don't allocate buffer for external
symbol table.  Replace bfd_read with _bfd_mmap_read_temporary.
* elflink.c (elf_link_read_relocs_from_section): Add 2 arguments
to return mmap memory address and size.
(_bfd_elf_link_info_read_relocs): Don't allocate buffer for
external relocation information.  Replace bfd_read with
_bfd_mmap_read_temporary.
(bfd_elf_final_link): Cache external relocations up to
_bfd_minimum_mmap_size bytes when mmap is used.
* libbfd.c (_bfd_mmap_read_temporary): New.
* libbfd-in.h (_bfd_mmap_read_temporary): Likewise.
* libbfd.h: Regenerated.

bfd/elf.c
bfd/elflink.c
bfd/libbfd-in.h
bfd/libbfd.c
bfd/libbfd.h

index 5d2996505f44274c1c02d4613122fd56cefd54ed..868abeccddbcbdf1355e71ff834c7e0d632fd36a 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -460,19 +460,16 @@ bfd_elf_get_elf_syms (bfd *ibfd,
       goto out;
     }
   pos = symtab_hdr->sh_offset + symoffset * extsym_size;
-  if (extsym_buf == NULL)
-    {
-      alloc_ext = bfd_malloc (amt);
-      extsym_buf = alloc_ext;
-    }
-  if (extsym_buf == NULL
-      || bfd_seek (ibfd, pos, SEEK_SET) != 0
-      || bfd_read (extsym_buf, amt, ibfd) != amt)
+  size_t alloc_ext_size = amt;
+  if (bfd_seek (ibfd, pos, SEEK_SET) != 0
+      || !_bfd_mmap_read_temporary (&extsym_buf, &alloc_ext_size,
+                                   &alloc_ext, ibfd, false))
     {
       intsym_buf = NULL;
       goto out;
     }
 
+  size_t alloc_extshndx_size = 0;
   if (shndx_hdr == NULL || shndx_hdr->sh_size == 0)
     extshndx_buf = NULL;
   else
@@ -483,15 +480,13 @@ bfd_elf_get_elf_syms (bfd *ibfd,
          intsym_buf = NULL;
          goto out;
        }
+      alloc_extshndx_size = amt;
       pos = shndx_hdr->sh_offset + symoffset * sizeof (Elf_External_Sym_Shndx);
-      if (extshndx_buf == NULL)
-       {
-         alloc_extshndx = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
-         extshndx_buf = alloc_extshndx;
-       }
-      if (extshndx_buf == NULL
-         || bfd_seek (ibfd, pos, SEEK_SET) != 0
-         || bfd_read (extshndx_buf, amt, ibfd) != amt)
+      if (bfd_seek (ibfd, pos, SEEK_SET) != 0
+         || !_bfd_mmap_read_temporary ((void **) &extshndx_buf,
+                                       &alloc_extshndx_size,
+                                       (void **) &alloc_extshndx,
+                                       ibfd, false))
        {
          intsym_buf = NULL;
          goto out;
@@ -530,8 +525,8 @@ bfd_elf_get_elf_syms (bfd *ibfd,
       }
 
  out:
-  free (alloc_ext);
-  free (alloc_extshndx);
+  _bfd_munmap_readonly_temporary (alloc_ext, alloc_ext_size);
+  _bfd_munmap_readonly_temporary (alloc_extshndx, alloc_extshndx_size);
 
   return intsym_buf;
 }
index 2991e06fe46b22d4d6ffc113f6f0917783e1d9ba..7e9f4c7f0b042db2fbb546731b395fb80148ee05 100644 (file)
@@ -2644,8 +2644,11 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
    may be either a REL or a RELA section.  The relocations are
    translated into RELA relocations and stored in INTERNAL_RELOCS,
    which should have already been allocated to contain enough space.
-   The EXTERNAL_RELOCS are a buffer where the external form of the
-   relocations should be stored.
+   The *EXTERNAL_RELOCS_P are a buffer where the external form of the
+   relocations should be stored.  If *EXTERNAL_RELOCS_ADDR is NULL,
+   *EXTERNAL_RELOCS_ADDR and *EXTERNAL_RELOCS_SIZE returns the mmap
+   memory address and size.  Otherwise, *EXTERNAL_RELOCS_ADDR is
+   unchanged and *EXTERNAL_RELOCS_SIZE returns 0.
 
    Returns FALSE if something goes wrong.  */
 
@@ -2653,7 +2656,8 @@ static bool
 elf_link_read_relocs_from_section (bfd *abfd,
                                   const asection *sec,
                                   Elf_Internal_Shdr *shdr,
-                                  void *external_relocs,
+                                  void **external_relocs_addr,
+                                  size_t *external_relocs_size,
                                   Elf_Internal_Rela *internal_relocs)
 {
   const struct elf_backend_data *bed;
@@ -2663,13 +2667,17 @@ elf_link_read_relocs_from_section (bfd *abfd,
   Elf_Internal_Rela *irela;
   Elf_Internal_Shdr *symtab_hdr;
   size_t nsyms;
+  void *external_relocs = *external_relocs_addr;
 
   /* Position ourselves at the start of the section.  */
   if (bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0)
     return false;
 
   /* Read the relocations.  */
-  if (bfd_read (external_relocs, shdr->sh_size, abfd) != shdr->sh_size)
+  *external_relocs_size = shdr->sh_size;
+  if (!_bfd_mmap_read_temporary (&external_relocs,
+                                external_relocs_size,
+                                external_relocs_addr, abfd, true))
     return false;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -2754,6 +2762,7 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
                                bool keep_memory)
 {
   void *alloc1 = NULL;
+  size_t alloc1_size;
   Elf_Internal_Rela *alloc2 = NULL;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   struct bfd_elf_section_data *esdo = elf_section_data (o);
@@ -2782,26 +2791,12 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
        goto error_return;
     }
 
-  if (external_relocs == NULL)
-    {
-      bfd_size_type size = 0;
-
-      if (esdo->rel.hdr)
-       size += esdo->rel.hdr->sh_size;
-      if (esdo->rela.hdr)
-       size += esdo->rela.hdr->sh_size;
-
-      alloc1 = bfd_malloc (size);
-      if (alloc1 == NULL)
-       goto error_return;
-      external_relocs = alloc1;
-    }
-
+  alloc1 = external_relocs;
   internal_rela_relocs = internal_relocs;
   if (esdo->rel.hdr)
     {
       if (!elf_link_read_relocs_from_section (abfd, o, esdo->rel.hdr,
-                                             external_relocs,
+                                             &alloc1, &alloc1_size,
                                              internal_relocs))
        goto error_return;
       external_relocs = (((bfd_byte *) external_relocs)
@@ -2812,7 +2807,7 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
 
   if (esdo->rela.hdr
       && (!elf_link_read_relocs_from_section (abfd, o, esdo->rela.hdr,
-                                             external_relocs,
+                                             &alloc1, &alloc1_size,
                                              internal_rela_relocs)))
     goto error_return;
 
@@ -2820,7 +2815,7 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
   if (keep_memory)
     esdo->relocs = internal_relocs;
 
-  free (alloc1);
+  _bfd_munmap_readonly_temporary (alloc1, alloc1_size);
 
   /* Don't free alloc2, since if it was allocated we are passing it
      back (under the name of internal_relocs).  */
@@ -2828,7 +2823,7 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
   return internal_relocs;
 
  error_return:
-  free (alloc1);
+  _bfd_munmap_readonly_temporary (alloc1, alloc1_size);
   if (alloc2 != NULL)
     {
       if (keep_memory)
@@ -12446,7 +12441,14 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
      section, so that we know the sizes of the reloc sections.  We
      also figure out some maximum sizes.  */
   max_contents_size = 0;
+#ifdef USE_MMAP
+  /* Mmap is used only if section size >= the minimum mmap section
+     size.  max_external_reloc_size covers all relocation sections
+     smaller than the minimum mmap section size.   */
+  max_external_reloc_size = _bfd_minimum_mmap_size;
+#else
   max_external_reloc_size = 0;
+#endif
   max_internal_reloc_count = 0;
   max_sym_count = 0;
   max_sym_shndx_count = 0;
@@ -12535,8 +12537,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                      if (esdi->rela.hdr != NULL)
                        ext_size += esdi->rela.hdr->sh_size;
 
+#ifndef USE_MMAP
                      if (ext_size > max_external_reloc_size)
                        max_external_reloc_size = ext_size;
+#endif
                      if (sec->reloc_count > max_internal_reloc_count)
                        max_internal_reloc_count = sec->reloc_count;
                    }
index c5a79cf932c509ddec2f149dbada2db7b9d28495..889b221a9508d4aa031ee7094499227be39a8533 100644 (file)
@@ -905,6 +905,9 @@ extern void _bfd_munmap_readonly_temporary
 #define _bfd_munmap_readonly_temporary(ptr, rsize) free (ptr)
 #endif
 
+extern bool _bfd_mmap_read_temporary
+  (void **, size_t *, void **, bfd *, bool) ATTRIBUTE_HIDDEN;
+
 static inline void *
 _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
 {
index e5147a29d6977b596ca73b5d80a86b4dea9c2939..869f0ed5c6611dd964dbeff1f329d739dbe5b954 100644 (file)
@@ -1174,6 +1174,58 @@ _bfd_mmap_readonly_persistent (bfd *abfd, size_t rsize)
 }
 #endif
 
+/* Attempt to read *SIZE_P bytes from ABFD's iostream to *DATA_P.
+   Return true if the full the amount has been read.  If *DATA_P is
+   NULL, mmap should be used, return the memory address at the
+   current offset in *DATA_P as well as return mmap address and size
+   in *MMAP_BASE and *SIZE_P.  Otherwise, return NULL in *MMAP_BASE
+   and 0 in *SIZE_P.  If FINAL_LINK is true, this is called from
+   elf_link_read_relocs_from_section.  */
+
+bool
+_bfd_mmap_read_temporary (void **data_p, size_t *size_p,
+                         void **mmap_base, bfd *abfd,
+                         bool final_link ATTRIBUTE_UNUSED)
+{
+  void *data = *data_p;
+  size_t size = *size_p;
+
+#ifdef USE_MMAP
+  /* NB: When FINAL_LINK is true, the size of the preallocated buffer
+     is _bfd_minimum_mmap_size and use mmap if the data size >=
+     _bfd_minimum_mmap_size.  Otherwise, use mmap if ABFD isn't an IR
+     input or the data size >= _bfd_minimum_mmap_size.  */
+  bool use_mmmap;
+  bool mmap_size = size >= _bfd_minimum_mmap_size;
+  if (final_link)
+    use_mmmap = mmap_size;
+  else
+    use_mmmap = (mmap_size
+                && data == NULL
+                && (abfd->flags & BFD_PLUGIN) == 0);
+  if (use_mmmap)
+    {
+      data = _bfd_mmap_readonly_temporary (abfd, size, mmap_base,
+                                          size_p);
+      if (data == NULL || data == MAP_FAILED)
+       abort ();
+      *data_p = data;
+      return true;
+    }
+#endif
+
+  if (data == NULL)
+    {
+      data = bfd_malloc (size);
+      if (data == NULL)
+       return false;
+      *data_p = data;
+    }
+  *mmap_base = NULL;
+  *size_p = 0;
+  return bfd_read (data, size, abfd) == size;
+}
+
 /* Default implementation */
 
 bool
index 0caf0f39e61cf39783fb4413df70956af2d21956..0676f461382c56af52bdc2e5631ce4d62e168224 100644 (file)
@@ -911,6 +911,9 @@ extern void _bfd_munmap_readonly_temporary
 #define _bfd_munmap_readonly_temporary(ptr, rsize) free (ptr)
 #endif
 
+extern bool _bfd_mmap_read_temporary
+  (void **, size_t *, void **, bfd *, bool) ATTRIBUTE_HIDDEN;
+
 static inline void *
 _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
 {