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