[RFC,v2] Add support for non-contiguous memory regions

Message ID CAKdteOY8M+aMNh7iozcU2BqL8OpDwCdJs_XF+MBiFj25vkdaoQ@mail.gmail.com
State New
Headers show
Series
  • [RFC,v2] Add support for non-contiguous memory regions
Related show

Commit Message

Christophe Lyon Jan. 6, 2020, 3:34 p.m.
Hi,

This is a follow-up to
https://sourceware.org/ml/binutils/2019-11/msg00402.html

This version detects cases it isn't (yet?) able to handle and emits an
error message (and uses abort() which can probably be made more
user-friendly).

Specifically, I have:
* improved the testcase in ld-elf, hoping that it's now generic enough
(at least it passes on arm and powerpc).

* added a powerpc testcase, based on the sample provided by Simon. It
now fails with an error message when relaxation was applied.

* added several arm testcases, some of which involve farcall stubs
insertion. Something annoying with stubs handling is that I had to
modify arm-specific code to generate an error in a case where the stub
destination could not be allocated. We'd have to do this in every
target that has XXX_build_one_stub().
I now add the SEC_LINKER_CREATED flag on the stubs sections so that we
can emit a helpful error message in case the stubs do not fit in
memory.

There's a new test to check that section order does not change.

Finally, I had to change the way I remove input_section statements in
lang_size_sections_1() to correctly handle the case where we have to
remove the head of the list.

There are now 4 patches, to hopefully make review/comments easier:
* patch1: is the main (code) patch
* patch2: generic test
* patch3: arm tests
* patch4: powerpc test

Thoughts?

Thanks,

Christophe

Comments

Nick Clifton Jan. 8, 2020, 1:14 p.m. | #1
Hi Christophe,

> There are now 4 patches, to hopefully make review/comments easier:

> * patch1: is the main (code) patch

> * patch2: generic test

> * patch3: arm tests

> * patch4: powerpc test


I think that you need to test some non-ELF based targets.  Or maybe a
--enable-targets=all configuration.  I am seeing this error when building:

  bfd/ecoff.c:81:1: error: missing initializer for field 'already_assigned' of 'asection' {aka 'struct bfd_section'} [-Werror=missing-field-initializers]

Also when adding a new feature like this, it would be nice if you could
include a line or two in ld/NEWS describing what it does.  This makes it
easier for me to advertise the feature when announcing a new release...

Also you should add a description of the option to ld/ld.texi.

Cheers
  Nick
Christophe Lyon Jan. 8, 2020, 1:59 p.m. | #2
On Wed, 8 Jan 2020 at 14:14, Nick Clifton <nickc@redhat.com> wrote:
>

> Hi Christophe,

>

> > There are now 4 patches, to hopefully make review/comments easier:

> > * patch1: is the main (code) patch

> > * patch2: generic test

> > * patch3: arm tests

> > * patch4: powerpc test

>

> I think that you need to test some non-ELF based targets.  Or maybe a

> --enable-targets=all configuration.  I am seeing this error when building:

>

>   bfd/ecoff.c:81:1: error: missing initializer for field 'already_assigned' of 'asection' {aka 'struct bfd_section'} [-Werror=missing-field-initializers]


Right, I didn't try non-elf targets.
If it helps your testing, I was just missing:
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index be3d42e..2ce2c8f 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -77,7 +77,9 @@ static asection bfd_debug_section =
   /* symbol_ptr_ptr,                                              */
      NULL,
   /* map_head, map_tail                                                   */
-     { NULL }, { NULL }
+     { NULL }, { NULL },
+  /* already_assigned                                             */
+     NULL,
 };

 /* Create an ECOFF object.  */

>

> Also when adding a new feature like this, it would be nice if you could

> include a line or two in ld/NEWS describing what it does.  This makes it

> easier for me to advertise the feature when announcing a new release...

>

> Also you should add a description of the option to ld/ld.texi.


Sure, I am aware of that. At this stage, I'm still looking for comments
on the design. I fear I'm doing changes in a very wrong way.
OTOH, if this approach looks fine to you, I'll make sure to submit a patch
including some docs before the 2.34 deadline :-)

Thanks,

