mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-10 05:37:06 +00:00
seem to be simply duplication of the x86 instruction set tests into the addrcheck and helgrind trees. I'm not sure what this duplication achieves. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3264
672 lines
20 KiB
C
672 lines
20 KiB
C
/*--------------------------------------------------------------------*/
|
|
/*--- Management of function redirection and wrapping. ---*/
|
|
/*--- vg_redir.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, an extensible x86 protected-mode
|
|
emulator for monitoring program execution on x86-Unixes.
|
|
|
|
Copyright (C) 2000-2004 Julian Seward
|
|
jseward@acm.org
|
|
Copyright (C) 2003-2005 Jeremy Fitzhardinge
|
|
jeremy@goop.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 "core.h"
|
|
#include "vg_symtab2.h"
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- General purpose redirection. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/*
|
|
wraps and redirections, indexed by from_addr
|
|
|
|
Redirection and wrapping are two distinct mechanisms which Valgrind
|
|
can use to change the client's control flow.
|
|
|
|
Redirection intercepts a call to a client function, and re-points it
|
|
to a new piece of code (presumably functionally equivalent). The
|
|
original code is never run.
|
|
|
|
Wrapping does call the client's original code, but calls "before"
|
|
and "after" functions which can inspect (and perhaps modify) the
|
|
function's arguments and return value.
|
|
*/
|
|
struct _CodeRedirect {
|
|
enum redir_type {
|
|
R_REDIRECT, /* plain redirection */
|
|
R_WRAPPER, /* wrap with valgrind-internal code */
|
|
R_CLIENT_WRAPPER, /* wrap with client-side code */
|
|
} type;
|
|
|
|
const Char *from_lib; /* library qualifier pattern */
|
|
const Char *from_sym; /* symbol */
|
|
Addr from_addr; /* old addr */
|
|
|
|
/* used for redirection */
|
|
const Char *to_lib; /* library qualifier pattern */
|
|
const Char *to_sym; /* symbol */
|
|
Addr to_addr; /* new addr */
|
|
|
|
/* used for wrapping */
|
|
const FuncWrapper *wrapper;
|
|
|
|
CodeRedirect *next; /* next pointer on unresolved list */
|
|
};
|
|
|
|
static Char *straddr(void *p)
|
|
{
|
|
static Char buf[16];
|
|
|
|
VG_(sprintf)(buf, "%p", *(Addr *)p);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static SkipList sk_resolved_redir = SKIPLIST_INIT(CodeRedirect, from_addr,
|
|
VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
|
|
static CodeRedirect *unresolved_redir = NULL;
|
|
|
|
static Bool match_lib(const Char *pattern, const SegInfo *si)
|
|
{
|
|
/* pattern == NULL matches everything, otherwise use globbing
|
|
|
|
If the pattern starts with:
|
|
file:, then match filename
|
|
soname:, then match soname
|
|
something else, match filename
|
|
*/
|
|
const Char *name = si->filename;
|
|
|
|
if (pattern == NULL)
|
|
return True;
|
|
|
|
if (VG_(strncmp)(pattern, "file:", 5) == 0) {
|
|
pattern += 5;
|
|
name = si->filename;
|
|
}
|
|
if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
|
|
pattern += 7;
|
|
name = si->soname;
|
|
}
|
|
|
|
if (name == NULL)
|
|
return False;
|
|
|
|
return VG_(string_match)(pattern, name);
|
|
}
|
|
|
|
static inline Bool from_resolved(const CodeRedirect *redir)
|
|
{
|
|
return redir->from_addr != 0;
|
|
}
|
|
|
|
static inline Bool to_resolved(const CodeRedirect *redir)
|
|
{
|
|
if (redir->type == R_REDIRECT)
|
|
return redir->to_addr != 0;
|
|
vg_assert(redir->wrapper != NULL);
|
|
return True;
|
|
}
|
|
|
|
Bool VG_(is_resolved)(const CodeRedirect *redir)
|
|
{
|
|
return from_resolved(redir) && to_resolved(redir);
|
|
}
|
|
|
|
/* Resolve a redir using si if possible, and add it to the resolved
|
|
list */
|
|
Bool VG_(resolve_redir)(CodeRedirect *redir, const SegInfo *si)
|
|
{
|
|
Bool resolved;
|
|
|
|
vg_assert(si != NULL);
|
|
vg_assert(si->seg != NULL);
|
|
|
|
/* no redirection from Valgrind segments */
|
|
if (si->seg->flags & SF_VALGRIND)
|
|
return False;
|
|
|
|
resolved = VG_(is_resolved)(redir);
|
|
|
|
if (0 && VG_(clo_trace_redir))
|
|
VG_(printf)(" consider FROM binding %s:%s -> %s:%s in %s(%s)\n",
|
|
redir->from_lib, redir->from_sym,
|
|
redir->to_lib, redir->to_sym,
|
|
si->filename, si->soname);
|
|
|
|
vg_assert(!resolved);
|
|
|
|
if (!from_resolved(redir)) {
|
|
vg_assert(redir->from_sym != NULL);
|
|
|
|
if (match_lib(redir->from_lib, si)) {
|
|
redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
|
|
if (VG_(clo_trace_redir) && redir->from_addr != 0)
|
|
VG_(printf)(" bind FROM: %p = %s:%s\n",
|
|
redir->from_addr,redir->from_lib, redir->from_sym );
|
|
}
|
|
}
|
|
|
|
if (!to_resolved(redir)) {
|
|
vg_assert(redir->type == R_REDIRECT);
|
|
vg_assert(redir->to_sym != NULL);
|
|
|
|
if (match_lib(redir->to_lib, si)) {
|
|
redir->to_addr = VG_(reverse_search_one_symtab)(si, redir->to_sym);
|
|
if (VG_(clo_trace_redir) && redir->to_addr != 0)
|
|
VG_(printf)(" bind TO: %p = %s:%s\n",
|
|
redir->to_addr,redir->to_lib, redir->to_sym );
|
|
|
|
}
|
|
}
|
|
|
|
resolved = from_resolved(redir) && to_resolved(redir);
|
|
|
|
if (0 && VG_(clo_trace_redir))
|
|
VG_(printf)("resolve_redir: %s:%s from=%p %s:%s to=%p\n",
|
|
redir->from_lib, redir->from_sym, redir->from_addr,
|
|
redir->to_lib, redir->to_sym, redir->to_addr);
|
|
|
|
if (resolved) {
|
|
switch(redir->type) {
|
|
case R_REDIRECT:
|
|
if (VG_(clo_trace_redir)) {
|
|
VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
|
|
redir->from_lib, redir->from_sym, redir->from_addr);
|
|
VG_(message)(Vg_DebugMsg, " %s:%s=%p)",
|
|
redir->to_lib, redir->to_sym, redir->to_addr);
|
|
}
|
|
|
|
if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
|
|
/* For some given (from, to) redir, the "from" function got
|
|
called before the .so containing "to" became available. We
|
|
know this because there is already a translation for the
|
|
entry point of the original "from". So the redirect will
|
|
never actually take effect unless that translation is
|
|
discarded.
|
|
|
|
Note, we only really need to discard the first bb of the
|
|
old entry point, and so we avoid the problem of having to
|
|
figure out how big that bb was -- since it is at least 1
|
|
byte of original code, we can just pass 1 as the original
|
|
size to invalidate_translations() and it will indeed get
|
|
rid of the translation.
|
|
|
|
Note, this is potentially expensive -- discarding
|
|
translations causes complete unchaining.
|
|
*/
|
|
if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
|
|
VG_(message)(Vg_UserMsg,
|
|
"Discarding translation due to redirect of already called function" );
|
|
VG_(message)(Vg_UserMsg,
|
|
" %s (%p -> %p)",
|
|
redir->from_sym, redir->from_addr, redir->to_addr );
|
|
}
|
|
VG_(discard_translations)((Addr64)redir->from_addr, 1);
|
|
}
|
|
|
|
{
|
|
CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
|
|
|
|
if (r == NULL)
|
|
VG_(SkipList_Insert)(&sk_resolved_redir, redir);
|
|
else {
|
|
/* XXX leak redir */
|
|
if (VG_(clo_trace_redir))
|
|
VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%s:%s:%p duplicated\n",
|
|
redir->from_lib, redir->from_sym, redir->from_addr,
|
|
redir->to_lib, redir->to_sym, redir->to_addr);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case R_WRAPPER:
|
|
if (VG_(clo_trace_redir)) {
|
|
VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
|
|
redir->from_lib, redir->from_sym, redir->from_addr);
|
|
}
|
|
|
|
/* XXX redir leaked */
|
|
//VG_(wrap_function)(redir->from_addr, redir->wrapper);
|
|
break;
|
|
|
|
case R_CLIENT_WRAPPER:
|
|
VG_(core_panic)("not implemented");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
/* Go through the complete redir list, resolving as much as possible with this SegInfo.
|
|
|
|
This should be called when a new SegInfo symtab is loaded.
|
|
*/
|
|
void VG_(resolve_seg_redirs)(SegInfo *si)
|
|
{
|
|
CodeRedirect **prevp = &unresolved_redir;
|
|
CodeRedirect *redir, *next;
|
|
|
|
if (VG_(clo_trace_redir))
|
|
VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
|
|
si->filename, si->soname);
|
|
|
|
/* visit each unresolved redir - if it becomes resolved, then
|
|
remove it from the unresolved list */
|
|
for(redir = unresolved_redir; redir != NULL; redir = next) {
|
|
next = redir->next;
|
|
|
|
if (VG_(resolve_redir)(redir, si)) {
|
|
*prevp = next;
|
|
redir->next = NULL;
|
|
} else
|
|
prevp = &redir->next;
|
|
}
|
|
}
|
|
|
|
/* Redirect a lib/symbol reference to a function at lib/symbol */
|
|
static void add_redirect_sym(const Char *from_lib, const Char *from_sym,
|
|
const Char *to_lib, const Char *to_sym)
|
|
{
|
|
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
|
|
|
|
redir->type = R_REDIRECT;
|
|
|
|
redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
|
|
redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
|
|
redir->from_addr = 0;
|
|
|
|
redir->to_lib = VG_(arena_strdup)(VG_AR_SYMTAB, to_lib);
|
|
redir->to_sym = VG_(arena_strdup)(VG_AR_SYMTAB, to_sym);
|
|
redir->to_addr = 0;
|
|
|
|
if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
|
|
VG_(message)(Vg_UserMsg,
|
|
"REDIRECT %s(%s) to %s(%s)",
|
|
from_lib, from_sym, to_lib, to_sym);
|
|
|
|
/* Check against all existing segments to see if this redirection
|
|
can be resolved immediately */
|
|
if (!VG_(resolve_redir_allsegs)(redir)) {
|
|
/* nope, add to list */
|
|
redir->next = unresolved_redir;
|
|
unresolved_redir = redir;
|
|
}
|
|
}
|
|
|
|
/* Redirect a lib/symbol reference to a function at addr */
|
|
void VG_(add_redirect_addr)(const Char *from_lib, const Char *from_sym,
|
|
Addr to_addr)
|
|
{
|
|
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
|
|
|
|
redir->type = R_REDIRECT;
|
|
|
|
redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
|
|
redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
|
|
redir->from_addr = 0;
|
|
|
|
redir->to_lib = NULL;
|
|
redir->to_sym = NULL;
|
|
redir->to_addr = to_addr;
|
|
|
|
/* Check against all existing segments to see if this redirection
|
|
can be resolved immediately */
|
|
if (!VG_(resolve_redir_allsegs)(redir)) {
|
|
/* nope, add to list */
|
|
redir->next = unresolved_redir;
|
|
unresolved_redir = redir;
|
|
}
|
|
}
|
|
|
|
CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
|
|
const FuncWrapper *wrapper)
|
|
{
|
|
CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
|
|
|
|
if (0)
|
|
VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
|
|
from_lib, from_sym, wrapper->before, wrapper->after);
|
|
|
|
redir->type = R_WRAPPER;
|
|
|
|
redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
|
|
redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
|
|
redir->from_addr = 0;
|
|
|
|
redir->to_lib = NULL;
|
|
redir->to_sym = NULL;
|
|
redir->to_addr = 0;
|
|
|
|
redir->wrapper = wrapper;
|
|
|
|
/* Check against all existing segments to see if this redirection
|
|
can be resolved immediately */
|
|
if (!VG_(resolve_redir_allsegs)(redir)) {
|
|
/* nope, add to list */
|
|
redir->next = unresolved_redir;
|
|
unresolved_redir = redir;
|
|
}
|
|
|
|
return redir;
|
|
}
|
|
|
|
/* HACK! This should be done properly (see ~/NOTES.txt) */
|
|
#ifdef __amd64__
|
|
/* Rerouted entry points for __NR_vgettimeofday and __NR_vtime.
|
|
96 == __NR_gettimeofday
|
|
201 == __NR_time
|
|
*/
|
|
asm(
|
|
"amd64_linux_rerouted__vgettimeofday:\n"
|
|
" movq $96, %rax\n"
|
|
" syscall\n"
|
|
" ret\n"
|
|
"amd64_linux_rerouted__vtime:\n"
|
|
" movq $201, %rax\n"
|
|
" syscall\n"
|
|
" ret\n"
|
|
);
|
|
#endif
|
|
|
|
/* If address 'a' is being redirected, return the redirected-to
|
|
address. */
|
|
Addr VG_(code_redirect)(Addr a)
|
|
{
|
|
CodeRedirect* r;
|
|
|
|
#ifdef __amd64__
|
|
/* HACK. Reroute the amd64-linux vsyscalls. This should be moved
|
|
out of here into an amd64-linux specific initialisation routine.
|
|
*/
|
|
extern void amd64_linux_rerouted__vgettimeofday;
|
|
extern void amd64_linux_rerouted__vtime;
|
|
if (a == 0xFFFFFFFFFF600000ULL)
|
|
return (Addr)&amd64_linux_rerouted__vgettimeofday;
|
|
if (a == 0xFFFFFFFFFF600400ULL)
|
|
return (Addr)&amd64_linux_rerouted__vtime;
|
|
#endif
|
|
|
|
r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
|
|
if (r == NULL)
|
|
return a;
|
|
|
|
vg_assert(r->to_addr != 0);
|
|
|
|
return r->to_addr;
|
|
}
|
|
|
|
void VG_(setup_code_redirect_table) ( void )
|
|
{
|
|
/* Redirect _dl_sysinfo_int80, which is glibc's default system call
|
|
routine, to the routine in our trampoline page so that the
|
|
special sysinfo unwind hack in vg_execontext.c will kick in.
|
|
*/
|
|
VG_(add_redirect_addr)("soname:ld-linux.so.2", "_dl_sysinfo_int80",
|
|
VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
|
|
|
|
/* Overenthusiastic use of PLT bypassing by the glibc people also
|
|
means we need to patch the following functions to our own
|
|
implementations of said, in mac_replace_strmem.c.
|
|
*/
|
|
add_redirect_sym("soname:libc.so.6", "stpcpy",
|
|
"*vgpreload_memcheck.so*", "stpcpy");
|
|
|
|
add_redirect_sym("soname:libc.so.6", "strlen",
|
|
"*vgpreload_memcheck.so*", "strlen");
|
|
|
|
add_redirect_sym("soname:libc.so.6", "strnlen",
|
|
"*vgpreload_memcheck.so*", "strnlen");
|
|
|
|
add_redirect_sym("soname:ld-linux.so.2", "stpcpy",
|
|
"*vgpreload_memcheck.so*", "stpcpy");
|
|
|
|
add_redirect_sym("soname:libc.so.6", "strchr",
|
|
"*vgpreload_memcheck.so*", "strchr");
|
|
add_redirect_sym("soname:ld-linux.so.2", "strchr",
|
|
"*vgpreload_memcheck.so*", "strchr");
|
|
|
|
add_redirect_sym("soname:libc.so.6", "strchrnul",
|
|
"*vgpreload_memcheck.so*", "glibc232_strchrnul");
|
|
|
|
add_redirect_sym("soname:libc.so.6", "rawmemchr",
|
|
"*vgpreload_memcheck.so*", "glibc232_rawmemchr");
|
|
}
|
|
|
|
|
|
//:: /*------------------------------------------------------------*/
|
|
//:: /*--- General function wrapping. ---*/
|
|
//:: /*------------------------------------------------------------*/
|
|
//::
|
|
//:: /*
|
|
//:: TODO:
|
|
//:: - hook into the symtab machinery
|
|
//:: - client-side wrappers?
|
|
//:: - better interfaces for before() functions to get to arguments
|
|
//:: - handle munmap of code (dlclose())
|
|
//:: - handle thread exit
|
|
//:: - handle longjmp
|
|
//:: */
|
|
//:: struct callkey {
|
|
//:: ThreadId tid; /* calling thread */
|
|
//:: Addr esp; /* address of args on stack */
|
|
//:: Addr eip; /* return address */
|
|
//:: };
|
|
//::
|
|
//:: struct call_instance {
|
|
//:: struct callkey key;
|
|
//::
|
|
//:: const FuncWrapper *wrapper;
|
|
//:: void *nonce;
|
|
//:: };
|
|
//::
|
|
//:: static inline Addr addrcmp(Addr a, Addr b)
|
|
//:: {
|
|
//:: if (a < b)
|
|
//:: return -1;
|
|
//:: else if (a > b)
|
|
//:: return 1;
|
|
//:: else
|
|
//:: return 0;
|
|
//:: }
|
|
//::
|
|
//:: static inline Int cmp(UInt a, UInt b)
|
|
//:: {
|
|
//:: if (a < b)
|
|
//:: return -1;
|
|
//:: else if (a > b)
|
|
//:: return 1;
|
|
//:: else
|
|
//:: return 0;
|
|
//:: }
|
|
//::
|
|
//:: static Int keycmp(const void *pa, const void *pb)
|
|
//:: {
|
|
//:: const struct callkey *a = (const struct callkey *)pa;
|
|
//:: const struct callkey *b = (const struct callkey *)pb;
|
|
//:: Int ret;
|
|
//::
|
|
//:: if ((ret = cmp(a->tid, b->tid)))
|
|
//:: return ret;
|
|
//::
|
|
//:: if ((ret = addrcmp(a->esp, b->esp)))
|
|
//:: return ret;
|
|
//::
|
|
//:: return addrcmp(a->eip, b->eip);
|
|
//:: }
|
|
//::
|
|
//:: /* List of wrapped call invocations which are currently active */
|
|
//:: static SkipList wrapped_frames = SKIPLIST_INIT(struct call_instance, key, keycmp,
|
|
//:: NULL, VG_AR_SYMTAB);
|
|
//::
|
|
//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
|
|
//:: {
|
|
//:: struct callkey key = { tid, argsp, retaddr };
|
|
//::
|
|
//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
|
|
//:: }
|
|
//::
|
|
//:: static void wrapper_return(Addr retaddr);
|
|
//::
|
|
//:: /* Called from generated code via helper */
|
|
//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
|
|
//:: {
|
|
//:: Addr retaddr = ARCH_RETADDR(tst->arch);
|
|
//:: Addr argp = (Addr)&ARCH_FUNC_ARG(tst->arch, 0);
|
|
//:: void *nonce = NULL;
|
|
//:: Bool mf = VG_(my_fault);
|
|
//:: VG_(my_fault) = True;
|
|
//::
|
|
//:: if (wrapper->before) {
|
|
//:: va_list args = ARCH_VA_LIST(tst->arch);
|
|
//:: nonce = (*wrapper->before)(args);
|
|
//:: }
|
|
//::
|
|
//:: if (wrapper->after) {
|
|
//:: /* If there's an after function, make sure it gets called */
|
|
//:: struct call_instance *call;
|
|
//::
|
|
//:: call = find_call(retaddr, argp, tst->tid);
|
|
//::
|
|
//:: if (call != NULL) {
|
|
//:: /* Found a stale outstanding call; clean it up and recycle
|
|
//:: the structure */
|
|
//:: if (call->wrapper->after)
|
|
//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
|
|
//:: } else {
|
|
//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
|
|
//::
|
|
//:: call->key.tid = tst->tid;
|
|
//:: call->key.esp = argp;
|
|
//:: call->key.eip = retaddr;
|
|
//::
|
|
//:: VG_(SkipList_Insert)(&wrapped_frames, call);
|
|
//::
|
|
//:: wrapper_return(retaddr);
|
|
//:: }
|
|
//::
|
|
//:: call->wrapper = wrapper;
|
|
//:: call->nonce = nonce;
|
|
//:: } else
|
|
//:: vg_assert(nonce == NULL);
|
|
//::
|
|
//:: VG_(my_fault) = mf;
|
|
//:: }
|
|
//::
|
|
//:: /* Called from generated code via helper */
|
|
//:: void VG_(wrap_after)(ThreadState *tst)
|
|
//:: {
|
|
//:: Addr EIP = ARCH_INSTR_PTR(tst->arch); /* instruction after call */
|
|
//:: Addr ESP = ARCH_STACK_PTR(tst->arch); /* pointer to args */
|
|
//:: Word ret = ARCH_RETVAL(tst->arch); /* return value */
|
|
//:: struct call_instance *call;
|
|
//:: Bool mf = VG_(my_fault);
|
|
//::
|
|
//:: VG_(my_fault) = True;
|
|
//:: call = find_call(EIP, ESP, tst->tid);
|
|
//::
|
|
//:: if (0)
|
|
//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
|
|
//::
|
|
//:: if (call != NULL) {
|
|
//:: if (call->wrapper->after)
|
|
//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
|
|
//::
|
|
//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
|
|
//:: VG_(SkipNode_Free)(&wrapped_frames, call);
|
|
//:: }
|
|
//:: VG_(my_fault) = mf;
|
|
//:: }
|
|
//::
|
|
//::
|
|
//:: struct wrapped_function {
|
|
//:: Addr eip; /* eip of function entrypoint */
|
|
//:: const FuncWrapper *wrapper;
|
|
//:: };
|
|
//::
|
|
//:: struct wrapper_return {
|
|
//:: Addr eip; /* return address */
|
|
//:: };
|
|
//::
|
|
//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
|
|
//:: static SkipList wrapped_functions = SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
|
|
//:: NULL, VG_AR_SYMTAB);
|
|
//::
|
|
//:: /* A set of EIPs which are return addresses for wrapped functions */
|
|
//:: static SkipList wrapper_returns = SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
|
|
//:: NULL, VG_AR_SYMTAB);
|
|
//::
|
|
//:: /* Wrap function starting at eip */
|
|
//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
|
|
//:: {
|
|
//:: struct wrapped_function *func;
|
|
//::
|
|
//:: if (0)
|
|
//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
|
|
//::
|
|
//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
|
|
//::
|
|
//:: if (func == NULL) {
|
|
//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
|
|
//:: VG_(invalidate_translations)(eip, 1, True);
|
|
//::
|
|
//:: func->eip = eip;
|
|
//:: VG_(SkipList_Insert)(&wrapped_functions, func);
|
|
//:: }
|
|
//::
|
|
//:: func->wrapper = wrapper;
|
|
//:: }
|
|
//::
|
|
//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
|
|
//:: {
|
|
//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
|
|
//::
|
|
//:: if (func)
|
|
//:: return func->wrapper;
|
|
//:: return NULL;
|
|
//:: }
|
|
//::
|
|
//:: Bool VG_(is_wrapper_return)(Addr eip)
|
|
//:: {
|
|
//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
|
|
//::
|
|
//:: return ret != NULL;
|
|
//:: }
|
|
//::
|
|
//:: /* Mark eip as being the return address of a wrapper, so that the
|
|
//:: codegen will generate the appropriate call. */
|
|
//:: void wrapper_return(Addr eip)
|
|
//:: {
|
|
//:: struct wrapper_return *ret;
|
|
//::
|
|
//:: if (VG_(is_wrapper_return)(eip))
|
|
//:: return;
|
|
//::
|
|
//:: VG_(invalidate_translations)(eip, 1, True);
|
|
//::
|
|
//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
|
|
//:: ret->eip = eip;
|
|
//::
|
|
//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
|
|
//:: }
|