public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/4] elf: Use mmap to map in section contents
@ 2024-03-05 17:00 H.J. Lu
  2024-03-05 17:00 ` [PATCH 1/4] elf: Use mmap to map in read-only sections H.J. Lu
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-05 17:00 UTC (permalink / raw)
  To: binutils; +Cc: goldstein.w.n

We can use mmap to map in ELF section contents, instead of copying them
into memory by hand.  We don't need to cache symbol nor relocation tables
if they are mapped in.  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		86.73		87.02		-0.3%
system		9.55		9.21		3.6%
total		100.40		97.66		0.7%
maximum set(GB)	17.34		13.14		24%
page faults	4047667		3042877		25%

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

user		5.41		5.44		-0.5%
system		0.80		0.76		5%
total		6.25		6.26		-0.2%
maximum set(MB)	1323		968		27%
page faults	323451		236371		27%

Data shows that these won't improve the single copy linker performance.
But they improve the overall system performance when linker is used by
reducing linker memory usage and page faults.  They allow more parallel
linker jobs on LLVM debug build.

Here is a quote from Noah Goldstein: "on a large project they are an
extremely large speedup".

H.J. Lu (4):
  elf: Use mmap to map in read-only sections
  elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents
  elf: Use mmap to map in symbol and relocation tables
  elf: Don't cache symbol nor relocation tables with mmap

 bfd/bfd-in2.h      |  43 ++++++--
 bfd/bfd.c          |  17 +++
 bfd/bfdio.c        |  16 +--
 bfd/bfdwin.c       |   8 +-
 bfd/cache.c        |  11 +-
 bfd/compress.c     |   2 +-
 bfd/elf-bfd.h      |  11 ++
 bfd/elf-eh-frame.c |   4 +-
 bfd/elf-sframe.c   |   4 +-
 bfd/elf.c          | 256 +++++++++++++++++++++++++++++++++++++--------
 bfd/elf32-i386.c   |   6 +-
 bfd/elf64-x86-64.c |  10 +-
 bfd/elfcode.h      |   7 +-
 bfd/elflink.c      |  72 ++++++++-----
 bfd/elfxx-target.h |   6 +-
 bfd/elfxx-x86.c    |   7 +-
 bfd/elfxx-x86.h    |   1 +
 bfd/libbfd-in.h    |  32 ++++++
 bfd/libbfd.c       | 247 ++++++++++++++++++++++++++++++++++++++++++-
 bfd/libbfd.h       |  36 ++++++-
 bfd/linker.c       |   7 +-
 bfd/lynx-core.c    |   2 +-
 bfd/opncls.c       |  25 ++++-
 bfd/section.c      |  24 ++++-
 24 files changed, 721 insertions(+), 133 deletions(-)

-- 
2.44.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/4] elf: Use mmap to map in read-only sections
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
@ 2024-03-05 17:00 ` H.J. Lu
  2024-03-05 17:00 ` [PATCH 2/4] elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents H.J. Lu
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-05 17:00 UTC (permalink / raw)
  To: binutils; +Cc: goldstein.w.n

Since all string sections are read-only, we can mmap them in instead of
copying into memory by hand.  Add _bfd_mmap_readonly_tracked and
_bfd_mmap_readonly_untracked to mmap in reado-only sections with
size >= 4 * page size.  Change setup_group to mmap in the group section
as read-only and allocate the memory for internal use separately.

Change the size type in the BFD mmap interface from bfd_size_type to
size_t to be consistent with the size type of the host mmap interface.

NB: All string sections in valid ELF inputs must be null terminated.
There is no need to terminate it again and it will lead segfault since
string sections are mmapped as read-only.

	* bfd.c (bfd_mmapped_entry): New.
	(bfd_mmapped): Likewise.
	(bfd): Add mmapped.
	* bfdio.c (bfd_iovec): Change the bmmap size type to size_t.
	(bfd_mmap): Likewise.
	(memory_bmmap): Likewise.
	* bfdwin.c (bfd_get_file_window): Use _bfd_pagesize.
	* cache.c (cache_bmmap): Change the bmmap size type to size_t.
	Remove pagesize_m1 and use pagesize_m1 instead.
	* elf.c (bfd_elf_get_str_section): Call _bfd_mmap_readonly_tracked
	instead of _bfd_alloc_and_read.  Don't terminate the string
	section again.
	(setup_group): Call _bfd_mmap_readonly_tracked instead of
	_bfd_alloc_and_read to map in the group section as read-only
	and allocate the memory for internal use separately.
	(get_hash_table_data): Call _bfd_mmap_readonly_untracked and
	_bfd_munmap_readonly_untracked instead of _bfd_malloc_and_read
	and free.
	(_bfd_elf_get_dynamic_symbols): Call _bfd_mmap_readonly_tracked
	instead of _bfd_alloc_and_read.  Don't terminate the string
	section again.  Call _bfd_mmap_readonly_untracked and
	_bfd_munmap_readonly_untracked instead of _bfd_malloc_and_read
	and free.
	(_bfd_elf_slurp_version_tables): Call _bfd_mmap_readonly_untracked
	and _bfd_munmap_readonly_untracked instead of _bfd_malloc_and_read
	and free.
	* elflink.c (bfd_elf_link_record_dynamic_symbol): Use bfd_malloc
	to get the unversioned symbol.
	* libbfd-in.h (_bfd_pagesize): New.
	(_bfd_pagesize_m1): Likewise.
	(_bfd_minimum_mmap_size): Likewise.
	(_bfd_mmap_readonly_tracked): Likewise.
	(_bfd_mmap_readonly_untracked): Likewise.
	(_bfd_munmap_readonly_untracked): Likewise.
	* libbfd.c (MAP_ANONYMOUS): New. Define if not defined.
	(bfd_allocate_mmapped_page): New.
	(_bfd_mmap_readonly_untracked): Likewise.
	(_bfd_munmap_readonly_untracked): Likewise.
	(_bfd_mmap_readonly_tracked): Likewise.
	(_bfd_pagesize): Likewise.
	(_bfd_pagesize_m1): Likewise.
	(_bfd_minimum_mmap_size): Likewise.
	(bfd_init_pagesize): Likewise.
	* ynx-core.c (lynx_core_file_p): Use _bfd_pagesize.
	* opncls.c: Include <sys/mman.h>.
	(_bfd_delete_bfd): Munmap tracked mmapped memories.
	(opncls_bmmap): Change the bmmap size type to size_t.
	* bfd-in2.h: Regenerated.
	* libbfd.h: Likewise.
---
 bfd/bfd-in2.h   |  21 ++++++-
 bfd/bfd.c       |  17 ++++++
 bfd/bfdio.c     |  16 ++---
 bfd/bfdwin.c    |   8 +--
 bfd/cache.c     |  11 ++--
 bfd/elf.c       | 121 ++++++++++++++++++++++++++++---------
 bfd/elflink.c   |  16 ++---
 bfd/libbfd-in.h |  29 +++++++++
 bfd/libbfd.c    | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/libbfd.h    |  33 +++++++++-
 bfd/lynx-core.c |   2 +-
 bfd/opncls.c    |  20 +++++-
 12 files changed, 386 insertions(+), 65 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index a335df522d2..856cb625997 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1951,6 +1951,20 @@ struct bfd_build_id
     bfd_byte data[1];
   };
 
+struct bfd_mmapped_entry
+  {
+    void *addr;
+    size_t size;
+  };
+
+struct bfd_mmapped
+  {
+    struct bfd_mmapped *next;
+    unsigned int max_entry;
+    unsigned int next_entry;
+    struct bfd_mmapped_entry entries[1];
+  };
+
 struct bfd
 {
   /* The filename the application opened the BFD with.  */
@@ -2280,6 +2294,9 @@ struct bfd
 
   /* For input BFDs, the build ID, if the object has one. */
   const struct bfd_build_id *build_id;
+
+  /* For input BFDs, mmapped entries. */
+  struct bfd_mmapped *mmapped;
 };
 
 static inline const char *
@@ -2768,9 +2785,9 @@ ufile_ptr bfd_get_size (bfd *abfd);
 
 ufile_ptr bfd_get_file_size (bfd *abfd);
 
-void *bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
+void *bfd_mmap (bfd *abfd, void *addr, size_t len,
     int prot, int flags, file_ptr offset,
-    void **map_addr, bfd_size_type *map_len)
+    void **map_addr, size_t *map_len)
 ATTRIBUTE_WARN_UNUSED_RESULT;
 
 time_t bfd_get_current_time (time_t now);
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 00c26a7dcd4..be4c9af0f62 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -74,6 +74,20 @@ EXTERNAL
 .    bfd_byte data[1];
 .  };
 .
+.struct bfd_mmapped_entry
+.  {
+.    void *addr;
+.    size_t size;
+.  };
+.
+.struct bfd_mmapped
+.  {
+.    struct bfd_mmapped *next;
+.    unsigned int max_entry;
+.    unsigned int next_entry;
+.    struct bfd_mmapped_entry entries[1];
+.  };
+.
 
 CODE_FRAGMENT
 .struct bfd
@@ -406,6 +420,9 @@ CODE_FRAGMENT
 .
 .  {* For input BFDs, the build ID, if the object has one. *}
 .  const struct bfd_build_id *build_id;
+.
+.  {* For input BFDs, mmapped entries. *}
+.  struct bfd_mmapped *mmapped;
 .};
 .
 
diff --git a/bfd/bfdio.c b/bfd/bfdio.c
index ab3e28ea204..a6b88b55625 100644
--- a/bfd/bfdio.c
+++ b/bfd/bfdio.c
@@ -219,9 +219,9 @@ DESCRIPTION
 .     Also write in MAP_ADDR the address of the page aligned buffer and in
 .     MAP_LEN the size mapped (a page multiple).  Use unmap with MAP_ADDR and
 .     MAP_LEN to unmap.  *}
-.  void *(*bmmap) (struct bfd *abfd, void *addr, bfd_size_type len,
+.  void *(*bmmap) (struct bfd *abfd, void *addr, size_t len,
 .		   int prot, int flags, file_ptr offset,
-.		   void **map_addr, bfd_size_type *map_len);
+.		   void **map_addr, size_t *map_len);
 .};
 
 .extern const struct bfd_iovec _bfd_memory_iovec;
@@ -638,9 +638,9 @@ FUNCTION
 	bfd_mmap
 
 SYNOPSIS
-	void *bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
+	void *bfd_mmap (bfd *abfd, void *addr, size_t len,
 			int prot, int flags, file_ptr offset,
-			void **map_addr, bfd_size_type *map_len)
+			void **map_addr, size_t *map_len)
 			ATTRIBUTE_WARN_UNUSED_RESULT;
 
 DESCRIPTION
@@ -651,9 +651,9 @@ DESCRIPTION
 */
 
 void *
-bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
+bfd_mmap (bfd *abfd, void *addr, size_t len,
 	  int prot, int flags, file_ptr offset,
-	  void **map_addr, bfd_size_type *map_len)
+	  void **map_addr, size_t *map_len)
 {
   while (abfd->my_archive != NULL
 	 && !bfd_is_thin_archive (abfd->my_archive))
@@ -815,10 +815,10 @@ memory_bstat (bfd *abfd, struct stat *statbuf)
 
 static void *
 memory_bmmap (bfd *abfd ATTRIBUTE_UNUSED, void *addr ATTRIBUTE_UNUSED,
-	      bfd_size_type len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED,
+	      size_t len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED,
 	      int flags ATTRIBUTE_UNUSED, file_ptr offset ATTRIBUTE_UNUSED,
 	      void **map_addr ATTRIBUTE_UNUSED,
-	      bfd_size_type *map_len ATTRIBUTE_UNUSED)
+	      size_t *map_len ATTRIBUTE_UNUSED)
 {
   return (void *)-1;
 }
diff --git a/bfd/bfdwin.c b/bfd/bfdwin.c
index beb17398744..d9ce0d25e90 100644
--- a/bfd/bfdwin.c
+++ b/bfd/bfdwin.c
@@ -165,7 +165,7 @@ bfd_get_file_window (bfd *abfd,
 		     bool writable)
 {
   static int ok_to_map = 1;
-  static size_t pagesize;
+  size_t pagesize = _bfd_pagesize;
   bfd_window_internal *i = windowp->i;
   bfd_size_type size_to_alloc = size;
 
@@ -175,12 +175,6 @@ bfd_get_file_window (bfd *abfd,
 	     windowp, windowp->data, (unsigned long) windowp->size,
 	     windowp->i, writable);
 
-  /* Make sure we know the page size, so we can be friendly to mmap.  */
-  if (pagesize == 0)
-    pagesize = getpagesize ();
-  if (pagesize == 0)
-    abort ();
-
   if (i == NULL)
     {
       i = bfd_zmalloc (sizeof (bfd_window_internal));
diff --git a/bfd/cache.c b/bfd/cache.c
index 4c00c00f8d5..e0bdb658c9e 100644
--- a/bfd/cache.c
+++ b/bfd/cache.c
@@ -482,12 +482,12 @@ cache_bstat (struct bfd *abfd, struct stat *sb)
 static void *
 cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
 	     void *addr ATTRIBUTE_UNUSED,
-	     bfd_size_type len ATTRIBUTE_UNUSED,
+	     size_t len ATTRIBUTE_UNUSED,
 	     int prot ATTRIBUTE_UNUSED,
 	     int flags ATTRIBUTE_UNUSED,
 	     file_ptr offset ATTRIBUTE_UNUSED,
 	     void **map_addr ATTRIBUTE_UNUSED,
-	     bfd_size_type *map_len ATTRIBUTE_UNUSED)
+	     size_t *map_len ATTRIBUTE_UNUSED)
 {
   void *ret = (void *) -1;
 
@@ -498,10 +498,10 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
 #ifdef HAVE_MMAP
   else
     {
-      static uintptr_t pagesize_m1;
+      uintptr_t pagesize_m1 = _bfd_pagesize_m1;
       FILE *f;
       file_ptr pg_offset;
-      bfd_size_type pg_len;
+      size_t pg_len;
 
       f = bfd_cache_lookup (abfd, CACHE_NO_SEEK_ERROR);
       if (f == NULL)
@@ -510,9 +510,6 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
 	  return ret;
 	}
 
-      if (pagesize_m1 == 0)
-	pagesize_m1 = getpagesize () - 1;
-
       /* Align.  */
       pg_offset = offset & ~pagesize_m1;
       pg_len = (len + (offset - pg_offset) + pagesize_m1) & ~pagesize_m1;
diff --git a/bfd/elf.c b/bfd/elf.c
index 8bffd3c5141..173605f294d 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -289,16 +289,24 @@ bfd_elf_get_str_section (bfd *abfd, unsigned int shindex)
 	 in case the string table is not terminated.  */
       if (shstrtabsize + 1 <= 1
 	  || bfd_seek (abfd, offset, SEEK_SET) != 0
-	  || (shstrtab = _bfd_alloc_and_read (abfd, shstrtabsize + 1,
-					      shstrtabsize)) == NULL)
+	  || (shstrtab
+	      = _bfd_mmap_readonly_tracked (abfd, shstrtabsize + 1,
+					    shstrtabsize)) == NULL)
 	{
 	  /* Once we've failed to read it, make sure we don't keep
 	     trying.  Otherwise, we'll keep allocating space for
 	     the string table over and over.  */
 	  i_shdrp[shindex]->sh_size = 0;
 	}
-      else
-	shstrtab[shstrtabsize] = '\0';
+      else if (shstrtab[shstrtabsize - 1] != '\0')
+	{
+	  /* It is an error if a string table isn't terminated.  */
+	  _bfd_error_handler
+	    /* xgettext:c-format */
+	    (_("%pB(%pA): string table is corrupt"),
+	     abfd, i_shdrp[shindex]->bfd_section);
+	  return NULL;
+	}
       i_shdrp[shindex]->contents = shstrtab;
     }
   return (char *) shstrtab;
@@ -653,8 +661,12 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 
 	      if (IS_VALID_GROUP_SECTION_HEADER (shdr, 2 * GRP_ENTRY_SIZE))
 		{
-		  unsigned char *src;
+		  unsigned char *src, *contents;
 		  Elf_Internal_Group *dest;
+		  size_t contents_size;
+#ifdef HAVE_MMAP
+		  void *contents_addr = NULL;
+#endif
 
 		  /* Make sure the group section has a BFD section
 		     attached to it.  */
@@ -667,12 +679,23 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 
 		  /* Read the raw contents.  */
 		  BFD_ASSERT (sizeof (*dest) >= 4 && sizeof (*dest) % 4 == 0);
+		  contents_size = shdr->sh_size;
 		  shdr->contents = NULL;
-		  if (_bfd_mul_overflow (shdr->sh_size,
+		  if (_bfd_mul_overflow (contents_size,
 					 sizeof (*dest) / 4, &amt)
 		      || bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0
-		      || !(shdr->contents
-			   = _bfd_alloc_and_read (abfd, amt, shdr->sh_size)))
+#ifdef HAVE_MMAP
+		      || !(contents
+			   = _bfd_mmap_readonly_untracked (abfd,
+							   contents_size,
+							   &contents_addr,
+							   &contents_size))
+#else
+		      || !(contents
+			   = _bfd_alloc_and_read (abfd, amt,
+						  contents_size))
+#endif
+		      )
 		    {
 		      _bfd_error_handler
 			/* xgettext:c-format */
@@ -684,11 +707,17 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 		      continue;
 		    }
 
+#ifdef HAVE_MMAP
+		  shdr->contents = bfd_alloc (abfd, amt);
+#else
+		  shdr->contents = contents;
+#endif
+
 		  /* Translate raw contents, a flag word followed by an
 		     array of elf section indices all in target byte order,
 		     to the flag word followed by an array of elf section
 		     pointers.  */
-		  src = shdr->contents + shdr->sh_size;
+		  src = contents + shdr->sh_size;
 		  dest = (Elf_Internal_Group *) (shdr->contents + amt);
 
 		  while (1)
@@ -698,7 +727,7 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 		      src -= 4;
 		      --dest;
 		      idx = H_GET_32 (abfd, src);
-		      if (src == shdr->contents)
+		      if (src == contents)
 			{
 			  dest->shdr = NULL;
 			  dest->flags = idx;
@@ -726,6 +755,11 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 			  dest->shdr = NULL;
 			}
 		    }
+
+#ifdef HAVE_MMAP
+		  _bfd_munmap_readonly_untracked (contents_addr,
+						  contents_size);
+#endif
 		}
 	    }
 
@@ -1919,6 +1953,8 @@ get_hash_table_data (bfd *abfd, bfd_size_type number,
   unsigned char *e_data = NULL;
   bfd_vma *i_data = NULL;
   bfd_size_type size;
+  void *e_data_addr;
+  size_t e_data_size ATTRIBUTE_UNUSED;
 
   if (ent_size != 4 && ent_size != 8)
     return NULL;
@@ -1940,7 +1976,8 @@ get_hash_table_data (bfd *abfd, bfd_size_type number,
       return NULL;
     }
 
-  e_data = _bfd_malloc_and_read (abfd, size, size);
+  e_data = _bfd_mmap_readonly_untracked (abfd, size, &e_data_addr,
+					 &e_data_size);
   if (e_data == NULL)
     return NULL;
 
@@ -1958,7 +1995,7 @@ get_hash_table_data (bfd *abfd, bfd_size_type number,
     while (number--)
       i_data[number] = bfd_get_64 (abfd, e_data + number * ent_size);
 
-  free (e_data);
+  _bfd_munmap_readonly_untracked (e_data_addr, e_data_size);
   return i_data;
 }
 
@@ -2007,6 +2044,10 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
   size_t verneed_size = 0;
   size_t extsym_size;
   const struct elf_backend_data *bed;
+  void *dynbuf_addr = NULL;
+  void *esymbuf_addr = NULL;
+  size_t dynbuf_size = 0;
+  size_t esymbuf_size = 0;
 
   /* Return TRUE if symbol table is bad.  */
   if (elf_bad_symtab (abfd))
@@ -2024,7 +2065,9 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
   if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0)
     goto error_return;
 
-  dynbuf = _bfd_malloc_and_read (abfd, phdr->p_filesz, phdr->p_filesz);
+  dynbuf_size = phdr->p_filesz;
+  dynbuf = _bfd_mmap_readonly_untracked (abfd, dynbuf_size,
+					 &dynbuf_addr, &dynbuf_size);
   if (dynbuf == NULL)
     goto error_return;
 
@@ -2102,11 +2145,18 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
     goto error_return;
 
   /* Dynamic string table must be valid until ABFD is closed.  */
-  strbuf = (char *) _bfd_alloc_and_read (abfd, dt_strsz + 1, dt_strsz);
+  strbuf = (char *) _bfd_mmap_readonly_tracked (abfd, dt_strsz + 1,
+						dt_strsz);
   if (strbuf == NULL)
     goto error_return;
-  /* Since this is a string table, make sure that it is terminated.  */
-  strbuf[dt_strsz] = 0;
+  if (strbuf[dt_strsz - 1] != 0)
+    {
+      /* It is an error if a string table is't terminated.  */
+      _bfd_error_handler
+	/* xgettext:c-format */
+	(_("%pB: DT_STRTAB table is corrupt"), abfd);
+      goto error_return;
+    }
 
   /* Get the real symbol count from DT_HASH or DT_GNU_HASH.  Prefer
      DT_HASH since it is simpler than DT_GNU_HASH.  */
@@ -2281,7 +2331,10 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
   if (filepos == (file_ptr) -1
       || bfd_seek (abfd, filepos, SEEK_SET) != 0)
     goto error_return;
-  esymbuf = _bfd_malloc_and_read (abfd, amt, amt);
+  esymbuf_size = amt;
+  esymbuf = _bfd_mmap_readonly_untracked (abfd, esymbuf_size,
+					  &esymbuf_addr,
+					  &esymbuf_size);
   if (esymbuf == NULL)
     goto error_return;
 
@@ -2325,7 +2378,7 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
 	goto error_return;
 
       /* DT_VERSYM info must be valid until ABFD is closed.  */
-      versym = _bfd_alloc_and_read (abfd, amt, amt);
+      versym = _bfd_mmap_readonly_tracked (abfd, amt, amt);
 
       if (dt_verdef)
 	{
@@ -2337,8 +2390,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
 	    goto error_return;
 
 	  /* DT_VERDEF info must be valid until ABFD is closed.  */
-	  verdef = _bfd_alloc_and_read (abfd, verdef_size,
-					verdef_size);
+	  verdef = _bfd_mmap_readonly_tracked (abfd, verdef_size,
+					       verdef_size);
 	}
 
       if (dt_verneed)
@@ -2351,8 +2404,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
 	    goto error_return;
 
 	  /* DT_VERNEED info must be valid until ABFD is closed.  */
-	  verneed = _bfd_alloc_and_read (abfd, verneed_size,
-					 verneed_size);
+	  verneed = _bfd_mmap_readonly_tracked (abfd, verneed_size,
+						verneed_size);
 	}
     }
 
@@ -2375,8 +2428,8 @@ _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
   /* Restore file position for elf_object_p.  */
   if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0)
     res = false;
-  free (dynbuf);
-  free (esymbuf);
+  _bfd_munmap_readonly_untracked (dynbuf_addr, dynbuf_size);
+  _bfd_munmap_readonly_untracked (esymbuf_addr, esymbuf_size);
   free (gnubuckets);
   free (gnuchains);
   free (mipsxlat);
@@ -9435,6 +9488,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
   bfd_byte *contents = NULL;
   unsigned int freeidx = 0;
   size_t amt;
+  void *contents_addr = NULL;
+  size_t contents_size = 0;
 
   if (elf_dynverref (abfd) != 0 || elf_tdata (abfd)->dt_verneed != NULL)
     {
@@ -9471,7 +9526,10 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 
 	  if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
 	    goto error_return_verref;
-	  contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
+	  contents_size = hdr->sh_size;
+	  contents = _bfd_mmap_readonly_untracked (abfd, contents_size,
+						   &contents_addr,
+						   &contents_size);
 	  if (contents == NULL)
 	    goto error_return_verref;
 
@@ -9604,8 +9662,9 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
       elf_tdata (abfd)->cverrefs = i;
 
       if (contents != elf_tdata (abfd)->dt_verneed)
-	free (contents);
+	_bfd_munmap_readonly_untracked (contents_addr, contents_size);
       contents = NULL;
+      contents_addr = NULL;
     }
 
   if (elf_dynverdef (abfd) != 0 || elf_tdata (abfd)->dt_verdef != NULL)
@@ -9646,7 +9705,10 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 
 	  if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
 	    goto error_return_verdef;
-	  contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
+	  contents_size = hdr->sh_size;
+	  contents = _bfd_mmap_readonly_untracked (abfd, contents_size,
+						   &contents_addr,
+						   &contents_size);
 	  if (contents == NULL)
 	    goto error_return_verdef;
 
@@ -9800,8 +9862,9 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 	}
 
       if (contents != elf_tdata (abfd)->dt_verdef)
-	free (contents);
+	_bfd_munmap_readonly_untracked (contents_addr, contents_size);
       contents = NULL;
+      contents_addr = NULL;
     }
   else if (default_imported_symver)
     {
@@ -9857,7 +9920,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
  error_return:
   if (contents != elf_tdata (abfd)->dt_verneed
       && contents != elf_tdata (abfd)->dt_verdef)
-    free (contents);
+    _bfd_munmap_readonly_untracked (contents_addr, contents_size);
   return false;
 }
 \f
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 5a6cb07b2ce..42029f29f7a 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -549,22 +549,24 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
 	    return false;
 	}
 
+      char *unversioned_name = NULL;
+
       /* We don't put any version information in the dynamic string
 	 table.  */
       name = h->root.root.string;
       p = strchr (name, ELF_VER_CHR);
       if (p != NULL)
-	/* We know that the p points into writable memory.  In fact,
-	   there are only a few symbols that have read-only names, being
-	   those like _GLOBAL_OFFSET_TABLE_ that are created specially
-	   by the backends.  Most symbols will have names pointing into
-	   an ELF string table read from a file, or to objalloc memory.  */
-	*p = 0;
+	{
+	  unversioned_name = bfd_malloc (p - name + 1);
+	  memcpy (unversioned_name, name, p - name);
+	  unversioned_name[p - name] = 0;
+	  name = unversioned_name;
+	}
 
       indx = _bfd_elf_strtab_add (dynstr, name, p != NULL);
 
       if (p != NULL)
-	*p = ELF_VER_CHR;
+	free (unversioned_name);
 
       if (indx == (size_t) -1)
 	return false;
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index b8b2ce7ba09..031b1988a38 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -851,6 +851,10 @@ extern struct bfd_link_info *_bfd_get_link_info (bfd *)
 extern bool _bfd_link_keep_memory (struct bfd_link_info *)
   ATTRIBUTE_HIDDEN;
 
+extern uintptr_t _bfd_pagesize ATTRIBUTE_HIDDEN;
+extern uintptr_t _bfd_pagesize_m1 ATTRIBUTE_HIDDEN;
+extern uintptr_t _bfd_minimum_mmap_size ATTRIBUTE_HIDDEN;
+
 #if GCC_VERSION >= 7000
 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
 #else
@@ -888,6 +892,19 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
   return NULL;
 }
 
+#ifdef HAVE_MMAP
+extern void *_bfd_mmap_readonly_tracked
+  (bfd *, size_t, size_t) ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_untracked
+  (bfd *, size_t, void **, size_t *) ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_untracked
+  (void *, size_t) ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly_tracked(abfd, asize, rsize) \
+  _bfd_alloc_and_read (abfd, asize, rsize)
+#define _bfd_munmap_readonly_untracked(ptr, rsize) free (ptr)
+#endif
+
 static inline void *
 _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
 {
@@ -910,3 +927,15 @@ _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
     }
   return NULL;
 }
+
+#ifndef HAVE_MMAP
+static inline void *
+_bfd_mmap_readonly_untracked (bfd *abfd, size_t rsize, void **map_addr,
+			      size_t *map_size)
+{
+  void *mem = _bfd_malloc_and_read (abfd, rsize, rsize);
+  *map_addr = mem;
+  *map_size = rsize;
+  return mem;
+}
+#endif
diff --git a/bfd/libbfd.c b/bfd/libbfd.c
index f8d148c9677..e1bb9fb6117 100644
--- a/bfd/libbfd.c
+++ b/bfd/libbfd.c
@@ -1038,6 +1038,147 @@ bfd_get_bits (const void *p, int bits, bool big_p)
   return data;
 }
 \f
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS  MAP_ANON
+#endif
+
+/* Allocate a page to track mmapped memory and return the page and
+   the first entry.  Return NULL if mmap fails.  */
+
+static struct bfd_mmapped *
+bfd_allocate_mmapped_page (bfd *abfd, struct bfd_mmapped_entry **entry)
+{
+  struct bfd_mmapped * mmapped
+    = (struct bfd_mmapped *) mmap (NULL, _bfd_pagesize,
+				   PROT_READ | PROT_WRITE,
+				   MAP_PRIVATE | MAP_ANONYMOUS,
+				   -1, 0);
+  if (mmapped == MAP_FAILED)
+    return NULL;
+
+  mmapped->next = abfd->mmapped;
+  mmapped->max_entry
+    = ((_bfd_pagesize - offsetof (struct bfd_mmapped, entries))
+       / sizeof (struct bfd_mmapped_entry));
+  mmapped->next_entry = 1;
+  abfd->mmapped = mmapped;
+  *entry = mmapped->entries;
+  return mmapped;
+}
+
+/* Mmap a memory region of RSIZE bytes with FLAGS at the current offset.
+   Return mmap address and size in MAP_ADDR and MAP_SIZE.  Return NULL
+   on invalid input and (void *) -1 for mmap failure.  */
+
+static void *
+bfd_mmap_local (bfd *abfd, size_t rsize, int flags, void **map_addr,
+		size_t *map_size)
+{
+  if (!_bfd_constant_p (rsize))
+    {
+      ufile_ptr filesize = bfd_get_file_size (abfd);
+      if (filesize != 0 && rsize > filesize)
+	{
+	  bfd_set_error (bfd_error_file_truncated);
+	  return NULL;
+	}
+    }
+
+  void *mem;
+  ufile_ptr offset = bfd_tell (abfd);
+  mem = bfd_mmap (abfd, NULL, rsize, flags, MAP_PRIVATE, offset,
+		  map_addr, map_size);
+  return mem;
+}
+
+/* Mmap a readonly memory region of RSIZE bytes at the current offset.
+   Return mmap address and size in MAP_ADDR and MAP_SIZE.  Return NULL
+   on invalid input and (void *) -1 for mmap failure.  */
+
+void *
+_bfd_mmap_readonly_untracked (bfd *abfd, size_t rsize, void **map_addr,
+			      size_t *map_size)
+{
+  /* Use mmap only if section size >= the minimum mmap section size.  */
+  if (rsize < _bfd_minimum_mmap_size)
+    {
+      void *mem = _bfd_malloc_and_read (abfd, rsize, rsize);
+      /* NB: Set *MAP_ADDR to MEM and *MAP_SIZE to 0 to indicate that
+	 _bfd_malloc_and_read is called.  */
+      *map_addr = mem;
+      *map_size = 0;
+      return mem;
+    }
+
+  return bfd_mmap_local (abfd, rsize, PROT_READ, map_addr, map_size);
+}
+
+/* Munmap RSIZE bytes at PTR.  */
+
+void
+_bfd_munmap_readonly_untracked (void *ptr, size_t rsize)
+{
+  /* NB: Since _bfd_munmap_readonly_untracked is called like free, PTR
+     may be NULL.  Otherwise, PTR and RSIZE must be valid.  If RSIZE is
+     0, _bfd_malloc_and_read is called.  */
+  if (ptr == NULL)
+    return;
+  if (rsize != 0)
+    {
+      if (munmap (ptr, rsize) != 0)
+	abort ();
+    }
+  else
+    free (ptr);
+}
+
+/* Mmap a readonly memory region of RSIZE bytes at the current offset.
+   Return NULL on invalid input or mmap failure.  */
+
+void *
+_bfd_mmap_readonly_tracked (bfd *abfd, size_t asize, size_t rsize)
+{
+  /* Use mmap only if section size >= the minimum mmap section size.  */
+  if (rsize < _bfd_minimum_mmap_size)
+    return _bfd_alloc_and_read (abfd, rsize, rsize);
+
+  void *mem, *map_addr;
+  size_t map_size;
+  mem = bfd_mmap_local (abfd, rsize, PROT_READ, &map_addr, &map_size);
+  if (mem == NULL)
+    return mem;
+  if (mem == (void *) -1)
+    return _bfd_alloc_and_read (abfd, asize, rsize);
+
+  struct bfd_mmapped_entry *entry;
+  unsigned int next_entry;
+  struct bfd_mmapped *mmapped = abfd->mmapped;
+  if (mmapped != NULL
+      && (next_entry = mmapped->next_entry) < mmapped->max_entry)
+    {
+      entry = &mmapped->entries[next_entry];
+      mmapped->next_entry++;
+    }
+  else
+    {
+      mmapped = bfd_allocate_mmapped_page (abfd, &entry);
+      if (mmapped == NULL)
+	{
+	  munmap (map_addr, map_size);
+	  return NULL;
+	}
+    }
+
+  entry->addr = map_addr;
+  entry->size = map_size;
+
+  return mem;
+}
+#endif
+
 /* Default implementation */
 
 bool
@@ -1326,3 +1467,19 @@ _bfd_generic_init_private_section_data (bfd *ibfd ATTRIBUTE_UNUSED,
 {
   return true;
 }
+
+uintptr_t _bfd_pagesize;
+uintptr_t _bfd_pagesize_m1;
+uintptr_t _bfd_minimum_mmap_size;
+
+__attribute__ ((unused, constructor))
+static void
+bfd_init_pagesize (void)
+{
+  _bfd_pagesize = getpagesize ();
+  if (_bfd_pagesize == 0)
+    abort ();
+  _bfd_pagesize_m1 = _bfd_pagesize - 1;
+  /* The minimum section size to use mmap.  */
+  _bfd_minimum_mmap_size = _bfd_pagesize * 4;
+}
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index a60063b5ae1..6b23c442d61 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -857,6 +857,10 @@ extern struct bfd_link_info *_bfd_get_link_info (bfd *)
 extern bool _bfd_link_keep_memory (struct bfd_link_info *)
   ATTRIBUTE_HIDDEN;
 
+extern uintptr_t _bfd_pagesize ATTRIBUTE_HIDDEN;
+extern uintptr_t _bfd_pagesize_m1 ATTRIBUTE_HIDDEN;
+extern uintptr_t _bfd_minimum_mmap_size ATTRIBUTE_HIDDEN;
+
 #if GCC_VERSION >= 7000
 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
 #else
@@ -894,6 +898,19 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
   return NULL;
 }
 
+#ifdef HAVE_MMAP
+extern void *_bfd_mmap_readonly_tracked
+  (bfd *, size_t, size_t) ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_untracked
+  (bfd *, size_t, void **, size_t *) ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_untracked
+  (void *, size_t) ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly_tracked(abfd, asize, rsize) \
+  _bfd_alloc_and_read (abfd, asize, rsize)
+#define _bfd_munmap_readonly_untracked(ptr, rsize) free (ptr)
+#endif
+
 static inline void *
 _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
 {
@@ -916,6 +933,18 @@ _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
     }
   return NULL;
 }
+
+#ifndef HAVE_MMAP
+static inline void *
+_bfd_mmap_readonly_untracked (bfd *abfd, size_t rsize, void **map_addr,
+			      size_t *map_size)
+{
+  void *mem = _bfd_malloc_and_read (abfd, rsize, rsize);
+  *map_addr = mem;
+  *map_size = rsize;
+  return mem;
+}
+#endif
 /* Extracted from libbfd.c.  */
 void *bfd_malloc (bfd_size_type /*size*/) ATTRIBUTE_HIDDEN;
 
@@ -969,9 +998,9 @@ struct bfd_iovec
      Also write in MAP_ADDR the address of the page aligned buffer and in
      MAP_LEN the size mapped (a page multiple).  Use unmap with MAP_ADDR and
      MAP_LEN to unmap.  */
-  void *(*bmmap) (struct bfd *abfd, void *addr, bfd_size_type len,
+  void *(*bmmap) (struct bfd *abfd, void *addr, size_t len,
 		  int prot, int flags, file_ptr offset,
-		  void **map_addr, bfd_size_type *map_len);
+		  void **map_addr, size_t *map_len);
 };
 extern const struct bfd_iovec _bfd_memory_iovec;
 
diff --git a/bfd/lynx-core.c b/bfd/lynx-core.c
index 44d94ad8745..9ec5a0d2028 100644
--- a/bfd/lynx-core.c
+++ b/bfd/lynx-core.c
@@ -96,7 +96,7 @@ lynx_core_file_p (bfd *abfd)
   asection *newsect;
   size_t amt;
 
-  pagesize = getpagesize ();	/* Serious cross-target issue here...  This
+  pagesize = _bfd_pagesize;	/* Serious cross-target issue here...  This
 				   really needs to come from a system-specific
 				   header file.  */
 
diff --git a/bfd/opncls.c b/bfd/opncls.c
index a0a5c40fba8..d54065e8be9 100644
--- a/bfd/opncls.c
+++ b/bfd/opncls.c
@@ -27,6 +27,10 @@
 #include "libiberty.h"
 #include "elf-bfd.h"
 
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
 #ifndef S_IXUSR
 #define S_IXUSR 0100	/* Execute by owner.  */
 #endif
@@ -163,6 +167,18 @@ _bfd_new_bfd_contained_in (bfd *obfd)
 static void
 _bfd_delete_bfd (bfd *abfd)
 {
+#ifdef HAVE_MMAP
+  struct bfd_mmapped *mmapped, *next;
+  for (mmapped = abfd->mmapped; mmapped != NULL; mmapped = next)
+    {
+      struct bfd_mmapped_entry *entries = mmapped->entries;
+      next = mmapped->next;
+      for (unsigned int i = 0; i < mmapped->next_entry; i++)
+	munmap (entries[i].addr, entries[i].size);
+      munmap (mmapped, _bfd_pagesize);
+    }
+#endif
+
   /* Give the target _bfd_free_cached_info a chance to free memory.  */
   if (abfd->memory && abfd->xvec)
     bfd_free_cached_info (abfd);
@@ -668,12 +684,12 @@ opncls_bstat (struct bfd *abfd, struct stat *sb)
 static void *
 opncls_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
 	      void *addr ATTRIBUTE_UNUSED,
-	      bfd_size_type len ATTRIBUTE_UNUSED,
+	      size_t len ATTRIBUTE_UNUSED,
 	      int prot ATTRIBUTE_UNUSED,
 	      int flags ATTRIBUTE_UNUSED,
 	      file_ptr offset ATTRIBUTE_UNUSED,
 	      void **map_addr ATTRIBUTE_UNUSED,
-	      bfd_size_type *map_len ATTRIBUTE_UNUSED)
+	      size_t *map_len ATTRIBUTE_UNUSED)
 {
   return (void *) -1;
 }
-- 
2.44.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/4] elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
  2024-03-05 17:00 ` [PATCH 1/4] elf: Use mmap to map in read-only sections H.J. Lu
