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 |