Christophe

>

> Cheers

>   Nick

>

>
Christophe Lyon Jan. 10, 2020, 10:22 a.m. | #3
Hi,


On Wed, 8 Jan 2020 at 14:59, Christophe Lyon <christophe.lyon@linaro.org> wrote:
>

> On Wed, 8 Jan 2020 at 14:14, Nick Clifton <nickc@redhat.com> wrote:

> >

> > Hi Christophe,

> >

> > > There are now 4 patches, to hopefully make review/comments easier:

> > > * patch1: is the main (code) patch

> > > * patch2: generic test

> > > * patch3: arm tests

> > > * patch4: powerpc test

> >

> > I think that you need to test some non-ELF based targets.  Or maybe a

> > --enable-targets=all configuration.  I am seeing this error when building:

> >

> >   bfd/ecoff.c:81:1: error: missing initializer for field 'already_assigned' of 'asection' {aka 'struct bfd_section'} [-Werror=missing-field-initializers]

>

> Right, I didn't try non-elf targets.

> If it helps your testing, I was just missing:

> diff --git a/bfd/ecoff.c b/bfd/ecoff.c

> index be3d42e..2ce2c8f 100644

> --- a/bfd/ecoff.c

> +++ b/bfd/ecoff.c

> @@ -77,7 +77,9 @@ static asection bfd_debug_section =

>    /* symbol_ptr_ptr,                                              */

>       NULL,

>    /* map_head, map_tail                                                   */

> -     { NULL }, { NULL }

> +     { NULL }, { NULL },

> +  /* already_assigned                                             */

> +     NULL,

>  };

>

>  /* Create an ECOFF object.  */

>

> >

> > Also when adding a new feature like this, it would be nice if you could

> > include a line or two in ld/NEWS describing what it does.  This makes it

> > easier for me to advertise the feature when announcing a new release...

> >

> > Also you should add a description of the option to ld/ld.texi.

>

> Sure, I am aware of that. At this stage, I'm still looking for comments

> on the design. I fear I'm doing changes in a very wrong way.

> OTOH, if this approach looks fine to you, I'll make sure to submit a patch

> including some docs before the 2.34 deadline :-)

>


Here is an updated version:
* fixes non-elf build
* adds entries in NEWS and ld.texi

I'm wondering whether I should do something cleaner than abort() when
detecting an unsupported case?

Also, I'm still not sure about the new option name....

Since it changes the processing to allow several output sections to
match, maybe --enable-non-contiguous-regions is not good although it
describes the original need.
Would --multiple-output-sections be better? (actually, there's still
only one output in the end....)

Thanks,

Christophe

> Thanks,

>

> Christophe

>

> >

> > Cheers

> >   Nick

> >

> >
From 99837adc0e45b58e22e106e70df255e768041be4 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/4] Add support for non-contiguous memory regions

2020-01-06  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.
	* ecoff.c (bfd_debug_section): Initialize already_assigned field.
	* elf32-arm.c (arm_build_one_stub): Add support for
	non_contiguous_regions.
	* emultempl/armelf.em (elf32_arm_add_stub_section): Add
	SEC_LINKER_CREATED flag.

	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.
	* (NEWS): Add --enable-non-contiguous-regions.
	* ld.texi: Add --enable-non-contiguous-regions documentation.

Change-Id: Iac2f395b3a1fbea4b3a732ba5a9b847e10b4b2f0
---
 bfd/bfd-in2.h          |   9 +++-
 bfd/ecoff.c            |   6 ++-
 bfd/elf32-arm.c        |  11 +++++
 bfd/section.c          |   9 +++-
 include/bfdlink.h      |   3 ++
 ld/NEWS                |   1 +
 ld/emultempl/armelf.em |   3 +-
 ld/ld.texi             |  32 ++++++++++++++
 ld/ldlang.c            | 116 +++++++++++++++++++++++++++++++++++++++++++++++--
 ld/ldlex.h             |   1 +
 ld/lexsup.c            |   5 +++
 11 files changed, 187 insertions(+), 9 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/ecoff.c b/bfd/ecoff.c
