elf.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
elf.c (7711B)
---
1 // ELF program loader
2
3 #define _XOPEN_SOURCE 500
4
5 #include <unistd.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <assert.h>
12
13 #include "vx32.h"
14 #include "vx32impl.h"
15 #include "elf.h"
16 #include "words.h"
17
18 #define VX32_ARG_MAX 10*1024
19 #define VX32_STACK 64*1024
20 #define VXMEMSIZE (1<<30)
21
22 int vx_elfbigmem;
23
24 static int elfloader(vxproc *p,
25 ssize_t (*readcb)(void*, off_t, void*, size_t), void*,
26 const char *const *argv, const char *const *envp);
27
28 // From a file.
29
30 struct filedesc
31 {
32 int fd;
33 };
34
35 static ssize_t loadfilecb(void *cbdata, off_t offset, void *buf, size_t size)
36 {
37 ssize_t rc;
38 struct filedesc *desc;
39
40 desc = cbdata;
41 rc = pread(desc->fd, buf, size, offset);
42 return rc;
43 }
44
45 int vxproc_loadelffile(vxproc *p, const char *file,
46 const char *const *argv, const char *const *envp)
47 {
48 int fd, rc;
49 struct filedesc desc;
50
51 if ((fd = open(file, O_RDONLY)) < 0)
52 return -1;
53 desc.fd = fd;
54 rc = elfloader(p, loadfilecb, &desc, argv, envp);
55 close(fd);
56 return rc;
57 }
58
59 // From memory.
60
61 struct memdesc
62 {
63 const void *buf;
64 size_t size;
65 };
66
67 static ssize_t loadmemcb(void *cbdata, off_t offset, void *buf, size_t size)
68 {
69 struct memdesc *desc;
70
71 desc = cbdata;
72 if (offset >= desc->size || offset + size >= desc->size)
73 return 0;
74 memmove(buf, desc->buf + offset, size);
75 return size;
76 }
77
78 int vxproc_loadelfmem(vxproc *p, const void *exe, size_t size,
79 const char *const *argv, const char *const *envp)
80 {
81 struct memdesc desc;
82
83 desc.buf = exe;
84 desc.size = size;
85 return elfloader(p, loadmemcb, &desc, argv, envp);
86 }
87
88
89 // In general.
90
91 // Count number of args in array.
92 static int countargs(const char *const *argv)
93 {
94 int i;
95
96 for(i=0; argv[i]; i++)
97 ;
98 return i;
99 }
100
101 // Copy the strings from argv onto the stack, recording
102 // their guest address in gargv.
103 static int copystrings(uint8_t *base, uint32_t *espp, int argc, const char *const argv[], uint32_t *gargv)
104 {
105 int i;
106 uint32_t esp = *espp;
107 for (i = argc-1; i >= 0; i--) {
108 int len = strlen(argv[i]);
109 if (len + 4096 > esp)
110 return -1;
111 esp -= len+1;
112 memmove(base+esp, argv[i], len+1);
113 gargv[i] = esp;
114 }
115 *espp = esp;
116 return 0;
117 }
118
119 // Copy pre-translated pointer array into guest address space
120 static int copyptrs(uint8_t *base, uint32_t *espp, int argc, uint32_t *gargv)
121 {
122 uint32_t esp = *espp;
123 esp &= ~3; // align
124 if (argc * 4 + 4096 > esp)
125 return -1;
126 esp -= argc*4;
127 memmove(base+esp, gargv, argc*4);
128 *espp = esp;
129 return 0;
130 }
131
132 #define ELF_MAX_PH 32
133
134 static int elfloader(vxproc *proc,
135 ssize_t (*readcb)(void*, off_t, void*, size_t),
136 void *cbdata,
137 const char *const *argv, const char *const *envp)
138 {
139 vxmem *mem;
140 int i;
141 size_t size;
142 ssize_t act;
143 struct Proghdr ph[ELF_MAX_PH];
144 vxmmap *mm;
145 static const char *null;
146
147 if (argv == NULL)
148 argv = &null;
149 if (envp == NULL)
150 envp = &null;
151
152 size = 4096;
153 // For "big mem" we need more than 1/2 GB, but 64-bit Linux
154 // has only about 1 GB of address space to give out with MAP_32BIT,
155 // and we've used up some of it for the vxemu structure.
156 // Used to ask for (1<<30) - (1<<24), which should be close enough to 1GB
157 // to run the SPEC programs but leave enough for things like vxemu.
158 // On Ubuntu 8.10, I get intermittent ouf of memory errors from this
159 // mmap, so back off to 1<<29.
160 if (vx_elfbigmem)
161 size = (1<<29);
162
163 mm = NULL;
164
165 if ((mem = vxmem_chunk_new(size)) == NULL)
166 return -1;
167
168 // Read and check the ELF header
169 struct Elf32 h;
170 act = readcb(cbdata, 0, &h, sizeof(h));
171 if (act < 0)
172 goto error;
173 if (act < sizeof(h)) {
174 noexec:
175 errno = ENOEXEC;
176 goto error;
177 }
178 if (ltoh32(h.e_magic) != ELF_MAGIC)
179 goto noexec;
180
181 // Read the program header table
182 off_t phoff = ltoh32(h.e_phoff);
183 size_t phnum = ltoh16(h.e_phnum);
184 if (phnum <= 0 || phnum > ELF_MAX_PH) // arbitrary limit for security
185 goto noexec;
186
187 act = readcb(cbdata, phoff, ph, phnum * sizeof ph[0]);
188 if (act < 0)
189 goto error;
190 if (act < phnum * sizeof ph[0])
191 goto noexec;
192
193 // Load each program segment
194 size_t stackhi = VXMEMSIZE;
195 size_t addrhi = 0;
196 for (i = 0; i < phnum; i++) {
197 const struct Proghdr *p = &ph[i];
198 if (ltoh32(p->p_type) != ELF_PROG_LOAD)
199 continue;
200
201 // Validate the program segment
202 off_t offset = ltoh32(p->p_offset);
203 size_t filesz = ltoh32(p->p_filesz);
204 size_t va = ltoh32(p->p_va);
205 size_t memsz = ltoh32(p->p_memsz);
206 if (filesz > memsz)
207 goto noexec;
208
209 // Validate the memory page region the segment loads into
210 size_t memlo = VXPAGETRUNC(va);
211 size_t memhi = VXPAGEROUND(va + memsz);
212 if (memlo > VXMEMSIZE || memhi > VXMEMSIZE || memhi < memlo)
213 goto noexec;
214
215 // Make sure the VX process is big enough, and mapped
216 if (size < memhi) {
217 if (mm) {
218 vxmem_unmap(mem, mm);
219 mm = NULL;
220 }
221 if (vxmem_resize(mem, memhi) < 0)
222 goto error;
223 size = memhi;
224 }
225
226 if (mm == NULL) {
227 mm = vxmem_map(mem, 0);
228 if (mm == NULL)
229 goto error;
230 }
231
232 // Temporarily give ourselves write permissions
233 // on the segment in order to load it.
234 if (vxmem_setperm(mem, memlo, memhi - memlo,
235 VXPERM_READ | VXPERM_WRITE) < 0)
236 return -1;
237
238 // Load the segment.
239 // Any bss portion is already cleared automatically
240 // by virtue of the vxproc_clear() above.
241 act = readcb(cbdata, offset, mm->base + va, filesz);
242 if (act < 0)
243 return -1;
244 if (act < filesz)
245 goto noexec;
246
247 // Set permissions appropriately on the segment's pages
248 int flags = ltoh32(p->p_flags);
249 int perm = 0;
250 switch (flags & (ELF_PROG_FLAG_READ | ELF_PROG_FLAG_WRITE |
251 ELF_PROG_FLAG_EXEC)) {
252 case ELF_PROG_FLAG_READ:
253 perm = VXPERM_READ;
254 break;
255 case ELF_PROG_FLAG_READ | ELF_PROG_FLAG_WRITE:
256 perm = VXPERM_READ | VXPERM_WRITE;
257 break;
258 case ELF_PROG_FLAG_READ | ELF_PROG_FLAG_EXEC:
259 perm = VXPERM_READ | VXPERM_EXEC;
260 break;
261 default:
262 goto noexec; // invalid perms
263 }
264 if (vxmem_setperm(mem, memlo, memhi - memlo, perm) < 0)
265 goto error;
266
267 // Find the lowest-used va, for locating the stack
268 if (va < stackhi)
269 stackhi = va;
270
271 // Find the highest va too
272 if (memhi > addrhi)
273 addrhi = memhi;
274 }
275
276 if (size > addrhi)
277 vxmem_setperm(mem, addrhi, size - addrhi, VXPERM_READ|VXPERM_WRITE);
278
279 if (mm == NULL) {
280 mm = vxmem_map(mem, 0);
281 if (mm == NULL)
282 goto error;
283 }
284
285 // Set up the process's stack,
286 // growing downward from the executable's base load address.
287 // Initially we enable read/write access on 64K of stack,
288 // but the process is free to change that if it wants a bigger stack.
289 stackhi = VXPAGETRUNC(stackhi);
290 if (stackhi < VX32_STACK)
291 goto noexec;
292 if (vxmem_setperm(mem, stackhi - VX32_STACK, VX32_STACK,
293 VXPERM_READ | VXPERM_WRITE) < 0)
294 goto error;
295 proc->cpu->reg[ESP] = stackhi;
296
297 // Push the argument and environment arrays on the stack.
298 uint32_t esp = stackhi;
299 uint32_t argc;
300 uint32_t envc;
301 argc = countargs(argv);
302 envc = countargs(envp);
303 uint32_t *argvenv = malloc((argc+1+envc+1)*sizeof argvenv[0]);
304 if (argv == NULL)
305 goto error;
306 if (copystrings(mm->base, &esp, envc, envp, argvenv+argc+1) < 0 ||
307 copystrings(mm->base, &esp, argc, argv, argvenv) < 0) {
308 free(argvenv);
309 goto error;
310 }
311 if (copyptrs(mm->base, &esp, argc+1+envc+1, argvenv) < 0) {
312 free(argvenv);
313 goto error;
314 }
315
316 // Set up stack just like Linux: argc, then argv pointers begin, then env pointers.
317 esp -= 4;
318 *(uint32_t*)(mm->base+esp) = argc;
319
320 if (proc->mem)
321 vxmem_free(proc->mem);
322 proc->mem = mem;
323
324 // Set up the process's initial register state
325 for (i = 0; i < 8; i++)
326 proc->cpu->reg[i] = 0;
327 proc->cpu->reg[ESP] = esp;
328 proc->cpu->eflags = 0;
329 proc->cpu->eip = ltoh32(h.e_entry);
330
331 return 0;
332
333 error:
334 if (mm)
335 vxmem_unmap(mem, mm);
336 vxmem_free(mem);
337 return -1;
338 }
339