@ 2024-03-05 17:00 ` H.J. Lu
  2024-03-05 17:00 ` [PATCH 3/4] elf: Use mmap to map in symbol and relocation tables H.J. Lu
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-05 17:00 UTC (permalink / raw)
  To: binutils; +Cc: goldstein.w.n

Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents to mmap
in sections.  A backend must opt-in to use mmap.  It should replace all
usages of bfd_malloc_and_get_section and free with _bfd_elf_mmap_section
and _bfd_elf_munmap_section_contents on section contents.

	* compress.c (bfd_get_full_section_contents): Don't allocate
	buffer if mmapped_p is true.
	* elf-bfd.h (elf_backend_data): Add use_mmap.
	(_bfd_elf_mmap_section): New.
	(_bfd_elf_munmap_section_contents): Likewise.
	* elf-eh-frame.c (_bfd_elf_parse_eh_frame): Replace
	bfd_malloc_and_get_section and free with
	_bfd_elf_mmap_section and _bfd_elf_munmap_section_contents on
	section contents.
	* elf-sframe.c (_bfd_elf_parse_sframe): Likewise.
	* elf.c: Include <sys/mman.h> if HAVE_MMAP is defined.
	(_bfd_elf_make_section_from_shdr): Replace
	bfd_malloc_and_get_section and free with
	_bfd_elf_mmap_section and _bfd_elf_munmap_section_contents on
	section contents.
	(_bfd_elf_print_private_bfd_data): Likewise.
	(_bfd_elf_mmap_section): New.
	(_bfd_elf_munmap_section_contents): Likewise.
	* elf32-i386.c (elf_i386_scan_relocs): Replace
	bfd_malloc_and_get_section and free with
	_bfd_elf_mmap_section and _bfd_elf_munmap_section_contents on
	section contents.
	* elf64-x86-64.c (elf_x86_64_scan_relocs): Likewise.
	(elf_x86_64_get_synthetic_symtab): Likewise.
	* elfcode.h (elf_checksum_contents): Likewise.
	* elflink.c (elf_link_add_object_symbols): Likewise.
	(bfd_elf_get_bfd_needed_list): Likewise.
	* elfxx-target.h (elf_backend_use_mmap): New.
	(elfNN_bed): Add elf_backend_use_mmap.
	* elfxx-x86.c (elf_x86_size_or_finish_relative_reloc): Replace
	bfd_malloc_and_get_section and free with
	_bfd_elf_mmap_section and _bfd_elf_munmap_section_contents on
	section contents.
	(_bfd_x86_elf_get_synthetic_symtab): Replace free with
	_bfd_elf_munmap_section_contents.
	* elfxx-x86.h (elf_backend_use_mmap): New.
	* libbfd.c (_bfd_generic_get_section_contents): Call
	bfd_mmap_local for mmapped_p.
	* opncls.c (_bfd_delete_bfd): Also munmap section contents.
	* section.c (asection): Add mmapped_p, contents_addr and
	contents_size.
	(BFD_FAKE_SECTION): Updated.
	(bfd_malloc_and_get_section): Add a sanity check for not
	mmapped_p.
	* bfd-in2.h: Regenerated.
