diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 9a698316980..43d4ff0b925 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -6717,6 +6717,9 @@ struct bfd be used only for archive elements. */ int archive_pass; + /* The total size of memory from bfd_alloc. */ + bfd_size_type alloc_size; + /* Stuff only useful for object files: The start address. */ bfd_vma start_address; diff --git a/bfd/bfd.c b/bfd/bfd.c index 0952aaea19c..eb555ad5df2 100644 --- a/bfd/bfd.c +++ b/bfd/bfd.c @@ -286,6 +286,9 @@ CODE_FRAGMENT . be used only for archive elements. *} . int archive_pass; . +. {* The total size of memory from bfd_alloc. *} +. bfd_size_type alloc_size; +. . {* Stuff only useful for object files: . The start address. *} . bfd_vma start_address; diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 8f985ab8917..65c08ca9d4a 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -2482,6 +2482,9 @@ extern char *_bfd_elfcore_strndup extern Elf_Internal_Rela *_bfd_elf_link_read_relocs (bfd *, asection *, void *, Elf_Internal_Rela *, bool); +extern Elf_Internal_Rela *_bfd_elf_link_info_read_relocs + (bfd *, struct bfd_link_info *, asection *, void *, Elf_Internal_Rela *, + bool); extern bool _bfd_elf_link_output_relocs (bfd *, asection *, Elf_Internal_Shdr *, Elf_Internal_Rela *, diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index cf7cd076b17..1898ba3e33a 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -1915,13 +1915,14 @@ elf_i386_check_relocs (bfd *abfd, if (elf_section_data (sec)->this_hdr.contents != contents) { - if (!converted && !info->keep_memory) + if (!converted && !_bfd_link_keep_memory (info)) free (contents); else { /* Cache the section contents for elf_link_input_bfd if any load is converted or --no-keep-memory isn't used. */ elf_section_data (sec)->this_hdr.contents = contents; + info->cache_size += sec->size; } } diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 98fb88113c0..4c55c5999c3 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -2365,13 +2365,14 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, if (elf_section_data (sec)->this_hdr.contents != contents) { - if (!converted && !info->keep_memory) + if (!converted && !_bfd_link_keep_memory (info)) free (contents); else { /* Cache the section contents for elf_link_input_bfd if any load is converted or --no-keep-memory isn't used. */ elf_section_data (sec)->this_hdr.contents = contents; + info->cache_size += sec->size; } } diff --git a/bfd/elflink.c b/bfd/elflink.c index 9a05208253c..003c9546b03 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -2619,14 +2619,16 @@ elf_link_read_relocs_from_section (bfd *abfd, according to the KEEP_MEMORY argument. If O has two relocation sections (both REL and RELA relocations), then the REL_HDR relocations will appear first in INTERNAL_RELOCS, followed by the - RELA_HDR relocations. */ + RELA_HDR relocations. If INFO isn't NULL and KEEP_MEMORY is true, + update cache_size. */ Elf_Internal_Rela * -_bfd_elf_link_read_relocs (bfd *abfd, - asection *o, - void *external_relocs, - Elf_Internal_Rela *internal_relocs, - bool keep_memory) +_bfd_elf_link_info_read_relocs (bfd *abfd, + struct bfd_link_info *info, + asection *o, + void *external_relocs, + Elf_Internal_Rela *internal_relocs, + bool keep_memory) { void *alloc1 = NULL; Elf_Internal_Rela *alloc2 = NULL; @@ -2646,7 +2648,11 @@ _bfd_elf_link_read_relocs (bfd *abfd, size = (bfd_size_type) o->reloc_count * sizeof (Elf_Internal_Rela); if (keep_memory) - internal_relocs = alloc2 = (Elf_Internal_Rela *) bfd_alloc (abfd, size); + { + internal_relocs = alloc2 = (Elf_Internal_Rela *) bfd_alloc (abfd, size); + if (info) + info->cache_size += size; + } else internal_relocs = alloc2 = (Elf_Internal_Rela *) bfd_malloc (size); if (internal_relocs == NULL) @@ -2710,6 +2716,22 @@ _bfd_elf_link_read_relocs (bfd *abfd, return NULL; } +/* This is similar to _bfd_elf_link_info_read_relocs, except for that + NULL is passed to _bfd_elf_link_info_read_relocs for pointer to + struct bfd_link_info. */ + +Elf_Internal_Rela * +_bfd_elf_link_read_relocs (bfd *abfd, + asection *o, + void *external_relocs, + Elf_Internal_Rela *internal_relocs, + bool keep_memory) +{ + return _bfd_elf_link_info_read_relocs (abfd, NULL, o, external_relocs, + internal_relocs, keep_memory); + +} + /* Compute the size of, and allocate space for, REL_HDR which is the section header for a section containing relocations for O. */ @@ -4026,8 +4048,10 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info) || bfd_is_abs_section (o->output_section)) continue; - internal_relocs = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL, - info->keep_memory); + internal_relocs = _bfd_elf_link_info_read_relocs (abfd, info, + o, NULL, + NULL, + _bfd_link_keep_memory (info)); if (internal_relocs == NULL) return false; @@ -5356,9 +5380,10 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info) && (s->flags & SEC_DEBUGGING) != 0)) continue; - internal_relocs = _bfd_elf_link_read_relocs (abfd, s, NULL, - NULL, - info->keep_memory); + internal_relocs = _bfd_elf_link_info_read_relocs (abfd, info, + s, NULL, + NULL, + _bfd_link_keep_memory (info)); if (internal_relocs == NULL) goto error_free_vers; @@ -11186,8 +11211,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) /* Get the swapped relocs. */ internal_relocs - = _bfd_elf_link_read_relocs (input_bfd, o, flinfo->external_relocs, - flinfo->internal_relocs, false); + = _bfd_elf_link_info_read_relocs (input_bfd, flinfo->info, o, + flinfo->external_relocs, + flinfo->internal_relocs, + false); if (internal_relocs == NULL && o->reloc_count > 0) return false; @@ -13279,8 +13306,12 @@ init_reloc_cookie (struct elf_reloc_cookie *cookie, info->callbacks->einfo (_("%P%X: can not read symbols: %E\n")); return false; } - if (info->keep_memory) - symtab_hdr->contents = (bfd_byte *) cookie->locsyms; + if (_bfd_link_keep_memory (info) ) + { + symtab_hdr->contents = (bfd_byte *) cookie->locsyms; + info->cache_size += (cookie->locsymcount + * sizeof (Elf_External_Sym_Shndx)); + } } return true; } @@ -13312,8 +13343,9 @@ init_reloc_cookie_rels (struct elf_reloc_cookie *cookie, } else { - cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, - info->keep_memory); + cookie->rels = _bfd_elf_link_info_read_relocs (abfd, info, sec, + NULL, NULL, + _bfd_link_keep_memory (info)); if (cookie->rels == NULL) return false; cookie->rel = cookie->rels; @@ -13881,14 +13913,22 @@ elf_gc_propagate_vtable_entries_used (struct elf_link_hash_entry *h, void *okp) return true; } +struct link_info_ok +{ + struct bfd_link_info *info; + bool ok; +}; + static bool -elf_gc_smash_unused_vtentry_relocs (struct elf_link_hash_entry *h, void *okp) +elf_gc_smash_unused_vtentry_relocs (struct elf_link_hash_entry *h, + void *ptr) { asection *sec; bfd_vma hstart, hend; Elf_Internal_Rela *relstart, *relend, *rel; const struct elf_backend_data *bed; unsigned int log_file_align; + struct link_info_ok *info = (struct link_info_ok *) ptr; /* Take care of both those symbols that do not describe vtables as well as those that are not loaded. */ @@ -13904,9 +13944,10 @@ elf_gc_smash_unused_vtentry_relocs (struct elf_link_hash_entry *h, void *okp) hstart = h->root.u.def.value; hend = hstart + h->size; - relstart = _bfd_elf_link_read_relocs (sec->owner, sec, NULL, NULL, true); + relstart = _bfd_elf_link_info_read_relocs (sec->owner, info->info, + sec, NULL, NULL, true); if (!relstart) - return *(bool *) okp = false; + return info->ok = false; bed = get_elf_backend_data (sec->owner); log_file_align = bed->s->log_file_align; @@ -14029,6 +14070,7 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info) elf_gc_mark_hook_fn gc_mark_hook; const struct elf_backend_data *bed = get_elf_backend_data (abfd); struct elf_link_hash_table *htab; + struct link_info_ok info_ok; if (!bed->can_gc_sections || !is_elf_hash_table (info->hash)) @@ -14070,8 +14112,10 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info) return false; /* Kill the vtable relocations that were not used. */ - elf_link_hash_traverse (htab, elf_gc_smash_unused_vtentry_relocs, &ok); - if (!ok) + info_ok.info = info; + info_ok.ok = true; + elf_link_hash_traverse (htab, elf_gc_smash_unused_vtentry_relocs, &info_ok); + if (!info_ok.ok) return false; /* Mark dynamically referenced symbols. */ diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h index 1f7e22186ec..89d2997aedf 100644 --- a/bfd/libbfd-in.h +++ b/bfd/libbfd-in.h @@ -894,6 +894,9 @@ extern bfd_byte * _bfd_write_unsigned_leb128 extern struct bfd_link_info *_bfd_get_link_info (bfd *); +extern bool _bfd_link_keep_memory (struct bfd_link_info *) + ATTRIBUTE_HIDDEN; + #if GCC_VERSION >= 7000 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res) #else diff --git a/bfd/libbfd.h b/bfd/libbfd.h index c37ddc03cfd..69496411622 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -899,6 +899,9 @@ extern bfd_byte * _bfd_write_unsigned_leb128 extern struct bfd_link_info *_bfd_get_link_info (bfd *); +extern bool _bfd_link_keep_memory (struct bfd_link_info *) + ATTRIBUTE_HIDDEN; + #if GCC_VERSION >= 7000 #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res) #else diff --git a/bfd/linker.c b/bfd/linker.c index c350cd3fd15..755ff19923b 100644 --- a/bfd/linker.c +++ b/bfd/linker.c @@ -3535,3 +3535,38 @@ _bfd_nolink_bfd_define_start_stop (struct bfd_link_info *info ATTRIBUTE_UNUSED, { return (struct bfd_link_hash_entry *) _bfd_ptr_bfd_null_error (sec->owner); } + +/* Return false if linker should avoid caching relocation infomation + and symbol tables of input files in memory. */ + +bool +_bfd_link_keep_memory (struct bfd_link_info * info) +{ + bfd *abfd; + bfd_size_type size; + + if (!info->keep_memory) + return false; + + if (info->max_cache_size == (bfd_size_type) -1) + return true; + + abfd = info->input_bfds; + size = info->cache_size; + do + { + if (size >= info->max_cache_size) + { + /* Over the limit. Reduce the memory usage. */ + info->keep_memory = false; + return false; + } + if (!abfd) + break; + size += abfd->alloc_size; + abfd = abfd->link.next; + } + while (1); + + return true; +} diff --git a/bfd/opncls.c b/bfd/opncls.c index 4fb79324c0a..96ff1e9f6a9 100644 --- a/bfd/opncls.c +++ b/bfd/opncls.c @@ -1032,6 +1032,8 @@ bfd_alloc (bfd *abfd, bfd_size_type size) ret = objalloc_alloc ((struct objalloc *) abfd->memory, ul_size); if (ret == NULL) bfd_set_error (bfd_error_no_memory); + else + abfd->alloc_size += size; return ret; } diff --git a/include/bfdlink.h b/include/bfdlink.h index 7f1b12dbf37..54968585410 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -682,6 +682,13 @@ struct bfd_link_info /* The version information. */ struct bfd_elf_version_tree *version_info; + + /* Size of cache. Backend can use it to keep strace cache size. */ + bfd_size_type cache_size; + + /* The maximum cache size. Backend can use cache_size and and + max_cache_size to decide if keep_memory should be honored. */ + bfd_size_type max_cache_size; }; /* Some forward-definitions used by some callbacks. */ diff --git a/ld/NEWS b/ld/NEWS index 11bc5745ebf..92dd4fd97cd 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -1,5 +1,8 @@ -*- text -*- +* Add --max-cache-size=SIZE to set the the maximum cache size to SIZE + bytes. + Changes in 2.37: * arm-symbianelf support removed. diff --git a/ld/ld.texi b/ld/ld.texi index dd8f571d4e4..67c50839b37 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -2839,6 +2839,12 @@ has been used. The @option{--reduce-memory-overheads} switch may be also be used to enable other tradeoffs in future versions of the linker. +@kindex --max-cache-size=@var{size} +@item --max-cache-size=@var{size} +@command{ld} normally caches the relocation information and symbol tables +of input files in memory with the unlimited size. This option sets the +maximum cache size to @var{size}. + @kindex --build-id @kindex --build-id=@var{style} @item --build-id diff --git a/ld/ldlex.h b/ld/ldlex.h index 9e8bf5fb835..9707d57caf8 100644 --- a/ld/ldlex.h +++ b/ld/ldlex.h @@ -140,6 +140,7 @@ enum option_values OPTION_WARN_TEXTREL, OPTION_WARN_ALTERNATE_EM, OPTION_REDUCE_MEMORY_OVERHEADS, + OPTION_MAX_CACHE_SIZE, #if BFD_SUPPORTS_PLUGINS OPTION_PLUGIN, OPTION_PLUGIN_OPT, diff --git a/ld/ldmain.c b/ld/ldmain.c index 42660eb9a3c..e4c67740e37 100644 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@ -331,6 +331,7 @@ main (int argc, char **argv) link_info.allow_undefined_version = true; link_info.keep_memory = true; + link_info.max_cache_size = (bfd_size_type) -1; link_info.combreloc = true; link_info.strip_discarded = true; link_info.prohibit_multiple_definition_absolute = false; diff --git a/ld/lexsup.c b/ld/lexsup.c index 00274c500d0..c128fe3a96b 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -433,6 +433,10 @@ static const struct ld_option ld_options[] = OPTION_REDUCE_MEMORY_OVERHEADS}, '\0', NULL, N_("Reduce memory overheads, possibly taking much longer"), TWO_DASHES }, + { {"max-cache-size=SIZE", required_argument, NULL, + OPTION_MAX_CACHE_SIZE}, + '\0', NULL, N_("Set the maximum cache size to SIZE bytes"), + TWO_DASHES }, { {"relax", no_argument, NULL, OPTION_RELAX}, '\0', NULL, N_("Reduce code size by using target specific optimizations"), TWO_DASHES }, { {"no-relax", no_argument, NULL, OPTION_NO_RELAX}, @@ -1631,6 +1635,17 @@ parse_args (unsigned argc, char **argv) config.hash_table_size = 1021; break; + case OPTION_MAX_CACHE_SIZE: + { + char *end; + bfd_size_type cache_size = strtoul (optarg, &end, 0); + if (*end != '\0') + einfo (_("%F%P: invalid cache memory size: %s\n"), + optarg); + link_info.max_cache_size = cache_size; + } + break; + case OPTION_HASH_SIZE: { bfd_size_type new_size; diff --git a/ld/testsuite/ld-bootstrap/bootstrap.exp b/ld/testsuite/ld-bootstrap/bootstrap.exp index b21b48ab20e..9c27c5ff5d9 100644 --- a/ld/testsuite/ld-bootstrap/bootstrap.exp +++ b/ld/testsuite/ld-bootstrap/bootstrap.exp @@ -55,7 +55,8 @@ if [check_plugin_api_available] { # really test -r. Use ld1 to link a fresh ld, ld2. Use ld2 to link a # new ld, ld3. ld2 and ld3 should be identical. set test_flags {"" "strip" "--static" "-Wl,--traditional-format" - "-Wl,--no-keep-memory" "-Wl,--relax"} + "-Wl,--no-keep-memory" "-Wl,--relax" + "-Wl,--max-cache-size=-1"} if { [istarget "powerpc-*-*"] } { lappend test_flags "-Wl,--ppc476-workaround" }