blob: c79ac09d334d32092f1cde462bea434f853801cf [file] [log] [blame]
jseward2886b0e2004-01-04 03:46:11 +00001
nethercotebb1c9912004-01-04 16:43:23 +00002/*--------------------------------------------------------------------*/
nethercote107e1c02004-10-13 17:55:31 +00003/*--- User-mode execve(), and other stuff shared between stage1 ---*/
4/*--- and stage2. ume.c ---*/
nethercotebb1c9912004-01-04 16:43:23 +00005/*--------------------------------------------------------------------*/
6
jseward2886b0e2004-01-04 03:46:11 +00007/*
njnb9c427c2004-12-01 14:14:42 +00008 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
jseward2886b0e2004-01-04 03:46:11 +000010
njn53612422005-03-12 16:22:54 +000011 Copyright (C) 2000-2005 Julian Seward
jseward2886b0e2004-01-04 03:46:11 +000012 jseward@acm.org
13
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
29 The GNU General Public License is contained in the file COPYING.
30*/
31
fitzhardinge7e343cd2003-12-16 02:14:00 +000032
33#define _GNU_SOURCE
34#define _FILE_OFFSET_BITS 64
35
nethercotef1e5e152004-09-01 23:58:16 +000036#include "core.h"
fitzhardinge7e343cd2003-12-16 02:14:00 +000037
fitzhardinge7e343cd2003-12-16 02:14:00 +000038#include <sys/mman.h>
39#include <fcntl.h>
40#include <errno.h>
fitzhardinge7e343cd2003-12-16 02:14:00 +000041#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <unistd.h>
fitzhardinge7e343cd2003-12-16 02:14:00 +000045#include <assert.h>
46
47#include "ume.h"
48
nethercote3f458152004-11-01 18:42:23 +000049#if ELFSZ == 64
50#define ESZ(x) Elf64_##x
51#elif ELFSZ == 32
52#define ESZ(x) Elf32_##x
53#else
54#error ELFSZ needs to ==32 or ==64
55#endif
56
nethercote1fe54502004-07-26 15:28:33 +000057struct elfinfo
58{
59 ESZ(Ehdr) e;
60 ESZ(Phdr) *p;
61 int fd;
62};
63
nethercote30d37842004-07-26 10:05:55 +000064static void check_mmap(void* res, void* base, int len)
nethercotebfed1c82004-07-17 12:57:44 +000065{
66 if ((void*)-1 == res) {
nethercote969ecf12004-10-13 17:29:01 +000067 fprintf(stderr, "valgrind: mmap(%p, %d) failed in UME.\n", base, len);
nethercotebfed1c82004-07-17 12:57:44 +000068 exit(1);
69 }
70}
71
nethercote31779c72004-07-30 21:50:15 +000072// 'extra' allows the caller to pass in extra args to 'fn', like free
73// variables to a closure.
74void foreach_map(int (*fn)(char *start, char *end,
fitzhardinge7e343cd2003-12-16 02:14:00 +000075 const char *perm, off_t offset,
nethercote31779c72004-07-30 21:50:15 +000076 int maj, int min, int ino, void* extra),
77 void* extra)
fitzhardinge7e343cd2003-12-16 02:14:00 +000078{
79 static char buf[10240];
80 char *bufptr = buf;
81 int ret, fd;
82
83 fd = open("/proc/self/maps", O_RDONLY);
84
85 if (fd == -1) {
86 perror("open /proc/self/maps");
87 return;
88 }
89
90 ret = read(fd, buf, sizeof(buf));
91
92 if (ret == -1) {
93 perror("read /proc/self/maps");
94 close(fd);
95 return;
96 }
97 close(fd);
98
99 if (ret == sizeof(buf)) {
100 fprintf(stderr, "buf too small\n");
101 return;
102 }
103
104 while(bufptr && bufptr < buf+ret) {
105 char perm[5];
njnc6168192004-11-29 13:54:10 +0000106 ULong offset;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000107 int maj, min;
108 int ino;
109 void *segstart, *segend;
110
nethercote545fe672004-11-01 16:52:43 +0000111 sscanf(bufptr, "%p-%p %s %llx %x:%x %d",
fitzhardinge7e343cd2003-12-16 02:14:00 +0000112 &segstart, &segend, perm, &offset, &maj, &min, &ino);
113 bufptr = strchr(bufptr, '\n');
114 if (bufptr != NULL)
115 bufptr++; /* skip \n */
116
nethercote31779c72004-07-30 21:50:15 +0000117 if (!(*fn)(segstart, segend, perm, offset, maj, min, ino, extra))
fitzhardinge7e343cd2003-12-16 02:14:00 +0000118 break;
119 }
120}
121
nethercote31779c72004-07-30 21:50:15 +0000122/*------------------------------------------------------------*/
123/*--- Finding auxv on the stack ---*/
124/*------------------------------------------------------------*/
fitzhardinge7e343cd2003-12-16 02:14:00 +0000125
nethercoteebf1d862004-11-01 18:22:05 +0000126struct ume_auxv *find_auxv(UWord* sp)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000127{
nethercoteebf1d862004-11-01 18:22:05 +0000128 sp++; // skip argc (Nb: is word-sized, not int-sized!)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000129
nethercoteebf1d862004-11-01 18:22:05 +0000130 while (*sp != 0) // skip argv
131 sp++;
132 sp++;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000133
nethercoteebf1d862004-11-01 18:22:05 +0000134 while (*sp != 0) // skip env
135 sp++;
136 sp++;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000137
nethercoteebf1d862004-11-01 18:22:05 +0000138 return (struct ume_auxv *)sp;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000139}
140
nethercote31779c72004-07-30 21:50:15 +0000141/*------------------------------------------------------------*/
142/*--- Loading ELF files ---*/
143/*------------------------------------------------------------*/
fitzhardinge7e343cd2003-12-16 02:14:00 +0000144
sewardj2c5ffbe2005-03-12 13:32:06 +0000145static
fitzhardinge7e343cd2003-12-16 02:14:00 +0000146struct elfinfo *readelf(int fd, const char *filename)
147{
148 struct elfinfo *e = malloc(sizeof(*e));
149 int phsz;
150
nethercote7c018f42004-07-17 16:40:50 +0000151 assert(e);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000152 e->fd = fd;
153
154 if (pread(fd, &e->e, sizeof(e->e), 0) != sizeof(e->e)) {
sewardj703eec52005-04-07 02:24:23 +0000155 fprintf(stderr, "valgrind: %s: can't read ELF header: %s\n",
fitzhardinge7e343cd2003-12-16 02:14:00 +0000156 filename, strerror(errno));
157 return NULL;
158 }
159
160 if (memcmp(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) {
sewardj703eec52005-04-07 02:24:23 +0000161 fprintf(stderr, "valgrind: %s: bad ELF magic number\n", filename);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000162 return NULL;
163 }
njn35172bc2005-03-26 00:04:03 +0000164 if (e->e.e_ident[EI_CLASS] != VGA_ELF_CLASS) {
sewardj703eec52005-04-07 02:24:23 +0000165 fprintf(stderr,
166 "valgrind: wrong ELF executable class "
167 "(eg. 32-bit instead of 64-bit)\n");
fitzhardinge7e343cd2003-12-16 02:14:00 +0000168 return NULL;
169 }
njn35172bc2005-03-26 00:04:03 +0000170 if (e->e.e_ident[EI_DATA] != VGA_ELF_ENDIANNESS) {
sewardj703eec52005-04-07 02:24:23 +0000171 fprintf(stderr, "valgrind: executable has wrong endian-ness\n");
fitzhardinge7e343cd2003-12-16 02:14:00 +0000172 return NULL;
173 }
174 if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) {
sewardj703eec52005-04-07 02:24:23 +0000175 fprintf(stderr, "valgrind: this is not an executable\n");
fitzhardinge7e343cd2003-12-16 02:14:00 +0000176 return NULL;
177 }
178
njn35172bc2005-03-26 00:04:03 +0000179 if (e->e.e_machine != VGA_ELF_MACHINE) {
sewardj703eec52005-04-07 02:24:23 +0000180 fprintf(stderr, "valgrind: executable is not for "
181 "this architecture\n");
fitzhardinge7e343cd2003-12-16 02:14:00 +0000182 return NULL;
183 }
184
185 if (e->e.e_phentsize != sizeof(ESZ(Phdr))) {
sewardj703eec52005-04-07 02:24:23 +0000186 fprintf(stderr, "valgrind: sizeof ELF Phdr wrong\n");
fitzhardinge7e343cd2003-12-16 02:14:00 +0000187 return NULL;
188 }
189
190 phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum;
191 e->p = malloc(phsz);
nethercote7c018f42004-07-17 16:40:50 +0000192 assert(e->p);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000193
194 if (pread(fd, e->p, phsz, e->e.e_phoff) != phsz) {
nethercote08eaff32004-07-22 12:41:12 +0000195 fprintf(stderr, "valgrind: can't read phdr: %s\n", strerror(errno));
fitzhardinge7e343cd2003-12-16 02:14:00 +0000196 return NULL;
197 }
198
199 return e;
200}
201
fitzhardinge7e343cd2003-12-16 02:14:00 +0000202/* Map an ELF file. Returns the brk address. */
sewardj2c5ffbe2005-03-12 13:32:06 +0000203static
fitzhardingeb50068f2004-02-24 23:42:55 +0000204ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000205{
206 int i;
nethercotebfed1c82004-07-17 12:57:44 +0000207 void* res;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000208 ESZ(Addr) elfbrk = 0;
209
210 for(i = 0; i < e->e.e_phnum; i++) {
211 ESZ(Phdr) *ph = &e->p[i];
212 ESZ(Addr) addr, brkaddr;
213 ESZ(Word) memsz;
214
215 if (ph->p_type != PT_LOAD)
216 continue;
217
nethercote6c3cf412004-10-26 13:32:11 +0000218 addr = ph->p_vaddr+base;
219 memsz = ph->p_memsz;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000220 brkaddr = addr+memsz;
221
222 if (brkaddr > elfbrk)
223 elfbrk = brkaddr;
224 }
225
fitzhardinge7e343cd2003-12-16 02:14:00 +0000226 for(i = 0; i < e->e.e_phnum; i++) {
227 ESZ(Phdr) *ph = &e->p[i];
228 ESZ(Addr) addr, bss, brkaddr;
229 ESZ(Off) off;
230 ESZ(Word) filesz;
231 ESZ(Word) memsz;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000232 unsigned prot = 0;
233
234 if (ph->p_type != PT_LOAD)
235 continue;
236
nethercote6c3cf412004-10-26 13:32:11 +0000237 if (ph->p_flags & PF_X) prot |= PROT_EXEC;
238 if (ph->p_flags & PF_W) prot |= PROT_WRITE;
239 if (ph->p_flags & PF_R) prot |= PROT_READ;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000240
nethercote6c3cf412004-10-26 13:32:11 +0000241 addr = ph->p_vaddr+base;
242 off = ph->p_offset;
243 filesz = ph->p_filesz;
244 bss = addr+filesz;
245 memsz = ph->p_memsz;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000246 brkaddr = addr+memsz;
247
njnf7fbe6c2004-11-30 11:40:24 +0000248 // Tom says: In the following, do what the Linux kernel does and only
249 // map the pages that are required instead of rounding everything to
250 // the specified alignment (ph->p_align). (AMD64 doesn't work if you
251 // use ph->p_align -- part of stage2's memory gets trashed somehow.)
njn098da062005-03-26 16:22:43 +0000252 //
253 // The condition handles the case of a zero-length segment.
254 if (PGROUNDUP(bss)-PGROUNDDN(addr) > 0) {
255 res = mmap((char *)PGROUNDDN(addr), PGROUNDUP(bss)-PGROUNDDN(addr),
256 prot, MAP_FIXED|MAP_PRIVATE, e->fd, PGROUNDDN(off));
257 check_mmap(res, (char*)PGROUNDDN(addr),
258 PGROUNDUP(bss)-PGROUNDDN(addr));
259 }
njnf7fbe6c2004-11-30 11:40:24 +0000260
261 // if memsz > filesz, fill the remainder with zeroed pages
fitzhardinge7e343cd2003-12-16 02:14:00 +0000262 if (memsz > filesz) {
263 UInt bytes;
264
njnf7fbe6c2004-11-30 11:40:24 +0000265 bytes = PGROUNDUP(brkaddr)-PGROUNDUP(bss);
nethercotebfed1c82004-07-17 12:57:44 +0000266 if (bytes > 0) {
njnf7fbe6c2004-11-30 11:40:24 +0000267 res = mmap((char *)PGROUNDUP(bss), bytes,
nethercotebfed1c82004-07-17 12:57:44 +0000268 prot, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
njnf7fbe6c2004-11-30 11:40:24 +0000269 check_mmap(res, (char*)PGROUNDUP(bss), bytes);
nethercotebfed1c82004-07-17 12:57:44 +0000270 }
fitzhardinge7e343cd2003-12-16 02:14:00 +0000271
nethercote73b526f2004-10-31 18:48:21 +0000272 bytes = bss & (VKI_PAGE_SIZE - 1);
njn098da062005-03-26 16:22:43 +0000273
274 // The 'prot' condition allows for a read-only bss
275 if ((prot & PROT_WRITE) && (bytes > 0)) {
nethercote73b526f2004-10-31 18:48:21 +0000276 bytes = VKI_PAGE_SIZE - bytes;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000277 memset((char *)bss, 0, bytes);
278 }
279 }
280 }
281
282 return elfbrk;
283}
284
nethercote31779c72004-07-30 21:50:15 +0000285// Forward declaration.
fitzhardinge7e343cd2003-12-16 02:14:00 +0000286static int do_exec_inner(const char *exe, struct exeinfo *info);
287
fitzhardinge7e343cd2003-12-16 02:14:00 +0000288static int match_ELF(const char *hdr, int len)
289{
290 ESZ(Ehdr) *e = (ESZ(Ehdr) *)hdr;
291 return (len > sizeof(*e)) && memcmp(&e->e_ident[0], ELFMAG, SELFMAG) == 0;
292}
293
nethercote31779c72004-07-30 21:50:15 +0000294static int load_ELF(char *hdr, int len, int fd, const char *name,
295 struct exeinfo *info)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000296{
297 struct elfinfo *e;
298 struct elfinfo *interp = NULL;
fitzhardingeca9bd9c2004-09-08 20:05:02 +0000299 ESZ(Addr) minaddr = ~0; /* lowest mapped address */
300 ESZ(Addr) maxaddr = 0; /* highest mapped address */
301 ESZ(Addr) interp_addr = 0; /* interpreter (ld.so) address */
302 ESZ(Word) interp_size = 0; /* interpreter size */
nethercote73b526f2004-10-31 18:48:21 +0000303 ESZ(Word) interp_align = VKI_PAGE_SIZE;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000304 int i;
305 void *entry;
nethercote7f390022004-10-25 17:18:24 +0000306 ESZ(Addr) ebase = 0;
307
308#ifdef HAVE_PIE
309 ebase = info->exe_base;
310#endif
fitzhardinge7e343cd2003-12-16 02:14:00 +0000311
312 e = readelf(fd, name);
313
314 if (e == NULL)
315 return ENOEXEC;
316
317 info->phnum = e->e.e_phnum;
nethercote7f390022004-10-25 17:18:24 +0000318 info->entry = e->e.e_entry + ebase;
sewardjb5f6f512005-03-10 23:59:00 +0000319 info->phdr = 0;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000320
321 for(i = 0; i < e->e.e_phnum; i++) {
322 ESZ(Phdr) *ph = &e->p[i];
323
324 switch(ph->p_type) {
325 case PT_PHDR:
nethercote7f390022004-10-25 17:18:24 +0000326 info->phdr = ph->p_vaddr + ebase;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000327 break;
328
329 case PT_LOAD:
330 if (ph->p_vaddr < minaddr)
331 minaddr = ph->p_vaddr;
332 if (ph->p_vaddr+ph->p_memsz > maxaddr)
333 maxaddr = ph->p_vaddr+ph->p_memsz;
334 break;
335
336 case PT_INTERP: {
337 char *buf = malloc(ph->p_filesz+1);
338 int j;
339 int intfd;
340 int baseaddr_set;
341
nethercote7c018f42004-07-17 16:40:50 +0000342 assert(buf);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000343 pread(fd, buf, ph->p_filesz, ph->p_offset);
344 buf[ph->p_filesz] = '\0';
345
346 intfd = open(buf, O_RDONLY);
347 if (intfd == -1) {
348 perror("open interp");
349 exit(1);
350 }
351
352 interp = readelf(intfd, buf);
353 if (interp == NULL) {
354 fprintf(stderr, "Can't read interpreter\n");
355 return 1;
356 }
357 free(buf);
358
359 baseaddr_set = 0;
360 for(j = 0; j < interp->e.e_phnum; j++) {
361 ESZ(Phdr) *iph = &interp->p[j];
362 ESZ(Addr) end;
363
364 if (iph->p_type != PT_LOAD)
365 continue;
366
367 if (!baseaddr_set) {
nethercote7f390022004-10-25 17:18:24 +0000368 interp_addr = iph->p_vaddr;
369 interp_align = iph->p_align;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000370 baseaddr_set = 1;
371 }
372
373 /* assumes that all segments in the interp are close */
374 end = (iph->p_vaddr - interp_addr) + iph->p_memsz;
375
376 if (end > interp_size)
377 interp_size = end;
378 }
379 break;
nethercoteb24cbc82004-09-03 23:25:33 +0000380
381 default:
382 // do nothing
383 break;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000384 }
385 }
386 }
387
sewardjb5f6f512005-03-10 23:59:00 +0000388 if (info->phdr == 0)
389 info->phdr = minaddr + e->e.e_phoff;
390
fitzhardinge7e343cd2003-12-16 02:14:00 +0000391 if (info->exe_base != info->exe_end) {
392 if (minaddr >= maxaddr ||
nethercote7f390022004-10-25 17:18:24 +0000393 (minaddr + ebase < info->exe_base ||
394 maxaddr + ebase > info->exe_end)) {
nethercote2e1cb4c2004-08-05 12:16:13 +0000395 fprintf(stderr, "Executable range %p-%p is outside the\n"
396 "acceptable range %p-%p\n",
nethercote7f390022004-10-25 17:18:24 +0000397 (void *)minaddr + ebase, (void *)maxaddr + ebase,
398 (void *)info->exe_base, (void *)info->exe_end);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000399 return ENOMEM;
400 }
401 }
402
nethercote7f390022004-10-25 17:18:24 +0000403 info->brkbase = mapelf(e, ebase); /* map the executable */
fitzhardinge7e343cd2003-12-16 02:14:00 +0000404
fitzhardinge92360792003-12-24 10:11:11 +0000405 if (info->brkbase == 0)
406 return ENOMEM;
407
fitzhardinge7e343cd2003-12-16 02:14:00 +0000408 if (interp != NULL) {
409 /* reserve a chunk of address space for interpreter */
nethercotebfed1c82004-07-17 12:57:44 +0000410 void* res;
411 char* base = (char *)info->exe_base;
412 char* baseoff;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000413 int flags = MAP_PRIVATE|MAP_ANONYMOUS;
414
415 if (info->map_base != 0) {
nethercote7f390022004-10-25 17:18:24 +0000416 base = (char *)ROUNDUP(info->map_base, interp_align);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000417 flags |= MAP_FIXED;
418 }
419
nethercotebfed1c82004-07-17 12:57:44 +0000420 res = mmap(base, interp_size, PROT_NONE, flags, -1, 0);
421 check_mmap(res, base, interp_size);
422 base = res;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000423
424 baseoff = base - interp_addr;
425
fitzhardingeb50068f2004-02-24 23:42:55 +0000426 mapelf(interp, (ESZ(Addr))baseoff);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000427
428 close(interp->fd);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000429
430 entry = baseoff + interp->e.e_entry;
431 info->interp_base = (ESZ(Addr))base;
thughes54d08592004-09-26 14:42:47 +0000432
sewardjb5f6f512005-03-10 23:59:00 +0000433 free(interp->p);
thughes54d08592004-09-26 14:42:47 +0000434 free(interp);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000435 } else
nethercote7f390022004-10-25 17:18:24 +0000436 entry = (void *)e->e.e_entry;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000437
nethercote7f390022004-10-25 17:18:24 +0000438 info->exe_base = minaddr + ebase;
439 info->exe_end = maxaddr + ebase;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000440
nethercotea3c3cf22004-11-01 18:38:00 +0000441 info->init_eip = (Addr)entry;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000442
sewardjb5f6f512005-03-10 23:59:00 +0000443 free(e->p);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000444 free(e);
445
446 return 0;
447}
448
449
450static int match_script(const char *hdr, Int len)
451{
452 return (len > 2) && memcmp(hdr, "#!", 2) == 0;
453}
454
nethercote31779c72004-07-30 21:50:15 +0000455static int load_script(char *hdr, int len, int fd, const char *name,
456 struct exeinfo *info)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000457{
458 char *interp;
459 char *const end = hdr+len;
460 char *cp;
461 char *arg = NULL;
462 int eol;
463
464 interp = hdr + 2;
465 while(interp < end && (*interp == ' ' || *interp == '\t'))
466 interp++;
467
468 if (*interp != '/')
469 return ENOEXEC; /* absolute path only for interpreter */
470
471 /* skip over interpreter name */
472 for(cp = interp; cp < end && *cp != ' ' && *cp != '\t' && *cp != '\n'; cp++)
473 ;
474
475 eol = (*cp == '\n');
476
477 *cp++ = '\0';
478
479 if (!eol && cp < end) {
480 /* skip space before arg */
481 while (cp < end && (*cp == '\t' || *cp == ' '))
482 cp++;
483
484 /* arg is from here to eol */
485 arg = cp;
486 while (cp < end && *cp != '\n')
487 cp++;
488 *cp = '\0';
489 }
490
nethercoted6a56872004-07-26 15:32:47 +0000491 info->interp_name = strdup(interp);
492 assert(NULL != info->interp_name);
nethercote71980f02004-01-24 18:18:54 +0000493 if (arg != NULL && *arg != '\0') {
nethercoted6a56872004-07-26 15:32:47 +0000494 info->interp_args = strdup(arg);
495 assert(NULL != info->interp_args);
nethercote71980f02004-01-24 18:18:54 +0000496 }
fitzhardinge7e343cd2003-12-16 02:14:00 +0000497
498 if (info->argv && info->argv[0] != NULL)
499 info->argv[0] = (char *)name;
500
501 if (0)
nethercoted6a56872004-07-26 15:32:47 +0000502 printf("#! script: interp_name=\"%s\" interp_args=\"%s\"\n",
503 info->interp_name, info->interp_args);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000504
505 return do_exec_inner(interp, info);
506}
507
fitzhardingefd7da3a2004-09-08 20:05:29 +0000508/*
509 Emulate the normal Unix permissions checking algorithm.
510
511 If owner matches, then use the owner permissions, else
512 if group matches, then use the group permissions, else
513 use other permissions.
514
515 Note that we can't deal with SUID/SGID, so we refuse to run them
516 (otherwise the executable may misbehave if it doesn't have the
517 permissions it thinks it does).
518*/
519static int check_perms(int fd)
520{
521 struct stat st;
522
523 if (fstat(fd, &st) == -1)
524 return errno;
525
526 if (st.st_mode & (S_ISUID | S_ISGID)) {
527 //fprintf(stderr, "Can't execute suid/sgid executable %s\n", exe);
528 return EACCES;
529 }
530
531 if (geteuid() == st.st_uid) {
532 if (!(st.st_mode & S_IXUSR))
533 return EACCES;
534 } else {
535 int grpmatch = 0;
536
537 if (getegid() == st.st_gid)
538 grpmatch = 1;
539 else {
540 gid_t groups[32];
541 int ngrp = getgroups(32, groups);
542 int i;
543
544 for(i = 0; i < ngrp; i++)
545 if (groups[i] == st.st_gid) {
546 grpmatch = 1;
547 break;
548 }
549 }
550
551 if (grpmatch) {
552 if (!(st.st_mode & S_IXGRP))
553 return EACCES;
554 } else if (!(st.st_mode & S_IXOTH))
555 return EACCES;
556 }
557
558 return 0;
559}
560
fitzhardinge7e343cd2003-12-16 02:14:00 +0000561static int do_exec_inner(const char *exe, struct exeinfo *info)
562{
563 int fd;
fitzhardinged2e37112004-09-09 08:10:42 +0000564 int err;
nethercote73b526f2004-10-31 18:48:21 +0000565 char buf[VKI_PAGE_SIZE];
fitzhardinge7e343cd2003-12-16 02:14:00 +0000566 int bufsz;
567 int i;
568 int ret;
nethercote31779c72004-07-30 21:50:15 +0000569 static const struct {
570 int (*match)(const char *hdr, int len);
571 int (*load) ( char *hdr, int len, int fd2, const char *name,
572 struct exeinfo *);
573 } formats[] = {
574 { match_ELF, load_ELF },
575 { match_script, load_script },
576 };
fitzhardinge7e343cd2003-12-16 02:14:00 +0000577
578 fd = open(exe, O_RDONLY);
579 if (fd == -1) {
580 if (0)
581 fprintf(stderr, "Can't open executable %s: %s\n",
582 exe, strerror(errno));
583 return errno;
584 }
585
fitzhardinged2e37112004-09-09 08:10:42 +0000586 err = check_perms(fd);
fitzhardingefd7da3a2004-09-08 20:05:29 +0000587 if (err != 0) {
588 close(fd);
589 return err;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000590 }
591
592 bufsz = pread(fd, buf, sizeof(buf), 0);
593 if (bufsz < 0) {
594 fprintf(stderr, "Can't read executable header: %s\n",
595 strerror(errno));
596 close(fd);
597 return errno;
598 }
599
600 ret = ENOEXEC;
601 for(i = 0; i < sizeof(formats)/sizeof(*formats); i++) {
602 if ((formats[i].match)(buf, bufsz)) {
603 ret = (formats[i].load)(buf, bufsz, fd, exe, info);
604 break;
605 }
606 }
607
608 close(fd);
609
610 return ret;
611}
612
nethercoteea147e72004-07-26 15:43:57 +0000613// See ume.h for an indication of which entries of 'info' are inputs, which
614// are outputs, and which are both.
fitzhardinge7e343cd2003-12-16 02:14:00 +0000615int do_exec(const char *exe, struct exeinfo *info)
616{
nethercoted6a56872004-07-26 15:32:47 +0000617 info->interp_name = NULL;
618 info->interp_args = NULL;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000619
620 return do_exec_inner(exe, info);
621}
nethercotebb1c9912004-01-04 16:43:23 +0000622
623/*--------------------------------------------------------------------*/
624/*--- end ume.c ---*/
625/*--------------------------------------------------------------------*/