index be3d42e..adbf9ca 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -76,8 +76,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 dca208f..7b6b7a6 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -5085,6 +5085,17 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
 
   stub_bfd = stub_sec->owner;
 
+  /* 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 (_("No output section assigned to %pA. "
+	       "Retry without --enable-non-contiguous-regions.\n"),
+	     stub_entry->target_section);
+      abort();
+    }
+
   /* This is the address of the stub destination.  */
   sym_value = (stub_entry->target_value
 	       + stub_entry->target_section->output_offset
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/NEWS b/ld/NEWS
index bc21c39..97194f6 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,6 @@
 -*- text -*-
 
+* Add command-line option --enable-non-contiguous-regions.
 * cr16c support removed.
 
 Changes in 2.33:
diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em
index 9a1bc9a..445977b 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 ed538fb..78977f9 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -459,6 +459,38 @@ 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
+
+
 @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 eedcb7f..75dc5b1 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,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;
 
@@ -5152,6 +5212,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,10 +5504,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:
@@ -5864,7 +5936,7 @@ lang_size_sections_1
 		  *relax = TRUE;
 	      }
 	    dot = size_input_section (prev, output_section_statement,
-				      fill, dot);
+				      fill, &removed, dot);
 	  }
 	  break;
 
@@ -5969,7 +6041,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 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;
Simon Richter Jan. 10, 2020, 10:42 a.m. | #4
Hi,

On Fri, Jan 10, 2020 at 11:22:31AM +0100, Christophe Lyon wrote:

> Since it changes the processing to allow several output sections to

> match, maybe --enable-non-contiguous-regions is not good although it

> describes the original need.

> Would --multiple-output-sections be better? (actually, there's still

> only one output in the end....)


Wait, this option means that if an input section is listed multiple times,
it isn't duplicated like it used to be?

That might be a problem for people who are building overlays for banked
memory, or to conserve RAM.

   Simon
Christophe Lyon Jan. 10, 2020, 11:02 a.m. | #5
On Fri, 10 Jan 2020 at 11:42, Simon Richter <Simon.Richter@hogyros.de> wrote:
>

> Hi,

>

> On Fri, Jan 10, 2020 at 11:22:31AM +0100, Christophe Lyon wrote:

>

> > Since it changes the processing to allow several output sections to

> > match, maybe --enable-non-contiguous-regions is not good although it

> > describes the original need.

> > Would --multiple-output-sections be better? (actually, there's still

> > only one output in the end....)

>

> Wait, this option means that if an input section is listed multiple times,

> it isn't duplicated like it used to be?

>

> That might be a problem for people who are building overlays for banked

> memory, or to conserve RAM.

>


I haven't checked that. Is there an existing testcase?

>    Simon
Christophe Lyon Jan. 13, 2020, 10:24 a.m. | #6
On Fri, 10 Jan 2020 at 12:02, Christophe Lyon
<christophe.lyon@linaro.org> wrote:
>

> On Fri, 10 Jan 2020 at 11:42, Simon Richter <Simon.Richter@hogyros.de> wrote:

> >

> > Hi,

> >

> > On Fri, Jan 10, 2020 at 11:22:31AM +0100, Christophe Lyon wrote:

> >

> > > Since it changes the processing to allow several output sections to

> > > match, maybe --enable-non-contiguous-regions is not good although it

> > > describes the original need.

> > > Would --multiple-output-sections be better? (actually, there's still

> > > only one output in the end....)

> >

> > Wait, this option means that if an input section is listed multiple times,

> > it isn't duplicated like it used to be?

> >

> > That might be a problem for people who are building overlays for banked

> > memory, or to conserve RAM.

> >

>

> I haven't checked that. Is there an existing testcase?

>

I should probably have phrased that differently: I ran the testsuite
for arm and powerpc, no regression.
Do you have a case in mind that is not covered by the existing testsuite?


> >    Simon
Simon Richter Jan. 13, 2020, 2:37 p.m. | #7
Hi,

sorry, overlooked the original email.

> > > Wait, this option means that if an input section is listed multiple times,

> > > it isn't duplicated like it used to be?


> > > That might be a problem for people who are building overlays for banked

> > > memory, or to conserve RAM.


> > I haven't checked that. Is there an existing testcase?


