- 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
This commit is contained in:
Nicholas Nethercote 2009-01-21 02:26:56 +00:00
parent 74e8c02cea
commit c07262448b
10 changed files with 570 additions and 631 deletions

View File

@ -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 \

View File

@ -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)

View File

@ -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);
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;
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;
}
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;
/* 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;
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 ---*/

286
coregrind/m_ume/main.c Normal file
View File

@ -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 ---*/
/*--------------------------------------------------------------------*/

150
coregrind/m_ume/script.c Normal file
View File

@ -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 ---*/
/*--------------------------------------------------------------------*/

View File

@ -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 */
/*--------------------------------------------------------------------*/

View File

@ -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

View File

@ -1,150 +0,0 @@
// 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;
}

View File

@ -1,3 +0,0 @@
# This one currently not used because it doesn't compile any more
prog: vgtest_ume
vgopts: -q

View File

@ -1,4 +0,0 @@
Calling VG_(foreach_map)()
Calling VG_(find_auxv)()
Calling VG_(do_exec)("hello")
Hello, world!