From: Nicholas Nethercote Date: Wed, 21 Jan 2009 02:26:56 +0000 (+0000) Subject: - Split up m_ume.c into m_ume/{main,elf,script}.c. This will make merging X-Git-Tag: svn/VALGRIND_3_5_0~1072 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c07262448b1e21bd4f1713c0d34bbc54facfc82a;p=thirdparty%2Fvalgrind.git - Split up m_ume.c into m_ume/{main,elf,script}.c. This will make merging 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 --- diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 9410b3c83c..4f247e5597 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -140,6 +140,7 @@ noinst_HEADERS = \ 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 \ @@ -163,7 +164,7 @@ noinst_HEADERS = \ 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 @@ -203,7 +204,6 @@ COREGRIND_SOURCES_COMMON = \ m_trampoline.S \ m_translate.c \ m_transtab.c \ - m_ume.c \ m_vki.c \ m_vkiscnums.c \ m_wordfm.c \ @@ -222,7 +222,10 @@ COREGRIND_SOURCES_COMMON = \ 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 \ diff --git a/coregrind/m_initimg/initimg-linux.c b/coregrind/m_initimg/initimg-linux.c index a886fba8d4..4d8472d29e 100644 --- a/coregrind/m_initimg/initimg-linux.c +++ b/coregrind/m_initimg/initimg-linux.c @@ -412,6 +412,38 @@ static char *copy_str(char **tab, const char *str) ---------------------------------------------------------------- */ +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, @@ -425,9 +457,9 @@ Addr setup_client_stack( void* init_sp, 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 */ @@ -442,7 +474,7 @@ Addr setup_client_stack( void* init_sp, 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 ==================== */ @@ -636,7 +668,7 @@ Addr setup_client_stack( void* init_sp, *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) diff --git a/coregrind/m_ume.c b/coregrind/m_ume/elf.c similarity index 50% rename from coregrind/m_ume.c rename to coregrind/m_ume/elf.c index 9e13a8e751..ec5d77c645 100644 --- a/coregrind/m_ume.c +++ b/coregrind/m_ume/elf.c @@ -1,7 +1,6 @@ /*--------------------------------------------------------------------*/ -/*--- User-mode execve(), and other stuff shared between stage1 ---*/ -/*--- and stage2. m_ume.c ---*/ +/*--- User-mode execve() for ELF executables m_ume_elf.c ---*/ /*--------------------------------------------------------------------*/ /* @@ -29,24 +28,24 @@ 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 @@ -56,19 +55,19 @@ /* --- !!! --- 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) @@ -86,32 +85,6 @@ 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 ---*/ /*------------------------------------------------------------*/ @@ -189,23 +162,23 @@ ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) 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; @@ -214,7 +187,7 @@ ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) 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; @@ -248,33 +221,33 @@ ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) // 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; @@ -324,15 +297,15 @@ static Bool match_ELF(const char *hdr, Int len) - 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; @@ -374,66 +347,66 @@ static Int load_ELF(Int fd, const HChar* name, /*MOD*/ExeInfo* info) 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 @@ -447,17 +420,17 @@ static Int load_ELF(Int fd, const HChar* name, /*MOD*/ExeInfo* info) 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; @@ -539,334 +512,7 @@ static Int load_ELF(Int fd, const HChar* name, /*MOD*/ExeInfo* info) 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 ---*/ diff --git a/coregrind/m_ume/main.c b/coregrind/m_ume/main.c new file mode 100644 index 0000000000..45530b70e1 --- /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-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 ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_ume/script.c b/coregrind/m_ume/script.c new file mode 100644 index 0000000000..77bc6d0cab --- /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-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 ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_ume.h b/coregrind/pub_core_ume.h index 18859310b5..21e8c08fcc 100644 --- a/coregrind/pub_core_ume.h +++ b/coregrind/pub_core_ume.h @@ -37,7 +37,7 @@ //-------------------------------------------------------------------- /*------------------------------------------------------------*/ -/*--- Loading ELF files ---*/ +/*--- Loading files ---*/ /*------------------------------------------------------------*/ // Info needed to load and run a program. IN/INOUT/OUT refers to the @@ -67,7 +67,7 @@ typedef // 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); @@ -78,21 +78,6 @@ extern SysRes VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, // 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 */ /*--------------------------------------------------------------------*/ diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index 8f54377f13..54cdbfc237 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -172,7 +172,6 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ 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 \ @@ -190,7 +189,6 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ zeropage.stderr.exp zeropage.stderr.exp2 zeropage.stdout.exp \ zeropage.vgtest -# vgtest_ume is not working check_PROGRAMS = \ addressable \ badaddrvalue badfree badjump badjump2 \ @@ -370,7 +368,3 @@ endif # -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 - diff --git a/memcheck/tests/vgtest_ume.c b/memcheck/tests/vgtest_ume.c deleted file mode 100644 index 0dcee7b55b..0000000000 --- a/memcheck/tests/vgtest_ume.c +++ /dev/null @@ -1,150 +0,0 @@ - -// This file is a unit self-test for ume.c, jmp_with_stack.c - -#include -#include -#include -#include -#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; -} diff --git a/memcheck/tests/vgtest_ume.disabled b/memcheck/tests/vgtest_ume.disabled deleted file mode 100644 index 118d9377ec..0000000000 --- a/memcheck/tests/vgtest_ume.disabled +++ /dev/null @@ -1,3 +0,0 @@ -# This one currently not used because it doesn't compile any more -prog: vgtest_ume -vgopts: -q diff --git a/memcheck/tests/vgtest_ume.stderr.exp b/memcheck/tests/vgtest_ume.stderr.exp deleted file mode 100644 index 518a10ab7f..0000000000 --- a/memcheck/tests/vgtest_ume.stderr.exp +++ /dev/null @@ -1,4 +0,0 @@ -Calling VG_(foreach_map)() -Calling VG_(find_auxv)() -Calling VG_(do_exec)("hello") -Hello, world!