---
 bfd/bfd-in2.h      | 22 +++++++++---
 bfd/compress.c     |  2 +-
 bfd/elf-bfd.h      | 11 ++++++
 bfd/elf-eh-frame.c |  4 +--
 bfd/elf-sframe.c   |  4 +--
 bfd/elf.c          | 88 +++++++++++++++++++++++++++++++++++++++++++---
 bfd/elf32-i386.c   |  6 ++--
 bfd/elf64-x86-64.c | 10 +++---
 bfd/elfcode.h      |  7 ++--
 bfd/elflink.c      | 12 +++----
 bfd/elfxx-target.h |  6 +++-
 bfd/elfxx-x86.c    |  7 ++--
 bfd/elfxx-x86.h    |  1 +
 bfd/libbfd.c       | 57 ++++++++++++++++++++++++++++--
 bfd/opncls.c       |  5 +++
 bfd/section.c      | 24 ++++++++++---
 16 files changed, 223 insertions(+), 43 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 856cb625997..dbf439a9fa3 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -688,6 +688,9 @@ typedef struct bfd_section
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
 
+  /* Nonzero if this section contents are mmapped, rather than malloced.  */
+  unsigned int mmapped_p:1;
+
   /* Bits used by various backends.  The generic code doesn't touch
      these fields.  */
 
