public inbox for binutils-cvs@sourceware.org
 help / color / mirror / Atom feed
* [binutils-gdb] elf: Use mmap to map in read-only sections
@ 2024-04-03 16:17 H.J. Lu
  0 siblings, 0 replies; only message in thread
From: H.J. Lu @ 2024-04-03 16:17 UTC (permalink / raw)
  To: binutils-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9ba56acee518492cfe21434b974c807f52ac7950

commit 9ba56acee518492cfe21434b974c807f52ac7950
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Thu Feb 29 11:17:01 2024 -0800

    elf: Use mmap to map in read-only sections
    
    There are many linker input files in LLVM debug build with huge string
    sections.  All these string sections can be treated as read-only.  But
    linker copies all of them into memory which consumes huge amount of
    memory and slows down linker significantly.
    
    Add _bfd_mmap_readonly_persistent and _bfd_mmap_readonly_temporary to
    mmap in reado-only sections with size >= 4 * page size.
    
    NB: All string sections in valid ELF inputs must be null terminated.
    There is no need to terminate it again and string sections are mmapped
    as read-only.
    
            * bfd.c (bfd_mmapped_entry): New.
            (bfd_mmapped): Likewise.
            (bfd): Add mmapped.
            * bfdwin.c (bfd_get_file_window): Use _bfd_pagesize.
            * cache.c (cache_bmmap): Remove pagesize_m1 and use pagesize_m1
            instead.
            * elf.c (bfd_elf_get_str_section): Call
            _bfd_mmap_readonly_persistent instead of _bfd_alloc_and_read.
            Don't terminate the string section again.
            (get_hash_table_data): Call _bfd_mmap_readonly_temporary and
            _bfd_munmap_readonly_temporary instead of _bfd_malloc_and_read
            and free.
            (_bfd_elf_get_dynamic_symbols): Call _bfd_mmap_readonly_persistent
            instead of _bfd_alloc_and_read.  Don't terminate the string
            section again.  Call _bfd_mmap_readonly_temporary and
            _bfd_munmap_readonly_temporary instead of _bfd_malloc_and_read
            and free.
            (_bfd_elf_slurp_version_tables): Call _bfd_mmap_readonly_temporary
            and _bfd_munmap_readonly_temporary 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_persistent): Likewise.
            (_bfd_mmap_readonly_temporary): Likewise.
            (_bfd_munmap_readonly_temporary): Likewise.
            * libbfd.c
            (bfd_allocate_mmapped_page): New.
            (_bfd_mmap_readonly_temporary): Likewise.
            (_bfd_munmap_readonly_temporary): Likewise.
            (_bfd_mmap_readonly_persistent): Likewise.
            (_bfd_pagesize): Likewise.
            (_bfd_pagesize_m1): Likewise.
            (_bfd_minimum_mmap_size): Likewise.
            (bfd_init_pagesize): Likewise.
            * lynx-core.c (lynx_core_file_p): Use _bfd_pagesize.
            * opncls.c (_bfd_delete_bfd): Munmap tracked mmapped memories.
            * sysdep.h (MAP_ANONYMOUS): New. Define if undefined.
            * bfd-in2.h: Regenerated.
            * libbfd.h: Likewise.

Diff:
---
 bfd/bfd-in2.h   |  17 +++++++
 bfd/bfd.c       |  17 +++++++
 bfd/bfdwin.c    |   8 +--
 bfd/cache.c     |   7 +--
 bfd/elf.c       |  79 ++++++++++++++++++++---------
 bfd/elflink.c   |  16 +++---
 bfd/libbfd-in.h |  29 +++++++++++
 bfd/libbfd.c    | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/libbfd.h    |  29 +++++++++++
 bfd/lynx-core.c |   2 +-
 bfd/opncls.c    |  12 +++++
 bfd/sysdep.h    |   4 ++
 12 files changed, 328 insertions(+), 43 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index b332f5adad1..8b86607ec1d 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1962,6 +1962,20 @@ enum bfd_lto_object_type
     lto_fat_ir_object          /* A fat LTO IR object.  */
   };
 
+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.  */
@@ -2291,6 +2305,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 *
diff --git a/bfd/bfd.c b/bfd/bfd.c
index e579788e42f..8fd86f68e6e 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -82,6 +82,20 @@ EXTERNAL
 .    lto_fat_ir_object		{* A fat LTO IR object.  *}
 .  };
 .
+.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
@@ -414,6 +428,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/bfdwin.c b/bfd/bfdwin.c
index 2919c71b3cb..73e44635bcb 100644
--- a/bfd/bfdwin.c
+++ b/bfd/bfdwin.c
@@ -157,7 +157,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;
 
@@ -167,12 +167,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 d0e7be293a5..0f994c74239 100644
--- a/bfd/cache.c
+++ b/bfd/cache.c
@@ -494,10 +494,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)
@@ -506,9 +506,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 f7717547fad..34100d81355 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -289,16 +289,23 @@ 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_persistent (abfd, 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;
@@ -1897,6 +1904,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;
@@ -1918,7 +1927,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_temporary (abfd, size, &e_data_addr,
+					 &e_data_size);
   if (e_data == NULL)
     return NULL;
 
@@ -1936,7 +1946,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_temporary (e_data_addr, e_data_size);
   return i_data;
 }
 