> I should probably have phrased that differently: I ran the testsuite

> for arm and powerpc, no regression.

> Do you have a case in mind that is not covered by the existing testsuite?


Did you run the testsuite with --enable-non-contiguous-regions? If so, that
looks like a gap in test coverage to me, e.g. I'd fully expect to be able
to do something like

trampoline.S:

        .section .text.trampoline
        stmdb sp, {r10}
        ldr r10, =BANK_SWITCH
        str r11, [r10]
        ldmia sp, {r10}
        nop
        nop
        nop
        nop
        nop
        b r11

and then reuse that section on every bank:

        SECTIONS {
                .bank0 0x0 : {
                        *(.text.trampoline)
                        *(.text.bank0)
                } >BANK0
                .bank1 0x10000 : {
                        *(.text.trampoline)
                        *(.text.bank1)
                } >BANK1
        }

in order to generate binary images that contain the repeated parts for all
memory banks.

The official method

        SECTIONS {
                .text.trampoline : {
                        *(.text.trampoline)
                }
                OVERLAY : {
                        .bank0 : {
                                *(.text.bank0)
                        }
                        .bank1 : {
                                *(.text.bank1)
                        }
                }
        }

is a lot less useful for generating images ready for programming, because
it assumes that we are building a single image that a loader will pull into
RAM piecewise.

I'm certain there are other use cases for repeated sections as well, bank
switching is more of a thing on smaller CPUs like the 8051, which have
interrupt trampolines on every bank because the interrupt handling logic
does not know about the memory map[1].

As usual, I'm only looking for possible problems here, with an emphasis on
"possible" -- whether these are relevant from a project management POV is
up to the actual project maintainers.

As a user, it'd be awesome to be able to use the combination of
non-contiguous regions, banking with a trampoline and linker relaxations to
automatically distribute code over multiple banks and generate the most
efficient jump instructions between them, but that would be a rather
monumental task.

   Simon

