[RFC PATCH] avoid intermingling executable and nonexecutable sections in a segment

Roland McGrath mcgrathr@google.com
Tue Nov 20 19:48:00 GMT 2012

This patch is a strawman.  It unconditionally changes behavior in a way
that is not desireable for most targets.  I'm looking for advice on how
best to achieve this change in a target-specific way.

For *-nacl targets, executable and non-executable sections must be strictly
separated--they may never appear in the same PT_LOAD segment.  The vanilla
logic will merge an executable PT_LOAD segment with a following read-only
segment if they are less than a page apart.

What is the right hook to use to change this behavior for my target?

The override_segment_assignment hook is ideal.  If I could provide that
function specifically for my target, then it would be trivial.  However, I
don't see how I would go about replacing that hook in a target-specific
way.  bfd_link_callbacks is set up generically by ldmain, and this hook is
always set to ldlang_override_segment_assignment.

What would really be best IMHO is if this could somehow be part of the
linker emulation.  It corresponds exactly to the ${SEPARATE_CODE} setting
honored by elf.sc.  The cleanest thing would be if setting that just
propagated into this change to the segment-creation logic.

I'm also thoroughly unclear on what's necessary to ensure that objcopy et
al won't rejigger the phdrs after link time and negate any such decision.
(I find it utterly insane that anything but the linker itself ever rewrites
phdrs, so I am not well-positioned to understand the logic of what parts of
BFD rewrite them how.)

It's clearly possible to do this in my elf_backend_modify_segment_map hook.
It can split a segment into two.  But that seems fragile and error-prone,
since I'd have to figure out the right way to handle all possible values of
all the 'struct elf_segment_map' fields.  Conversely, all the logic in
_bfd_elf_map_sections_to_segments is already there to figure that stuff out.



diff --git a/bfd/elf.c b/bfd/elf.c
index b8bb6d3..a5887c1 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -3777,6 +3777,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
struct bfd_link_info *info)
       asection **hdrpp;
       bfd_boolean phdr_in_segment = TRUE;
       bfd_boolean writable;
+      bfd_boolean executable;
       int tls_count = 0;
       asection *first_tls = NULL;
       asection *dynsec, *eh_frame_hdr;
@@ -3859,6 +3860,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
struct bfd_link_info *info)
       phdr_index = 0;
       maxpagesize = bed->maxpagesize;
       writable = FALSE;
+      executable = FALSE;
       dynsec = bfd_get_section_by_name (abfd, ".dynamic");
       if (dynsec != NULL
 	  && (dynsec->flags & SEC_LOAD) == 0)
@@ -3955,6 +3957,12 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
struct bfd_link_info *info)
 		 ends precisely on a page boundary.  */
 	      new_segment = TRUE;
+          else if (executable != ((hdr->flags & SEC_CODE) != 0))
+            {
+              /* We don't want to intermingle executable and non-executable
+                 sections in the same segment.  */
+	      new_segment = TRUE;
+            }
 	      /* Otherwise, we can use the same segment.  */
@@ -3974,6 +3982,8 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
struct bfd_link_info *info)
 	      if ((hdr->flags & SEC_READONLY) == 0)
 		writable = TRUE;
+	      if ((hdr->flags & SEC_CODE) != 0)
+		executable = TRUE;
 	      last_hdr = hdr;
 	      /* .tbss sections effectively have zero size.  */
 	      if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
@@ -3999,6 +4009,11 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
struct bfd_link_info *info)
 	    writable = FALSE;

+	  if ((hdr->flags & SEC_CODE) == 0)
+	    executable = FALSE;
+	  else
+	    executable = TRUE;
 	  last_hdr = hdr;
 	  /* .tbss sections effectively have zero size.  */
 	  if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)

More information about the Binutils mailing list