diff options
Diffstat (limited to 'coregrind/m_ume')
-rw-r--r-- | coregrind/m_ume/.svn/entries | 79 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/format | 1 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/prop-base/elf.c.svn-base | 13 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/prop-base/main.c.svn-base | 13 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/prop-base/script.c.svn-base | 13 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/text-base/elf.c.svn-base | 519 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/text-base/main.c.svn-base | 286 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/text-base/priv_ume.h.svn-base | 61 | ||||
-rw-r--r-- | coregrind/m_ume/.svn/text-base/script.c.svn-base | 150 | ||||
-rw-r--r-- | coregrind/m_ume/elf.c | 519 | ||||
-rw-r--r-- | coregrind/m_ume/main.c | 286 | ||||
-rw-r--r-- | coregrind/m_ume/priv_ume.h | 61 | ||||
-rw-r--r-- | coregrind/m_ume/script.c | 150 |
13 files changed, 2151 insertions, 0 deletions
diff --git a/coregrind/m_ume/.svn/entries b/coregrind/m_ume/.svn/entries new file mode 100644 index 0000000..4640964 --- /dev/null +++ b/coregrind/m_ume/.svn/entries @@ -0,0 +1,79 @@ +8 + +dir +9703 +svn://svn.valgrind.org/valgrind/trunk/coregrind/m_ume +svn://svn.valgrind.org/valgrind + + + +2009-03-10T22:02:09.669944Z +9344 +njn + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +a5019735-40e9-0310-863c-91ae7b9d1cf9 + +script.c +file + + + + +2009-03-13T17:30:23.000000Z +e4edb1c3650ba8c369c9f148363f11fe +2009-03-10T22:02:09.669944Z +9344 +njn +has-props + +main.c +file + + + + +2009-03-13T17:30:23.000000Z +7f41ba6a27ded5878cb6009968b33d4c +2009-03-10T22:02:09.669944Z +9344 +njn +has-props + +elf.c +file + + + + +2009-03-13T17:30:23.000000Z +251bf25de96fca92bfeb3c6f3819dba2 +2009-03-10T22:02:09.669944Z +9344 +njn +has-props + +priv_ume.h +file + + + + +2009-03-13T17:30:23.000000Z +bdcbc5ed2e810f3865bab8657981ca5e +2009-01-21T04:11:13.444440Z +9008 +njn + diff --git a/coregrind/m_ume/.svn/format b/coregrind/m_ume/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/coregrind/m_ume/.svn/format @@ -0,0 +1 @@ +8 diff --git a/coregrind/m_ume/.svn/prop-base/elf.c.svn-base b/coregrind/m_ume/.svn/prop-base/elf.c.svn-base new file mode 100644 index 0000000..fe534b9 --- /dev/null +++ b/coregrind/m_ume/.svn/prop-base/elf.c.svn-base @@ -0,0 +1,13 @@ +K 13 +svn:eol-style +V 6 +native +K 12 +svn:keywords +V 23 +author date id revision +K 13 +svn:mergeinfo +V 0 + +END diff --git a/coregrind/m_ume/.svn/prop-base/main.c.svn-base b/coregrind/m_ume/.svn/prop-base/main.c.svn-base new file mode 100644 index 0000000..fe534b9 --- /dev/null +++ b/coregrind/m_ume/.svn/prop-base/main.c.svn-base @@ -0,0 +1,13 @@ +K 13 +svn:eol-style +V 6 +native +K 12 +svn:keywords +V 23 +author date id revision +K 13 +svn:mergeinfo +V 0 + +END diff --git a/coregrind/m_ume/.svn/prop-base/script.c.svn-base b/coregrind/m_ume/.svn/prop-base/script.c.svn-base new file mode 100644 index 0000000..fe534b9 --- /dev/null +++ b/coregrind/m_ume/.svn/prop-base/script.c.svn-base @@ -0,0 +1,13 @@ +K 13 +svn:eol-style +V 6 +native +K 12 +svn:keywords +V 23 +author date id revision +K 13 +svn:mergeinfo +V 0 + +END diff --git a/coregrind/m_ume/.svn/text-base/elf.c.svn-base b/coregrind/m_ume/.svn/text-base/elf.c.svn-base new file mode 100644 index 0000000..b3edc20 --- /dev/null +++ b/coregrind/m_ume/.svn/text-base/elf.c.svn-base @@ -0,0 +1,519 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve() for ELF executables m_ume_elf.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_aspacemgr.h" // various mapping fns +#include "pub_core_debuglog.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcbase.h" // VG_(memcmp), etc +#include "pub_core_libcprint.h" +#include "pub_core_libcfile.h" // VG_(open) et al +#include "pub_core_machine.h" // VG_ELF_CLASS (XXX: which should be moved) +#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free) +#include "pub_core_syscall.h" // VG_(strerror) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" + + +#if defined(HAVE_ELF) + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#define _GNU_SOURCE +#define _FILE_OFFSET_BITS 64 +/* This is for ELF types etc, and also the AT_ constants. */ +#include <elf.h> +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + + +#if VG_WORDSIZE == 8 +#define ESZ(x) Elf64_##x +#elif VG_WORDSIZE == 4 +#define ESZ(x) Elf32_##x +#else +#error VG_WORDSIZE needs to ==4 or ==8 +#endif + +struct elfinfo +{ + ESZ(Ehdr) e; + ESZ(Phdr) *p; + Int fd; +}; + +static void check_mmap(SysRes res, Addr base, SizeT len) +{ + if (res.isError) { + VG_(printf)("valgrind: mmap(0x%llx, %lld) failed in UME " + "with error %lu (%s).\n", + (ULong)base, (Long)len, + res.err, VG_(strerror)(res.err) ); + if (res.err == VKI_EINVAL) { + VG_(printf)("valgrind: this can be caused by executables with " + "very large text, data or bss segments.\n"); + } + VG_(exit)(1); + } +} + +/*------------------------------------------------------------*/ +/*--- Loading ELF files ---*/ +/*------------------------------------------------------------*/ + +static +struct elfinfo *readelf(Int fd, const char *filename) +{ + SysRes sres; + struct elfinfo *e = VG_(malloc)("ume.re.1", sizeof(*e)); + Int phsz; + + vg_assert(e); + e->fd = fd; + + sres = VG_(pread)(fd, &e->e, sizeof(e->e), 0); + if (sres.isError || sres.res != sizeof(e->e)) { + VG_(printf)("valgrind: %s: can't read ELF header: %s\n", + filename, VG_(strerror)(sres.err)); + goto bad; + } + + if (VG_(memcmp)(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) { + VG_(printf)("valgrind: %s: bad ELF magic number\n", filename); + goto bad; + } + if (e->e.e_ident[EI_CLASS] != VG_ELF_CLASS) { + VG_(printf)("valgrind: wrong ELF executable class " + "(eg. 32-bit instead of 64-bit)\n"); + goto bad; + } + if (e->e.e_ident[EI_DATA] != VG_ELF_DATA2XXX) { + VG_(printf)("valgrind: executable has wrong endian-ness\n"); + goto bad; + } + if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) { + VG_(printf)("valgrind: this is not an executable\n"); + goto bad; + } + + if (e->e.e_machine != VG_ELF_MACHINE) { + VG_(printf)("valgrind: executable is not for " + "this architecture\n"); + goto bad; + } + + if (e->e.e_phentsize != sizeof(ESZ(Phdr))) { + VG_(printf)("valgrind: sizeof ELF Phdr wrong\n"); + goto bad; + } + + phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum; + e->p = VG_(malloc)("ume.re.2", phsz); + vg_assert(e->p); + + sres = VG_(pread)(fd, e->p, phsz, e->e.e_phoff); + if (sres.isError || sres.res != phsz) { + VG_(printf)("valgrind: can't read phdr: %s\n", + VG_(strerror)(sres.err)); + VG_(free)(e->p); + goto bad; + } + + return e; + + bad: + VG_(free)(e); + return NULL; +} + +/* Map an ELF file. Returns the brk address. */ +static +ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) +{ + Int i; + SysRes res; + ESZ(Addr) elfbrk = 0; + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, brkaddr; + ESZ(Word) memsz; + + if (ph->p_type != PT_LOAD) + continue; + + addr = ph->p_vaddr+base; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + if (brkaddr > elfbrk) + elfbrk = brkaddr; + } + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, bss, brkaddr; + ESZ(Off) off; + ESZ(Word) filesz; + ESZ(Word) memsz; + unsigned prot = 0; + + if (ph->p_type != PT_LOAD) + continue; + + if (ph->p_flags & PF_X) prot |= VKI_PROT_EXEC; + if (ph->p_flags & PF_W) prot |= VKI_PROT_WRITE; + if (ph->p_flags & PF_R) prot |= VKI_PROT_READ; + + addr = ph->p_vaddr+base; + off = ph->p_offset; + filesz = ph->p_filesz; + bss = addr+filesz; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + // Tom says: In the following, do what the Linux kernel does and only + // map the pages that are required instead of rounding everything to + // the specified alignment (ph->p_align). (AMD64 doesn't work if you + // use ph->p_align -- part of stage2's memory gets trashed somehow.) + // + // The condition handles the case of a zero-length segment. + if (VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr) > 0) { + if (0) VG_(debugLog)(0,"ume","mmap_file_fixed_client #1\n"); + res = VG_(am_mmap_file_fixed_client)( + VG_PGROUNDDN(addr), + VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr), + prot, /*VKI_MAP_FIXED|VKI_MAP_PRIVATE, */ + e->fd, VG_PGROUNDDN(off) + ); + if (0) VG_(am_show_nsegments)(0,"after #1"); + check_mmap(res, VG_PGROUNDDN(addr), + VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr)); + } + + // if memsz > filesz, fill the remainder with zeroed pages + if (memsz > filesz) { + UInt bytes; + + bytes = VG_PGROUNDUP(brkaddr)-VG_PGROUNDUP(bss); + if (bytes > 0) { + if (0) VG_(debugLog)(0,"ume","mmap_anon_fixed_client #2\n"); + res = VG_(am_mmap_anon_fixed_client)( + VG_PGROUNDUP(bss), bytes, + prot + ); + if (0) VG_(am_show_nsegments)(0,"after #2"); + check_mmap(res, VG_PGROUNDUP(bss), bytes); + } + + bytes = bss & (VKI_PAGE_SIZE - 1); + + // The 'prot' condition allows for a read-only bss + if ((prot & VKI_PROT_WRITE) && (bytes > 0)) { + bytes = VKI_PAGE_SIZE - bytes; + VG_(memset)((char *)bss, 0, bytes); + } + } + } + + return elfbrk; +} + +Bool VG_(match_ELF)(Char *hdr, Int len) +{ + ESZ(Ehdr) *e = (ESZ(Ehdr) *)hdr; + return (len > sizeof(*e)) && VG_(memcmp)(&e->e_ident[0], ELFMAG, SELFMAG) == 0; +} + + +/* load_ELF pulls an ELF executable into the address space, prepares + it for execution, and writes info about it into INFO. In + particular it fills in .init_eip, which is the starting point. + + Returns zero on success, non-zero (a VKI_E.. value) on failure. + + The sequence of activities is roughly as follows: + + - use readelf() to extract program header info from the exe file. + + - scan the program header, collecting info (not sure what all those + info-> fields are, or whether they are used, but still) and in + particular looking out fo the PT_INTERP header, which describes + the interpreter. If such a field is found, the space needed to + hold the interpreter is computed into interp_size. + + - map the executable in, by calling mapelf(). This maps in all + loadable sections, and I _think_ also creates any .bss areas + required. mapelf() returns the address just beyond the end of + the furthest-along mapping it creates. The executable is mapped + starting at EBASE, which is usually read from it (eg, 0x8048000 + etc) except if it's a PIE, in which case I'm not sure what + happens. + + The returned address is recorded in info->brkbase as the start + point of the brk (data) segment, as it is traditional to place + the data segment just after the executable. Neither load_ELF nor + mapelf creates the brk segment, though: that is for the caller of + load_ELF to attend to. + + - If the initial phdr scan didn't find any mention of an + interpreter (interp == NULL), this must be a statically linked + executable, and we're pretty much done. + + - Otherwise, we need to use mapelf() a second time to load the + interpreter. The interpreter can go anywhere, but mapelf() wants + to be told a specific address to put it at. So an advisory query + is passed to aspacem, asking where it would put an anonymous + client mapping of size INTERP_SIZE. That address is then used + as the mapping address for the interpreter. + + - The entry point in INFO is set to the interpreter's entry point, + and we're done. */ +Int VG_(load_ELF)(Int fd, const HChar* name, /*MOD*/ExeInfo* info) +{ + SysRes sres; + struct elfinfo *e; + struct elfinfo *interp = NULL; + ESZ(Addr) minaddr = ~0; /* lowest mapped address */ + ESZ(Addr) maxaddr = 0; /* highest mapped address */ + ESZ(Addr) interp_addr = 0; /* interpreter (ld.so) address */ + ESZ(Word) interp_size = 0; /* interpreter size */ + ESZ(Word) interp_align = VKI_PAGE_SIZE; + Int i; + void *entry; + ESZ(Addr) ebase = 0; + + /* The difference between where the interpreter got mapped and + where it asked to be mapped. Needed for computing the ppc64 ELF + entry point and initial tocptr (R2) value. */ + ESZ(Word) interp_offset = 0; + +#ifdef HAVE_PIE + ebase = info->exe_base; +#endif + + e = readelf(fd, name); + + if (e == NULL) + return VKI_ENOEXEC; + + /* The kernel maps position-independent executables at TASK_SIZE*2/3; + duplicate this behavior as close as we can. */ + if (e->e.e_type == ET_DYN && ebase == 0) { + ebase = VG_PGROUNDDN(info->exe_base + + (info->exe_end - info->exe_base) * 2 / 3); + /* We really don't want to load PIEs at zero or too close. It + works, but it's unrobust (NULL pointer reads and writes + become legit, which is really bad) and causes problems for + exp-ptrcheck, which assumes all numbers below 1MB are + nonpointers. So, hackily, move it above 1MB. */ + /* Later .. is appears ppc32-linux tries to put [vdso] at 1MB, + which totally screws things up, because nothing else can go + there. So bump the hacky load addess along by 0x8000, to + 0x108000. */ + if (ebase < 0x108000) + ebase = 0x108000; + } + + info->phnum = e->e.e_phnum; + info->entry = e->e.e_entry + ebase; + info->phdr = 0; + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + + switch(ph->p_type) { + case PT_PHDR: + info->phdr = ph->p_vaddr + ebase; + break; + + case PT_LOAD: + if (ph->p_vaddr < minaddr) + minaddr = ph->p_vaddr; + if (ph->p_vaddr+ph->p_memsz > maxaddr) + maxaddr = ph->p_vaddr+ph->p_memsz; + break; + + case PT_INTERP: { + HChar *buf = VG_(malloc)("ume.LE.1", ph->p_filesz+1); + Int j; + Int intfd; + Int baseaddr_set; + + vg_assert(buf); + VG_(pread)(fd, buf, ph->p_filesz, ph->p_offset); + buf[ph->p_filesz] = '\0'; + + sres = VG_(open)(buf, VKI_O_RDONLY, 0); + if (sres.isError) { + VG_(printf)("valgrind: m_ume.c: can't open interpreter\n"); + VG_(exit)(1); + } + intfd = sres.res; + + interp = readelf(intfd, buf); + if (interp == NULL) { + VG_(printf)("valgrind: m_ume.c: can't read interpreter\n"); + return 1; + } + VG_(free)(buf); + + baseaddr_set = 0; + for (j = 0; j < interp->e.e_phnum; j++) { + ESZ(Phdr) *iph = &interp->p[j]; + ESZ(Addr) end; + + if (iph->p_type != PT_LOAD) + continue; + + if (!baseaddr_set) { + interp_addr = iph->p_vaddr; + interp_align = iph->p_align; + baseaddr_set = 1; + } + + /* assumes that all segments in the interp are close */ + end = (iph->p_vaddr - interp_addr) + iph->p_memsz; + + if (end > interp_size) + interp_size = end; + } + break; + + default: + // do nothing + break; + } + } + } + + if (info->phdr == 0) + info->phdr = minaddr + ebase + e->e.e_phoff; + + if (info->exe_base != info->exe_end) { + if (minaddr >= maxaddr || + (minaddr + ebase < info->exe_base || + maxaddr + ebase > info->exe_end)) { + VG_(printf)("Executable range %p-%p is outside the\n" + "acceptable range %p-%p\n", + (char *)minaddr + ebase, (char *)maxaddr + ebase, + (char *)info->exe_base, (char *)info->exe_end); + return VKI_ENOMEM; + } + } + + info->brkbase = mapelf(e, ebase); /* map the executable */ + + if (info->brkbase == 0) + return VKI_ENOMEM; + + if (interp != NULL) { + /* reserve a chunk of address space for interpreter */ + MapRequest mreq; + Addr advised; + Bool ok; + + /* Don't actually reserve the space. Just get an advisory + indicating where it would be allocated, and pass that to + mapelf(), which in turn asks aspacem to do some fixed maps at + the specified address. This is a bit of hack, but it should + work because there should be no intervening transactions with + aspacem which could cause those fixed maps to fail. + + Placement policy is: + + if the interpreter asks to be loaded at zero + ignore that and put it wherever we like (mappings at zero + are bad news) + else + try and put it where it asks for, but if that doesn't work, + just put it anywhere. + */ + if (interp_addr == 0) { + mreq.rkind = MAny; + mreq.start = 0; + mreq.len = interp_size; + } else { + mreq.rkind = MHint; + mreq.start = interp_addr; + mreq.len = interp_size; + } + + advised = VG_(am_get_advisory)( &mreq, True/*client*/, &ok ); + + if (!ok) { + /* bomb out */ + SysRes res = VG_(mk_SysRes_Error)(VKI_EINVAL); + if (0) VG_(printf)("reserve for interp: failed\n"); + check_mmap(res, (Addr)interp_addr, interp_size); + /*NOTREACHED*/ + } + + (void)mapelf(interp, (ESZ(Addr))advised - interp_addr); + + VG_(close)(interp->fd); + + entry = (void *)(advised - interp_addr + interp->e.e_entry); + info->interp_base = (ESZ(Addr))advised; + interp_offset = advised - interp_addr; + + VG_(free)(interp->p); + VG_(free)(interp); + } else + entry = (void *)(ebase + e->e.e_entry); + + info->exe_base = minaddr + ebase; + info->exe_end = maxaddr + ebase; + +#if defined(VGP_ppc64_linux) + /* On PPC64, a func ptr is represented by a TOC entry ptr. This + TOC entry contains three words; the first word is the function + address, the second word is the TOC ptr (r2), and the third word + is the static chain value. */ + info->init_ip = ((ULong*)entry)[0]; + info->init_toc = ((ULong*)entry)[1]; + info->init_ip += interp_offset; + info->init_toc += interp_offset; +#else + info->init_ip = (Addr)entry; + info->init_toc = 0; /* meaningless on this platform */ +#endif + VG_(free)(e->p); + VG_(free)(e); + + return 0; +} + +#endif /* defined(HAVE_ELF) */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/.svn/text-base/main.c.svn-base b/coregrind/m_ume/.svn/text-base/main.c.svn-base new file mode 100644 index 0000000..786e176 --- /dev/null +++ b/coregrind/m_ume/.svn/text-base/main.c.svn-base @@ -0,0 +1,286 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve(), and other stuff shared between stage1 ---*/ +/*--- and stage2. m_ume.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcfile.h" // VG_(close) et al +#include "pub_core_libcprint.h" // VG_(message) +#include "pub_core_mallocfree.h" // VG_(strdup) +#include "pub_core_syscall.h" // VG_(mk_SysRes_Error) +#include "pub_core_options.h" // VG_(clo_xml) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" + + +typedef struct { + const HChar *name; + Bool (*match_fn)(Char *hdr, Int len); + Int (*load_fn)(Int fd, const HChar *name, ExeInfo *info); +} ExeHandler; + +static ExeHandler exe_handlers[] = { +#if defined(HAVE_ELF) + { "ELF", VG_(match_ELF), VG_(load_ELF) }, +#endif +#if defined(HAVE_SCRIPT) + { "script", VG_(match_script), VG_(load_script) }, +#endif +}; +#define EXE_HANDLER_COUNT (sizeof(exe_handlers)/sizeof(exe_handlers[0])) + + +// Check the file looks executable. +SysRes +VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, Bool allow_setuid) +{ + Int fd, ret, i; + SysRes res; + Char buf[4096]; + SizeT bufsz = 4096, fsz; + Bool is_setuid = False; + + // Check it's readable + res = VG_(open)(exe_name, VKI_O_RDONLY, 0); + if (res.isError) { + return res; + } + fd = res.res; + + // Check we have execute permissions + ret = VG_(check_executable)(&is_setuid, (HChar*)exe_name, allow_setuid); + if (0 != ret) { + VG_(close)(fd); + if (is_setuid && !VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "Warning: Can't execute setuid/setgid executable: %s", + exe_name); + VG_(message)(Vg_UserMsg, "Possible workaround: remove " + "--trace-children=yes, if in effect"); + VG_(message)(Vg_UserMsg, ""); + } + return VG_(mk_SysRes_Error)(ret); + } + + fsz = (SizeT)VG_(fsize)(fd); + if (fsz < bufsz) + bufsz = fsz; + + res = VG_(pread)(fd, buf, bufsz, 0); + if (res.isError || res.res != bufsz) { + VG_(close)(fd); + return VG_(mk_SysRes_Error)(VKI_EACCES); + } + bufsz = res.res; + + // Look for a matching executable format + for (i = 0; i < EXE_HANDLER_COUNT; i++) { + if ((*exe_handlers[i].match_fn)(buf, bufsz)) { + res = VG_(mk_SysRes_Success)(i); + break; + } + } + if (i == EXE_HANDLER_COUNT) { + // Rejected by all executable format handlers. + res = VG_(mk_SysRes_Error)(VKI_ENOEXEC); + } + + // Write the 'out_fd' param if necessary, or close the file. + if (!res.isError && out_fd) { + *out_fd = fd; + } else { + VG_(close)(fd); + } + + return res; +} + +// returns: 0 = success, non-0 is failure +// +// We can execute only binaries (ELF, etc) or scripts that begin with "#!". +// (Not, for example, scripts that don't begin with "#!"; see the +// VG_(do_exec)() invocation from m_main.c for how that's handled.) +Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info) +{ + SysRes res; + Int fd; + Int ret; + + res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/); + if (res.isError) + return res.err; + + vg_assert2(res.res >= 0 && res.res < EXE_HANDLER_COUNT, + "invalid VG_(pre_exec_check) result"); + + ret = (*exe_handlers[res.res].load_fn)(fd, exe, info); + + VG_(close)(fd); + + return ret; +} + + +static Bool is_hash_bang_file(Char* f) +{ + SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); + if (!res.isError) { + Char buf[3] = {0,0,0}; + Int fd = res.res; + Int n = VG_(read)(fd, buf, 2); + if (n == 2 && VG_STREQ("#!", buf)) + return True; + } + return False; +} + +// Look at the first 80 chars, and if any are greater than 127, it's binary. +// This is crude, but should be good enough. Note that it fails on a +// zero-length file, as we want. +static Bool is_binary_file(Char* f) +{ + SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); + if (!res.isError) { + UChar buf[80]; + Int fd = res.res; + Int n = VG_(read)(fd, buf, 80); + Int i; + for (i = 0; i < n; i++) { + if (buf[i] > 127) + return True; // binary char found + } + return False; + } else { + // Something went wrong. This will only happen if we earlier + // succeeded in opening the file but fail here (eg. the file was + // deleted between then and now). + VG_(printf)("valgrind: %s: unknown error\n", f); + VG_(exit)(126); // 126 == NOEXEC + } +} + +// If the do_exec fails we try to emulate what the shell does (I used +// bash as a guide). It's worth noting that the shell can execute some +// things that VG_(do_exec)() (which subsitutes for the kernel's exec()) +// will refuse to (eg. scripts lacking a "#!" prefix). +static Int do_exec_shell_followup(Int ret, HChar* exe_name, ExeInfo* info) +{ + Char* default_interp_name = "/bin/sh"; + SysRes res; + struct vg_stat st; + + if (VKI_ENOEXEC == ret) { + // It was an executable file, but in an unacceptable format. Probably + // is a shell script lacking the "#!" prefix; try to execute it so. + + // Is it a binary file? + if (is_binary_file(exe_name)) { + VG_(printf)("valgrind: %s: cannot execute binary file\n", exe_name); + VG_(exit)(126); // 126 == NOEXEC + } + + // Looks like a script. Run it with /bin/sh. This includes + // zero-length files. + + info->interp_name = VG_(strdup)("ume.desf.1", default_interp_name); + info->interp_args = NULL; + if (info->argv && info->argv[0] != NULL) + info->argv[0] = (char *)exe_name; + + ret = VG_(do_exec_inner)(info->interp_name, info); + + if (0 != ret) { + // Something went wrong with executing the default interpreter + VG_(printf)("valgrind: %s: bad interpreter (%s): %s\n", + exe_name, info->interp_name, VG_(strerror)(ret)); + VG_(exit)(126); // 126 == NOEXEC + } + + } else if (0 != ret) { + // Something else went wrong. Try to make the error more specific, + // and then print a message and abort. + + // Was it a directory? + res = VG_(stat)(exe_name, &st); + if (!res.isError && VKI_S_ISDIR(st.st_mode)) { + VG_(printf)("valgrind: %s: is a directory\n", exe_name); + + // Was it not executable? + } else if (0 != VG_(check_executable)(NULL, exe_name, + False/*allow_setuid*/)) { + VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret)); + + // Did it start with "#!"? If so, it must have been a bad interpreter. + } else if (is_hash_bang_file(exe_name)) { + VG_(printf)("valgrind: %s: bad interpreter: %s\n", + exe_name, VG_(strerror)(ret)); + + // Otherwise it was something else. + } else { + VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret)); + } + // 126 means NOEXEC; I think this is Posix, and that in some cases we + // should be returning 127, meaning NOTFOUND. Oh well. + VG_(exit)(126); + } + return ret; +} + + +// This emulates the kernel's exec(). If it fails, it then emulates the +// shell's handling of the situation. +// See ume.h for an indication of which entries of 'info' are inputs, which +// are outputs, and which are both. +/* returns: 0 = success, non-0 is failure */ +Int VG_(do_exec)(const HChar* exe_name, ExeInfo* info) +{ + Int ret; + + info->interp_name = NULL; + info->interp_args = NULL; + + ret = VG_(do_exec_inner)(exe_name, info); + + if (0 != ret) { + Char* exe_name_casted = (Char*)exe_name; + ret = do_exec_shell_followup(ret, exe_name_casted, info); + } + return ret; +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/.svn/text-base/priv_ume.h.svn-base b/coregrind/m_ume/.svn/text-base/priv_ume.h.svn-base new file mode 100644 index 0000000..2ac4189 --- /dev/null +++ b/coregrind/m_ume/.svn/text-base/priv_ume.h.svn-base @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------*/ +/*--- User-mode execve(). priv_ume.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2005 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PRIV_UME_H +#define __PRIV_UME_H + +#if defined(VGO_linux) +# define HAVE_ELF +# define HAVE_SCRIPT + +#elif defined(VGO_aix5) +// The AIX port doesn't use UME. + +#else +#error unknown architecture +#endif + +extern int VG_(do_exec_inner)(const HChar *exe, ExeInfo *info); + +#if defined(HAVE_ELF) +extern Bool VG_(match_ELF) ( Char *hdr, Int len ); +extern Int VG_(load_ELF) ( Int fd, const HChar *name, ExeInfo *info ); +#endif + +#if defined(HAVE_SCRIPT) +extern Bool VG_(match_script) ( Char *hdr, Int len ); +extern Int VG_(load_script) ( Int fd, const HChar *name, ExeInfo *info ); +#endif + +#endif /* __PRIV_UME_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/coregrind/m_ume/.svn/text-base/script.c.svn-base b/coregrind/m_ume/.svn/text-base/script.c.svn-base new file mode 100644 index 0000000..8df563e --- /dev/null +++ b/coregrind/m_ume/.svn/text-base/script.c.svn-base @@ -0,0 +1,150 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve() for #! scripts. m_ume_script.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcfile.h" // VG_(close) et al +#include "pub_core_libcprint.h" +#include "pub_core_mallocfree.h" // VG_(strdup) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" // self + + +#if defined(HAVE_SCRIPT) + +Bool VG_(match_script)(Char *hdr, Int len) +{ + Char* end = hdr + len; + Char* interp = hdr + 2; + + // len < 4: need '#', '!', plus at least a '/' and one more char + if (len < 4) return False; + if (0 != VG_(memcmp)(hdr, "#!", 2)) return False; + + // Find interpreter name, make sure it's an absolute path (starts with + // '/') and has at least one more char. First, skip over any space + // between the #! and the start of the interpreter name + while (interp < end && VG_(isspace)(*interp)) interp++; + + // overrun? + if (interp >= end) return False; // can't find start of interp name + + // interp should now point at the / + if (*interp != '/') return False; // absolute path only for interpreter + + // check for something plausible after the / + interp++; + if (interp >= end) return False; + if (VG_(isspace)(*interp)) return False; + + // Here we should get the full interpreter name and check it with + // check_executable(). See the "EXEC FAILED" failure when running shell + // for an example. + + return True; // looks like a #! script +} + + +/* returns: 0 = success, non-0 is failure */ +Int VG_(load_script)(Int fd, const HChar* name, ExeInfo* info) +{ + Char hdr[4096]; + Int len = 4096; + Int eol; + Char* interp; + Char* end; + Char* cp; + Char* arg = NULL; + SysRes res; + + // Read the first part of the file. + res = VG_(pread)(fd, hdr, len, 0); + if (res.isError) { + VG_(close)(fd); + return VKI_EACCES; + } else { + len = res.res; + } + + vg_assert('#' == hdr[0] && '!' == hdr[1]); + + end = hdr + len; + interp = hdr + 2; + while (interp < end && VG_(isspace)(*interp)) + interp++; + + vg_assert(*interp == '/'); /* absolute path only for interpreter */ + + /* skip over interpreter name */ + for (cp = interp; cp < end && !VG_(isspace)(*cp); cp++) + ; + + eol = (*cp == '\n'); + + *cp++ = '\0'; + + if (!eol && cp < end) { + /* skip space before arg */ + while (cp < end && VG_(isspace)(*cp) && *cp != '\n') + cp++; + + /* arg is from here to eol */ + arg = cp; + while (cp < end && *cp != '\n') + cp++; + *cp = '\0'; + } + + info->interp_name = VG_(strdup)("ume.ls.1", interp); + vg_assert(NULL != info->interp_name); + if (arg != NULL && *arg != '\0') { + info->interp_args = VG_(strdup)("ume.ls.2", arg); + vg_assert(NULL != info->interp_args); + } + + if (info->argv && info->argv[0] != NULL) + info->argv[0] = (char *)name; + + if (0) + VG_(printf)("#! script: interp_name=\"%s\" interp_args=\"%s\"\n", + info->interp_name, info->interp_args); + + return VG_(do_exec_inner)(interp, info); +} + +#endif /* defined(HAVE_SCRIPT) */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/elf.c b/coregrind/m_ume/elf.c new file mode 100644 index 0000000..b3edc20 --- /dev/null +++ b/coregrind/m_ume/elf.c @@ -0,0 +1,519 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve() for ELF executables m_ume_elf.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_aspacemgr.h" // various mapping fns +#include "pub_core_debuglog.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcbase.h" // VG_(memcmp), etc +#include "pub_core_libcprint.h" +#include "pub_core_libcfile.h" // VG_(open) et al +#include "pub_core_machine.h" // VG_ELF_CLASS (XXX: which should be moved) +#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free) +#include "pub_core_syscall.h" // VG_(strerror) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" + + +#if defined(HAVE_ELF) + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#define _GNU_SOURCE +#define _FILE_OFFSET_BITS 64 +/* This is for ELF types etc, and also the AT_ constants. */ +#include <elf.h> +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + + +#if VG_WORDSIZE == 8 +#define ESZ(x) Elf64_##x +#elif VG_WORDSIZE == 4 +#define ESZ(x) Elf32_##x +#else +#error VG_WORDSIZE needs to ==4 or ==8 +#endif + +struct elfinfo +{ + ESZ(Ehdr) e; + ESZ(Phdr) *p; + Int fd; +}; + +static void check_mmap(SysRes res, Addr base, SizeT len) +{ + if (res.isError) { + VG_(printf)("valgrind: mmap(0x%llx, %lld) failed in UME " + "with error %lu (%s).\n", + (ULong)base, (Long)len, + res.err, VG_(strerror)(res.err) ); + if (res.err == VKI_EINVAL) { + VG_(printf)("valgrind: this can be caused by executables with " + "very large text, data or bss segments.\n"); + } + VG_(exit)(1); + } +} + +/*------------------------------------------------------------*/ +/*--- Loading ELF files ---*/ +/*------------------------------------------------------------*/ + +static +struct elfinfo *readelf(Int fd, const char *filename) +{ + SysRes sres; + struct elfinfo *e = VG_(malloc)("ume.re.1", sizeof(*e)); + Int phsz; + + vg_assert(e); + e->fd = fd; + + sres = VG_(pread)(fd, &e->e, sizeof(e->e), 0); + if (sres.isError || sres.res != sizeof(e->e)) { + VG_(printf)("valgrind: %s: can't read ELF header: %s\n", + filename, VG_(strerror)(sres.err)); + goto bad; + } + + if (VG_(memcmp)(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) { + VG_(printf)("valgrind: %s: bad ELF magic number\n", filename); + goto bad; + } + if (e->e.e_ident[EI_CLASS] != VG_ELF_CLASS) { + VG_(printf)("valgrind: wrong ELF executable class " + "(eg. 32-bit instead of 64-bit)\n"); + goto bad; + } + if (e->e.e_ident[EI_DATA] != VG_ELF_DATA2XXX) { + VG_(printf)("valgrind: executable has wrong endian-ness\n"); + goto bad; + } + if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) { + VG_(printf)("valgrind: this is not an executable\n"); + goto bad; + } + + if (e->e.e_machine != VG_ELF_MACHINE) { + VG_(printf)("valgrind: executable is not for " + "this architecture\n"); + goto bad; + } + + if (e->e.e_phentsize != sizeof(ESZ(Phdr))) { + VG_(printf)("valgrind: sizeof ELF Phdr wrong\n"); + goto bad; + } + + phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum; + e->p = VG_(malloc)("ume.re.2", phsz); + vg_assert(e->p); + + sres = VG_(pread)(fd, e->p, phsz, e->e.e_phoff); + if (sres.isError || sres.res != phsz) { + VG_(printf)("valgrind: can't read phdr: %s\n", + VG_(strerror)(sres.err)); + VG_(free)(e->p); + goto bad; + } + + return e; + + bad: + VG_(free)(e); + return NULL; +} + +/* Map an ELF file. Returns the brk address. */ +static +ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) +{ + Int i; + SysRes res; + ESZ(Addr) elfbrk = 0; + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, brkaddr; + ESZ(Word) memsz; + + if (ph->p_type != PT_LOAD) + continue; + + addr = ph->p_vaddr+base; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + if (brkaddr > elfbrk) + elfbrk = brkaddr; + } + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, bss, brkaddr; + ESZ(Off) off; + ESZ(Word) filesz; + ESZ(Word) memsz; + unsigned prot = 0; + + if (ph->p_type != PT_LOAD) + continue; + + if (ph->p_flags & PF_X) prot |= VKI_PROT_EXEC; + if (ph->p_flags & PF_W) prot |= VKI_PROT_WRITE; + if (ph->p_flags & PF_R) prot |= VKI_PROT_READ; + + addr = ph->p_vaddr+base; + off = ph->p_offset; + filesz = ph->p_filesz; + bss = addr+filesz; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + // Tom says: In the following, do what the Linux kernel does and only + // map the pages that are required instead of rounding everything to + // the specified alignment (ph->p_align). (AMD64 doesn't work if you + // use ph->p_align -- part of stage2's memory gets trashed somehow.) + // + // The condition handles the case of a zero-length segment. + if (VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr) > 0) { + if (0) VG_(debugLog)(0,"ume","mmap_file_fixed_client #1\n"); + res = VG_(am_mmap_file_fixed_client)( + VG_PGROUNDDN(addr), + VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr), + prot, /*VKI_MAP_FIXED|VKI_MAP_PRIVATE, */ + e->fd, VG_PGROUNDDN(off) + ); + if (0) VG_(am_show_nsegments)(0,"after #1"); + check_mmap(res, VG_PGROUNDDN(addr), + VG_PGROUNDUP(bss)-VG_PGROUNDDN(addr)); + } + + // if memsz > filesz, fill the remainder with zeroed pages + if (memsz > filesz) { + UInt bytes; + + bytes = VG_PGROUNDUP(brkaddr)-VG_PGROUNDUP(bss); + if (bytes > 0) { + if (0) VG_(debugLog)(0,"ume","mmap_anon_fixed_client #2\n"); + res = VG_(am_mmap_anon_fixed_client)( + VG_PGROUNDUP(bss), bytes, + prot + ); + if (0) VG_(am_show_nsegments)(0,"after #2"); + check_mmap(res, VG_PGROUNDUP(bss), bytes); + } + + bytes = bss & (VKI_PAGE_SIZE - 1); + + // The 'prot' condition allows for a read-only bss + if ((prot & VKI_PROT_WRITE) && (bytes > 0)) { + bytes = VKI_PAGE_SIZE - bytes; + VG_(memset)((char *)bss, 0, bytes); + } + } + } + + return elfbrk; +} + +Bool VG_(match_ELF)(Char *hdr, Int len) +{ + ESZ(Ehdr) *e = (ESZ(Ehdr) *)hdr; + return (len > sizeof(*e)) && VG_(memcmp)(&e->e_ident[0], ELFMAG, SELFMAG) == 0; +} + + +/* load_ELF pulls an ELF executable into the address space, prepares + it for execution, and writes info about it into INFO. In + particular it fills in .init_eip, which is the starting point. + + Returns zero on success, non-zero (a VKI_E.. value) on failure. + + The sequence of activities is roughly as follows: + + - use readelf() to extract program header info from the exe file. + + - scan the program header, collecting info (not sure what all those + info-> fields are, or whether they are used, but still) and in + particular looking out fo the PT_INTERP header, which describes + the interpreter. If such a field is found, the space needed to + hold the interpreter is computed into interp_size. + + - map the executable in, by calling mapelf(). This maps in all + loadable sections, and I _think_ also creates any .bss areas + required. mapelf() returns the address just beyond the end of + the furthest-along mapping it creates. The executable is mapped + starting at EBASE, which is usually read from it (eg, 0x8048000 + etc) except if it's a PIE, in which case I'm not sure what + happens. + + The returned address is recorded in info->brkbase as the start + point of the brk (data) segment, as it is traditional to place + the data segment just after the executable. Neither load_ELF nor + mapelf creates the brk segment, though: that is for the caller of + load_ELF to attend to. + + - If the initial phdr scan didn't find any mention of an + interpreter (interp == NULL), this must be a statically linked + executable, and we're pretty much done. + + - Otherwise, we need to use mapelf() a second time to load the + interpreter. The interpreter can go anywhere, but mapelf() wants + to be told a specific address to put it at. So an advisory query + is passed to aspacem, asking where it would put an anonymous + client mapping of size INTERP_SIZE. That address is then used + as the mapping address for the interpreter. + + - The entry point in INFO is set to the interpreter's entry point, + and we're done. */ +Int VG_(load_ELF)(Int fd, const HChar* name, /*MOD*/ExeInfo* info) +{ + SysRes sres; + struct elfinfo *e; + struct elfinfo *interp = NULL; + ESZ(Addr) minaddr = ~0; /* lowest mapped address */ + ESZ(Addr) maxaddr = 0; /* highest mapped address */ + ESZ(Addr) interp_addr = 0; /* interpreter (ld.so) address */ + ESZ(Word) interp_size = 0; /* interpreter size */ + ESZ(Word) interp_align = VKI_PAGE_SIZE; + Int i; + void *entry; + ESZ(Addr) ebase = 0; + + /* The difference between where the interpreter got mapped and + where it asked to be mapped. Needed for computing the ppc64 ELF + entry point and initial tocptr (R2) value. */ + ESZ(Word) interp_offset = 0; + +#ifdef HAVE_PIE + ebase = info->exe_base; +#endif + + e = readelf(fd, name); + + if (e == NULL) + return VKI_ENOEXEC; + + /* The kernel maps position-independent executables at TASK_SIZE*2/3; + duplicate this behavior as close as we can. */ + if (e->e.e_type == ET_DYN && ebase == 0) { + ebase = VG_PGROUNDDN(info->exe_base + + (info->exe_end - info->exe_base) * 2 / 3); + /* We really don't want to load PIEs at zero or too close. It + works, but it's unrobust (NULL pointer reads and writes + become legit, which is really bad) and causes problems for + exp-ptrcheck, which assumes all numbers below 1MB are + nonpointers. So, hackily, move it above 1MB. */ + /* Later .. is appears ppc32-linux tries to put [vdso] at 1MB, + which totally screws things up, because nothing else can go + there. So bump the hacky load addess along by 0x8000, to + 0x108000. */ + if (ebase < 0x108000) + ebase = 0x108000; + } + + info->phnum = e->e.e_phnum; + info->entry = e->e.e_entry + ebase; + info->phdr = 0; + + for (i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + + switch(ph->p_type) { + case PT_PHDR: + info->phdr = ph->p_vaddr + ebase; + break; + + case PT_LOAD: + if (ph->p_vaddr < minaddr) + minaddr = ph->p_vaddr; + if (ph->p_vaddr+ph->p_memsz > maxaddr) + maxaddr = ph->p_vaddr+ph->p_memsz; + break; + + case PT_INTERP: { + HChar *buf = VG_(malloc)("ume.LE.1", ph->p_filesz+1); + Int j; + Int intfd; + Int baseaddr_set; + + vg_assert(buf); + VG_(pread)(fd, buf, ph->p_filesz, ph->p_offset); + buf[ph->p_filesz] = '\0'; + + sres = VG_(open)(buf, VKI_O_RDONLY, 0); + if (sres.isError) { + VG_(printf)("valgrind: m_ume.c: can't open interpreter\n"); + VG_(exit)(1); + } + intfd = sres.res; + + interp = readelf(intfd, buf); + if (interp == NULL) { + VG_(printf)("valgrind: m_ume.c: can't read interpreter\n"); + return 1; + } + VG_(free)(buf); + + baseaddr_set = 0; + for (j = 0; j < interp->e.e_phnum; j++) { + ESZ(Phdr) *iph = &interp->p[j]; + ESZ(Addr) end; + + if (iph->p_type != PT_LOAD) + continue; + + if (!baseaddr_set) { + interp_addr = iph->p_vaddr; + interp_align = iph->p_align; + baseaddr_set = 1; + } + + /* assumes that all segments in the interp are close */ + end = (iph->p_vaddr - interp_addr) + iph->p_memsz; + + if (end > interp_size) + interp_size = end; + } + break; + + default: + // do nothing + break; + } + } + } + + if (info->phdr == 0) + info->phdr = minaddr + ebase + e->e.e_phoff; + + if (info->exe_base != info->exe_end) { + if (minaddr >= maxaddr || + (minaddr + ebase < info->exe_base || + maxaddr + ebase > info->exe_end)) { + VG_(printf)("Executable range %p-%p is outside the\n" + "acceptable range %p-%p\n", + (char *)minaddr + ebase, (char *)maxaddr + ebase, + (char *)info->exe_base, (char *)info->exe_end); + return VKI_ENOMEM; + } + } + + info->brkbase = mapelf(e, ebase); /* map the executable */ + + if (info->brkbase == 0) + return VKI_ENOMEM; + + if (interp != NULL) { + /* reserve a chunk of address space for interpreter */ + MapRequest mreq; + Addr advised; + Bool ok; + + /* Don't actually reserve the space. Just get an advisory + indicating where it would be allocated, and pass that to + mapelf(), which in turn asks aspacem to do some fixed maps at + the specified address. This is a bit of hack, but it should + work because there should be no intervening transactions with + aspacem which could cause those fixed maps to fail. + + Placement policy is: + + if the interpreter asks to be loaded at zero + ignore that and put it wherever we like (mappings at zero + are bad news) + else + try and put it where it asks for, but if that doesn't work, + just put it anywhere. + */ + if (interp_addr == 0) { + mreq.rkind = MAny; + mreq.start = 0; + mreq.len = interp_size; + } else { + mreq.rkind = MHint; + mreq.start = interp_addr; + mreq.len = interp_size; + } + + advised = VG_(am_get_advisory)( &mreq, True/*client*/, &ok ); + + if (!ok) { + /* bomb out */ + SysRes res = VG_(mk_SysRes_Error)(VKI_EINVAL); + if (0) VG_(printf)("reserve for interp: failed\n"); + check_mmap(res, (Addr)interp_addr, interp_size); + /*NOTREACHED*/ + } + + (void)mapelf(interp, (ESZ(Addr))advised - interp_addr); + + VG_(close)(interp->fd); + + entry = (void *)(advised - interp_addr + interp->e.e_entry); + info->interp_base = (ESZ(Addr))advised; + interp_offset = advised - interp_addr; + + VG_(free)(interp->p); + VG_(free)(interp); + } else + entry = (void *)(ebase + e->e.e_entry); + + info->exe_base = minaddr + ebase; + info->exe_end = maxaddr + ebase; + +#if defined(VGP_ppc64_linux) + /* On PPC64, a func ptr is represented by a TOC entry ptr. This + TOC entry contains three words; the first word is the function + address, the second word is the TOC ptr (r2), and the third word + is the static chain value. */ + info->init_ip = ((ULong*)entry)[0]; + info->init_toc = ((ULong*)entry)[1]; + info->init_ip += interp_offset; + info->init_toc += interp_offset; +#else + info->init_ip = (Addr)entry; + info->init_toc = 0; /* meaningless on this platform */ +#endif + VG_(free)(e->p); + VG_(free)(e); + + return 0; +} + +#endif /* defined(HAVE_ELF) */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/main.c b/coregrind/m_ume/main.c new file mode 100644 index 0000000..786e176 --- /dev/null +++ b/coregrind/m_ume/main.c @@ -0,0 +1,286 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve(), and other stuff shared between stage1 ---*/ +/*--- and stage2. m_ume.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcfile.h" // VG_(close) et al +#include "pub_core_libcprint.h" // VG_(message) +#include "pub_core_mallocfree.h" // VG_(strdup) +#include "pub_core_syscall.h" // VG_(mk_SysRes_Error) +#include "pub_core_options.h" // VG_(clo_xml) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" + + +typedef struct { + const HChar *name; + Bool (*match_fn)(Char *hdr, Int len); + Int (*load_fn)(Int fd, const HChar *name, ExeInfo *info); +} ExeHandler; + +static ExeHandler exe_handlers[] = { +#if defined(HAVE_ELF) + { "ELF", VG_(match_ELF), VG_(load_ELF) }, +#endif +#if defined(HAVE_SCRIPT) + { "script", VG_(match_script), VG_(load_script) }, +#endif +}; +#define EXE_HANDLER_COUNT (sizeof(exe_handlers)/sizeof(exe_handlers[0])) + + +// Check the file looks executable. +SysRes +VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, Bool allow_setuid) +{ + Int fd, ret, i; + SysRes res; + Char buf[4096]; + SizeT bufsz = 4096, fsz; + Bool is_setuid = False; + + // Check it's readable + res = VG_(open)(exe_name, VKI_O_RDONLY, 0); + if (res.isError) { + return res; + } + fd = res.res; + + // Check we have execute permissions + ret = VG_(check_executable)(&is_setuid, (HChar*)exe_name, allow_setuid); + if (0 != ret) { + VG_(close)(fd); + if (is_setuid && !VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "Warning: Can't execute setuid/setgid executable: %s", + exe_name); + VG_(message)(Vg_UserMsg, "Possible workaround: remove " + "--trace-children=yes, if in effect"); + VG_(message)(Vg_UserMsg, ""); + } + return VG_(mk_SysRes_Error)(ret); + } + + fsz = (SizeT)VG_(fsize)(fd); + if (fsz < bufsz) + bufsz = fsz; + + res = VG_(pread)(fd, buf, bufsz, 0); + if (res.isError || res.res != bufsz) { + VG_(close)(fd); + return VG_(mk_SysRes_Error)(VKI_EACCES); + } + bufsz = res.res; + + // Look for a matching executable format + for (i = 0; i < EXE_HANDLER_COUNT; i++) { + if ((*exe_handlers[i].match_fn)(buf, bufsz)) { + res = VG_(mk_SysRes_Success)(i); + break; + } + } + if (i == EXE_HANDLER_COUNT) { + // Rejected by all executable format handlers. + res = VG_(mk_SysRes_Error)(VKI_ENOEXEC); + } + + // Write the 'out_fd' param if necessary, or close the file. + if (!res.isError && out_fd) { + *out_fd = fd; + } else { + VG_(close)(fd); + } + + return res; +} + +// returns: 0 = success, non-0 is failure +// +// We can execute only binaries (ELF, etc) or scripts that begin with "#!". +// (Not, for example, scripts that don't begin with "#!"; see the +// VG_(do_exec)() invocation from m_main.c for how that's handled.) +Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info) +{ + SysRes res; + Int fd; + Int ret; + + res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/); + if (res.isError) + return res.err; + + vg_assert2(res.res >= 0 && res.res < EXE_HANDLER_COUNT, + "invalid VG_(pre_exec_check) result"); + + ret = (*exe_handlers[res.res].load_fn)(fd, exe, info); + + VG_(close)(fd); + + return ret; +} + + +static Bool is_hash_bang_file(Char* f) +{ + SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); + if (!res.isError) { + Char buf[3] = {0,0,0}; + Int fd = res.res; + Int n = VG_(read)(fd, buf, 2); + if (n == 2 && VG_STREQ("#!", buf)) + return True; + } + return False; +} + +// Look at the first 80 chars, and if any are greater than 127, it's binary. +// This is crude, but should be good enough. Note that it fails on a +// zero-length file, as we want. +static Bool is_binary_file(Char* f) +{ + SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); + if (!res.isError) { + UChar buf[80]; + Int fd = res.res; + Int n = VG_(read)(fd, buf, 80); + Int i; + for (i = 0; i < n; i++) { + if (buf[i] > 127) + return True; // binary char found + } + return False; + } else { + // Something went wrong. This will only happen if we earlier + // succeeded in opening the file but fail here (eg. the file was + // deleted between then and now). + VG_(printf)("valgrind: %s: unknown error\n", f); + VG_(exit)(126); // 126 == NOEXEC + } +} + +// If the do_exec fails we try to emulate what the shell does (I used +// bash as a guide). It's worth noting that the shell can execute some +// things that VG_(do_exec)() (which subsitutes for the kernel's exec()) +// will refuse to (eg. scripts lacking a "#!" prefix). +static Int do_exec_shell_followup(Int ret, HChar* exe_name, ExeInfo* info) +{ + Char* default_interp_name = "/bin/sh"; + SysRes res; + struct vg_stat st; + + if (VKI_ENOEXEC == ret) { + // It was an executable file, but in an unacceptable format. Probably + // is a shell script lacking the "#!" prefix; try to execute it so. + + // Is it a binary file? + if (is_binary_file(exe_name)) { + VG_(printf)("valgrind: %s: cannot execute binary file\n", exe_name); + VG_(exit)(126); // 126 == NOEXEC + } + + // Looks like a script. Run it with /bin/sh. This includes + // zero-length files. + + info->interp_name = VG_(strdup)("ume.desf.1", default_interp_name); + info->interp_args = NULL; + if (info->argv && info->argv[0] != NULL) + info->argv[0] = (char *)exe_name; + + ret = VG_(do_exec_inner)(info->interp_name, info); + + if (0 != ret) { + // Something went wrong with executing the default interpreter + VG_(printf)("valgrind: %s: bad interpreter (%s): %s\n", + exe_name, info->interp_name, VG_(strerror)(ret)); + VG_(exit)(126); // 126 == NOEXEC + } + + } else if (0 != ret) { + // Something else went wrong. Try to make the error more specific, + // and then print a message and abort. + + // Was it a directory? + res = VG_(stat)(exe_name, &st); + if (!res.isError && VKI_S_ISDIR(st.st_mode)) { + VG_(printf)("valgrind: %s: is a directory\n", exe_name); + + // Was it not executable? + } else if (0 != VG_(check_executable)(NULL, exe_name, + False/*allow_setuid*/)) { + VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret)); + + // Did it start with "#!"? If so, it must have been a bad interpreter. + } else if (is_hash_bang_file(exe_name)) { + VG_(printf)("valgrind: %s: bad interpreter: %s\n", + exe_name, VG_(strerror)(ret)); + + // Otherwise it was something else. + } else { + VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret)); + } + // 126 means NOEXEC; I think this is Posix, and that in some cases we + // should be returning 127, meaning NOTFOUND. Oh well. + VG_(exit)(126); + } + return ret; +} + + +// This emulates the kernel's exec(). If it fails, it then emulates the +// shell's handling of the situation. +// See ume.h for an indication of which entries of 'info' are inputs, which +// are outputs, and which are both. +/* returns: 0 = success, non-0 is failure */ +Int VG_(do_exec)(const HChar* exe_name, ExeInfo* info) +{ + Int ret; + + info->interp_name = NULL; + info->interp_args = NULL; + + ret = VG_(do_exec_inner)(exe_name, info); + + if (0 != ret) { + Char* exe_name_casted = (Char*)exe_name; + ret = do_exec_shell_followup(ret, exe_name_casted, info); + } + return ret; +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/priv_ume.h b/coregrind/m_ume/priv_ume.h new file mode 100644 index 0000000..2ac4189 --- /dev/null +++ b/coregrind/m_ume/priv_ume.h @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------*/ +/*--- User-mode execve(). priv_ume.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2005 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PRIV_UME_H +#define __PRIV_UME_H + +#if defined(VGO_linux) +# define HAVE_ELF +# define HAVE_SCRIPT + +#elif defined(VGO_aix5) +// The AIX port doesn't use UME. + +#else +#error unknown architecture +#endif + +extern int VG_(do_exec_inner)(const HChar *exe, ExeInfo *info); + +#if defined(HAVE_ELF) +extern Bool VG_(match_ELF) ( Char *hdr, Int len ); +extern Int VG_(load_ELF) ( Int fd, const HChar *name, ExeInfo *info ); +#endif + +#if defined(HAVE_SCRIPT) +extern Bool VG_(match_script) ( Char *hdr, Int len ); +extern Int VG_(load_script) ( Int fd, const HChar *name, ExeInfo *info ); +#endif + +#endif /* __PRIV_UME_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/coregrind/m_ume/script.c b/coregrind/m_ume/script.c new file mode 100644 index 0000000..8df563e --- /dev/null +++ b/coregrind/m_ume/script.c @@ -0,0 +1,150 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve() for #! scripts. m_ume_script.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcfile.h" // VG_(close) et al +#include "pub_core_libcprint.h" +#include "pub_core_mallocfree.h" // VG_(strdup) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" // self + + +#if defined(HAVE_SCRIPT) + +Bool VG_(match_script)(Char *hdr, Int len) +{ + Char* end = hdr + len; + Char* interp = hdr + 2; + + // len < 4: need '#', '!', plus at least a '/' and one more char + if (len < 4) return False; + if (0 != VG_(memcmp)(hdr, "#!", 2)) return False; + + // Find interpreter name, make sure it's an absolute path (starts with + // '/') and has at least one more char. First, skip over any space + // between the #! and the start of the interpreter name + while (interp < end && VG_(isspace)(*interp)) interp++; + + // overrun? + if (interp >= end) return False; // can't find start of interp name + + // interp should now point at the / + if (*interp != '/') return False; // absolute path only for interpreter + + // check for something plausible after the / + interp++; + if (interp >= end) return False; + if (VG_(isspace)(*interp)) return False; + + // Here we should get the full interpreter name and check it with + // check_executable(). See the "EXEC FAILED" failure when running shell + // for an example. + + return True; // looks like a #! script +} + + +/* returns: 0 = success, non-0 is failure */ +Int VG_(load_script)(Int fd, const HChar* name, ExeInfo* info) +{ + Char hdr[4096]; + Int len = 4096; + Int eol; + Char* interp; + Char* end; + Char* cp; + Char* arg = NULL; + SysRes res; + + // Read the first part of the file. + res = VG_(pread)(fd, hdr, len, 0); + if (res.isError) { + VG_(close)(fd); + return VKI_EACCES; + } else { + len = res.res; + } + + vg_assert('#' == hdr[0] && '!' == hdr[1]); + + end = hdr + len; + interp = hdr + 2; + while (interp < end && VG_(isspace)(*interp)) + interp++; + + vg_assert(*interp == '/'); /* absolute path only for interpreter */ + + /* skip over interpreter name */ + for (cp = interp; cp < end && !VG_(isspace)(*cp); cp++) + ; + + eol = (*cp == '\n'); + + *cp++ = '\0'; + + if (!eol && cp < end) { + /* skip space before arg */ + while (cp < end && VG_(isspace)(*cp) && *cp != '\n') + cp++; + + /* arg is from here to eol */ + arg = cp; + while (cp < end && *cp != '\n') + cp++; + *cp = '\0'; + } + + info->interp_name = VG_(strdup)("ume.ls.1", interp); + vg_assert(NULL != info->interp_name); + if (arg != NULL && *arg != '\0') { + info->interp_args = VG_(strdup)("ume.ls.2", arg); + vg_assert(NULL != info->interp_args); + } + + if (info->argv && info->argv[0] != NULL) + info->argv[0] = (char *)name; + + if (0) + VG_(printf)("#! script: interp_name=\"%s\" interp_args=\"%s\"\n", + info->interp_name, info->interp_args); + + return VG_(do_exec_inner)(interp, info); +} + +#endif /* defined(HAVE_SCRIPT) */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ |