aboutsummaryrefslogtreecommitdiff
path: root/bfd/libbfd.c
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2024-02-29 11:17:01 -0800
committerH.J. Lu <hjl.tools@gmail.com>2024-04-03 09:11:00 -0700
commit9ba56acee518492cfe21434b974c807f52ac7950 (patch)
tree33eba694516786c5249f41fae1c4b60abd068723 /bfd/libbfd.c
parentf89ae595dd1f5195dd6e8e57bc2217463c436888 (diff)
downloadbinutils-9ba56acee518492cfe21434b974c807f52ac7950.zip
binutils-9ba56acee518492cfe21434b974c807f52ac7950.tar.gz
binutils-9ba56acee518492cfe21434b974c807f52ac7950.tar.bz2
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.
Diffstat (limited to 'bfd/libbfd.c')
-rw-r--r--bfd/libbfd.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/bfd/libbfd.c b/bfd/libbfd.c
index f8d148c..a79c814 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;
}
+#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;
+}