@@ -826,6 +829,15 @@ typedef struct bfd_section
      regions is enabled.  */
   struct bfd_section *already_assigned;
 
+  /* If the mmapped_p flag is set, this points to the actual mmapped
+     address of contents.  If it is set to NULL, contents isn't
+     mmapped.  */
+  void *contents_addr;
+
+  /* If the mmapped_p flag is set, this is the actual mmapped size of
+     contents.  */
+  size_t contents_size;
+
   /* Explicitly specified section type, if non-zero.  */
   unsigned int type;
 
@@ -975,8 +987,8 @@ discarded_section (const asection *sec)
   /* linker_mark, linker_has_input, gc_mark, decompress_status,     */ \
      0,           0,                1,       0,                        \
 								       \
-  /* segment_mark, sec_info_type, use_rela_p,                       */ \
-     0,            0,             0,                                   \
+  /* segment_mark, sec_info_type, use_rela_p, mmapped_p,           */  \
+     0,            0,             0,          0,                       \
 								       \
   /* sec_flg0, sec_flg1, sec_flg2, sec_flg3, sec_flg4, sec_flg5,    */ \
      0,        0,        0,        0,        0,        0,              \
@@ -1002,8 +1014,10 @@ discarded_section (const asection *sec)
   /* symbol,                    symbol_ptr_ptr,                     */ \
      (struct bfd_symbol *) SYM, &SEC.symbol,                           \
 								       \
