Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (c) 2014 The Native Client Authors. All rights reserved. | |
| 3 * Use of this source code is governed by a BSD-style license that can be | |
| 4 * found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 #include <elf.h> | |
| 8 #include <errno.h> | |
| 9 #include <fcntl.h> | |
| 10 #include <link.h> | |
| 11 #include <stdint.h> | |
| 12 #include <stdio.h> | |
| 13 #include <stdlib.h> | |
| 14 #include <string.h> | |
| 15 #include <sys/mman.h> | |
| 16 #include <unistd.h> | |
| 17 | |
| 18 #include "native_client/src/nonsfi/irt/irt_interfaces.h" | |
| 19 #include "native_client/src/shared/platform/nacl_log.h" | |
| 20 #include "native_client/src/trusted/service_runtime/nacl_config.h" | |
| 21 | |
| 22 | |
| 23 #define PAGE_SIZE 0x1000 | |
|
bradn
2014/05/02 17:39:11
There's an odd irony in baking this in given it wo
Mark Seaborn
2014/05/02 18:18:50
Since you mention it, I've added a comment.
| |
| 24 #define PAGE_MASK (PAGE_SIZE - 1) | |
| 25 #define MAX_PHNUM 128 | |
| 26 | |
| 27 static uintptr_t PageSizeRoundDown(uintptr_t addr) { | |
| 28 return addr & ~PAGE_MASK; | |
| 29 } | |
| 30 | |
| 31 static uintptr_t PageSizeRoundUp(uintptr_t addr) { | |
| 32 return PageSizeRoundDown(addr + PAGE_SIZE - 1); | |
| 33 } | |
| 34 | |
| 35 static int ElfFlagsToMmapFlags(int pflags) { | |
| 36 return ((pflags & PF_X) != 0 ? PROT_EXEC : 0) | | |
| 37 ((pflags & PF_R) != 0 ? PROT_READ : 0) | | |
| 38 ((pflags & PF_W) != 0 ? PROT_WRITE : 0); | |
| 39 } | |
| 40 | |
| 41 static void CheckElfHeaders(ElfW(Ehdr) *ehdr) { | |
| 42 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { | |
| 43 NaClLog(LOG_FATAL, "Not an ELF file: no ELF header\n"); | |
| 44 } | |
| 45 if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { | |
| 46 NaClLog(LOG_FATAL, "Unexpected ELF class: not ELFCLASS32\n"); | |
| 47 } | |
| 48 if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { | |
| 49 NaClLog(LOG_FATAL, "Not a little-endian ELF file\n"); | |
| 50 } | |
| 51 if (ehdr->e_type != ET_DYN) { | |
| 52 NaClLog(LOG_FATAL, "Not a relocatable ELF object (not ET_DYN)\n"); | |
| 53 } | |
| 54 if (ehdr->e_machine != NACL_ELF_E_MACHINE) { | |
| 55 NaClLog(LOG_FATAL, "Unexpected ELF e_machine field\n"); | |
| 56 } | |
| 57 if (ehdr->e_version != EV_CURRENT) { | |
| 58 NaClLog(LOG_FATAL, "Unexpected ELF e_version field\n"); | |
| 59 } | |
| 60 if (ehdr->e_ehsize != sizeof(*ehdr)) { | |
| 61 NaClLog(LOG_FATAL, "Unexpected ELF e_ehsize field\n"); | |
| 62 } | |
| 63 if (ehdr->e_phentsize != sizeof(ElfW(Phdr))) { | |
| 64 NaClLog(LOG_FATAL, "Unexpected ELF e_phentsize field\n"); | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 static uintptr_t LoadElfFile(const char *filename) { | |
| 69 int fd = open(filename, O_RDONLY); | |
| 70 if (fd < 0) { | |
| 71 NaClLog(LOG_FATAL, "Failed to open %s: %s\n", filename, strerror(errno)); | |
| 72 } | |
| 73 | |
| 74 /* Read ELF file headers. */ | |
| 75 ElfW(Ehdr) ehdr; | |
| 76 ssize_t bytes_read = pread(fd, &ehdr, sizeof(ehdr), 0); | |
| 77 if (bytes_read != sizeof(ehdr)) { | |
| 78 NaClLog(LOG_FATAL, "Failed to read ELF file headers\n"); | |
| 79 } | |
| 80 CheckElfHeaders(&ehdr); | |
| 81 | |
| 82 /* Read ELF program headers. */ | |
| 83 if (ehdr.e_phnum > MAX_PHNUM) { | |
| 84 NaClLog(LOG_FATAL, "ELF file has too many program headers\n"); | |
| 85 } | |
| 86 ElfW(Phdr) phdr[MAX_PHNUM]; | |
| 87 ssize_t phdrs_size = sizeof(phdr[0]) * ehdr.e_phnum; | |
| 88 bytes_read = pread(fd, phdr, phdrs_size, ehdr.e_phoff); | |
| 89 if (bytes_read != phdrs_size) { | |
| 90 NaClLog(LOG_FATAL, "Failed to read ELF program headers\n"); | |
| 91 } | |
| 92 | |
| 93 /* Find the first PT_LOAD segment. */ | |
| 94 size_t i = 0; | |
|
bradn
2014/05/02 17:39:11
Given you're using i beyond the loop, maybe a more
Mark Seaborn
2014/05/02 18:18:50
Done.
| |
| 95 while (i < ehdr.e_phnum && phdr[i].p_type != PT_LOAD) | |
| 96 ++i; | |
| 97 if (i == ehdr.e_phnum) { | |
| 98 NaClLog(LOG_FATAL, "ELF file has no PT_LOAD header\n"); | |
| 99 } | |
| 100 | |
| 101 /* | |
| 102 * ELF requires that PT_LOAD segments be in ascending order of p_vaddr. | |
| 103 * Find the last one to calculate the whole address span of the image. | |
| 104 */ | |
| 105 ElfW(Phdr) *first_load = &phdr[i]; | |
| 106 ElfW(Phdr) *last_load = &phdr[ehdr.e_phnum - 1]; | |
| 107 while (last_load > first_load && last_load->p_type != PT_LOAD) | |
| 108 --last_load; | |
| 109 | |
| 110 if (first_load->p_vaddr != 0) { | |
| 111 NaClLog(LOG_FATAL, "First PT_LOAD segment's load address is not 0\n"); | |
| 112 } | |
| 113 size_t span = last_load->p_vaddr + last_load->p_memsz; | |
| 114 | |
| 115 /* Reserve address space. */ | |
| 116 void *mapping = mmap(NULL, span, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, | |
| 117 -1, 0); | |
| 118 if (mapping == MAP_FAILED) { | |
| 119 NaClLog(LOG_FATAL, "Failed to reserve address space for executable\n"); | |
| 120 } | |
| 121 uintptr_t load_bias = (uintptr_t) mapping; | |
| 122 | |
| 123 uintptr_t prev_segment_end = 0; | |
| 124 ElfW(Phdr) *ph; | |
| 125 for (ph = first_load; ph <= last_load; ++ph) { | |
| 126 if (ph->p_type == PT_LOAD) { | |
|
bradn
2014/05/02 17:39:11
might be more readable with continue, to avoid the
Mark Seaborn
2014/05/02 18:18:50
Done.
| |
| 127 int prot = ElfFlagsToMmapFlags(ph->p_flags); | |
| 128 uintptr_t segment_start = PageSizeRoundDown(ph->p_vaddr); | |
| 129 uintptr_t segment_end = PageSizeRoundUp(ph->p_vaddr + ph->p_memsz); | |
| 130 if (segment_start < prev_segment_end) { | |
| 131 NaClLog(LOG_FATAL, "PT_LOAD segments overlap or are not sorted\n"); | |
| 132 } | |
| 133 prev_segment_end = segment_end; | |
| 134 void *segment_addr = (void *) (load_bias + segment_start); | |
| 135 void *map_result = mmap((void *) segment_addr, | |
| 136 segment_end - segment_start, | |
| 137 prot, MAP_PRIVATE | MAP_FIXED, fd, | |
| 138 PageSizeRoundDown(ph->p_offset)); | |
| 139 if (map_result != segment_addr) { | |
| 140 NaClLog(LOG_FATAL, "Failed to map ELF segment\n"); | |
| 141 } | |
| 142 | |
| 143 /* Handle the BSS. */ | |
| 144 if (ph->p_memsz < ph->p_filesz) { | |
| 145 NaClLog(LOG_FATAL, "Bad ELF segment: p_memsz < p_filesz\n"); | |
| 146 } | |
| 147 if (ph->p_memsz > ph->p_filesz) { | |
| 148 if ((ph->p_flags & PF_W) == 0) { | |
| 149 NaClLog(LOG_FATAL, | |
| 150 "Bad ELF segment: non-writable segment with BSS\n"); | |
| 151 } | |
| 152 | |
| 153 uintptr_t bss_start = ph->p_vaddr + ph->p_filesz; | |
| 154 uintptr_t bss_map_start = PageSizeRoundUp(bss_start); | |
| 155 /* | |
| 156 * Zero the BSS to the end of the page. | |
| 157 * | |
| 158 * Zeroing beyond p_memsz might be more than is necessary for | |
| 159 * Non-SFI NaCl. On Linux, programs such as ld.so use the rest of | |
| 160 * the page, after p_memsz, as part of the brk() heap and assume | |
| 161 * that it has been zeroed. Non-SFI NaCl does not provide a brk() | |
| 162 * heap, though. However, zeroing to the end of the page is simple | |
| 163 * enough, and it's consistent with the case in additional pages | |
| 164 * must be mapped, which will all be fully zeroed. | |
| 165 */ | |
| 166 memset((void *) (load_bias + bss_start), 0, bss_map_start - bss_start); | |
| 167 | |
| 168 if (bss_map_start < segment_end) { | |
| 169 void *map_addr = (void *) (load_bias + bss_map_start); | |
| 170 map_result = mmap(map_addr, segment_end - bss_map_start, | |
| 171 prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, | |
| 172 -1, 0); | |
| 173 if (map_result != map_addr) { | |
| 174 NaClLog(LOG_FATAL, "Failed to map BSS for ELF segment\n"); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 if (close(fd) != 0) { | |
| 182 NaClLog(LOG_FATAL, "close() failed\n"); | |
| 183 } | |
| 184 | |
| 185 return load_bias + ehdr.e_entry; | |
|
bradn
2014/05/02 17:39:11
Check it's in one of the sections?
Mark Seaborn
2014/05/02 18:18:50
OK. I've even checked that it's in an executable
| |
| 186 } | |
| 187 | |
| 188 int main(int argc, char **argv, char **environ) { | |
| 189 if (argc < 2) { | |
| 190 fprintf(stderr, "Usage: %s <executable> <args...>\n", argv[0]); | |
| 191 return 1; | |
| 192 } | |
| 193 const char *nexe_filename = argv[1]; | |
| 194 uintptr_t entry = LoadElfFile(nexe_filename); | |
| 195 return nacl_irt_nonsfi_entry(argc, argv, environ, (nacl_entry_func_t) entry); | |
| 196 } | |
| OLD | NEW |