@@ -1985,6 +1995,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))
@@ -2002,7 +2016,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_temporary (abfd, dynbuf_size,
+					 &dynbuf_addr, &dynbuf_size);
   if (dynbuf == NULL)
     goto error_return;
 
@@ -2080,11 +2096,17 @@ _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_persistent (abfd, 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.  */
@@ -2259,7 +2281,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_temporary (abfd, esymbuf_size,
+					  &esymbuf_addr,
+					  &esymbuf_size);
   if (esymbuf == NULL)
     goto error_return;
 
@@ -2303,7 +2328,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_persistent (abfd, amt);
 
       if (dt_verdef)
 	{
@@ -2315,8 +2340,7 @@ _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_persistent (abfd, verdef_size);
 	}
 
       if (dt_verneed)
@@ -2329,8 +2353,7 @@ _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_persistent (abfd, verneed_size);
 	}
     }
 
@@ -2353,8 +2376,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_temporary (dynbuf_addr, dynbuf_size);
+  _bfd_munmap_readonly_temporary (esymbuf_addr, esymbuf_size);
   free (gnubuckets);
   free (gnuchains);
   free (mipsxlat);
@@ -9413,6 +9436,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)
     {
@@ -9449,7 +9474,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_temporary (abfd, contents_size,
+						   &contents_addr,
+						   &contents_size);
 	  if (contents == NULL)
 	    goto error_return_verref;
 
@@ -9582,8 +9610,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_temporary (contents_addr, contents_size);
       contents = NULL;
+      contents_addr = NULL;
     }
 
   if (elf_dynverdef (abfd) != 0 || elf_tdata (abfd)->dt_verdef != NULL)
@@ -9624,7 +9653,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_temporary (abfd, contents_size,
+						   &contents_addr,
+						   &contents_size);
 	  if (contents == NULL)
 	    goto error_return_verdef;
 
@@ -9778,8 +9810,9 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 	}
 
       if (contents != elf_tdata (abfd)->dt_verdef)
-	free (contents);
+	_bfd_munmap_readonly_temporary (contents_addr, contents_size);
       contents = NULL;
+      contents_addr = NULL;
     }
   else if (default_imported_symver)
     {
@@ -9835,7 +9868,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_temporary (contents_addr, contents_size);
   return false;
 }
 \f
diff --git a/bfd/elflink.c b/bfd/elflink.c
index e9cef470333..44709c9830c 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..c5a79cf932c 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 USE_MMAP
+extern void *_bfd_mmap_readonly_persistent
+  (bfd *, size_t) ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_temporary
+  (bfd *, size_t, void **, size_t *) ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_temporary
+  (void *, size_t) ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly_persistent(abfd, rsize) \
+  _bfd_alloc_and_read (abfd, rsize, rsize)
+#define _bfd_munmap_readonly_temporary(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 USE_MMAP
+static inline void *
+_bfd_mmap_readonly_temporary (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..a79c814a0dc 100644
--- a/bfd/libbfd.c
+++ b/bfd/libbfd.c
@@ -1038,6 +1038,141 @@ bfd_get_bits (const void *p, int bits, bool big_p)
   return data;
 }
 \f
+#ifdef USE_MMAP
+/* 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 PROT at the current offset.
+   Return mmap address and size in MAP_ADDR and MAP_SIZE.  Return NULL
+   on invalid input and MAP_FAILED for mmap failure.  */
+
+static void *
+bfd_mmap_local (bfd *abfd, size_t rsize, int prot, 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, prot, 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 MAP_FAILED for mmap failure.  */
+
+void *
+_bfd_mmap_readonly_temporary (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_temporary (void *ptr, size_t rsize)
+{
+  /* NB: Since _bfd_munmap_readonly_temporary 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_persistent (bfd *abfd, 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 == MAP_FAILED)
+    return _bfd_alloc_and_read (abfd, rsize, 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 +1461,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 7de23a50d18..0caf0f39e61 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 USE_MMAP
+extern void *_bfd_mmap_readonly_persistent
+  (bfd *, size_t) ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_temporary
+  (bfd *, size_t, void **, size_t *) ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_temporary
+  (void *, size_t) ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly_persistent(abfd, rsize) \
+  _bfd_alloc_and_read (abfd, rsize, rsize)
+#define _bfd_munmap_readonly_temporary(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 USE_MMAP
+static inline void *
+_bfd_mmap_readonly_temporary (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;
 
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 c764d204831..e6337b88e18 100644
--- a/bfd/opncls.c
+++ b/bfd/opncls.c
@@ -163,6 +163,18 @@ _bfd_new_bfd_contained_in (bfd *obfd)
 static void
 _bfd_delete_bfd (bfd *abfd)
 {
+#ifdef USE_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);
diff --git a/bfd/sysdep.h b/bfd/sysdep.h
index b907bc26a09..173f5804df3 100644
--- a/bfd/sysdep.h
+++ b/bfd/sysdep.h
@@ -98,6 +98,10 @@
 #define MAP_FAILED ((void *) -1)
 #endif
 
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS  MAP_ANON
+#endif
+
 #include "filenames.h"
 
 #if !HAVE_DECL_FFS

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-04-03 16:17 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-03 16:17 [binutils-gdb] elf: Use mmap to map in read-only sections H.J. Lu

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