-  /* map_head, map_tail, already_assigned, type                     */ \
-     { NULL }, { NULL }, NULL,             0                           \
+  /* map_head, map_tail, already_assigned, contents_addr,           */ \
+     { NULL }, { NULL }, NULL,             NULL,                       \
+  /* contents_size, type                                            */ \
+     0,             0                                                  \
 								       \
     }
 
diff --git a/bfd/compress.c b/bfd/compress.c
index 8bc44de813b..6c211843e60 100644
--- a/bfd/compress.c
+++ b/bfd/compress.c
@@ -749,7 +749,7 @@ bfd_get_full_section_contents (bfd *abfd, sec_ptr sec, bfd_byte **ptr)
   switch (compress_status)
     {
     case COMPRESS_SECTION_NONE:
-      if (p == NULL)
+      if (p == NULL && !sec->mmapped_p)
 	{
 	  p = (bfd_byte *) bfd_malloc (allocsz);
 	  if (p == NULL)
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 3ed22fa6c52..f99b04e460c 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1775,6 +1775,12 @@ struct elf_backend_data
   /* True if the 64-bit Linux PRPSINFO structure's `pr_uid' and `pr_gid'
      members use a 16-bit data type.  */
   unsigned linux_prpsinfo64_ugid16 : 1;
+
+  /* True if the backend can use mmap to map in all input section
+     contents.  All bfd_malloc_and_get_section and free usages on
+     section contents must be replaced by _bfd_elf_mmap_section and
+     _bfd_elf_munmap_section_contents.  */
+  unsigned use_mmap : 1;
 };
 
 /* Information about reloc sections associated with a bfd_elf_section_data
@@ -3126,6 +3132,11 @@ extern bool _bfd_elf_maybe_set_textrel
 extern bool _bfd_elf_add_dynamic_tags
   (bfd *, struct bfd_link_info *, bool);
 
+extern bool _bfd_elf_mmap_section
+  (bfd *abfd, asection *section, bfd_byte **buf);
+extern void _bfd_elf_munmap_section_contents
+  (asection *, void *);
+
 /* Large common section.  */
 extern asection _bfd_elf_large_com_section;
 
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 9a504234163..f9836f3ea14 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -618,7 +618,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 
   /* Read the frame unwind information from abfd.  */
 
-  REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));
+  REQUIRE (_bfd_elf_mmap_section (abfd, sec, &ehbuf));
 
   /* If .eh_frame section size doesn't fit into int, we cannot handle
      it (it would need to use 64-bit .eh_frame format anyway).  */
@@ -1052,7 +1052,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
   hdr_info->u.dwarf.table = false;
   free (sec_info);
  success:
-  free (ehbuf);
+  _bfd_elf_munmap_section_contents (sec, ehbuf);
   free (local_cies);
 #undef REQUIRE
 }
diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c
index bfc875cd9fc..9a271d414b9 100644
--- a/bfd/elf-sframe.c
+++ b/bfd/elf-sframe.c
@@ -208,7 +208,7 @@ _bfd_elf_parse_sframe (bfd *abfd,
     }
 
   /* Read the SFrame stack trace information from abfd.  */
