From 2889e75bfb63b5638dffb3bf9ae598e85af86af9 Mon Sep 17 00:00:00 2001
From: Nick Clifton <nickc@redhat.com>
Date: Wed, 21 Feb 2007 16:43:50 +0000
Subject: [PATCH] Take into account MEMORY regions when creating a segment map.

---
 bfd/ChangeLog     |  5 +++++
 bfd/elf.c         |  4 ++++
 include/ChangeLog |  5 +++++
 include/bfdlink.h |  7 +++++++
 ld/ChangeLog      |  7 +++++++
 ld/ldlang.c       | 36 ++++++++++++++++++++++++++++++++++++
 ld/ldlang.h       |  4 ++++
 ld/ldmain.c       |  3 ++-
 8 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index f1d2ed7452..0bd6795229 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,8 @@
+2007-02-21  Nick Clifton  <nickc@redhat.com>
+
+	* elf.c (_bfd_elf_map_sections_to_segments): If the
+	override_segment_assignment callback is defined then call it.
+
 2007-02-21  Alan Modra  <amodra@bigpond.net.au>
 
 	* elf32-spu.c (spu_elf_size_stubs): Correct order of warning args.
diff --git a/bfd/elf.c b/bfd/elf.c
index 460502c2e4..95f34010ac 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -3953,6 +3953,10 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
 	      new_segment = FALSE;
 	    }
 
+	  /* Allow interested parties a chance to override our decision.  */
+	  if (last_hdr && info->callbacks->override_segment_assignment)
+	    new_segment = info->callbacks->override_segment_assignment (info, abfd, hdr, last_hdr, new_segment);
+
 	  if (! new_segment)
 	    {
 	      if ((hdr->flags & SEC_READONLY) == 0)
diff --git a/include/ChangeLog b/include/ChangeLog
index 6c25355f30..e5b7dba324 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,8 @@
+2007-02-21  Nick Clifton  <nickc@redhat.com>
+
+	* bfdlink.h (struct bfd_link_callbacks): Add
+	override_segment_assignment field.
+
 2007-02-17  Mark Mitchell  <mark@codesourcery.com>
             Nathan Sidwell  <nathan@codesourcery.com>
             Vladimir Prus  <vladimir@codesourcery.com
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 6842243d09..46e3cf5c54 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -566,6 +566,13 @@ struct bfd_link_callbacks
   /* General link info message.  */
   void (*einfo)
     (const char *fmt, ...);
+  /* This callback provides a chance for users of the BFD library to
+     override its decision about whether to place two adjacent sections
+     into the same segment.  */
+  bfd_boolean (*override_segment_assignment)
+    (struct bfd_link_info *, bfd * abfd,
+     asection * current_section, asection * previous_section,
+     bfd_boolean new_segment);
 };
 
 /* The linker builds link_order structures which tell the code how to
diff --git a/ld/ChangeLog b/ld/ChangeLog
index cbe0544979..7c6506b04e 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,10 @@
+2007-02-21  Nick Clifton  <nickc@redhat.com>
+
+	* ldlang.c (ldlang_override_segment_assignment): New function.
+	* ldlang.h (ldlang_override_segment_assignment): Prototype.
+	* ldmain.c (link_callbacks): Add
+	ldlang_override_segment_assignment.
+
 2007-02-20  Alan Modra  <amodra@bigpond.net.au>
 
 	* ldexp.c (fold_name <LOADADDR>): Ensure result is always absolute.
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 8a69c7028d..fa597f3c05 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -4704,6 +4704,42 @@ lang_size_sections_1
   return dot;
 }
 
+/* Callback routine that is used in _bfd_elf_map_sections_to_segments.  */
+
+bfd_boolean
+ldlang_override_segment_assignment (struct bfd_link_info * info ATTRIBUTE_UNUSED,
+				    bfd * abfd ATTRIBUTE_UNUSED,
+				    asection * current_section,
+				    asection * previous_section,
+				    bfd_boolean new_segment)
+{
+  lang_output_section_statement_type * cur;
+  lang_output_section_statement_type * prev;
+  
+  if (new_segment)
+    return TRUE;
+
+  /* Paranoia checks.  */
+  if (current_section == NULL || previous_section == NULL)
+    return new_segment;
+
+  /* Find the memory regions associated with the two sections.
+     We call lang_output_section_find() here rather than scanning the list
+     of output sections looking for a matching section pointer because if
+     we have a large number of sections a hash lookup is faster.  */
+  cur  = lang_output_section_find (current_section->name);
+  prev = lang_output_section_find (previous_section->name);
+
+  if (cur == NULL || prev == NULL)
+    return new_segment;
+
+  /* If the regions are different then force the sections to live in
+     different segments.  See the email thread starting here for the
+     reasons why this is necessary:
+     http://sourceware.org/ml/binutils/2007-02/msg00216.html  */
+  return cur->region != prev->region;
+}
+
 void
 one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
 {
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 0074159fc9..d34ea6894d 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -623,4 +623,8 @@ extern void add_excluded_libs (const char *);
 extern bfd_boolean load_symbols
   (lang_input_statement_type *, lang_statement_list_type *);
 
+extern bfd_boolean
+ldlang_override_segment_assignment
+  (struct bfd_link_info *, bfd *, asection *, asection *, bfd_boolean);
+
 #endif
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 9e6a0c393d..f47758ffed 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -161,7 +161,8 @@ static struct bfd_link_callbacks link_callbacks =
   reloc_dangerous,
   unattached_reloc,
   notice,
-  einfo
+  einfo,
+  ldlang_override_segment_assignment
 };
 
 struct bfd_link_info link_info;