From f97d789e0dd5961578173666b76e93c18b65ac27 Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Mon, 25 Nov 2019 08:55:37 +0000 Subject: [PATCH 1/4] Add support for non-contiguous memory regions 2020-01-06 Christophe Lyon bfd/ * bfd-in2.h: Regenerate. * section.c (asection): Add already_assigned field. (BFD_FAKE_SECTION): Add default initializer for it. * ecoff.c (bfd_debug_section): Initialize already_assigned field. * elf32-arm.c (arm_build_one_stub): Add support for non_contiguous_regions. * elf32-csky.c (csky_build_one_stub): Likewise. * elf32-hppa.c (hppa_build_one_stub): Likewise. * elf32-m68hc11.c (m68hc11_elf_build_one_stub): Likewise. * elf32-m68hc12.c (m68hc12_elf_build_one_stub): Likewise. * elf32-metag.c (metag_build_one_stub): Likewise. * elf32-nios2.c (nios2_build_one_stub): Likewise. * elf64-ppc.c (ppc_build_one_stub): Likewise. * elfnn-aarch64.c (aarch64_build_one_stub): Likewise. * elflink.c (elf_link_input_bfd): Likewise. include/ * bfdlink.h (bfd_link_info): Add non_contiguous_regions and non_contiguous_regions_warnings fields. ld/ * ldlang.c (lang_add_section): Add support for non_contiguous_regions. (size_input_section): Likewise. (lang_size_sections_1): Likewise. (process_insert_statements): Likewise. * ldlex.h (option_values): Add OPTION_NON_CONTIGUOUS_REGIONS and OPTION_NON_CONTIGUOUS_REGIONS_WARNINGS. * lexsup.c (ld_options): Add entries for --enable-non-contiguous-regions and --enable-non-contiguous-regions-warnings. (parse_args): Handle it. * NEWS: Add --enable-non-contiguous-regions and --enable-non-contiguous-regions-warnings. * ld.texi: Add --enable-non-contiguous-regions and --enable-non-contiguous-regions-warnings documentation. * emultempl/armelf.em (elf32_arm_add_stub_section): Add SEC_LINKER_CREATED flag. Change-Id: Iac2f395b3a1fbea4b3a732ba5a9b847e10b4b2f0 --- bfd/bfd-in2.h | 9 ++- bfd/ecoff.c | 6 +- bfd/elf32-arm.c | 11 ++++ bfd/elf32-csky.c | 11 ++++ bfd/elf32-hppa.c | 31 +++++++++++ bfd/elf32-m68hc11.c | 11 ++++ bfd/elf32-m68hc12.c | 11 ++++ bfd/elf32-metag.c | 14 ++++- bfd/elf32-nios2.c | 12 ++++ bfd/elf64-ppc.c | 11 ++++ bfd/elflink.c | 12 ++++ bfd/elfnn-aarch64.c | 15 ++++- bfd/section.c | 9 ++- include/bfdlink.h | 8 +++ ld/NEWS | 3 + ld/emultempl/armelf.em | 3 +- ld/ld.texi | 42 ++++++++++++++ ld/ldlang.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++-- ld/ldlex.h | 2 + ld/lexsup.c | 10 ++++ 20 files changed, 366 insertions(+), 11 deletions(-) diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 09a5a39..35d1abb 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1190,6 +1190,10 @@ typedef struct bfd_section struct bfd_link_order *link_order; struct bfd_section *s; } map_head, map_tail; + /* Points to the output section this section is already assigned to, if any. + This is used when support for non-contiguous memory regions is enabled. */ + struct bfd_section *already_assigned; + } asection; /* Relax table contains information about instructions which can @@ -1371,7 +1375,10 @@ discarded_section (const asection *sec) (struct bfd_symbol *) SYM, &SEC.symbol, \ \ /* map_head, map_tail */ \ - { NULL }, { NULL } \ + { NULL }, { NULL }, \ + \ + /* already_assigned */ \ + NULL \ } /* We use a macro to initialize the static asymbol structures because diff --git a/bfd/ecoff.c b/bfd/ecoff.c index 050fd7b..58620e5 100644 --- a/bfd/ecoff.c +++ b/bfd/ecoff.c @@ -78,8 +78,10 @@ static asection bfd_debug_section = NULL, /* symbol_ptr_ptr, */ NULL, - /* map_head, map_tail */ - { NULL }, { NULL } + /* map_head, map_tail, */ + { NULL }, { NULL }, + /* already_assigned */ + NULL, }; /* Create an ECOFF object. */ diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c index faf8376..a10d32b 100644 --- a/bfd/elf32-arm.c +++ b/bfd/elf32-arm.c @@ -5064,6 +5064,17 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry, stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry; info = (struct bfd_link_info *) in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } + globals = elf32_arm_hash_table (info); if (globals == NULL) return FALSE; diff --git a/bfd/elf32-csky.c b/bfd/elf32-csky.c index 04214f2..d12a898 100644 --- a/bfd/elf32-csky.c +++ b/bfd/elf32-csky.c @@ -3621,6 +3621,17 @@ csky_build_one_stub (struct bfd_hash_entry *gen_entry, stub_entry = (struct elf32_csky_stub_hash_entry *)gen_entry; info = (struct bfd_link_info *) in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } + globals = csky_elf_hash_table (info); if (globals == NULL) return FALSE; diff --git a/bfd/elf32-hppa.c b/bfd/elf32-hppa.c index 2e65308..7ac4b23 100644 --- a/bfd/elf32-hppa.c +++ b/bfd/elf32-hppa.c @@ -731,6 +731,17 @@ hppa_build_one_stub (struct bfd_hash_entry *bh, void *in_arg) switch (hsh->stub_type) { case hppa_stub_long_branch: + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hsh->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hsh->target_section); + abort(); + } + /* Create the long branch. A long branch is formed with "ldil" loading the upper bits of the target address into a register, then branching with "be" which adds in the lower bits. @@ -751,6 +762,16 @@ hppa_build_one_stub (struct bfd_hash_entry *bh, void *in_arg) break; case hppa_stub_long_branch_shared: + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hsh->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hsh->target_section); + abort(); + } /* Branches are relative. This is where we are going to. */ sym_value = (hsh->target_value + hsh->target_section->output_offset @@ -823,6 +844,16 @@ hppa_build_one_stub (struct bfd_hash_entry *bh, void *in_arg) break; case hppa_stub_export: + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hsh->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hsh->target_section); + abort(); + } /* Branches are relative. This is where we are going to. */ sym_value = (hsh->target_value + hsh->target_section->output_offset diff --git a/bfd/elf32-m68hc11.c b/bfd/elf32-m68hc11.c index 8d4d227..177feb6 100644 --- a/bfd/elf32-m68hc11.c +++ b/bfd/elf32-m68hc11.c @@ -415,6 +415,17 @@ m68hc11_elf_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry; info = (struct bfd_link_info *) in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } + htab = m68hc11_elf_hash_table (info); if (htab == NULL) return FALSE; diff --git a/bfd/elf32-m68hc12.c b/bfd/elf32-m68hc12.c index e41b4c7..9ff8270 100644 --- a/bfd/elf32-m68hc12.c +++ b/bfd/elf32-m68hc12.c @@ -535,6 +535,17 @@ m68hc12_elf_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry; info = (struct bfd_link_info *) in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } + htab = m68hc11_elf_hash_table (info); stub_sec = stub_entry->stub_sec; diff --git a/bfd/elf32-metag.c b/bfd/elf32-metag.c index 08eb70b..477baae 100644 --- a/bfd/elf32-metag.c +++ b/bfd/elf32-metag.c @@ -3459,7 +3459,7 @@ metag_type_of_stub (asection *input_sec, #define MOV_PC_A0_3 0xa3180ca0 static bfd_boolean -metag_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg ATTRIBUTE_UNUSED) +metag_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) { struct elf_metag_stub_hash_entry *hsh; asection *stub_sec; @@ -3470,6 +3470,18 @@ metag_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg ATTRIBUTE_U /* Massage our args to the form they really have. */ hsh = (struct elf_metag_stub_hash_entry *) gen_entry; + info = (struct bfd_link_info *) in_arg; + + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hsh->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hsh->target_section); + abort(); + } stub_sec = hsh->stub_sec; diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c index 947eaeb..517bdc4 100644 --- a/bfd/elf32-nios2.c +++ b/bfd/elf32-nios2.c @@ -2491,6 +2491,18 @@ nios2_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg ATTRIBUTE_U asection *stub_sec = hsh->stub_sec; bfd_vma sym_value; + info = (struct bfd_link_info *) in_arg; + + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hsh->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hsh->target_section); + abort(); + } /* Make a note of the offset within the stubs for this entry. */ hsh->stub_offset = stub_sec->size; diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index b3f8f6b..8fca552 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -11349,6 +11349,17 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry = (struct ppc_stub_hash_entry *) gen_entry; info = in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section != NULL + && stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } htab = ppc_hash_table (info); if (htab == NULL) return FALSE; diff --git a/bfd/elflink.c b/bfd/elflink.c index 5217528..deef7f5 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -10569,6 +10569,18 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) discarding, we don't need to keep it. */ if (isym->st_shndx != SHN_UNDEF && isym->st_shndx < SHN_LORESERVE + && isec->output_section == NULL + && flinfo->info->non_contiguous_regions + && flinfo->info->non_contiguous_regions_warnings) + { + _bfd_error_handler (_("warning: --enable-non-contiguous-regions " + "discards section `%s' from '%s'\n"), + isec->name, isec->owner->filename); + continue; + } + + if (isym->st_shndx != SHN_UNDEF + && isym->st_shndx < SHN_LORESERVE && bfd_section_removed_from_list (output_bfd, isec->output_section)) continue; diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c index 5fabcd8..75edd45 100644 --- a/bfd/elfnn-aarch64.c +++ b/bfd/elfnn-aarch64.c @@ -3278,7 +3278,7 @@ _bfd_aarch64_add_stub_entry_after (const char *stub_name, static bfd_boolean aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, - void *in_arg ATTRIBUTE_UNUSED) + void *in_arg) { struct elf_aarch64_stub_hash_entry *stub_entry; asection *stub_sec; @@ -3295,6 +3295,19 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, /* Massage our args to the form they really have. */ stub_entry = (struct elf_aarch64_stub_hash_entry *) gen_entry; + info = (struct bfd_link_info *) in_arg; + + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + { + _bfd_error_handler (_("Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + abort(); + } + stub_sec = stub_entry->stub_sec; /* Make a note of the offset within the stubs for this entry. */ diff --git a/bfd/section.c b/bfd/section.c index 0c15a0d..c1a1a4b 100644 --- a/bfd/section.c +++ b/bfd/section.c @@ -549,6 +549,10 @@ CODE_FRAGMENT . struct bfd_link_order *link_order; . struct bfd_section *s; . } map_head, map_tail; +. {* Points to the output section this section is already assigned to, if any. +. This is used when support for non-contiguous memory regions is enabled. *} +. struct bfd_section *already_assigned; +. .} asection; . .{* Relax table contains information about instructions which can @@ -730,7 +734,10 @@ CODE_FRAGMENT . (struct bfd_symbol *) SYM, &SEC.symbol, \ . \ . {* map_head, map_tail *} \ -. { NULL }, { NULL } \ +. { NULL }, { NULL }, \ +. \ +. {* already_assigned *} \ +. NULL \ . } . .{* We use a macro to initialize the static asymbol structures because diff --git a/include/bfdlink.h b/include/bfdlink.h index 8d85530..529ea6e 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -501,6 +501,14 @@ struct bfd_link_info /* TRUE if "-Map map" is passed to linker. */ unsigned int has_map_file : 1; + /* TRUE if "--enable-non-contiguous-regions" is passed to the + linker. */ + unsigned int non_contiguous_regions : 1; + + /* TRUE if "--enable-non-contiguous-regions-warnings" is passed to + the linker. */ + unsigned int non_contiguous_regions_warnings : 1; + /* Char that may appear as the first char of a symbol, but should be skipped (like symbol_leading_char) when looking up symbols in wrap_hash. Used by PowerPC Linux for 'dot' symbols. */ diff --git a/ld/NEWS b/ld/NEWS index f659ccf..2611a3e 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -1,5 +1,8 @@ -*- text -*- +* Add command-line options --enable-non-contiguous-regions and + --enable-non-contiguous-regions-warnings. + Changes in 2.34: * cr16c support removed. diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em index efdcf5a..fb5bbf8 100644 --- a/ld/emultempl/armelf.em +++ b/ld/emultempl/armelf.em @@ -227,7 +227,8 @@ elf32_arm_add_stub_section (const char * stub_sec_name, struct hook_stub_info info; flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE - | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP); + | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP + | SEC_LINKER_CREATED); stub_sec = bfd_make_section_anyway_with_flags (stub_file->the_bfd, stub_sec_name, flags); if (stub_sec == NULL) diff --git a/ld/ld.texi b/ld/ld.texi index 9bb3d55..07933b2 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -459,6 +459,48 @@ will contain a colon separated list of audit interfaces to use. This option is only meaningful on ELF platforms supporting the rtld-audit interface. The -P option is provided for Solaris compatibility. +@kindex --enable-non-contiguous-regions +@item --enable-non-contiguous-regions +This option avoids generating an error if an input section does not +fit a matching output section. The linker tries to allocate the input +section to subseque nt matching output sections, and generates an +error only if no output section is large enough. This is useful when +several non-contiguous memory regions are available and the input +section does not require a particular one. The order in which input +sections are evaluated does not change, for instance: + +@smallexample + MEMORY @{ + MEM1 (rwx) : ORIGIN : 0x1000, LENGTH = 0x14 + MEM2 (rwx) : ORIGIN : 0x1000, LENGTH = 0x40 + MEM3 (rwx) : ORIGIN : 0x2000, LENGTH = 0x40 + @} + SECTIONS @{ + mem1 : @{ *(.data.*); @} > MEM1 + mem2 : @{ *(.data.*); @} > MEM2 + mem3 : @{ *(.data.*); @} > MEM2 + @} + + with input sections: + .data.1: size 8 + .data.2: size 0x10 + .data.3: size 4 + + results in .data.1 affected to mem1, and .data.2 and .data.3 + affected to mem2, even though .data.3 would fit in mem3. +@end smallexample + +This option is incompatible with INSERT statements because it changes +the way input sections are mapped to output sections. + +@kindex --enable-non-contiguous-regions-warnings +@item --enable-non-contiguous-regions-warnings +This option enables warnings when +@code{--enable-non-contiguous-regions} allows possibly unexpected +matches in sections mapping, potentially leading to silently +discarding a section instead of failing because it does not fit any +output region. + @cindex entry point, from command line @kindex -e @var{entry} @kindex --entry=@var{entry} diff --git a/ld/ldlang.c b/ld/ldlang.c index 91c160b..d34919e 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -2537,6 +2537,11 @@ lang_add_section (lang_statement_list_type *ptr, /* This prevents future calls from assigning this section. */ section->output_section = bfd_abs_section_ptr; } + else if (link_info.non_contiguous_regions_warnings) + einfo (_("%P:%pS: warning: --enable-non-contiguous-regions makes " + "section `%pA' from '%pB' match /DISCARD/ clause.\n"), + NULL, section, section->owner); + return; } @@ -2550,7 +2555,33 @@ lang_add_section (lang_statement_list_type *ptr, } if (section->output_section != NULL) - return; + { + if (!link_info.non_contiguous_regions) + return; + + /* SECTION has already been handled in a special way + (eg. LINK_ONCE): skip it. */ + if (bfd_is_abs_section (section->output_section)) + return; + + /* Already assigned to the same output section, do not process + it again, to avoid creating loops between duplicate sections + later. */ + if (section->output_section == output->bfd_section) + return; + + if (link_info.non_contiguous_regions_warnings && output->bfd_section) + einfo (_("%P:%pS: warning: --enable-non-contiguous-regions may " + "change behaviour for section `%pA' from '%pB' (assigned to " + "%pA, but additional match: %pA)\n"), + NULL, section, section->owner, section->output_section, + output->bfd_section); + + /* SECTION has already been assigned to an output section, but + the user allows it to be mapped to another one in case it + overflows. We'll later update the actual output section in + size_input_section as appropriate. */ + } /* We don't copy the SEC_NEVER_LOAD flag from an input section to an output section, because we want to be able to include a @@ -4194,6 +4225,12 @@ process_insert_statements (lang_statement_union_type **start) lang_statement_union_type **ptr; lang_statement_union_type *first; + if (link_info.non_contiguous_regions) + { + einfo (_("warning: INSERT statement in linker script is " + "incompatible with --enable-non-contiguous-regions.\n")); + } + where = lang_output_section_find (i->where); if (where != NULL && i->is_before) { @@ -5116,11 +5153,27 @@ size_input_section (lang_statement_union_type **this_ptr, lang_output_section_statement_type *output_section_statement, fill_type *fill, + bfd_boolean *removed, bfd_vma dot) { lang_input_section_type *is = &((*this_ptr)->input_section); asection *i = is->section; asection *o = output_section_statement->bfd_section; + *removed = 0; + + if (link_info.non_contiguous_regions) + { + /* If the input section I has already been successfully assigned + to an output section other than O, don't bother with it and + let the caller remove it from the list. Keep processing in + case we have already handled O, because the repeated passes + have reinitialized its size. */ + if (i->already_assigned && i->already_assigned != o) + { + *removed = 1; + return dot; + } + } if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS) i->output_offset = i->vma - o->vma; @@ -5152,6 +5205,43 @@ size_input_section dot += alignment_needed; } + if (link_info.non_contiguous_regions) + { + /* If I would overflow O, let the caller remove I from the + list. */ + if (output_section_statement->region) + { + bfd_vma end = output_section_statement->region->origin + + output_section_statement->region->length; + + if (dot + TO_ADDR (i->size) > end) + { + if (i->flags & SEC_LINKER_CREATED) + { + einfo (_("Output section %s not large enough for the " + "linker-created stubs section %s.\n"), + i->output_section->name, i->name); + abort(); + } + + if (i->rawsize && i->rawsize != i->size) + { + einfo (_("Relaxation not supported with " + "--enable-non-contiguous-regions (section %s " + "would overflow %s after it changed size).\n"), + i->name, i->output_section->name); + abort(); + } + + *removed = 1; + dot = end; + ASSERT (i->already_assigned == NULL); + i->output_section = NULL; + return dot; + } + } + } + /* Remember where in the output section this input section goes. */ i->output_offset = dot - o->vma; @@ -5159,6 +5249,14 @@ size_input_section dot += TO_ADDR (i->size); if (!(o->flags & SEC_FIXED_SIZE)) o->size = TO_SIZE (dot - o->vma); + + if (link_info.non_contiguous_regions) + { + /* Record that I was successfully assigned to O, and update + its actual output section too. */ + i->already_assigned = o; + i->output_section = o; + } } return dot; @@ -5445,10 +5543,14 @@ lang_size_sections_1 bfd_boolean check_regions) { lang_statement_union_type *s; + lang_statement_union_type *prev_s = NULL; + bfd_boolean removed_prev_s = FALSE; /* Size up the sections from their constituent parts. */ - for (s = *prev; s != NULL; s = s->header.next) + for (s = *prev; s != NULL; prev_s = s, s = s->header.next) { + bfd_boolean removed=FALSE; + switch (s->header.type) { case lang_output_section_statement_enum: @@ -5874,7 +5976,7 @@ lang_size_sections_1 *relax = TRUE; } dot = size_input_section (prev, output_section_statement, - fill, dot); + fill, &removed, dot); } break; @@ -5979,7 +6081,43 @@ lang_size_sections_1 FAIL (); break; } - prev = &s->header.next; + + /* If an input section doesn't fit in the current output + section, remove it from the list. Handle the case where we + have to remove an input_section statement here: there is a + special case to remove the first element of the list. */ + if (link_info.non_contiguous_regions && removed) + { + /* If we removed the first element during the previous + iteration, override the loop assignment of prev_s. */ + if (removed_prev_s) + prev_s = NULL; + + if (prev_s) + { + /* If there was a real previous input section, just skip + the current one. */ + prev_s->header.next=s->header.next; + s = prev_s; + removed_prev_s = FALSE; + } + else + { + /* Remove the first input section of the list. */ + *prev = s->header.next; + removed_prev_s = TRUE; + } + + /* Move to next element, unless we removed the head of the + list. */ + if (!removed_prev_s) + prev = &s->header.next; + } + else + { + prev = &s->header.next; + removed_prev_s = FALSE; + } } return dot; } diff --git a/ld/ldlex.h b/ld/ldlex.h index 5287f19..22b928d 100644 --- a/ld/ldlex.h +++ b/ld/ldlex.h @@ -150,6 +150,8 @@ enum option_values OPTION_FORCE_GROUP_ALLOCATION, OPTION_PRINT_MAP_DISCARDED, OPTION_NO_PRINT_MAP_DISCARDED, + OPTION_NON_CONTIGUOUS_REGIONS, + OPTION_NON_CONTIGUOUS_REGIONS_WARNINGS, }; /* The initial parser states. */ diff --git a/ld/lexsup.c b/ld/lexsup.c index 3d15cc4..2597e2d 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -122,6 +122,10 @@ static const struct ld_option ld_options[] = 'E', NULL, N_("Export all dynamic symbols"), TWO_DASHES }, { {"no-export-dynamic", no_argument, NULL, OPTION_NO_EXPORT_DYNAMIC}, '\0', NULL, N_("Undo the effect of --export-dynamic"), TWO_DASHES }, + { {"enable-non-contiguous-regions", no_argument, NULL, OPTION_NON_CONTIGUOUS_REGIONS}, + '\0', NULL, N_("Enable support of non-contiguous memory regions"), TWO_DASHES }, + { {"enable-non-contiguous-regions-warnings", no_argument, NULL, OPTION_NON_CONTIGUOUS_REGIONS_WARNINGS}, + '\0', NULL, N_("Enable warnings when --enable-non-contiguous-regions may cause unexpected behaviour"), TWO_DASHES }, { {"EB", no_argument, NULL, OPTION_EB}, '\0', NULL, N_("Link big-endian objects"), ONE_DASH }, { {"EL", no_argument, NULL, OPTION_EL}, @@ -845,6 +849,12 @@ parse_args (unsigned argc, char **argv) case OPTION_NO_EXPORT_DYNAMIC: link_info.export_dynamic = FALSE; break; + case OPTION_NON_CONTIGUOUS_REGIONS: + link_info.non_contiguous_regions = TRUE; + break; + case OPTION_NON_CONTIGUOUS_REGIONS_WARNINGS: + link_info.non_contiguous_regions_warnings = TRUE; + break; case 'e': lang_add_entry (optarg, TRUE); break; -- 2.7.4