This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]