public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: "H.J. Lu" <hjl.tools@gmail.com>
To: binutils@sourceware.org
Cc: goldstein.w.n@gmail.com, sam@gentoo.org, amodra@gmail.com
Subject: [PATCH v10 3/6] elf: Use mmap to map in symbol and relocation tables
Date: Thu, 14 Mar 2024 06:25:49 -0700	[thread overview]
Message-ID: <20240314132552.83916-4-hjl.tools@gmail.com> (raw)
In-Reply-To: <20240314132552.83916-1-hjl.tools@gmail.com>

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.

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): 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); Replace bfd_read with
	_bfd_mmap_read_temporary.
	(bfd_elf_final_link): Don't cache external relocations 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       | 47 ++++++++++++++++++++++++++++++++------------
 bfd/elflink.c   | 52 +++++++++++++++++++++++++++++++++++++------------
 bfd/libbfd-in.h |  3 +++
 bfd/libbfd.c    | 37 +++++++++++++++++++++++++++++++++++
 bfd/libbfd.h    |  3 +++
 5 files changed, 118 insertions(+), 24 deletions(-)

diff --git a/bfd/elf.c b/bfd/elf.c
index 557c1ac5f4a..40440445be1 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -460,19 +460,30 @@ bfd_elf_get_elf_syms (bfd *ibfd,
       goto out;
     }
   pos = symtab_hdr->sh_offset + symoffset * extsym_size;
+  size_t alloc_ext_size = amt;
   if (extsym_buf == NULL)
     {
-      alloc_ext = bfd_malloc (amt);
-      extsym_buf = alloc_ext;
+#ifdef USE_MMAP
+      if ((ibfd->flags & BFD_PLUGIN) != 0
+	  || amt < _bfd_minimum_mmap_size)
+	{
+#endif
+	  alloc_ext = bfd_malloc (amt);
+	  extsym_buf = alloc_ext;
+#ifdef USE_MMAP
+	}
+#endif
     }
-  if (extsym_buf == NULL
-      || bfd_seek (ibfd, pos, SEEK_SET) != 0
-      || bfd_read (extsym_buf, amt, ibfd) != 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 +494,27 @@ 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;
+#ifdef USE_MMAP
+	  if ((ibfd->flags & BFD_PLUGIN) != 0
+	      || amt < _bfd_minimum_mmap_size)
+	    {
+#endif
+	      alloc_extshndx
+		= (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+	      extshndx_buf = alloc_extshndx;
+#ifdef USE_MMAP
+	    }
+#endif
 	}
-      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 +553,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;
 }
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 216c124b207..a7abda161cb 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -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,
 				   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);
@@ -2791,17 +2800,26 @@ _bfd_elf_link_info_read_relocs (bfd *abfd,
       if (esdo->rela.hdr)
 	size += esdo->rela.hdr->sh_size;
 
-      alloc1 = bfd_malloc (size);
-      if (alloc1 == NULL)
-	goto error_return;
-      external_relocs = alloc1;
+#ifdef USE_MMAP
+      if (size < _bfd_minimum_mmap_size)
+	{
+#endif
+	  alloc1 = bfd_malloc (size);
+	  if (alloc1 == NULL)
+	    goto error_return;
+	  external_relocs = alloc1;
+#ifdef USE_MMAP
+	}
+#endif
     }
+  else
+    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 +2830,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 +2838,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 +2846,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)
@@ -12444,7 +12462,15 @@ 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.  The initial value covers all sections smaller than the
+     minimum mmap section size.  It may be increased for compressed
+     or linker created sections or sections whose rawsize != 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;
@@ -12533,8 +12559,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;
 		    }
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index c5a79cf932c..889b221a950 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -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)
 {
diff --git a/bfd/libbfd.c b/bfd/libbfd.c
index e5147a29d69..0663dcd1890 100644
--- a/bfd/libbfd.c
+++ b/bfd/libbfd.c
@@ -1174,6 +1174,43 @@ _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 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 link)
+{
+  void *data = *data_p;
+  size_t size = *size_p;
+
+#ifdef USE_MMAP
+  /* NB: The size of the external relocation buffer allocated by
+     elf_link_read_relocs_from_section is _bfd_minimum_mmap_size.
+     Use mmap if the data size >= _bfd_minimum_mmap_size.  */
+  if (data == NULL || (link && size >= _bfd_minimum_mmap_size))
+    {
+      data = _bfd_mmap_readonly_temporary (abfd, size, mmap_base,
+					   size_p);
+      if (data == NULL || data == MAP_FAILED)
+	abort ();
+      *data_p = data;
+      return true;
+    }
+  else
+#endif
+    {
+      *mmap_base = NULL;
+      *size_p = 0;
+      return bfd_read (data, size, abfd) == size;
+    }
+}
+
 /* Default implementation */
 
 bool
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 47f40889a95..876c1f223c7 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -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)
 {
-- 
2.44.0


  parent reply	other threads:[~2024-03-14 13:25 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-14 13:25 [PATCH v10 0/6] elf: Use mmap to map in section contents H.J. Lu
2024-03-14 13:25 ` [PATCH v10 1/6] elf: Use mmap to map in read-only sections H.J. Lu
2024-03-14 13:25 ` [PATCH v10 2/6] elf: Add _bfd_elf_m[un]map_section_contents H.J. Lu
2024-03-14 13:25 ` H.J. Lu [this message]
2024-03-14 13:25 ` [PATCH v10 4/6] elf: Don't cache symbol nor relocation tables with mmap H.J. Lu
2024-03-14 13:25 ` [PATCH v10 5/6] elf: Always keep symbol table and relocation info for eh_frame H.J. Lu
2024-03-14 13:25 ` [PATCH v10 6/6] elf: Add _bfd_elf_link_m[un]map_section_contents H.J. Lu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240314132552.83916-4-hjl.tools@gmail.com \
    --to=hjl.tools@gmail.com \
    --cc=amodra@gmail.com \
    --cc=binutils@sourceware.org \
    --cc=goldstein.w.n@gmail.com \
    --cc=sam@gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).