the DARWIN branch easier later.
- Remove the disabled vgtest_ume test, it's very unlikely it'll ever work
again.
- Move VG_(find_auxv) to initimg-linux.c, the only place it's used, and make
it static.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9004
pub_core_vkiscnums.h \
pub_core_wordfm.h \
pub_core_xarray.h \
+ m_aspacemgr/priv_aspacemgr.h \
m_coredump/priv_elf.h \
m_debuginfo/priv_misc.h \
m_debuginfo/priv_storage.h \
m_syswrap/priv_syswrap-linux-variants.h \
m_syswrap/priv_syswrap-aix5.h \
m_syswrap/priv_syswrap-main.h \
- m_aspacemgr/priv_aspacemgr.h \
+ m_ume/priv_ume.h \
launcher-aix5-bootblock.h \
m_initimg/simple_huffman.c
m_trampoline.S \
m_translate.c \
m_transtab.c \
- m_ume.c \
m_vki.c \
m_vkiscnums.c \
m_wordfm.c \
m_replacemalloc/replacemalloc_core.c \
m_scheduler/scheduler.c \
m_scheduler/sema.c \
- m_syswrap/syswrap-main.c
+ m_syswrap/syswrap-main.c \
+ m_ume/elf.c \
+ m_ume/main.c \
+ m_ume/script.c
COREGRIND_LINUX_SOURCE = \
m_coredump/coredump-elf.c \
---------------------------------------------------------------- */
+struct auxv
+{
+ Word a_type;
+ union {
+ void *a_ptr;
+ Word a_val;
+ } u;
+};
+
+static
+struct auxv *find_auxv(UWord* sp)
+{
+ sp++; // skip argc (Nb: is word-sized, not int-sized!)
+
+ while (*sp != 0) // skip argv
+ sp++;
+ sp++;
+
+ while (*sp != 0) // skip env
+ sp++;
+ sp++;
+
+#if defined(VGA_ppc32) || defined(VGA_ppc64)
+# if defined AT_IGNOREPPC
+ while (*sp == AT_IGNOREPPC) // skip AT_IGNOREPPC entries
+ sp += 2;
+# endif
+#endif
+
+ return (struct auxv *)sp;
+}
+
static
Addr setup_client_stack( void* init_sp,
char** orig_envp,
char *strtab; /* string table */
char *stringbase;
Addr *ptr;
- struct ume_auxv *auxv;
- const struct ume_auxv *orig_auxv;
- const struct ume_auxv *cauxv;
+ struct auxv *auxv;
+ const struct auxv *orig_auxv;
+ const struct auxv *cauxv;
unsigned stringsize; /* total size of strings in bytes */
unsigned auxsize; /* total size of auxv in bytes */
Int argc; /* total argc */
vg_assert( VG_(args_for_client) );
/* use our own auxv as a prototype */
- orig_auxv = VG_(find_auxv)(init_sp);
+ orig_auxv = find_auxv(init_sp);
/* ==================== compute sizes ==================== */
*ptr++ = 0;
/* --- auxv --- */
- auxv = (struct ume_auxv *)ptr;
+ auxv = (struct auxv *)ptr;
*client_auxv = (UInt *)auxv;
# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
/*--------------------------------------------------------------------*/
-/*--- User-mode execve(), and other stuff shared between stage1 ---*/
-/*--- and stage2. m_ume.c ---*/
+/*--- User-mode execve() for ELF executables m_ume_elf.c ---*/
/*--------------------------------------------------------------------*/
/*
The GNU General Public License is contained in the file COPYING.
*/
-
#include "pub_core_basics.h"
#include "pub_core_vki.h"
-#if defined(VGO_linux)
-
-#include "pub_core_aspacemgr.h" // various mapping fns
+#include "pub_core_aspacemgr.h" // various mapping fns
#include "pub_core_debuglog.h"
-#include "pub_core_libcbase.h"
-#include "pub_core_machine.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_(close) et al
-#include "pub_core_libcproc.h" // VG_(geteuid), VG_(getegid)
-#include "pub_core_libcassert.h" // VG_(exit), vg_assert
-#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free)
-#include "pub_core_syscall.h" // VG_(strerror)
-#include "pub_core_options.h" // VG_(clo_xml)
-#include "pub_core_ume.h" // self
+#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
/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
-#if VG_WORDSIZE == 8
-#define ESZ(x) Elf64_##x
-#elif VG_WORDSIZE == 4
-#define ESZ(x) Elf32_##x
+#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;
+ ESZ(Ehdr) e;
+ ESZ(Phdr) *p;
+ Int fd;
};
static void check_mmap(SysRes res, Addr base, SizeT len)
}
}
-/*------------------------------------------------------------*/
-/*--- Finding auxv on the stack ---*/
-/*------------------------------------------------------------*/
-
-struct ume_auxv *VG_(find_auxv)(UWord* sp)
-{
- sp++; // skip argc (Nb: is word-sized, not int-sized!)
-
- while (*sp != 0) // skip argv
- sp++;
- sp++;
-
- while (*sp != 0) // skip env
- sp++;
- sp++;
-
-#if defined(VGA_ppc32) || defined(VGA_ppc64)
-# if defined AT_IGNOREPPC
- while (*sp == AT_IGNOREPPC) // skip AT_IGNOREPPC entries
- sp += 2;
-# endif
-#endif
-
- return (struct ume_auxv *)sp;
-}
-
/*------------------------------------------------------------*/
/*--- Loading ELF files ---*/
/*------------------------------------------------------------*/
SysRes res;
ESZ(Addr) elfbrk = 0;
- for(i = 0; i < e->e.e_phnum; i++) {
+ 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;
+ continue;
addr = ph->p_vaddr+base;
memsz = ph->p_memsz;
brkaddr = addr+memsz;
if (brkaddr > elfbrk)
- elfbrk = brkaddr;
+ elfbrk = brkaddr;
}
- for(i = 0; i < e->e.e_phnum; i++) {
+ for (i = 0; i < e->e.e_phnum; i++) {
ESZ(Phdr) *ph = &e->p[i];
ESZ(Addr) addr, bss, brkaddr;
ESZ(Off) off;
unsigned prot = 0;
if (ph->p_type != PT_LOAD)
- continue;
+ continue;
if (ph->p_flags & PF_X) prot |= VKI_PROT_EXEC;
if (ph->p_flags & PF_W) prot |= VKI_PROT_WRITE;
// if memsz > filesz, fill the remainder with zeroed pages
if (memsz > filesz) {
- UInt bytes;
+ UInt bytes;
- bytes = VG_PGROUNDUP(brkaddr)-VG_PGROUNDUP(bss);
- if (bytes > 0) {
+ 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)(
+ res = VG_(am_mmap_anon_fixed_client)(
VG_PGROUNDUP(bss), bytes,
- prot
+ prot
);
if (0) VG_(am_show_nsegments)(0,"after #2");
check_mmap(res, VG_PGROUNDUP(bss), bytes);
}
- bytes = bss & (VKI_PAGE_SIZE - 1);
+ 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);
- }
+ bytes = VKI_PAGE_SIZE - bytes;
+ VG_(memset)((char *)bss, 0, bytes);
+ }
}
}
return elfbrk;
}
-static Bool match_ELF(const char *hdr, Int len)
+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;
- The entry point in INFO is set to the interpreter's entry point,
and we're done. */
-static Int load_ELF(Int fd, const HChar* name, /*MOD*/ExeInfo* info)
+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(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;
info->entry = e->e.e_entry + ebase;
info->phdr = 0;
- for(i = 0; i < e->e.e_phnum; i++) {
+ 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;
+ 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;
-
+ 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;
+ 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';
+ VG_(pread)(fd, buf, ph->p_filesz, ph->p_offset);
+ buf[ph->p_filesz] = '\0';
- sres = VG_(open)(buf, VKI_O_RDONLY, 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);
- }
+ 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;
+ 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
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"
+ (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;
+ return VKI_ENOMEM;
}
}
- info->brkbase = mapelf(e, ebase); /* map the executable */
+ info->brkbase = mapelf(e, ebase); /* map the executable */
if (info->brkbase == 0)
return VKI_ENOMEM;
return 0;
}
-
-static Bool 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
-}
-
-// Forward declaration.
-static Int do_exec_inner(const HChar* exe, ExeInfo* info);
-
-/* returns: 0 = success, non-0 is failure */
-static Int 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 do_exec_inner(interp, info);
-}
-
-
-typedef enum {
- VG_EXE_FORMAT_ELF = 1,
- VG_EXE_FORMAT_SCRIPT = 2,
-} ExeFormat;
-
-// Check the file looks executable.
-SysRes
-VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, Bool allow_setuid)
-{
- Int fd, ret;
- 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;
-
- if (match_ELF(buf, bufsz)) {
- res = VG_(mk_SysRes_Success)(VG_EXE_FORMAT_ELF);
- } else if (match_script(buf, bufsz)) {
- res = VG_(mk_SysRes_Success)(VG_EXE_FORMAT_SCRIPT);
- } else {
- 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 ELF binaries 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.)
-static Int 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;
-
- switch (res.res) {
- case VG_EXE_FORMAT_ELF: ret = load_ELF (fd, exe, info); break;
- case VG_EXE_FORMAT_SCRIPT: ret = load_script(fd, exe, info); break;
- default:
- vg_assert2(0, "unrecognised VG_EXE_FORMAT value\n");
- }
-
- 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 = 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 = 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;
-}
-
-#endif /* defined(VGO_linux) */
+#endif /* defined(HAVE_ELF) */
/*--------------------------------------------------------------------*/
/*--- end ---*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- 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-2008 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 ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- User-mode execve() for #! scripts. m_ume_script.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2008 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 ---*/
+/*--------------------------------------------------------------------*/
//--------------------------------------------------------------------
/*------------------------------------------------------------*/
-/*--- Loading ELF files ---*/
+/*--- Loading files ---*/
/*------------------------------------------------------------*/
// Info needed to load and run a program. IN/INOUT/OUT refers to the
// Do a number of appropriate checks to see if the file looks executable by
// the kernel: ie. it's a file, it's readable and executable, and it's in
-// either ELF or "#!" format. On success, 'out_fd' gets the fd of the file
+// either binary or "#!" format. On success, 'out_fd' gets the fd of the file
// if it's non-NULL. Otherwise the fd is closed.
extern SysRes VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd,
Bool allow_setuid);
// the program.
extern Int VG_(do_exec)(const HChar* exe, ExeInfo* info);
-/*------------------------------------------------------------*/
-/*--- Finding and dealing with auxv ---*/
-/*------------------------------------------------------------*/
-
-struct ume_auxv
-{
- Word a_type;
- union {
- void *a_ptr;
- Word a_val;
- } u;
-};
-
-extern struct ume_auxv *VG_(find_auxv)(UWord* orig_esp);
-
#endif /* __PUB_CORE_UME_H */
/*--------------------------------------------------------------------*/
vcpu_fbench.stdout.exp vcpu_fbench.stderr.exp vcpu_fbench.vgtest \
vcpu_fnfns.stdout.exp vcpu_fnfns.stdout.exp-glibc28-amd64 \
vcpu_fnfns.stderr.exp vcpu_fnfns.vgtest \
- vgtest_ume.stderr.exp vgtest_ume.disabled \
with-space.stderr.exp with-space.stdout.exp with-space.vgtest \
wrap1.vgtest wrap1.stdout.exp wrap1.stderr.exp \
wrap2.vgtest wrap2.stdout.exp wrap2.stderr.exp \
zeropage.stderr.exp zeropage.stderr.exp2 zeropage.stdout.exp \
zeropage.vgtest
-# vgtest_ume is not working
check_PROGRAMS = \
addressable \
badaddrvalue badfree badjump badjump2 \
# -Wl,-T,$(top_builddir)/valt_load_address.lds
#hello_DEPENDENCIES = $(top_builddir)/valt_load_address.lds
-# vgtest_ume is not working
-#vgtest_ume_CFLAGS = -DVGA_$(VG_ARCH_PRI) -DVGO_$(VG_OS)
-#vgtest_ume_LDADD = ../../coregrind/m_ume.o
-
+++ /dev/null
-
-// This file is a unit self-test for ume.c, jmp_with_stack.c
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <elf.h>
-#include "../../include/pub_tool_basics.h"
-#include "../../coregrind/pub_core_ume.h"
-
-#define STKSZ (64*1024)
-
-static void* init_sp;
-
-//-------------------------------------------------------------------
-// Test VG_(foreach_map)()
-//-------------------------------------------------------------------
-
-static int x[8];
-
-static int f(char *start, char *end, const char *perm, off_t off,
- int maj, int min, int ino, void* dummy) {
- // Just do some nonsense action with each of the values so that Memcheck
- // checks that they are valid.
- x[0] = ( start == 0 ? 0 : 1 );
- x[1] = ( end == 0 ? 0 : 1 );
- x[2] = ( perm == 0 ? 0 : 1 );
- x[3] = ( off == 0 ? 0 : 1 );
- x[4] = ( maj == 0 ? 0 : 1 );
- x[5] = ( min == 0 ? 0 : 1 );
- x[6] = ( ino == 0 ? 0 : 1 );
- x[7] = ( dummy == 0 ? 0 : 1 );
-
- return /*True*/1 + x[0] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7];
-}
-
-static void test__foreach_map(void)
-{
- fprintf(stderr, "Calling VG_(foreach_map)()\n");
- VG_(foreach_map)(f, /*dummy*/NULL);
-}
-
-//-------------------------------------------------------------------
-// Test VG_(find_auxv)()
-//-------------------------------------------------------------------
-
-static void test__find_auxv(void)
-{
- struct ume_auxv *auxv;
-
- assert(init_sp != NULL);
-
- fprintf(stderr, "Calling VG_(find_auxv)()\n");
- auxv = VG_(find_auxv)((UWord*)init_sp);
-
- // Check the auxv value looks sane
- assert((void*)auxv > (void*)init_sp);
- assert((unsigned int)auxv - (unsigned int)init_sp < 0x10000);
-
- // Scan the auxv, check it looks sane
- for (; auxv->a_type != AT_NULL; auxv++) {
- switch(auxv->a_type) {
- // Check a_type value looks like a plausible small constant
- case 1 ... 64:
- break;
-
- default:
- fprintf(stderr, "auxv->a_type = %lld\n", (Long)auxv->a_type);
- assert(0);
- }
- }
-}
-
-//-------------------------------------------------------------------
-// Test VG_(do_exec)()
-//-------------------------------------------------------------------
-
-static void push_auxv(unsigned char **espp, int type, void *val)
-{
- struct ume_auxv *auxp = (struct ume_auxv *)*espp;
- auxp--;
- auxp->a_type = type;
- auxp->u.a_ptr = val;
- *espp = (unsigned char *)auxp;
-}
-
-static void push(unsigned char **espp, void *v)
-{
- void **vp = *(void ***)espp;
- *--vp = v;
- *espp = (unsigned char *)vp;
-}
-
-static void test__do_exec(void)
-{
- struct exeinfo info;
- int err;
- unsigned char* newstack;
- unsigned char *esp;
-
- info.argv = NULL;
- info.exe_base = 0x50000000;
- info.exe_end = 0x50ffffff;
-
- fprintf(stderr, "Calling VG_(do_exec)(\"hello\")\n");
- err = VG_(do_exec)("hello", &info);
- assert(0 == err);
-
-// printf("info.exe_base=%p exe_end=%p\n",
-// (void*)info.exe_base, (void*)info.exe_end);
-
- newstack = malloc(STKSZ);
- assert(0 != newstack);
-
- esp = newstack+STKSZ;
-
- /*
- Set the new executable's stack up like the kernel would after
- exec.
-
- These are being pushed onto the stack, towards decreasing
- addresses.
- */
- push_auxv(&esp, AT_NULL, 0); // auxv terminator
- push_auxv(&esp, AT_ENTRY, (void *)info.entry); // entrypoint of the main executable */
- push_auxv(&esp, AT_BASE, (void *)info.interp_base); // base address of ld-linux.so
- push_auxv(&esp, AT_PHDR, (void *)info.phdr); // where the ELF PHDRs are mapped
- push_auxv(&esp, AT_PHNUM, (void*)info.phnum); // and how many of them
-
- push(&esp, 0); /* no env */
- push(&esp, 0); /* no argv */
- push(&esp, 0); /* argc=0 */
-
-// fprintf(stderr, "ume_go: %p %p\n", (void*)info.init_eip, (void*)esp);
-
- VG_(jump_and_switch_stacks)((Addr)esp, info.init_eip);
-
- assert(0); // UNREACHABLE
-}
-
-int main(int argc, char** argv)
-{
- init_sp = argv - 1;
-
- test__foreach_map();
- test__find_auxv();
- test__do_exec();
-
- return 0;
-}
+++ /dev/null
-# This one currently not used because it doesn't compile any more
-prog: vgtest_ume
-vgopts: -q
+++ /dev/null
-Calling VG_(foreach_map)()
-Calling VG_(find_auxv)()
-Calling VG_(do_exec)("hello")
-Hello, world!