[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka8894.html
Christophe Lyon Jan. 13, 2020, 4:24 p.m. | #8
On Mon, 13 Jan 2020 at 15:37, Simon Richter <Simon.Richter@hogyros.de> wrote:
>

> Hi,

>

> sorry, overlooked the original email.

>

> > > > Wait, this option means that if an input section is listed multiple times,

> > > > it isn't duplicated like it used to be?

>

> > > > That might be a problem for people who are building overlays for banked

> > > > memory, or to conserve RAM.

>

> > > I haven't checked that. Is there an existing testcase?

>

> > I should probably have phrased that differently: I ran the testsuite

> > for arm and powerpc, no regression.

> > Do you have a case in mind that is not covered by the existing testsuite?

>

> Did you run the testsuite with --enable-non-contiguous-regions? If so, that

Yes, but after I sent my previous email.... and it shows a few
regressions, which I'm looking at.

However the *overlay* tests under ld-elf still work.

> looks like a gap in test coverage to me, e.g. I'd fully expect to be able

> to do something like

>

> trampoline.S:

>

>         .section .text.trampoline

>         stmdb sp, {r10}

>         ldr r10, =BANK_SWITCH

>         str r11, [r10]

>         ldmia sp, {r10}

>         nop

>         nop

>         nop

>         nop

>         nop

>         b r11

>

> and then reuse that section on every bank:

>

>         SECTIONS {

>                 .bank0 0x0 : {

>                         *(.text.trampoline)

>                         *(.text.bank0)

>                 } >BANK0

>                 .bank1 0x10000 : {

>                         *(.text.trampoline)

>                         *(.text.bank1)

>                 } >BANK1

>         }

>

> in order to generate binary images that contain the repeated parts for all

> memory banks.

>

> The official method

>

>         SECTIONS {

>                 .text.trampoline : {

>                         *(.text.trampoline)

>                 }

>                 OVERLAY : {

>                         .bank0 : {

>                                 *(.text.bank0)

>                         }

>                         .bank1 : {

>                                 *(.text.bank1)

>                         }

>                 }

>         }

>

> is a lot less useful for generating images ready for programming, because

> it assumes that we are building a single image that a loader will pull into

> RAM piecewise.

>

> I'm certain there are other use cases for repeated sections as well, bank

> switching is more of a thing on smaller CPUs like the 8051, which have

> interrupt trampolines on every bank because the interrupt handling logic

> does not know about the memory map[1].

>

> As usual, I'm only looking for possible problems here, with an emphasis on

> "possible" -- whether these are relevant from a project management POV is

> up to the actual project maintainers.

>

> As a user, it'd be awesome to be able to use the combination of

> non-contiguous regions, banking with a trampoline and linker relaxations to

> automatically distribute code over multiple banks and generate the most

> efficient jump instructions between them, but that would be a rather

> monumental task.

>

>    Simon

>

> [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka8894.html
Simon Richter Jan. 13, 2020, 5:11 p.m. | #9
Hi,

On Mon, Jan 13, 2020 at 05:24:07PM +0100, Christophe Lyon wrote:

> > Did you run the testsuite with --enable-non-contiguous-regions? If so, that


> Yes, but after I sent my previous email.... and it shows a few

> regressions, which I'm looking at.


> However the *overlay* tests under ld-elf still work.


Yes, the way overlays are implemented it should be unaffected, because
input sections aren't used twice in that setup.

The bank switching trampoline example is one case where a user might want
to have input sections duplicated during linking -- there might be others.

Unrelated to your patch, that might be an interesting use case as well:
statically linking libraries into overlays:

library.S:

                .section .text
                .globl function
        function:
                blr

overlay1.S:

                .section .text
                mov r0, #0
                bl function

overlay2.S:

                .section .text
                mov r0, #1
                bl function

test.ld:

        SECTIONS {
                OVERLAY : NOCROSSREFS {
                        .text.overlay1 : {
                                overlay1.o(.text)
                                EXCLUDE_FILE(overlay*.o) *(.text)
                        }
                        .text.overlay2 : {
                                overlay2.o(.text)
                                EXCLUDE_FILE(overlay*.o) *(.text)
                        }
                }
        }

My expectation here would be that I'd end up with two copies of "function",
one in each branch of the overlay.

   Simon
Christophe Lyon Jan. 29, 2020, 9:27 p.m. | #10
On Mon, 13 Jan 2020 at 17:24, Christophe Lyon
<christophe.lyon@linaro.org> wrote:
>

> On Mon, 13 Jan 2020 at 15:37, Simon Richter <Simon.Richter@hogyros.de> wrote:

> >

> > Hi,

> >

> > sorry, overlooked the original email.

> >

> > > > > Wait, this option means that if an input section is listed multiple times,

> > > > > it isn't duplicated like it used to be?

> >

> > > > > That might be a problem for people who are building overlays for banked

> > > > > memory, or to conserve RAM.

> >

> > > > I haven't checked that. Is there an existing testcase?

> >

> > > I should probably have phrased that differently: I ran the testsuite

> > > for arm and powerpc, no regression.

> > > Do you have a case in mind that is not covered by the existing testsuite?

> >

> > Did you run the testsuite with --enable-non-contiguous-regions? If so, that

> Yes, but after I sent my previous email.... and it shows a few

> regressions, which I'm looking at.


I have patches for the problems I noticed (took me a while to realize
that my new option is incompatible with "INSERT", since it can change
the input section -> output section mapping).

>

> However the *overlay* tests under ld-elf still work.

>

> > looks like a gap in test coverage to me, e.g. I'd fully expect to be able

> > to do something like

> >

> > trampoline.S:

> >

> >         .section .text.trampoline

> >         stmdb sp, {r10}

> >         ldr r10, =BANK_SWITCH

> >         str r11, [r10]

> >         ldmia sp, {r10}

> >         nop

> >         nop

> >         nop

> >         nop

> >         nop

> >         b r11

> >

> > and then reuse that section on every bank:

> >

> >         SECTIONS {

> >                 .bank0 0x0 : {

> >                         *(.text.trampoline)

> >                         *(.text.bank0)

> >                 } >BANK0

> >                 .bank1 0x10000 : {

> >                         *(.text.trampoline)

> >                         *(.text.bank1)

> >                 } >BANK1

> >         }

> >

> > in order to generate binary images that contain the repeated parts for all

> > memory banks.

> >


What did you actually mean earlier by "Wait, this option means that if
an input section is listed multiple times,
it isn't duplicated like it used to be?"
In which cases are sections duplicated (when you say "used to be")? My
understanding is that input section have only one output section.

Thanks,

Christophe


> > The official method

> >

> >         SECTIONS {

> >                 .text.trampoline : {

> >                         *(.text.trampoline)

> >                 }

> >                 OVERLAY : {

> >                         .bank0 : {

> >                                 *(.text.bank0)

> >                         }

> >                         .bank1 : {

> >                                 *(.text.bank1)

> >                         }

> >                 }

> >         }

> >

> > is a lot less useful for generating images ready for programming, because

> > it assumes that we are building a single image that a loader will pull into

> > RAM piecewise.

> >

> > I'm certain there are other use cases for repeated sections as well, bank

> > switching is more of a thing on smaller CPUs like the 8051, which have

> > interrupt trampolines on every bank because the interrupt handling logic

> > does not know about the memory map[1].

> >

> > As usual, I'm only looking for possible problems here, with an emphasis on

> > "possible" -- whether these are relevant from a project management POV is

> > up to the actual project maintainers.

> >

> > As a user, it'd be awesome to be able to use the combination of

> > non-contiguous regions, banking with a trampoline and linker relaxations to

> > automatically distribute code over multiple banks and generate the most

> > efficient jump instructions between them, but that would be a rather

> > monumental task.

> >

> >    Simon

> >

> > [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka8894.html
Simon Richter Jan. 30, 2020, 10:16 a.m. | #11
Hi,

On 29.01.20 22:27, Christophe Lyon wrote:

> What did you actually mean earlier by "Wait, this option means that if

> an input section is listed multiple times,

> it isn't duplicated like it used to be?"

> In which cases are sections duplicated (when you say "used to be")? My

> understanding is that input section have only one output section.


True, no idea why I thought this would work, but I kind of expected it
to. Sorry for the noise.

   Simon

Patch

From 65fb590a60aef51a7e3e9f8df2b04fb7ada942a1 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/4] Add support for non-contiguous memory regions

2020-01-06  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.
	* elf32-arm.c (arm_build_one_stub): Add support for
	non_contiguous_regions.
	* emultempl/armelf.em (elf32_arm_add_stub_section): Add
	SEC_LINKER_CREATED flag.

	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.

Change-Id: Iac2f395b3a1fbea4b3a732ba5a9b847e10b4b2f0
---
 bfd/bfd-in2.h          |   9 +++-
 bfd/elf32-arm.c        |  11 +++++
 bfd/section.c          |   9 +++-
 include/bfdlink.h      |   3 ++
 ld/emultempl/armelf.em |   3 +-
 ld/ldlang.c            | 116 +++++++++++++++++++++++++++++++++++++++++++++++--
 ld/ldlex.h             |   1 +
 ld/lexsup.c            |   5 +++
 8 files changed, 150 insertions(+), 7 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/elf32-arm.c b/bfd/elf32-arm.c
index dca208f..7b6b7a6 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -5085,6 +5085,17 @@  arm_build_one_stub (struct bfd_hash_entry *gen_entry,
 
   stub_bfd = stub_sec->owner;
 
+  /* 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 (_("No output section assigned to %pA. "
+	       "Retry without --enable-non-contiguous-regions.\n"),
+	     stub_entry->target_section);
+      abort();
+    }
+
   /* This is the address of the stub destination.  */
   sym_value = (stub_entry->target_value
 	       + stub_entry->target_section->output_offset
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/emultempl/armelf.em b/ld/emultempl/armelf.em
index 9a1bc9a..445977b 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/ldlang.c b/ld/ldlang.c
index eedcb7f..75dc5b1 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,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;
 
@@ -5152,6 +5212,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,10 +5504,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:
@@ -5864,7 +5936,7 @@  lang_size_sections_1
 		  *relax = TRUE;
 	      }
 	    dot = size_input_section (prev, output_section_statement,
-				      fill, dot);
+				      fill, &removed, dot);
 	  }
 	  break;
 
@@ -5969,7 +6041,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 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