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

