Index: bfd/elf-nacl.c |
diff --git a/bfd/elf-nacl.c b/bfd/elf-nacl.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..842e367d97d8b687a647d07707f3229d0ee7c9fe |
--- /dev/null |
+++ b/bfd/elf-nacl.c |
@@ -0,0 +1,215 @@ |
+/* Native Client support for ELF |
+ Copyright 2012 Free Software Foundation, Inc. |
+ |
+ This file is part of BFD, the Binary File Descriptor library. |
+ |
+ This program is free software; you can redistribute it and/or modify |
+ it under the terms of the GNU General Public License as published by |
+ the Free Software Foundation; either version 3 of the License, or |
+ (at your option) any later version. |
+ |
+ This program is distributed in the hope that it will be useful, |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ GNU General Public License for more details. |
+ |
+ You should have received a copy of the GNU General Public License |
+ along with this program; if not, write to the Free Software |
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
+ MA 02111-1307, USA. */ |
+ |
+#include "sysdep.h" |
+#include "bfd.h" |
+#include "elf-bfd.h" |
+#include "elf-nacl.h" |
+#include "elf/common.h" |
+#include "elf/internal.h" |
+ |
+static bfd_boolean |
+segment_executable (struct elf_segment_map *seg) |
+{ |
+ if (seg->p_flags_valid) |
+ return (seg->p_flags & PF_X) != 0; |
+ else |
+ { |
+ /* The p_flags value has not been computed yet, |
+ so we have to look through the sections. */ |
+ unsigned int i; |
+ for (i = 0; i < seg->count; ++i) |
+ if (seg->sections[i]->flags & SEC_CODE) |
+ return TRUE; |
+ } |
+ return FALSE; |
+} |
+ |
+static bfd_boolean |
+segment_nonexecutable_and_has_contents (struct elf_segment_map *seg) |
+{ |
+ bfd_boolean any_contents = FALSE; |
+ unsigned int i; |
+ for (i = 0; i < seg->count; ++i) |
+ { |
+ if (seg->sections[i]->flags & SEC_CODE) |
+ return FALSE; |
+ if (seg->sections[i]->flags & SEC_HAS_CONTENTS) |
+ any_contents = TRUE; |
+ } |
+ return any_contents; |
+} |
+ |
+ |
+/* We permute the segment_map to get BFD to do the file layout we want: |
+ The first non-executable PT_LOAD segment appears first in the file |
+ and contains the ELF file header and phdrs. */ |
+bfd_boolean |
+nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) |
+{ |
+ struct elf_segment_map **m = &elf_tdata (abfd)->segment_map; |
+ struct elf_segment_map **first_load = NULL; |
+ struct elf_segment_map **last_load = NULL; |
+ bfd_boolean moved_headers = FALSE; |
+ |
+ if (info != NULL && info->user_phdrs) |
+ /* The linker script used PHDRS explicitly, so don't change what the |
+ user asked for. */ |
+ return TRUE; |
+ |
+ while (*m != NULL) |
+ { |
+ struct elf_segment_map *seg = *m; |
+ |
+ if (seg->p_type == PT_LOAD) |
+ { |
+ /* First, we're just finding the earliest PT_LOAD. |
+ By the normal rules, this will be the lowest-addressed one. |
+ We only have anything interesting to do if it's executable. */ |
+ last_load = m; |
+ if (first_load == NULL) |
+ { |
+ if (!segment_executable (*m)) |
+ return TRUE; |
+ first_load = m; |
+ } |
+ /* Now that we've noted the first PT_LOAD, we're looking for |
+ the first non-executable PT_LOAD with a nonempty p_filesz. */ |
+ else if (!moved_headers |
+ && segment_nonexecutable_and_has_contents (seg)) |
+ { |
+ /* This is the one we were looking for! |
+ |
+ First, clear the flags on previous segments that |
+ say they include the file header and phdrs. */ |
+ struct elf_segment_map *prevseg; |
+ for (prevseg = *first_load; |
+ prevseg != seg; |
+ prevseg = prevseg->next) |
+ if (prevseg->p_type == PT_LOAD) |
+ { |
+ prevseg->includes_filehdr = 0; |
+ prevseg->includes_phdrs = 0; |
+ } |
+ |
+ /* This segment will include those headers instead. */ |
+ seg->includes_filehdr = 1; |
+ seg->includes_phdrs = 1; |
+ |
+ moved_headers = TRUE; |
+ } |
+ } |
+ |
+ m = &seg->next; |
+ } |
+ |
+ if (first_load != last_load && moved_headers) |
+ { |
+ /* Now swap the first and last PT_LOAD segments' |
+ positions in segment_map. */ |
+ struct elf_segment_map *first = *first_load; |
+ struct elf_segment_map *last = *last_load; |
+ *first_load = first->next; |
+ first->next = last->next; |
+ last->next = first; |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* After nacl_modify_segment_map has done its work, the file layout has |
+ been done as we wanted. But the PT_LOAD phdrs are no longer in the |
+ proper order for the ELF rule that they must appear in ascending address |
+ order. So find the two segments we swapped before, and swap them back. */ |
+bfd_boolean |
+nacl_modify_program_headers (bfd *abfd, |
+ struct bfd_link_info *info ATTRIBUTE_UNUSED) |
+{ |
+ struct elf_segment_map **m = &elf_tdata (abfd)->segment_map; |
+ Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr; |
+ Elf_Internal_Phdr *p = phdr; |
+ |
+ if (info != NULL && info->user_phdrs) |
+ /* The linker script used PHDRS explicitly, so don't change what the |
+ user asked for. */ |
+ return TRUE; |
+ |
+ /* Find the PT_LOAD that contains the headers (should be the first). */ |
+ while (*m != NULL) |
+ { |
+ if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr) |
+ break; |
+ |
+ m = &(*m)->next; |
+ ++p; |
+ } |
+ |
+ if (*m != NULL) |
+ { |
+ struct elf_segment_map **first_load_seg = m; |
+ Elf_Internal_Phdr *first_load_phdr = p; |
+ struct elf_segment_map **next_load_seg = NULL; |
+ Elf_Internal_Phdr *next_load_phdr = NULL; |
+ |
+ /* Now move past that first one and find the PT_LOAD that should be |
+ before it by address order. */ |
+ |
+ m = &(*m)->next; |
+ ++p; |
+ |
+ while ((*m) != NULL) |
+ { |
+ if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr) |
+ { |
+ next_load_seg = m; |
+ next_load_phdr = p; |
+ break; |
+ } |
+ |
+ m = &(*m)->next; |
+ ++p; |
+ } |
+ |
+ /* Swap their positions in the segment_map back to how they used to be. |
+ The phdrs have already been set up by now, so we have to slide up |
+ the earlier ones to insert the one that should be first. */ |
+ if (next_load_seg != NULL) |
+ { |
+ Elf_Internal_Phdr move_phdr; |
+ struct elf_segment_map *first_seg = *first_load_seg; |
+ struct elf_segment_map *next_seg = *next_load_seg; |
+ struct elf_segment_map *first_next = first_seg->next; |
+ struct elf_segment_map *next_next = next_seg->next; |
+ |
+ first_seg->next = next_next; |
+ *first_load_seg = next_seg; |
+ |
+ next_seg->next = first_next; |
+ *next_load_seg = first_seg; |
+ |
+ move_phdr = *next_load_phdr; |
+ memmove (first_load_phdr + 1, first_load_phdr, |
+ (next_load_phdr - first_load_phdr) * sizeof move_phdr); |
+ *first_load_phdr = move_phdr; |
+ } |
+ } |
+ |
+ return TRUE; |
+} |