| 1 | /* Native Client support for ELF |
| 2 | Copyright (C) 2012-2019 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, see <http://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #include "sysdep.h" |
| 20 | #include "bfd.h" |
| 21 | #include "libbfd.h" |
| 22 | #include "elf-bfd.h" |
| 23 | #include "elf-nacl.h" |
| 24 | #include "elf/common.h" |
| 25 | #include "elf/internal.h" |
| 26 | |
| 27 | static bfd_boolean |
| 28 | segment_executable (struct elf_segment_map *seg) |
| 29 | { |
| 30 | if (seg->p_flags_valid) |
| 31 | return (seg->p_flags & PF_X) != 0; |
| 32 | else |
| 33 | { |
| 34 | /* The p_flags value has not been computed yet, |
| 35 | so we have to look through the sections. */ |
| 36 | unsigned int i; |
| 37 | for (i = 0; i < seg->count; ++i) |
| 38 | if (seg->sections[i]->flags & SEC_CODE) |
| 39 | return TRUE; |
| 40 | } |
| 41 | return FALSE; |
| 42 | } |
| 43 | |
| 44 | /* Determine if this segment is eligible to receive the file and program |
| 45 | headers. It must be read-only and non-executable. |
| 46 | Its first section must start far enough past the page boundary to |
| 47 | allow space for the headers. */ |
| 48 | static bfd_boolean |
| 49 | segment_eligible_for_headers (struct elf_segment_map *seg, |
| 50 | bfd_vma minpagesize, bfd_vma sizeof_headers) |
| 51 | { |
| 52 | unsigned int i; |
| 53 | if (seg->count == 0 || seg->sections[0]->lma % minpagesize < sizeof_headers) |
| 54 | return FALSE; |
| 55 | for (i = 0; i < seg->count; ++i) |
| 56 | { |
| 57 | if ((seg->sections[i]->flags & (SEC_CODE|SEC_READONLY)) != SEC_READONLY) |
| 58 | return FALSE; |
| 59 | } |
| 60 | return TRUE; |
| 61 | } |
| 62 | |
| 63 | |
| 64 | /* We permute the segment_map to get BFD to do the file layout we want: |
| 65 | The first non-executable PT_LOAD segment appears first in the file |
| 66 | and contains the ELF file header and phdrs. */ |
| 67 | bfd_boolean |
| 68 | nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) |
| 69 | { |
| 70 | const struct elf_backend_data *const bed = get_elf_backend_data (abfd); |
| 71 | struct elf_segment_map **m = &elf_seg_map (abfd); |
| 72 | struct elf_segment_map **first_load = NULL; |
| 73 | struct elf_segment_map **headers = NULL; |
| 74 | int sizeof_headers; |
| 75 | |
| 76 | if (info != NULL && info->user_phdrs) |
| 77 | /* The linker script used PHDRS explicitly, so don't change what the |
| 78 | user asked for. */ |
| 79 | return TRUE; |
| 80 | |
| 81 | if (info != NULL) |
| 82 | /* We're doing linking, so evalute SIZEOF_HEADERS as in a linker script. */ |
| 83 | sizeof_headers = bfd_sizeof_headers (abfd, info); |
| 84 | else |
| 85 | { |
| 86 | /* We're not doing linking, so this is objcopy or suchlike. |
| 87 | We just need to collect the size of the existing headers. */ |
| 88 | struct elf_segment_map *seg; |
| 89 | sizeof_headers = bed->s->sizeof_ehdr; |
| 90 | for (seg = *m; seg != NULL; seg = seg->next) |
| 91 | sizeof_headers += bed->s->sizeof_phdr; |
| 92 | } |
| 93 | |
| 94 | while (*m != NULL) |
| 95 | { |
| 96 | struct elf_segment_map *seg = *m; |
| 97 | |
| 98 | if (seg->p_type == PT_LOAD) |
| 99 | { |
| 100 | bfd_boolean executable = segment_executable (seg); |
| 101 | |
| 102 | if (executable |
| 103 | && seg->count > 0 |
| 104 | && seg->sections[0]->vma % bed->minpagesize == 0) |
| 105 | { |
| 106 | asection *lastsec = seg->sections[seg->count - 1]; |
| 107 | bfd_vma end = lastsec->vma + lastsec->size; |
| 108 | if (end % bed->minpagesize != 0) |
| 109 | { |
| 110 | /* This is an executable segment that starts on a page |
| 111 | boundary but does not end on a page boundary. Fill |
| 112 | it out to a whole page with code fill (the tail of |
| 113 | the segment will not be within any section). Thus |
| 114 | the entire code segment can be mapped from the file |
| 115 | as whole pages and that mapping will contain only |
| 116 | valid instructions. |
| 117 | |
| 118 | To accomplish this, we must fake out the code in |
| 119 | assign_file_positions_for_load_sections (elf.c) so |
| 120 | that it advances past the rest of the final page, |
| 121 | rather than trying to put the next (unaligned, or |
| 122 | unallocated) section. We do this by appending a |
| 123 | dummy section record to this element in the segment |
| 124 | map. No such output section ever actually exists, |
| 125 | but this gets the layout logic to advance the file |
| 126 | positions past this partial page. Since we are |
| 127 | lying to BFD like this, nothing will ever know to |
| 128 | write the section contents. So we do that by hand |
| 129 | after the fact, in nacl_final_write_processing, below. */ |
| 130 | |
| 131 | struct elf_segment_map *newseg; |
| 132 | asection *sec; |
| 133 | struct bfd_elf_section_data *secdata; |
| 134 | |
| 135 | BFD_ASSERT (!seg->p_size_valid); |
| 136 | |
| 137 | secdata = bfd_zalloc (abfd, sizeof *secdata); |
| 138 | if (secdata == NULL) |
| 139 | return FALSE; |
| 140 | |
| 141 | sec = bfd_zalloc (abfd, sizeof *sec); |
| 142 | if (sec == NULL) |
| 143 | return FALSE; |
| 144 | |
| 145 | /* Fill in only the fields that actually affect the logic |
| 146 | in assign_file_positions_for_load_sections. */ |
| 147 | sec->vma = end; |
| 148 | sec->lma = lastsec->lma + lastsec->size; |
| 149 | sec->size = bed->minpagesize - (end % bed->minpagesize); |
| 150 | sec->flags = (SEC_ALLOC | SEC_LOAD |
| 151 | | SEC_READONLY | SEC_CODE | SEC_LINKER_CREATED); |
| 152 | sec->used_by_bfd = secdata; |
| 153 | |
| 154 | secdata->this_hdr.sh_type = SHT_PROGBITS; |
| 155 | secdata->this_hdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; |
| 156 | secdata->this_hdr.sh_addr = sec->vma; |
| 157 | secdata->this_hdr.sh_size = sec->size; |
| 158 | |
| 159 | newseg = bfd_alloc (abfd, |
| 160 | sizeof *newseg + ((seg->count + 1) |
| 161 | * sizeof (asection *))); |
| 162 | if (newseg == NULL) |
| 163 | return FALSE; |
| 164 | memcpy (newseg, seg, |
| 165 | sizeof *newseg + (seg->count * sizeof (asection *))); |
| 166 | newseg->sections[newseg->count++] = sec; |
| 167 | *m = seg = newseg; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /* First, we're just finding the earliest PT_LOAD. |
| 172 | By the normal rules, this will be the lowest-addressed one. */ |
| 173 | if (first_load == NULL) |
| 174 | first_load = m; |
| 175 | |
| 176 | /* Now that we've noted the first PT_LOAD, we're looking for |
| 177 | the first non-executable PT_LOAD with a nonempty p_filesz. */ |
| 178 | else if (headers == NULL |
| 179 | && segment_eligible_for_headers (seg, bed->minpagesize, |
| 180 | sizeof_headers)) |
| 181 | headers = m; |
| 182 | } |
| 183 | m = &seg->next; |
| 184 | } |
| 185 | |
| 186 | if (headers != NULL) |
| 187 | { |
| 188 | struct elf_segment_map **last_load = NULL; |
| 189 | struct elf_segment_map *seg; |
| 190 | |
| 191 | m = first_load; |
| 192 | while ((seg = *m) != NULL) |
| 193 | { |
| 194 | if (seg->p_type == PT_LOAD) |
| 195 | { |
| 196 | /* Clear the flags on any previous segment that |
| 197 | included the file header and phdrs. */ |
| 198 | seg->includes_filehdr = 0; |
| 199 | seg->includes_phdrs = 0; |
| 200 | /* Also strip out empty segments. */ |
| 201 | if (seg->count == 0) |
| 202 | { |
| 203 | if (headers == &seg->next) |
| 204 | headers = m; |
| 205 | *m = seg->next; |
| 206 | continue; |
| 207 | } |
| 208 | last_load = m; |
| 209 | } |
| 210 | m = &seg->next; |
| 211 | } |
| 212 | |
| 213 | /* This segment will include those headers instead. */ |
| 214 | seg = *headers; |
| 215 | seg->includes_filehdr = 1; |
| 216 | seg->includes_phdrs = 1; |
| 217 | |
| 218 | if (last_load != NULL && first_load != last_load && first_load != headers) |
| 219 | { |
| 220 | /* Put the first PT_LOAD header last. */ |
| 221 | struct elf_segment_map *first = *first_load; |
| 222 | struct elf_segment_map *last = *last_load; |
| 223 | *first_load = first->next; |
| 224 | first->next = last->next; |
| 225 | last->next = first; |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | return TRUE; |
| 230 | } |
| 231 | |
| 232 | /* After nacl_modify_segment_map has done its work, the file layout has |
| 233 | been done as we wanted. But the PT_LOAD phdrs are no longer in the |
| 234 | proper order for the ELF rule that they must appear in ascending address |
| 235 | order. So find the two segments we swapped before, and swap them back. */ |
| 236 | bfd_boolean |
| 237 | nacl_modify_program_headers (bfd *abfd, struct bfd_link_info *info) |
| 238 | { |
| 239 | struct elf_segment_map **m = &elf_seg_map (abfd); |
| 240 | Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr; |
| 241 | Elf_Internal_Phdr *p = phdr; |
| 242 | |
| 243 | if (info != NULL && info->user_phdrs) |
| 244 | /* The linker script used PHDRS explicitly, so don't change what the |
| 245 | user asked for. */ |
| 246 | return TRUE; |
| 247 | |
| 248 | /* Find the PT_LOAD that contains the headers (should be the first). */ |
| 249 | while (*m != NULL) |
| 250 | { |
| 251 | if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr) |
| 252 | break; |
| 253 | |
| 254 | m = &(*m)->next; |
| 255 | ++p; |
| 256 | } |
| 257 | |
| 258 | if (*m != NULL) |
| 259 | { |
| 260 | struct elf_segment_map **first_load_seg = m; |
| 261 | Elf_Internal_Phdr *first_load_phdr = p; |
| 262 | struct elf_segment_map **next_load_seg = NULL; |
| 263 | Elf_Internal_Phdr *next_load_phdr = NULL; |
| 264 | |
| 265 | /* Now move past that first one and find the PT_LOAD that should be |
| 266 | before it by address order. */ |
| 267 | |
| 268 | m = &(*m)->next; |
| 269 | ++p; |
| 270 | |
| 271 | while (*m != NULL) |
| 272 | { |
| 273 | if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr) |
| 274 | { |
| 275 | next_load_seg = m; |
| 276 | next_load_phdr = p; |
| 277 | break; |
| 278 | } |
| 279 | |
| 280 | m = &(*m)->next; |
| 281 | ++p; |
| 282 | } |
| 283 | |
| 284 | /* Swap their positions in the segment_map back to how they used to be. |
| 285 | The phdrs have already been set up by now, so we have to slide up |
| 286 | the earlier ones to insert the one that should be first. */ |
| 287 | if (next_load_seg != NULL) |
| 288 | { |
| 289 | Elf_Internal_Phdr move_phdr; |
| 290 | struct elf_segment_map *first_seg = *first_load_seg; |
| 291 | struct elf_segment_map *next_seg = *next_load_seg; |
| 292 | struct elf_segment_map *first_next = first_seg->next; |
| 293 | struct elf_segment_map *next_next = next_seg->next; |
| 294 | |
| 295 | if (next_load_seg == &first_seg->next) |
| 296 | { |
| 297 | *first_load_seg = next_seg; |
| 298 | next_seg->next = first_seg; |
| 299 | first_seg->next = next_next; |
| 300 | } |
| 301 | else |
| 302 | { |
| 303 | *first_load_seg = first_next; |
| 304 | *next_load_seg = next_next; |
| 305 | |
| 306 | first_seg->next = *next_load_seg; |
| 307 | *next_load_seg = first_seg; |
| 308 | |
| 309 | next_seg->next = *first_load_seg; |
| 310 | *first_load_seg = next_seg; |
| 311 | } |
| 312 | |
| 313 | move_phdr = *next_load_phdr; |
| 314 | memmove (first_load_phdr + 1, first_load_phdr, |
| 315 | (next_load_phdr - first_load_phdr) * sizeof move_phdr); |
| 316 | *first_load_phdr = move_phdr; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | return TRUE; |
| 321 | } |
| 322 | |
| 323 | void |
| 324 | nacl_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED) |
| 325 | { |
| 326 | struct elf_segment_map *seg; |
| 327 | for (seg = elf_seg_map (abfd); seg != NULL; seg = seg->next) |
| 328 | if (seg->p_type == PT_LOAD |
| 329 | && seg->count > 1 |
| 330 | && seg->sections[seg->count - 1]->owner == NULL) |
| 331 | { |
| 332 | /* This is a fake section added in nacl_modify_segment_map, above. |
| 333 | It's not a real BFD section, so nothing wrote its contents. |
| 334 | Now write out its contents. */ |
| 335 | |
| 336 | asection *sec = seg->sections[seg->count - 1]; |
| 337 | char *fill; |
| 338 | |
| 339 | BFD_ASSERT (sec->flags & SEC_LINKER_CREATED); |
| 340 | BFD_ASSERT (sec->flags & SEC_CODE); |
| 341 | BFD_ASSERT (sec->size > 0); |
| 342 | |
| 343 | fill = abfd->arch_info->fill (sec->size, bfd_big_endian (abfd), TRUE); |
| 344 | |
| 345 | if (fill == NULL |
| 346 | || bfd_seek (abfd, sec->filepos, SEEK_SET) != 0 |
| 347 | || bfd_bwrite (fill, sec->size, abfd) != sec->size) |
| 348 | { |
| 349 | /* We don't have a proper way to report an error here. So |
| 350 | instead fudge things so that elf_write_shdrs_and_ehdr will |
| 351 | fail. */ |
| 352 | elf_elfheader (abfd)->e_shoff = (file_ptr) -1; |
| 353 | } |
| 354 | |
| 355 | free (fill); |
| 356 | } |
| 357 | } |