This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH RFC] Add support for non-contiguous memory regions
- From: Christophe Lyon <christophe dot lyon at linaro dot org>
- To: binutils <binutils at sourceware dot org>
- Date: Fri, 29 Nov 2019 11:12:03 +0100
- Subject: [PATCH RFC] Add support for non-contiguous memory regions
Hi,
The attached patches implement support for non-contiguous memory
regions, which was discussed in more detail in
https://sourceware.org/ml/binutils/2019-07/msg00020.html
In other words, it allows to list input sections in multiple output
regions, to help with cases were we have several memory banks, but
don't care in which one a given section goes. This avoids the painful
linker script changes needed when a memory banks become too small
after program changes (eg more data, or compiler code generation
changes).
Patch 1 contains the implementation, and patch 2 adds a new test.
This is an RFC because it lacks documentation, and I thought I should
check whether the approach is OK before polishing.
Patch 1 adds a new --enable-non-contiguous-regions linker flag which
is required to enable this new feature. Of course, we can discuss the
option name, suggestions welcome :-)
It works by allowing processing of input section multiple times until
a sufficiently large output region is found, the irrelevant ones are
removed when we detect that the candidate input section would overflow
the current output section.
At some point I thought I should insert padding when an input section
is too large to fit in the current output section, in order to make
sure it is full, but it turns out not to be necessary.
The added testcase defines 4 input sections: .data, .data.2, .data.3
and .data.4, and the linker script has
*(.data) *(.data.*)
for each of the 3 output sections .raml, .ramu and .ramz. .data.3 and
.data.3 do not fit in .raml, and are thus assigned respectively to
.ramu and .ramz.
I've run the tests with arm-none-eabi as target, and also checked that
there is no regression when forcing --enable-non-contiguous-regions:
actually only the ld/scripts/rgn-over* tests fail in such a case,
because the offending sections can be assigned to other large enough
output sections, and we no longer face the error cases these tests try
to catch.
One bit I'm not clear about in the expected results is the file offset
for .ramu and .ramz sections: why are they aligned on 0x1000, while
.ARM.attributes isn't ?
In fact, I feel my patch is surprisingly small for a feature that has
been requested for so long... :-)
Thoughts?
Thanks,
Christophe
From 17726590be307e467ab66e309f6967a5d0c12dcf Mon Sep 17 00:00:00 2001
From: Christophe Lyon <christophe.lyon@linaro.org>
Date: Mon, 25 Nov 2019 08:56:22 +0000
Subject: [PATCH 2/2] Add test for non-contiguous memory
2019-11-27 Christophe Lyon <christophe.lyon@linaro.org>
ld/
* testsuite/ld-elf/non-contiguous.d: New.
* testsuite/ld-elf/non-contiguous.ld: New.
* testsuite/ld-elf/non-contiguous.s: New.
---
ld/testsuite/ld-elf/non-contiguous.d | 46 +++++++++++++++++++++++++++++++++++
ld/testsuite/ld-elf/non-contiguous.ld | 29 ++++++++++++++++++++++
ld/testsuite/ld-elf/non-contiguous.s | 22 +++++++++++++++++
3 files changed, 97 insertions(+)
create mode 100644 ld/testsuite/ld-elf/non-contiguous.d
create mode 100644 ld/testsuite/ld-elf/non-contiguous.ld
create mode 100644 ld/testsuite/ld-elf/non-contiguous.s
diff --git a/ld/testsuite/ld-elf/non-contiguous.d b/ld/testsuite/ld-elf/non-contiguous.d
new file mode 100644
index 0000000..bcb9174
--- /dev/null
+++ b/ld/testsuite/ld-elf/non-contiguous.d
@@ -0,0 +1,46 @@
+#name: non-contiguous
+#source: non-contiguous.s
+#ld: --enable-non-contiguous-regions -T non-contiguous.ld
+#objdump: -rdtsh
+#xfail: [is_generic]
+
+.*: file format elf32-(little|big)arm
+
+Sections:
+Idx Name Size VMA LMA File off Algn
+ 0 \.raml 0000000c 1fff0000 1fff0000 00010000 2\*\*0
+ CONTENTS, ALLOC, LOAD, DATA
+ 1 \.ramu 00000014 20000000 1fff000c 00020000 2\*\*0
+ CONTENTS, ALLOC, LOAD, DATA
+ 2 \.ramz 0000003c 20040000 20000014 00030000 2\*\*0
+ CONTENTS, ALLOC, LOAD, DATA
+ 3 .ARM.attributes 00000014 00000000 00000000 0003003c 2\*\*0
+ CONTENTS, READONLY
+SYMBOL TABLE:
+1fff0000 l d .raml 00000000 .raml
+20000000 l d .ramu 00000000 .ramu
+20040000 l d .ramz 00000000 .ramz
+00000000 l d .ARM.attributes 00000000 .ARM.attributes
+00000000 l df \*ABS\* 00000000 .*/non-contiguous.o
+00000002 l \*ABS\* 00000000 ALIGN
+1fff000c g .raml 00000000 _raml_end
+2004003c g .ramz 00000000 _ramz_end
+20000000 g .ramu 00000000 _rmau_start
+1fff0000 g .raml 00000000 _rmal_start
+20000014 g .ramu 00000000 _ramu_end
+20040000 g .ramz 00000000 _rmaz_start
+
+
+Contents of section .raml:
+ 1fff0000 01000000 02000000 03000000 ............
+Contents of section .ramu:
+ 20000000 04000000 05000000 06000000 07000000 ................
+ 20000010 08000000 ....
+Contents of section .ramz:
+ 20040000 09090909 09090909 09090909 09090909 ................
+ 20040010 09090909 09090909 09090909 09090909 ................
+ 20040020 09090909 09090909 09090909 09090909 ................
+ 20040030 09090909 09090909 09090909 ............
+Contents of section .ARM.attributes:
+ 0000 41130000 00616561 62690001 09000000 A....aeabi......
+ 0010 08010901 ....
diff --git a/ld/testsuite/ld-elf/non-contiguous.ld b/ld/testsuite/ld-elf/non-contiguous.ld
new file mode 100644
index 0000000..1c99381
--- /dev/null
+++ b/ld/testsuite/ld-elf/non-contiguous.ld
@@ -0,0 +1,29 @@
+MEMORY
+{
+ RAML (rwx) : ORIGIN = 0x1FFF0000, LENGTH = 0x00010
+ RAMU (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040
+ RAMZ (rwx) : ORIGIN = 0x20040000, LENGTH = 0x00040
+}
+
+SECTIONS
+{
+ .raml : AT ( ADDR (.text) + SIZEOF (.text) )
+ { _rmal_start = . ;
+ *(.boot) ;
+ *(.data) *(.data.*) ;
+ _raml_end = . ;
+ } > RAML
+
+ .ramu : AT ( ADDR (.raml) + SIZEOF (.raml) )
+ { _rmau_start = . ;
+ *(.data) *(.data.*) ;
+ _ramu_end = . ;
+ } > RAMU
+
+ .ramz : AT ( ADDR (.ramu) + SIZEOF (.ramu) )
+ { _rmaz_start = . ;
+ *(.data) *(.data.*) ;
+ _ramz_end = . ;
+ } > RAMZ
+}
+
diff --git a/ld/testsuite/ld-elf/non-contiguous.s b/ld/testsuite/ld-elf/non-contiguous.s
new file mode 100644
index 0000000..e0aad36
--- /dev/null
+++ b/ld/testsuite/ld-elf/non-contiguous.s
@@ -0,0 +1,22 @@
+ .syntax unified
+ .section .data.1
+ # Fit in RAML
+ .word 1
+ .word 2
+ .word 3
+
+ .section .data.2
+ # Fit in RAMU
+ .word 4
+ .word 5
+ .word 6
+
+ .section .data.3
+ # Fit in RAMU
+ .word 7
+ .word 8
+
+ .section .data.4
+ # Fit in RAMZ
+ .fill 0x3c, 1, 9
+
--
2.7.4
From 5a6274a0db009b7e19835a0726c88360d9b9c6e8 Mon Sep 17 00:00:00 2001
From: Christophe Lyon <christophe.lyon@linaro.org>
Date: Mon, 25 Nov 2019 08:55:37 +0000
Subject: [PATCH 1/2] Add support for non-contiguous memory
2019-11-27 Christophe Lyon <christophe.lyon@linaro.org>
bfd/
* bfd-in2.h: Regenerate.
* section.c (asection): Add already_assigned field.
(BFD_FAKE_SECTION): Add default initializer for it.
include/
* bfdlink.h (bfd_link_info): Add non_contiguous_regions field.
ld/
* ldlang.c (lang_add_section): Add support for
non_contiguous_regions.
(size_input_section): Likewise.
(lang_size_sections_1): Likewise.
* ldlex.h (option_values): Add OPTION_NON_CONTIGUOUS_REGIONS.
* lexsup.c (ld_options): Add entry for
--enable-non-contiguous-regions.
(parse_args): Handle it.
---
bfd/bfd-in2.h | 9 +++++++-
bfd/section.c | 9 +++++++-
include/bfdlink.h | 3 +++
ld/ldlang.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
ld/ldlex.h | 1 +
ld/lexsup.c | 5 +++++
6 files changed, 89 insertions(+), 5 deletions(-)
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index a00dfa35..0e1126c 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1177,6 +1177,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
@@ -1358,7 +1362,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/section.c b/bfd/section.c
index 34e08ae..b1f0b71 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -536,6 +536,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
@@ -717,7 +721,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 32d1512..e1e7c1e 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -501,6 +501,9 @@ 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;
+
/* 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/ldlang.c b/ld/ldlang.c
index eedcb7f..f2acdbb 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -2550,7 +2550,14 @@ 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 affected 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
@@ -5109,11 +5116,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;
@@ -5145,6 +5168,24 @@ 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)
+ {
+ *removed = 1;
+ dot = end;
+ return dot;
+ }
+ }
+ }
+
/* Remember where in the output section this input section goes. */
i->output_offset = dot - o->vma;
@@ -5152,6 +5193,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;
@@ -5436,9 +5485,10 @@ lang_size_sections_1
bfd_boolean check_regions)
{
lang_statement_union_type *s;
+ lang_statement_union_type *prev_s = NULL;
/* 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)
{
switch (s->header.type)
{
@@ -5852,6 +5902,7 @@ lang_size_sections_1
case lang_input_section_enum:
{
asection *i;
+ bfd_boolean removed;
i = s->input_section.section;
if (relax)
@@ -5864,7 +5915,17 @@ lang_size_sections_1
*relax = TRUE;
}
dot = size_input_section (prev, output_section_statement,
- fill, dot);
+ fill, &removed, dot);
+
+ if (link_info.non_contiguous_regions && removed)
+ {
+ /* If I doesn't fit in the current output section,
+ remove it from the list. */
+ if (prev_s)
+ prev_s->header.next=s->header.next;
+ else
+ *prev = s->header.next;
+ }
}
break;
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 32a7a64..4e7a419 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -150,6 +150,7 @@ enum option_values
OPTION_FORCE_GROUP_ALLOCATION,
OPTION_PRINT_MAP_DISCARDED,
OPTION_NO_PRINT_MAP_DISCARDED,
+ OPTION_NON_CONTIGUOUS_REGIONS,
};
/* The initial parser states. */
diff --git a/ld/lexsup.c b/ld/lexsup.c
index d7766c3..13cab6d 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -122,6 +122,8 @@ 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 },
{ {"EB", no_argument, NULL, OPTION_EB},
'\0', NULL, N_("Link big-endian objects"), ONE_DASH },
{ {"EL", no_argument, NULL, OPTION_EL},
@@ -845,6 +847,9 @@ 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 'e':
lang_add_entry (optarg, TRUE);
break;
--
2.7.4