@@ -1951,6 +1951,20 @@ struct bfd_build_id
bfd_byte data[1];
};
+struct bfd_mmapped_entry
+ {
+ void *addr;
+ bfd_size_type 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 *
@@ -74,6 +74,20 @@ EXTERNAL
. bfd_byte data[1];
. };
.
+.struct bfd_mmapped_entry
+. {
+. void *addr;
+. bfd_size_type 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;
.};
.
@@ -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));
@@ -498,7 +498,6 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
#ifdef HAVE_MMAP
else
{
- static uintptr_t pagesize_m1;
FILE *f;
file_ptr pg_offset;
bfd_size_type pg_len;
@@ -510,12 +509,10 @@ 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;
+ pg_offset = offset & ~_bfd_pagesize_m1;
+ pg_len = ((len + (offset - pg_offset) + _bfd_pagesize_m1)
+ & ~_bfd_pagesize_m1);
ret = mmap (addr, pg_len, prot, flags, fileno (f), pg_offset);
if (ret == (void *) -1)
@@ -524,7 +521,7 @@ cache_bmmap (struct bfd *abfd ATTRIBUTE_UNUSED,
{
*map_addr = ret;
*map_len = pg_len;
- ret = (char *) ret + (offset & pagesize_m1);
+ ret = (char *) ret + (offset & _bfd_pagesize_m1);
}
}
#endif
@@ -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 (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;
@@ -1940,7 +1947,7 @@ 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_local (abfd, size);
if (e_data == NULL)
return NULL;
@@ -1958,7 +1965,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_local (e_data, size);
return i_data;
}
@@ -2007,6 +2014,8 @@ _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;
+ size_t esymbuf_size = 0;
+ size_t dynbuf_size = 0;
/* Return TRUE if symbol table is bad. */
if (elf_bad_symtab (abfd))
@@ -2024,7 +2033,8 @@ _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_local (abfd, dynbuf_size);
if (dynbuf == NULL)
goto error_return;
@@ -2102,11 +2112,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 (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 +2297,8 @@ _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_local (abfd, esymbuf_size);
if (esymbuf == NULL)
goto error_return;
@@ -2325,7 +2342,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 (abfd, amt, amt);
if (dt_verdef)
{
@@ -2337,8 +2354,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 (abfd, verdef_size, verdef_size);
}
if (dt_verneed)
@@ -2351,8 +2367,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 (abfd, verneed_size,
+ verneed_size);
}
}
@@ -2375,8 +2391,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_local (dynbuf, dynbuf_size);
+ _bfd_munmap_readonly_local (esymbuf, esymbuf_size);
free (gnubuckets);
free (gnuchains);
free (mipsxlat);
@@ -9435,6 +9451,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
bfd_byte *contents = NULL;
unsigned int freeidx = 0;
size_t amt;
+ size_t size = 0;
if (elf_dynverref (abfd) != 0 || elf_tdata (abfd)->dt_verneed != NULL)
{
@@ -9471,7 +9488,8 @@ _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);
+ size = hdr->sh_size;
+ contents = _bfd_mmap_readonly_local (abfd, size);
if (contents == NULL)
goto error_return_verref;
@@ -9604,7 +9622,7 @@ _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_local (contents, size);
contents = NULL;
}
@@ -9646,7 +9664,8 @@ _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);
+ size = hdr->sh_size;
+ contents = _bfd_mmap_readonly_local (abfd, size);
if (contents == NULL)
goto error_return_verdef;
@@ -9800,7 +9819,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
}
if (contents != elf_tdata (abfd)->dt_verdef)
- free (contents);
+ _bfd_munmap_readonly_local (contents, size);
contents = NULL;
}
else if (default_imported_symver)
@@ -9857,7 +9876,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_local (contents, size);
return false;
}
@@ -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;
@@ -851,6 +851,9 @@ 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;
+
#if GCC_VERSION >= 7000
#define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
#else
@@ -888,6 +891,21 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
return NULL;
}
+#ifdef HAVE_MMAP
+extern void *_bfd_mmap_readonly (bfd *, bfd_size_type, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_local (bfd *, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_local (void *, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly(abfd, asize, rsize) \
+ _bfd_alloc_and_read (abfd, asize, rsize)
+#define _bfd_mmap_readonly_local(abfd, rsize) \
+ _bfd_malloc_and_read (abfd, rsize, rsize)
+#define _bfd_munmap_readonly_local(ptr, rsize) free (ptr)
+#endif
+
static inline void *
_bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
{
@@ -1326,3 +1326,135 @@ _bfd_generic_init_private_section_data (bfd *ibfd ATTRIBUTE_UNUSED,
{
return true;
}
+
+#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 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. */
+
+static void *
+bfd_mmap_readonly_local_1 (bfd *abfd, bfd_size_type rsize,
+ void **map_addr, bfd_size_type *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_READ, MAP_PRIVATE, offset,
+ map_addr, map_size);
+ return mem;
+}
+
+/* Mmap a readonly memory region of RSIZE bytes at the current offset.
+ Return NULL on invalid input or mmap failure. */
+
+void *
+_bfd_mmap_readonly_local (bfd *abfd, bfd_size_type rsize)
+{
+ void *map_addr;
+ bfd_size_type map_size;
+ return bfd_mmap_readonly_local_1 (abfd, rsize, &map_addr, &map_size);
+}
+
+/* Munmap RSIZE bytes at PTR. */
+
+void
+_bfd_munmap_readonly_local (void *ptr, bfd_size_type rsize)
+{
+ /* NB: Since _bfd_munmap_readonly_local is called like free,
+ PTR may be NULL. */
+ if (ptr != NULL)
+ munmap (ptr, rsize);
+}
+
+/* Mmap a readonly memory region of RSIZE bytes at the current offset.
+ Return NULL on invalid input or mmap failure. */
+
+void *
+_bfd_mmap_readonly (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
+{
+ void *mem, *map_addr;
+ bfd_size_type map_size;
+ mem = bfd_mmap_readonly_local_1 (abfd, rsize, &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
+
+uintptr_t _bfd_pagesize;
+uintptr_t _bfd_pagesize_m1;
+
+__attribute__ ((unused, constructor))
+static void
+bfd_init_pagesize (void)
+{
+ _bfd_pagesize = getpagesize ();
+ if (_bfd_pagesize == 0)
+ abort ();
+ _bfd_pagesize_m1 = _bfd_pagesize - 1;
+}
@@ -857,6 +857,9 @@ 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;
+
#if GCC_VERSION >= 7000
#define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
#else
@@ -894,6 +897,21 @@ _bfd_alloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
return NULL;
}
+#ifdef HAVE_MMAP
+extern void *_bfd_mmap_readonly (bfd *, bfd_size_type, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+extern void *_bfd_mmap_readonly_local (bfd *, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+extern void _bfd_munmap_readonly_local (void *, bfd_size_type)
+ ATTRIBUTE_HIDDEN;
+#else
+#define _bfd_mmap_readonly(abfd, asize, rsize) \
+ _bfd_alloc_and_read (abfd, asize, rsize)
+#define _bfd_mmap_readonly_local(abfd, rsize) \
+ _bfd_malloc_and_read (abfd, rsize, rsize)
+#define _bfd_munmap_readonly_local(ptr, rsize) free (ptr)
+#endif
+
static inline void *
_bfd_malloc_and_read (bfd *abfd, bfd_size_type asize, bfd_size_type rsize)
{
@@ -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. */
@@ -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
@@ -176,6 +180,18 @@ _bfd_delete_bfd (bfd *abfd)
else
free ((char *) bfd_get_filename (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
+
free (abfd->arelt_data);
free (abfd);
}