| 1 | /* Native Client support for ELF |
| 2 | Copyright 2012 Free Software Foundation, Inc. |
| 3 | |
| 4 | This file is part of BFD, the Binary File Descriptor library. |
| 5 | |
| 6 | This program is free software; you can redistribute it and/or modify |
| 7 | it under the terms of the GNU General Public License as published by |
| 8 | the Free Software Foundation; either version 3 of the License, or |
| 9 | (at your option) any later version. |
| 10 | |
| 11 | This program is distributed in the hope that it will be useful, |
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | GNU General Public License for more details. |
| 15 | |
| 16 | You should have received a copy of the GNU General Public License |
| 17 | along with this program; if not, write to the Free Software |
| 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
| 19 | MA 02111-1307, USA. */ |
| 20 | |
| 21 | #include "sysdep.h" |
| 22 | #include "bfd.h" |
| 23 | #include "elf-bfd.h" |
| 24 | #include "elf-nacl.h" |
| 25 | #include "elf/common.h" |
| 26 | #include "elf/internal.h" |
| 27 | |
| 28 | static bfd_boolean |
| 29 | segment_executable (struct elf_segment_map *seg) |
| 30 | { |
| 31 | if (seg->p_flags_valid) |
| 32 | return (seg->p_flags & PF_X) != 0; |
| 33 | else |
| 34 | { |
| 35 | /* The p_flags value has not been computed yet, |
| 36 | so we have to look through the sections. */ |
| 37 | unsigned int i; |
| 38 | for (i = 0; i < seg->count; ++i) |
| 39 | if (seg->sections[i]->flags & SEC_CODE) |
| 40 | return TRUE; |
| 41 | } |
| 42 | return FALSE; |
| 43 | } |
| 44 | |
| 45 | static bfd_boolean |
| 46 | segment_nonexecutable_and_has_contents (struct elf_segment_map *seg) |
| 47 | { |
| 48 | bfd_boolean any_contents = FALSE; |
| 49 | unsigned int i; |
| 50 | for (i = 0; i < seg->count; ++i) |
| 51 | { |
| 52 | if (seg->sections[i]->flags & SEC_CODE) |
| 53 | return FALSE; |
| 54 | if (seg->sections[i]->flags & SEC_HAS_CONTENTS) |
| 55 | any_contents = TRUE; |
| 56 | } |
| 57 | return any_contents; |
| 58 | } |
| 59 | |
| 60 | |
| 61 | /* We permute the segment_map to get BFD to do the file layout we want: |
| 62 | The first non-executable PT_LOAD segment appears first in the file |
| 63 | and contains the ELF file header and phdrs. */ |
| 64 | bfd_boolean |
| 65 | nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) |
| 66 | { |
| 67 | struct elf_segment_map **m = &elf_tdata (abfd)->segment_map; |
| 68 | struct elf_segment_map **first_load = NULL; |
| 69 | struct elf_segment_map **last_load = NULL; |
| 70 | bfd_boolean moved_headers = FALSE; |
| 71 | |
| 72 | if (info != NULL && info->user_phdrs) |
| 73 | /* The linker script used PHDRS explicitly, so don't change what the |
| 74 | user asked for. */ |
| 75 | return TRUE; |
| 76 | |
| 77 | while (*m != NULL) |
| 78 | { |
| 79 | struct elf_segment_map *seg = *m; |
| 80 | |
| 81 | if (seg->p_type == PT_LOAD) |
| 82 | { |
| 83 | /* First, we're just finding the earliest PT_LOAD. |
| 84 | By the normal rules, this will be the lowest-addressed one. |
| 85 | We only have anything interesting to do if it's executable. */ |
| 86 | last_load = m; |
| 87 | if (first_load == NULL) |
| 88 | { |
| 89 | if (!segment_executable (*m)) |
| 90 | return TRUE; |
| 91 | first_load = m; |
| 92 | } |
| 93 | /* Now that we've noted the first PT_LOAD, we're looking for |
| 94 | the first non-executable PT_LOAD with a nonempty p_filesz. */ |
| 95 | else if (!moved_headers |
| 96 | && segment_nonexecutable_and_has_contents (seg)) |
| 97 | { |
| 98 | /* This is the one we were looking for! |
| 99 | |
| 100 | First, clear the flags on previous segments that |
| 101 | say they include the file header and phdrs. */ |
| 102 | struct elf_segment_map *prevseg; |
| 103 | for (prevseg = *first_load; |
| 104 | prevseg != seg; |
| 105 | prevseg = prevseg->next) |
| 106 | if (prevseg->p_type == PT_LOAD) |
| 107 | { |
| 108 | prevseg->includes_filehdr = 0; |
| 109 | prevseg->includes_phdrs = 0; |
| 110 | } |
| 111 | |
| 112 | /* This segment will include those headers instead. */ |
| 113 | seg->includes_filehdr = 1; |
| 114 | seg->includes_phdrs = 1; |
| 115 | |
| 116 | moved_headers = TRUE; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | m = &seg->next; |
| 121 | } |
| 122 | |
| 123 | if (first_load != last_load && moved_headers) |
| 124 | { |
| 125 | /* Now swap the first and last PT_LOAD segments' |
| 126 | positions in segment_map. */ |
| 127 | struct elf_segment_map *first = *first_load; |
| 128 | struct elf_segment_map *last = *last_load; |
| 129 | *first_load = first->next; |
| 130 | first->next = last->next; |
| 131 | last->next = first; |
| 132 | } |
| 133 | |
| 134 | return TRUE; |
| 135 | } |
| 136 | |
| 137 | /* After nacl_modify_segment_map has done its work, the file layout has |
| 138 | been done as we wanted. But the PT_LOAD phdrs are no longer in the |
| 139 | proper order for the ELF rule that they must appear in ascending address |
| 140 | order. So find the two segments we swapped before, and swap them back. */ |
| 141 | bfd_boolean |
| 142 | nacl_modify_program_headers (bfd *abfd, |
| 143 | struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| 144 | { |
| 145 | struct elf_segment_map **m = &elf_tdata (abfd)->segment_map; |
| 146 | Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr; |
| 147 | Elf_Internal_Phdr *p = phdr; |
| 148 | |
| 149 | if (info != NULL && info->user_phdrs) |
| 150 | /* The linker script used PHDRS explicitly, so don't change what the |
| 151 | user asked for. */ |
| 152 | return TRUE; |
| 153 | |
| 154 | /* Find the PT_LOAD that contains the headers (should be the first). */ |
| 155 | while (*m != NULL) |
| 156 | { |
| 157 | if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr) |
| 158 | break; |
| 159 | |
| 160 | m = &(*m)->next; |
| 161 | ++p; |
| 162 | } |
| 163 | |
| 164 | if (*m != NULL) |
| 165 | { |
| 166 | struct elf_segment_map **first_load_seg = m; |
| 167 | Elf_Internal_Phdr *first_load_phdr = p; |
| 168 | struct elf_segment_map **next_load_seg = NULL; |
| 169 | Elf_Internal_Phdr *next_load_phdr = NULL; |
| 170 | |
| 171 | /* Now move past that first one and find the PT_LOAD that should be |
| 172 | before it by address order. */ |
| 173 | |
| 174 | m = &(*m)->next; |
| 175 | ++p; |
| 176 | |
| 177 | while ((*m) != NULL) |
| 178 | { |
| 179 | if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr) |
| 180 | { |
| 181 | next_load_seg = m; |
| 182 | next_load_phdr = p; |
| 183 | break; |
| 184 | } |
| 185 | |
| 186 | m = &(*m)->next; |
| 187 | ++p; |
| 188 | } |
| 189 | |
| 190 | /* Swap their positions in the segment_map back to how they used to be. |
| 191 | The phdrs have already been set up by now, so we have to slide up |
| 192 | the earlier ones to insert the one that should be first. */ |
| 193 | if (next_load_seg != NULL) |
| 194 | { |
| 195 | Elf_Internal_Phdr move_phdr; |
| 196 | struct elf_segment_map *first_seg = *first_load_seg; |
| 197 | struct elf_segment_map *next_seg = *next_load_seg; |
| 198 | struct elf_segment_map *first_next = first_seg->next; |
| 199 | struct elf_segment_map *next_next = next_seg->next; |
| 200 | |
| 201 | first_seg->next = next_next; |
| 202 | *first_load_seg = next_seg; |
| 203 | |
| 204 | next_seg->next = first_next; |
| 205 | *next_load_seg = first_seg; |
| 206 | |
| 207 | move_phdr = *next_load_phdr; |
| 208 | memmove (first_load_phdr + 1, first_load_phdr, |
| 209 | (next_load_phdr - first_load_phdr) * sizeof move_phdr); |
| 210 | *first_load_phdr = move_phdr; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | return TRUE; |
| 215 | } |