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
Subject: [PATCH 1/4] elf: Use mmap to map in read-only sections
Date: Tue,  5 Mar 2024 09:00:54 -0800	[thread overview]
Message-ID: <20240305170057.292800-2-hjl.tools@gmail.com> (raw)
In-Reply-To: <20240305170057.292800-1-hjl.tools@gmail.com>

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


  reply	other threads:[~2024-03-05 17:01 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

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=20240305170057.292800-2-hjl.tools@gmail.com \
    --to=hjl.tools@gmail.com \
    --cc=binutils@sourceware.org \
    --cc=goldstein.w.n@gmail.com \
    /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).