-  if (!bfd_malloc_and_get_section (abfd, sec, &sfbuf))
+  if (!_bfd_elf_mmap_section (abfd, sec, &sfbuf))
     goto fail_no_free;
 
   /* Decode the buffer and keep decoded contents for later use.
@@ -241,7 +241,7 @@ fail_no_free:
     abfd, sec);
   return false;
 success:
-  free (sfbuf);
+  _bfd_elf_munmap_section_contents (sec, sfbuf);
   return true;
 }
 
diff --git a/bfd/elf.c b/bfd/elf.c
index 173605f294d..587788c4f1b 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -45,6 +45,10 @@ SECTION
 #include "safe-ctype.h"
 #include "elf-linux-core.h"
 
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
 #ifdef CORE_HEADER
 #include CORE_HEADER
 #endif
@@ -1150,12 +1154,12 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
     {
       bfd_byte *contents;
 
-      if (!bfd_malloc_and_get_section (abfd, newsect, &contents))
+      if (!_bfd_elf_mmap_section (abfd, newsect, &contents))
 	return false;
 
       elf_parse_notes (abfd, (char *) contents, hdr->sh_size,
 		       hdr->sh_offset, hdr->sh_addralign);
-      free (contents);
+      _bfd_elf_munmap_section_contents (newsect, contents);
     }
 
   if ((newsect->flags & SEC_ALLOC) != 0)
@@ -1735,7 +1739,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
 
       fprintf (f, _("\nDynamic Section:\n"));
 
-      if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
+      if (!_bfd_elf_mmap_section (abfd, s, &dynbuf))
 	goto error_return;
 
       elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
@@ -1857,7 +1861,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
 	  fprintf (f, "\n");
 	}
 
-      free (dynbuf);
+      _bfd_elf_munmap_section_contents (s, dynbuf);
       dynbuf = NULL;
     }
 
@@ -1914,7 +1918,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
   return true;
 
  error_return:
-  free (dynbuf);
+  _bfd_elf_munmap_section_contents (s, dynbuf);
   return false;
 }
 
@@ -14422,3 +14426,77 @@ _bfd_elf_write_secondary_reloc_section (bfd *abfd, asection *sec)
 
   return result;
 }
+
+/* Mmap in section contents.  */
+
+bool
+_bfd_elf_mmap_section (bfd *abfd, sec_ptr sec, bfd_byte **buf)
+{
+#ifdef HAVE_MMAP
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  if (bed->use_mmap
+      && sec->compress_status == COMPRESS_SECTION_NONE
+      && (sec->flags & SEC_LINKER_CREATED) == 0)
+    {
+      /* Use mmap only if section size >= the minimum mmap section
+	 size.  */
+      size_t readsz = bfd_get_section_limit_octets (abfd, sec);
+      size_t allocsz = bfd_get_section_alloc_size (abfd, sec);
+      if (readsz == allocsz && readsz >= _bfd_minimum_mmap_size)
+	{
+	  if (sec->contents != NULL)
+	    {
+	      if (!sec->mmapped_p)
+		abort ();
+	      *buf = sec->contents;
+	      return true;
+	    }
+	  if (sec->mmapped_p)
+	    abort ();
+	  sec->mmapped_p = 1;
+	}
+    }
+#endif
+  *buf = NULL;
+  bool ret = bfd_get_full_section_contents (abfd, sec, buf);
+  if (ret && sec->mmapped_p)
+    *buf = sec->contents;
+  return ret;
+}
+
+/* Munmap section contents.  */
+
+void
+_bfd_elf_munmap_section_contents (asection *sec ATTRIBUTE_UNUSED,
+				  void *contents)
+{
+  /* NB: Since _bfd_elf_munmap_section_contents is called like free,
+     CONTENTS may be NULL.  */
+  if (contents == NULL)
+    return;
+
+#ifdef HAVE_MMAP
+  if (sec->mmapped_p)
+    {
+      /* NB: Don't free CONTENTS if it has been cached.  */
+      if (elf_section_data (sec)->this_hdr.contents == contents)
+	return;
+
+      /* When bfd_mmap_local returns (void *) -1 on an unknown input,
+	 CONTENTS is malloced and CONTENTS_ADDR is set to NULL.  */
+      if (sec->contents_addr != NULL)
+	{
+	  /* NB: CONTENTS_ADDR and CONTENTS_SIZE must be valid.  */
+	  if (munmap (sec->contents_addr, sec->contents_size) != 0)
+	    abort ();
+	  sec->mmapped_p = 0;
+	  sec->contents = NULL;
+	  sec->contents_addr = NULL;
+	  sec->contents_size = 0;
+	  return;
+	}
+    }
+#endif
+
+  free (contents);
+}
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 703a48c2c0a..8aa1533d0ba 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1499,7 +1499,7 @@ elf_i386_scan_relocs (bfd *abfd,
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
-  else if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+  else if (!_bfd_elf_mmap_section (abfd, sec, &contents))
     {
       sec->check_relocs_failed = 1;
       return false;
@@ -1933,7 +1933,7 @@ elf_i386_scan_relocs (bfd *abfd,
   if (elf_section_data (sec)->this_hdr.contents != contents)
     {
       if (!converted && !_bfd_link_keep_memory (info))
-	free (contents);
+	_bfd_elf_munmap_section_contents (sec, contents);
       else
 	{
 	  /* Cache the section contents for elf_link_input_bfd if any
@@ -1951,7 +1951,7 @@ elf_i386_scan_relocs (bfd *abfd,
 
  error_return:
   if (elf_section_data (sec)->this_hdr.contents != contents)
-    free (contents);
+    _bfd_elf_munmap_section_contents (sec, contents);
   sec->check_relocs_failed = 1;
   return false;
 }
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 3300a2017bd..f1e06040a5a 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -2057,7 +2057,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
-  else if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+  else if (!_bfd_elf_mmap_section (abfd, sec, &contents))
     {
       sec->check_relocs_failed = 1;
       return false;
@@ -2591,7 +2591,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
   if (elf_section_data (sec)->this_hdr.contents != contents)
     {
       if (!converted && !_bfd_link_keep_memory (info))
-	free (contents);
+	_bfd_elf_munmap_section_contents (sec, contents);
       else
 	{
 	  /* Cache the section contents for elf_link_input_bfd if any
@@ -2609,7 +2609,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
 
  error_return:
   if (elf_section_data (sec)->this_hdr.contents != contents)
-    free (contents);
+    _bfd_elf_munmap_section_contents (sec, contents);
   sec->check_relocs_failed = 1;
   return false;
 }
@@ -5274,7 +5274,7 @@ elf_x86_64_get_synthetic_symtab (bfd *abfd,
 	continue;
 
       /* Get the PLT section contents.  */
-      if (!bfd_malloc_and_get_section (abfd, plt, &plt_contents))
+      if (!_bfd_elf_mmap_section (abfd, plt, &plt_contents))
 	break;
 
       /* Check what kind of PLT it is.  */
@@ -5367,7 +5367,7 @@ elf_x86_64_get_synthetic_symtab (bfd *abfd,
 
       if (plt_type == plt_unknown)
 	{
-	  free (plt_contents);
+	  _bfd_elf_munmap_section_contents (plt, plt_contents);
 	  continue;
 	}
 
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
index 1e0784611bc..6c2ad904fc0 100644
--- a/bfd/elfcode.h
+++ b/bfd/elfcode.h
@@ -1195,6 +1195,7 @@ elf_checksum_contents (bfd *abfd,
       Elf_Internal_Shdr i_shdr;
       Elf_External_Shdr x_shdr;
       bfd_byte *contents, *free_contents;
+      asection *sec = NULL;
 
       i_shdr = *i_shdrp[count];
       i_shdr.sh_offset = 0;
@@ -1210,8 +1211,6 @@ elf_checksum_contents (bfd *abfd,
       contents = i_shdr.contents;
       if (contents == NULL)
 	{
-	  asection *sec;
-
 	  sec = bfd_section_from_elf_index (abfd, count);
 	  if (sec != NULL)
 	    {
@@ -1220,7 +1219,7 @@ elf_checksum_contents (bfd *abfd,
 		{
 		  /* Force rereading from file.  */
 		  sec->flags &= ~SEC_IN_MEMORY;
-		  if (!bfd_malloc_and_get_section (abfd, sec, &free_contents))
+		  if (!_bfd_elf_mmap_section (abfd, sec, &free_contents))
 		    continue;
 		  contents = free_contents;
 		}
@@ -1229,7 +1228,7 @@ elf_checksum_contents (bfd *abfd,
       if (contents != NULL)
 	{
 	  (*process) (contents, i_shdr.sh_size, arg);
-	  free (free_contents);
+	  _bfd_elf_munmap_section_contents (sec, free_contents);
 	}
     }
 
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 42029f29f7a..47fb890f94f 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4426,10 +4426,10 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 	  unsigned int elfsec;
 	  unsigned long shlink;
 
-	  if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
+	  if (!_bfd_elf_mmap_section (abfd, s, &dynbuf))
 	    {
 	    error_free_dyn:
-	      free (dynbuf);
+	      _bfd_elf_munmap_section_contents (s, dynbuf);
 	      goto error_return;
 	    }
 
@@ -4535,7 +4535,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 		elf_tdata (abfd)->is_pie = (dyn.d_un.d_val & DF_1_PIE) != 0;
 	    }
 
-	  free (dynbuf);
+	  _bfd_elf_munmap_section_contents (s, dynbuf);
 	}
 
       /* DT_RUNPATH overrides DT_RPATH.  Do _NOT_ bfd_release, as that
@@ -8283,7 +8283,7 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
   if (s == NULL || s->size == 0 || (s->flags & SEC_HAS_CONTENTS) == 0)
     return true;
 
-  if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
+  if (!_bfd_elf_mmap_section (abfd, s, &dynbuf))
     goto error_return;
 
   elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
@@ -8329,12 +8329,12 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
 	}
     }
 
-  free (dynbuf);
+  _bfd_elf_munmap_section_contents (s, dynbuf);
 
   return true;
 
  error_return:
-  free (dynbuf);
+  _bfd_elf_munmap_section_contents (s, dynbuf);
   return false;
 }
 
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index a7f2fc6e320..cabe3730d18 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -148,6 +148,9 @@
 #ifndef elf_backend_strtab_flags
 #define elf_backend_strtab_flags 0
 #endif
+#ifndef elf_backend_use_mmap
+#define elf_backend_use_mmap false
+#endif
 
 #define bfd_elfNN_bfd_debug_info_start		_bfd_void_bfd
 #define bfd_elfNN_bfd_debug_info_end		_bfd_void_bfd
@@ -972,7 +975,8 @@ static const struct elf_backend_data elfNN_bed =
   elf_backend_extern_protected_data,
   elf_backend_always_renumber_dynsyms,
   elf_backend_linux_prpsinfo32_ugid16,
-  elf_backend_linux_prpsinfo64_ugid16
+  elf_backend_linux_prpsinfo64_ugid16,
+  elf_backend_use_mmap
 };
 
 /* Forward declaration for use when initialising alternative_target field.  */
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 508fd771da3..ed9822628a6 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -1566,9 +1566,8 @@ elf_x86_size_or_finish_relative_reloc
 			  = elf_section_data (sec)->this_hdr.contents;
 		      else
 			{
-			  if (!bfd_malloc_and_get_section (sec->owner,
-							   sec,
-							   &contents))
+			  if (!_bfd_elf_mmap_section (sec->owner,
+						      sec, &contents))
 			    info->callbacks->einfo
 			      /* xgettext:c-format */
 			      (_("%F%P: %pB: failed to allocate memory for section `%pA'\n"),
@@ -3789,7 +3788,7 @@ _bfd_x86_elf_get_synthetic_symtab (bfd *abfd,
     count = n;
 
   for (j = 0; plts[j].name != NULL; j++)
-    free (plts[j].contents);
+    _bfd_elf_munmap_section_contents (plts[j].sec, plts[j].contents);
 
   free (dynrelbuf);
 
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index b3af9b841ba..8680e3c29b5 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -960,6 +960,7 @@ extern void _bfd_x86_elf_link_report_relative_reloc
   _bfd_elf_x86_size_relative_relocs
 #define elf_backend_finish_relative_relocs \
   _bfd_elf_x86_finish_relative_relocs
+#define elf_backend_use_mmap true
 
 #define ELF_P_ALIGN ELF_MINPAGESIZE
 
diff --git a/bfd/libbfd.c b/bfd/libbfd.c
index e1bb9fb6117..21550f2914d 100644
--- a/bfd/libbfd.c
+++ b/bfd/libbfd.c
@@ -1202,6 +1202,19 @@ _bfd_generic_get_section_contents (bfd *abfd,
       return false;
     }
 
+#ifdef HAVE_MMAP
+  if (section->mmapped_p
+      && (section->contents != NULL || location != NULL))
+    {
+      _bfd_error_handler
+	/* xgettext:c-format */
+	(_("%pB: mapped section %pA has non-NULLL buffer"),
+	 abfd, section);
+      bfd_set_error (bfd_error_invalid_operation);
+      return false;
+    }
+#endif
+
   sz = bfd_get_section_limit_octets (abfd, section);
   if (offset + count < count
       || offset + count > sz
@@ -1214,8 +1227,48 @@ _bfd_generic_get_section_contents (bfd *abfd,
       return false;
     }
 
-  if (bfd_seek (abfd, section->filepos + offset, SEEK_SET) != 0
-      || bfd_read (location, count, abfd) != count)
+  if (bfd_seek (abfd, section->filepos + offset, SEEK_SET) != 0)
+    return false;
+
+#ifdef HAVE_MMAP
+  if (section->mmapped_p)
+    {
+      if (location != 0)
+	abort ();
+
+      int prot = ((section->reloc_count == 0)
+		  ? PROT_READ : PROT_READ | PROT_WRITE);
+
+      location = bfd_mmap_local (abfd, count, prot,
+				 &section->contents_addr,
+				 &section->contents_size);
+
+      if (location == NULL)
+	return false;
+
+      /* Check for the unknown input file.  */
+      if (location != (void *) -1)
+	{
+	  section->contents = location;
+	  return true;
+	}
+
+      /* Malloc the buffer and call bfd_read.  */
+      location = (bfd_byte *) bfd_malloc (count);
+      if (location == NULL)
+	{
+	  if (bfd_get_error () == bfd_error_no_memory)
+	    _bfd_error_handler
+	      /* xgettext:c-format */
+	      (_("error: %pB(%pA) is too large (%#" PRIx64 " bytes)"),
+	       abfd, section, (uint64_t) count);
+	  return false;
+	}
+      section->contents = location;
+    }
+#endif
+
+  if (bfd_read (location, count, abfd) != count)
     return false;
 
   return true;
diff --git a/bfd/opncls.c b/bfd/opncls.c
index d54065e8be9..1dd30a19ac6 100644
--- a/bfd/opncls.c
+++ b/bfd/opncls.c
@@ -168,6 +168,11 @@ static void
 _bfd_delete_bfd (bfd *abfd)
 {
 #ifdef HAVE_MMAP
+  asection *sec;
+  for (sec = abfd->sections; sec != NULL; sec = sec->next)
+    if (sec->mmapped_p)
+      munmap (sec->contents_addr, sec->contents_size);
+
   struct bfd_mmapped *mmapped, *next;
   for (mmapped = abfd->mmapped; mmapped != NULL; mmapped = next)
     {
diff --git a/bfd/section.c b/bfd/section.c
index 8cd30e80f2b..c9f7f96662c 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -422,6 +422,9 @@ CODE_FRAGMENT
 .  {* Nonzero if this section uses RELA relocations, rather than REL.  *}
 .  unsigned int use_rela_p:1;
 .
+.  {* Nonzero if this section contents are mmapped, rather than malloced.  *}
+.  unsigned int mmapped_p:1;
+.
 .  {* Bits used by various backends.  The generic code doesn't touch
 .     these fields.  *}
 .
@@ -560,6 +563,15 @@ CODE_FRAGMENT
 .     regions is enabled.  *}
 .  struct bfd_section *already_assigned;
 .
+.  {* If the mmapped_p flag is set, this points to the actual mmapped
+.     address of contents.  If it is set to NULL, contents isn't
+.     mmapped.  *}
+.  void *contents_addr;
+.
+.  {* If the mmapped_p flag is set, this is the actual mmapped size of
+.     contents.  *}
+.  size_t contents_size;
+.
 .  {* Explicitly specified section type, if non-zero.  *}
 .  unsigned int type;
 .
@@ -711,8 +723,8 @@ EXTERNAL
 .  {* linker_mark, linker_has_input, gc_mark, decompress_status,     *}	\
 .     0,           0,                1,       0,			\
 .									\
-.  {* segment_mark, sec_info_type, use_rela_p,                       *}	\
-.     0,            0,             0,					\
+.  {* segment_mark, sec_info_type, use_rela_p, mmapped_p,           *}	\
+.     0,            0,             0,	       0,			\
 .									\
 .  {* sec_flg0, sec_flg1, sec_flg2, sec_flg3, sec_flg4, sec_flg5,    *}	\
 .     0,        0,        0,        0,        0,        0,		\
@@ -738,8 +750,10 @@ EXTERNAL
 .  {* symbol,                    symbol_ptr_ptr,                     *}	\
 .     (struct bfd_symbol *) SYM, &SEC.symbol,				\
 .									\
-.  {* map_head, map_tail, already_assigned, type                     *}	\
-.     { NULL }, { NULL }, NULL,             0				\
+.  {* map_head, map_tail, already_assigned, contents_addr,           *}	\
+.     { NULL }, { NULL }, NULL,             NULL,			\
+.  {* contents_size, type					     *}	\
+.     0,	     0							\
 .									\
 .    }
 .
@@ -1625,6 +1639,8 @@ DESCRIPTION
 bool
 bfd_malloc_and_get_section (bfd *abfd, sec_ptr sec, bfd_byte **buf)
 {
+  if (sec->mmapped_p)
+    abort ();
   *buf = NULL;
   return bfd_get_full_section_contents (abfd, sec, buf);
 }
-- 
2.44.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/4] elf: Use mmap to map in symbol and relocation tables
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
  2024-03-05 17:00 ` [PATCH 1/4] elf: Use mmap to map in read-only sections H.J. Lu
  2024-03-05 17:00 ` [PATCH 2/4] elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents H.J. Lu
@ 2024-03-05 17:00 ` H.J. Lu
  2024-03-05 17:00 ` [PATCH 4/4] elf: Don't cache symbol nor relocation tables with mmap H.J. Lu
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-05 17:00 UTC (permalink / raw)
  To: binutils; +Cc: goldstein.w.n

Add _bfd_mmap_read_untracked to mmap in symbol tables and relocations
whose sizes >= 4 * page size.  Don't cache external relocations when
mmap is used.

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.28		85.04		-0.9%
system		12.46		10.16		14%
total		96		95.35		0.7%
page faults	4837944		4047667		16%

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

user		5.22		5.27		-1%
system		0.94		0.84		11%
total		6.20		6.13		0.7%
page faults	361272		323377		10%

	* elf.c (bfd_elf_get_elf_syms): Replace bfd_read with
	_bfd_mmap_read_untracked.
	* 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_untracked.
	(bfd_elf_final_link): Don't cache external relocations when mmap
	is used.
	* libbfd.c (_bfd_mmap_read_untracked ): New.
	* libbfd-in.h (_bfd_mmap_read_untracked): Likewise.
	* libbfd.h: Regenerated.
---
 bfd/elf.c       | 47 +++++++++++++++++++++++++++++++++++------------
 bfd/elflink.c   | 44 ++++++++++++++++++++++++++++++++------------
 bfd/libbfd-in.h |  3 +++
 bfd/libbfd.c    | 33 +++++++++++++++++++++++++++++++++
 bfd/libbfd.h    |  3 +++
 5 files changed, 106 insertions(+), 24 deletions(-)

diff --git a/bfd/elf.c b/bfd/elf.c
index 587788c4f1b..f1f9b52e10c 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -465,19 +465,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 HAVE_MMAP
+      if ((ibfd->flags & BFD_PLUGIN) != 0
+	  || amt < _bfd_minimum_mmap_size)
+	{
+#endif
+	  alloc_ext = bfd_malloc (amt);
+	  extsym_buf = alloc_ext;
+#ifdef HAVE_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_untracked (&extsym_buf, &alloc_ext_size,
+				    &alloc_ext, ibfd))
     {
       intsym_buf = NULL;
       goto out;
     }
 
+  size_t alloc_extshndx_size = 0;
   if (shndx_hdr == NULL || shndx_hdr->sh_size == 0)
     extshndx_buf = NULL;
   else
@@ -488,15 +499,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 HAVE_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 HAVE_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_untracked ((void **) &extshndx_buf,
+					&alloc_extshndx_size,
+					(void **) &alloc_extshndx,
+					ibfd))
 	{
 	  intsym_buf = NULL;
 	  goto out;
@@ -535,8 +558,8 @@ bfd_elf_get_elf_syms (bfd *ibfd,
       }
 
  out:
-  free (alloc_ext);
-  free (alloc_extshndx);
+  _bfd_munmap_readonly_untracked (alloc_ext, alloc_ext_size);
+  _bfd_munmap_readonly_untracked (alloc_extshndx, alloc_extshndx_size);
 
   return intsym_buf;
 }
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 47fb890f94f..56a9f0c365e 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_P is NULL,
+   *EXTERNAL_RELOCS_P and *EXTERNAL_RELOCS_SIZE_P returns the mmap
+   memory address and size.  Otherwise, *EXTERNAL_RELOCS_SIZE_P is
+   unchanged and EXTERNAL_RELOCS_SIZE_P 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_addr,
 				   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_addr = shdr->sh_size;
+  if (!_bfd_mmap_read_untracked (&external_relocs,
+				 external_relocs_size_addr,
+				 external_relocs_addr, abfd))
     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 HAVE_MMAP
+      if (size < _bfd_minimum_mmap_size)
+	{
+#endif
+	  alloc1 = bfd_malloc (size);
+	  if (alloc1 == NULL)
+	    goto error_return;
+	  external_relocs = alloc1;
+#ifdef HAVE_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_untracked (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_untracked (alloc1, alloc1_size);
   if (alloc2 != NULL)
     {
       if (keep_memory)
@@ -12741,12 +12759,14 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	goto error_return;
     }
 
+#ifndef HAVE_MMAP
   if (max_external_reloc_size != 0)
     {
       flinfo.external_relocs = bfd_malloc (max_external_reloc_size);
       if (flinfo.external_relocs == NULL)
 	goto error_return;
     }
+#endif
 
   if (max_internal_reloc_count != 0)
     {
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index 031b1988a38..d921c4153a7 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -905,6 +905,9 @@ extern void _bfd_munmap_readonly_untracked
 #define _bfd_munmap_readonly_untracked(ptr, rsize) free (ptr)
 #endif
 
+extern bool _bfd_mmap_read_untracked
+  (void **, size_t *, void **, bfd *) 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 21550f2914d..5479650391a 100644
--- a/bfd/libbfd.c
+++ b/bfd/libbfd.c
@@ -1179,6 +1179,39 @@ _bfd_mmap_readonly_tracked (bfd *abfd, size_t asize, size_t rsize)
 }
 #endif
 
+/* Attempt to read *SIZE_ADDR bytes from ABFD's iostream to *PTR_P.
+   Return true if the full the amount has been read.  If *PTR_P is
+   NULL, mmap should be used, return the memory address at the
+   current offset in *PTR_P as well as return mmap address and size
+   in *PTR_ADDR and *SIZE_ADDR.  Otherwise, return NULL in *PTR_ADDR
+   and 0 in *SIZE_ADDR.  */
+
+bool
+_bfd_mmap_read_untracked (void **ptr_p, size_t *size_addr,
+			  void **ptr_addr, bfd *abfd)
+{
+  void *ptr = *ptr_p;
+  size_t size = *size_addr;
+
+#ifdef HAVE_MMAP
+  if (ptr == NULL)
+    {
+      ptr = _bfd_mmap_readonly_untracked (abfd, size, ptr_addr,
+					  size_addr);
+      if (ptr == NULL || ptr == (void *) -1)
+	abort ();
+      *ptr_p = ptr;
+      return true;
+    }
+#endif
+  else
+    {
+      *ptr_addr = NULL;
+      *size_addr = 0;
+      return bfd_read (ptr, size, abfd) == size;
+    }
+}
+
 /* Default implementation */
 
 bool
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 6b23c442d61..c754bc4e99d 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -911,6 +911,9 @@ extern void _bfd_munmap_readonly_untracked
 #define _bfd_munmap_readonly_untracked(ptr, rsize) free (ptr)
 #endif
 
+extern bool _bfd_mmap_read_untracked
+  (void **, size_t *, void **, bfd *) ATTRIBUTE_HIDDEN;
+
 static inline void *
 _bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
 {
-- 
2.44.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/4] elf: Don't cache symbol nor relocation tables with mmap
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
                   ` (2 preceding siblings ...)
  2024-03-05 17:00 ` [PATCH 3/4] elf: Use mmap to map in symbol and relocation tables H.J. Lu
@ 2024-03-05 17:00 ` H.J. Lu
  2024-03-05 17:03 ` [PATCH 0/4] elf: Use mmap to map in section contents Noah Goldstein
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-05 17:00 UTC (permalink / raw)
  To: binutils; +Cc: goldstein.w.n

During a "-j 8" LLVM 17 debug build on a machine with 32GB RAM and 16GB
swap, ld was killed by kernel because of out of memory:

[79437.949336] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user.slice/user-1000.slice/session-9.scope,task=ld,pid=797431,uid=1000
[79437.949349] Out of memory: Killed process 797431 (ld) total-vm:9219600kB, anon-rss:6558156kB, file-rss:1792kB, shmem-rss:0kB, UID:1000 pgtables:17552kB oom_score_adj:0

Don't cache symbol nor relocation tables if they are mapped in.  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		86.73		87.02		-0.3%
system		9.55		9.21		3.6%
total		100.40		97.66		0.7%
maximum set(GB)	17.34		13.14		24%
page faults	4047667		3042877		25%

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

user		5.41		5.44		-0.5%
system		0.80		0.76		5%
total		6.25		6.26		-0.2%
maximum set(MB)	1323		968		27%
page faults	323451		236371		27%

These improve the overall system performance for parallel build by
reducing memory usage and page faults.

	* linker.c (_bfd_link_keep_memory): Return false if HAVE_MMAP
	is defined.
---
 bfd/linker.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/bfd/linker.c b/bfd/linker.c
index 36cca9624c2..d6145b3272e 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -3561,8 +3561,12 @@ _bfd_nolink_bfd_define_start_stop (struct bfd_link_info *info ATTRIBUTE_UNUSED,
    and symbol tables of input files in memory.  */
 
 bool
-_bfd_link_keep_memory (struct bfd_link_info * info)
+_bfd_link_keep_memory (struct bfd_link_info * info ATTRIBUTE_UNUSED)
 {
+#ifdef HAVE_MMAP
+  /* Don't cache symbol nor relocation tables if they are mapped in.  */
+  return false;
+#else
   bfd *abfd;
   bfd_size_type size;
 
@@ -3590,4 +3594,5 @@ _bfd_link_keep_memory (struct bfd_link_info * info)
   while (1);
 
   return true;
+#endif
 }
-- 
2.44.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/4] elf: Use mmap to map in section contents
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
                   ` (3 preceding siblings ...)
  2024-03-05 17:00 ` [PATCH 4/4] elf: Don't cache symbol nor relocation tables with mmap H.J. Lu
@ 2024-03-05 17:03 ` Noah Goldstein
  2024-03-06  0:50 ` Alan Modra
  2024-03-06  4:45 ` Sam James
  6 siblings, 0 replies; 10+ messages in thread
From: Noah Goldstein @ 2024-03-05 17:03 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Tue, Mar 5, 2024 at 11:01 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> We can use mmap to map in ELF section contents, instead of copying them
> into memory by hand.  We don't need to cache symbol nor relocation tables
> if they are mapped in.  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            86.73           87.02           -0.3%
> system          9.55            9.21            3.6%
> total           100.40          97.66           0.7%
> maximum set(GB) 17.34           13.14           24%
> page faults     4047667         3042877         25%
>
> and data to link the 275M cc1plus executable in GCC 14 stage 1 build is:
>
> user            5.41            5.44            -0.5%
> system          0.80            0.76            5%
> total           6.25            6.26            -0.2%
> maximum set(MB) 1323            968             27%
> page faults     323451          236371          27%
>
> Data shows that these won't improve the single copy linker performance.
> But they improve the overall system performance when linker is used by
> reducing linker memory usage and page faults.  They allow more parallel
> linker jobs on LLVM debug build.
>
> Here is a quote from Noah Goldstein: "on a large project they are an
> extremely large speedup".
For reference, it is ~2x speedup of link step of LLVM head on debug build.
>
> H.J. Lu (4):
>   elf: Use mmap to map in read-only sections
>   elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents
>   elf: Use mmap to map in symbol and relocation tables
>   elf: Don't cache symbol nor relocation tables with mmap
>
>  bfd/bfd-in2.h      |  43 ++++++--
>  bfd/bfd.c          |  17 +++
>  bfd/bfdio.c        |  16 +--
>  bfd/bfdwin.c       |   8 +-
>  bfd/cache.c        |  11 +-
>  bfd/compress.c     |   2 +-
>  bfd/elf-bfd.h      |  11 ++
>  bfd/elf-eh-frame.c |   4 +-
>  bfd/elf-sframe.c   |   4 +-
>  bfd/elf.c          | 256 +++++++++++++++++++++++++++++++++++++--------
>  bfd/elf32-i386.c   |   6 +-
>  bfd/elf64-x86-64.c |  10 +-
>  bfd/elfcode.h      |   7 +-
>  bfd/elflink.c      |  72 ++++++++-----
>  bfd/elfxx-target.h |   6 +-
>  bfd/elfxx-x86.c    |   7 +-
>  bfd/elfxx-x86.h    |   1 +
>  bfd/libbfd-in.h    |  32 ++++++
>  bfd/libbfd.c       | 247 ++++++++++++++++++++++++++++++++++++++++++-
>  bfd/libbfd.h       |  36 ++++++-
>  bfd/linker.c       |   7 +-
>  bfd/lynx-core.c    |   2 +-
>  bfd/opncls.c       |  25 ++++-
>  bfd/section.c      |  24 ++++-
>  24 files changed, 721 insertions(+), 133 deletions(-)
>
> --
> 2.44.0
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/4] elf: Use mmap to map in section contents
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
                   ` (4 preceding siblings ...)
  2024-03-05 17:03 ` [PATCH 0/4] elf: Use mmap to map in section contents Noah Goldstein
@ 2024-03-06  0:50 ` Alan Modra
  2024-03-06 16:16   ` H.J. Lu
  2024-03-06  4:45 ` Sam James
  6 siblings, 1 reply; 10+ messages in thread
From: Alan Modra @ 2024-03-06  0:50 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils, goldstein.w.n

On Tue, Mar 05, 2024 at 09:00:53AM -0800, H.J. Lu wrote:
> We can use mmap to map in ELF section contents, instead of copying them
> into memory by hand.  We don't need to cache symbol nor relocation tables
> if they are mapped in.  Data to link the 3.5GB clang executable in LLVM
> 17 debug build on Linux/x86-64 with 32GB RAM is:

I like the idea.  Some comments on the patch:
- You should probably replace occurrences of HAVE_MMAP with USE_MMAP,
  and change the config default for --with-mmap.
- There doesn't seem to be any reason to have an asize parameter to 
  _bfd_mmap_readonly_tracked if you are going to give an error on
  unterminated string tables.  (BTW, I don't disagree with giving an
  error.)
- Can you put contents_addr in bfd_elf_section_data rather than
  bfd_section so that the extra pointer doesn't affect non-ELF
  targets?
- _bfd_link_keep_memory should probably be moved to elflink.c, renamed
  to _elf_link_keep_memory and declared in elf-bfd.h.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/4] elf: Use mmap to map in section contents
  2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
                   ` (5 preceding siblings ...)
  2024-03-06  0:50 ` Alan Modra
@ 2024-03-06  4:45 ` Sam James
  6 siblings, 0 replies; 10+ messages in thread
From: Sam James @ 2024-03-06  4:45 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils, goldstein.w.n

[-- Attachment #1: Type: text/plain, Size: 1245 bytes --]

"H.J. Lu" <hjl.tools@gmail.com> writes:

> We can use mmap to map in ELF section contents, instead of copying them
> into memory by hand.  We don't need to cache symbol nor relocation tables
> if they are mapped in.  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		86.73		87.02		-0.3%
> system		9.55		9.21		3.6%
> total		100.40		97.66		0.7%
> maximum set(GB)	17.34		13.14		24%
> page faults	4047667		3042877		25%
>
> and data to link the 275M cc1plus executable in GCC 14 stage 1 build is:
>
> user		5.41		5.44		-0.5%
> system		0.80		0.76		5%
> total		6.25		6.26		-0.2%
> maximum set(MB)	1323		968		27%
> page faults	323451		236371		27%
>
> Data shows that these won't improve the single copy linker performance.
> But they improve the overall system performance when linker is used by
> reducing linker memory usage and page faults.  They allow more parallel
> linker jobs on LLVM debug build.
>
> Here is a quote from Noah Goldstein: "on a large project they are an
> extremely large speedup".

Thanks for working on this. I've suffered from OOMs when linking large
C++ projects with debug info a lot. I'm going to include it in my
regular test builds.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 377 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/4] elf: Use mmap to map in section contents
  2024-03-06  0:50 ` Alan Modra
@ 2024-03-06 16:16   ` H.J. Lu
  2024-03-06 20:51     ` H.J. Lu
  0 siblings, 1 reply; 10+ messages in thread
From: H.J. Lu @ 2024-03-06 16:16 UTC (permalink / raw)
  To: Alan Modra; +Cc: binutils, goldstein.w.n

On Tue, Mar 5, 2024 at 4:51 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Tue, Mar 05, 2024 at 09:00:53AM -0800, H.J. Lu wrote:
> > We can use mmap to map in ELF section contents, instead of copying them
> > into memory by hand.  We don't need to cache symbol nor relocation tables
> > if they are mapped in.  Data to link the 3.5GB clang executable in LLVM
> > 17 debug build on Linux/x86-64 with 32GB RAM is:
>
> I like the idea.  Some comments on the patch:
> - You should probably replace occurrences of HAVE_MMAP with USE_MMAP,
>   and change the config default for --with-mmap.

Fixed.

> - There doesn't seem to be any reason to have an asize parameter to
>   _bfd_mmap_readonly_tracked if you are going to give an error on
>   unterminated string tables.  (BTW, I don't disagree with giving an
>   error.)

Fixed.

> - Can you put contents_addr in bfd_elf_section_data rather than
>   bfd_section so that the extra pointer doesn't affect non-ELF
>   targets?

Fixed.

> - _bfd_link_keep_memory should probably be moved to elflink.c, renamed
>   to _elf_link_keep_memory and declared in elf-bfd.h.
>

I renamed it to _bfd_elf_link_keep_memory.

Here is the v2 patch:

https://patchwork.sourceware.org/project/binutils/list/?series=31657

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/4] elf: Use mmap to map in section contents
  2024-03-06 16:16   ` H.J. Lu
@ 2024-03-06 20:51     ` H.J. Lu
  0 siblings, 0 replies; 10+ messages in thread
From: H.J. Lu @ 2024-03-06 20:51 UTC (permalink / raw)
  To: Alan Modra; +Cc: binutils, goldstein.w.n

On Wed, Mar 6, 2024 at 8:16 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Mar 5, 2024 at 4:51 PM Alan Modra <amodra@gmail.com> wrote:
> >
> > On Tue, Mar 05, 2024 at 09:00:53AM -0800, H.J. Lu wrote:
> > > We can use mmap to map in ELF section contents, instead of copying them
> > > into memory by hand.  We don't need to cache symbol nor relocation tables
> > > if they are mapped in.  Data to link the 3.5GB clang executable in LLVM
> > > 17 debug build on Linux/x86-64 with 32GB RAM is:
> >
> > I like the idea.  Some comments on the patch:
> > - You should probably replace occurrences of HAVE_MMAP with USE_MMAP,
> >   and change the config default for --with-mmap.
>
> Fixed.
>
> > - There doesn't seem to be any reason to have an asize parameter to
> >   _bfd_mmap_readonly_tracked if you are going to give an error on
> >   unterminated string tables.  (BTW, I don't disagree with giving an
> >   error.)
>
> Fixed.
>
> > - Can you put contents_addr in bfd_elf_section_data rather than
> >   bfd_section so that the extra pointer doesn't affect non-ELF
> >   targets?
>
> Fixed.
>
> > - _bfd_link_keep_memory should probably be moved to elflink.c, renamed
> >   to _elf_link_keep_memory and declared in elf-bfd.h.
> >
>
> I renamed it to _bfd_elf_link_keep_memory.
>
> Here is the v2 patch:
>
> https://patchwork.sourceware.org/project/binutils/list/?series=31657
>

The v3 patch is at:

https://patchwork.sourceware.org/project/binutils/list/?series=31666

-- 
H.J.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-03-06 20:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-05 17:00 [PATCH 0/4] elf: Use mmap to map in section contents H.J. Lu
2024-03-05 17:00 ` [PATCH 1/4] elf: Use mmap to map in read-only sections H.J. Lu
2024-03-05 17:00 ` [PATCH 2/4] elf: Add _bfd_elf_mmap_section and _bfd_elf_munmap_section_contents H.J. Lu
2024-03-05 17:00 ` [PATCH 3/4] elf: Use mmap to map in symbol and relocation tables H.J. Lu
2024-03-05 17:00 ` [PATCH 4/4] elf: Don't cache symbol nor relocation tables with mmap H.J. Lu
2024-03-05 17:03 ` [PATCH 0/4] elf: Use mmap to map in section contents Noah Goldstein
2024-03-06  0:50 ` Alan Modra
2024-03-06 16:16   ` H.J. Lu
2024-03-06 20:51     ` H.J. Lu
2024-03-06  4:45 ` Sam James

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).