|
From: <sv...@va...> - 2009-10-29 09:27:26
|
Author: tom
Date: 2009-10-29 09:27:11 +0000 (Thu, 29 Oct 2009)
New Revision: 10920
Log:
Add support for ELF indirect functions. These are symbols of
type STT_GNU_IFUNC which, instead of pointing directly at the
function, point at a routine which will return the address of
the real function. Redirection of indirect functions is handled
by valgrind as follows:
- When a redirection specification matches an indirect
function symbol an active redirection is added in the
normal way, but with the isIFunc flag set.
- When a call is made to an address which matches an
active redirection with the isIFunc flag set the call
is redirected, but not to the target address of the
redirection - instead it is sent to a small wrapper
routine that is preloaded into the client.
- The wrapper routine calls the original client routine
and collects the result, which it reports to valgrind
using a client request, and then returns the result to
the caller.
- When valgrind gets the client request it looks up the
active redirection for the indirect function and then
adds a new active redirection which redirects from the
address returned by the indirection function to the
redirection target. This new redirection does not have
the isIFunc flag set so behaves as a normal redirection.
In addition to the above we also add a few new redirections to
memcheck to capture internal calls made by glibc to things like
strlen, as these internal calls do not go through the indirect
function and instead go direct to the chosen implementation.
Based on a patch from Dodji Seketeli and comments from Jakub
Jelinek, this commit closes bug 206013.
Modified:
trunk/coregrind/m_debuginfo/debuginfo.c
trunk/coregrind/m_debuginfo/priv_storage.h
trunk/coregrind/m_debuginfo/readelf.c
trunk/coregrind/m_redir.c
trunk/coregrind/m_scheduler/scheduler.c
trunk/coregrind/pub_core_clreq.h
trunk/coregrind/pub_core_redir.h
trunk/coregrind/vg_preloaded.c
trunk/include/pub_tool_debuginfo.h
trunk/memcheck/mc_replace_strmem.c
Modified: trunk/coregrind/m_debuginfo/debuginfo.c
===================================================================
--- trunk/coregrind/m_debuginfo/debuginfo.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/m_debuginfo/debuginfo.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -3435,14 +3435,16 @@
/*OUT*/Addr* tocptr,
/*OUT*/UInt* size,
/*OUT*/HChar** name,
- /*OUT*/Bool* isText )
+ /*OUT*/Bool* isText,
+ /*OUT*/Bool* isIFunc )
{
vg_assert(idx >= 0 && idx < si->symtab_used);
- if (avma) *avma = si->symtab[idx].addr;
- if (tocptr) *tocptr = si->symtab[idx].tocptr;
- if (size) *size = si->symtab[idx].size;
- if (name) *name = (HChar*)si->symtab[idx].name;
- if (isText) *isText = si->symtab[idx].isText;
+ if (avma) *avma = si->symtab[idx].addr;
+ if (tocptr) *tocptr = si->symtab[idx].tocptr;
+ if (size) *size = si->symtab[idx].size;
+ if (name) *name = (HChar*)si->symtab[idx].name;
+ if (isText) *isText = si->symtab[idx].isText;
+ if (isIFunc) *isIFunc = si->symtab[idx].isIFunc;
}
Modified: trunk/coregrind/m_debuginfo/priv_storage.h
===================================================================
--- trunk/coregrind/m_debuginfo/priv_storage.h 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/m_debuginfo/priv_storage.h 2009-10-29 09:27:11 UTC (rev 10920)
@@ -48,15 +48,16 @@
/* A structure to hold an ELF/XCOFF symbol (very crudely). */
typedef
struct {
- Addr addr; /* lowest address of entity */
- Addr tocptr; /* ppc64-linux only: value that R2 should have */
- UChar *name; /* name */
+ Addr addr; /* lowest address of entity */
+ Addr tocptr; /* ppc64-linux only: value that R2 should have */
+ UChar *name; /* name */
// XXX: this could be shrunk (on 32-bit platforms) by using 31 bits for
// the size and 1 bit for the isText. If you do this, make sure that
// all assignments to isText use 0 or 1 (or True or False), and that a
// positive number larger than 1 is never used to represent True.
- UInt size; /* size in bytes */
+ UInt size; /* size in bytes */
Bool isText;
+ Bool isIFunc; /* symbol is an indirect function? */
}
DiSym;
Modified: trunk/coregrind/m_debuginfo/readelf.c
===================================================================
--- trunk/coregrind/m_debuginfo/readelf.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/m_debuginfo/readelf.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -214,7 +214,8 @@
used on entry */
Bool* from_opd_out, /* ppc64-linux only: did we deref an
.opd entry? */
- Bool* is_text_out /* is this a text symbol? */
+ Bool* is_text_out, /* is this a text symbol? */
+ Bool* is_ifunc /* is this a STT_GNU_IFUNC function ?*/
)
{
Bool plausible;
@@ -232,6 +233,7 @@
*sym_size_out = (Int)sym->st_size;
*sym_tocptr_out = 0; /* unknown/inapplicable */
*from_opd_out = False;
+ *is_ifunc = False;
/* Figure out if we're interested in the symbol. Firstly, is it of
the right flavour? */
@@ -243,6 +245,9 @@
&&
(ELFXX_ST_TYPE(sym->st_info) == STT_FUNC
|| ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT
+#ifdef STT_GNU_IFUNC
+ || ELFXX_ST_TYPE(sym->st_info) == STT_GNU_IFUNC
+#endif
);
/* Work out the svma and bias for each section as it will appear in
@@ -325,6 +330,14 @@
*sym_avma_out += text_bias;
}
+# ifdef STT_GNU_IFUNC
+ /* Check for indirect functions. */
+ if (*is_text_out
+ && ELFXX_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) {
+ *is_ifunc = True;
+ }
+# endif
+
# if defined(VGP_ppc64_linux)
/* Allow STT_NOTYPE in the very special case where we're running on
ppc64-linux and the symbol is one which the .opd-chasing hack
@@ -570,7 +583,7 @@
Char *sym_name, *sym_name_really;
Int sym_size;
Addr sym_tocptr;
- Bool from_opd, is_text;
+ Bool from_opd, is_text, is_ifunc;
DiSym risym;
ElfXX_Sym *sym;
@@ -602,13 +615,14 @@
&sym_avma_really,
&sym_size,
&sym_tocptr,
- &from_opd, &is_text)) {
+ &from_opd, &is_text, &is_ifunc)) {
- risym.addr = sym_avma_really;
- risym.size = sym_size;
- risym.name = ML_(addStr) ( di, sym_name_really, -1 );
- risym.tocptr = sym_tocptr;
- risym.isText = is_text;
+ risym.addr = sym_avma_really;
+ risym.size = sym_size;
+ risym.name = ML_(addStr) ( di, sym_name_really, -1 );
+ risym.tocptr = sym_tocptr;
+ risym.isText = is_text;
+ risym.isIFunc = is_ifunc;
vg_assert(risym.name != NULL);
vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */
ML_(addSym) ( di, &risym );
@@ -646,6 +660,7 @@
Int size;
Bool from_opd;
Bool is_text;
+ Bool is_ifunc;
}
TempSym;
@@ -671,7 +686,7 @@
Char *sym_name, *sym_name_really;
Int sym_size;
Addr sym_tocptr;
- Bool from_opd, modify_size, modify_tocptr, is_text;
+ Bool from_opd, modify_size, modify_tocptr, is_text, is_ifunc;
DiSym risym;
ElfXX_Sym *sym;
OSet *oset;
@@ -713,7 +728,7 @@
&sym_avma_really,
&sym_size,
&sym_tocptr,
- &from_opd, &is_text)) {
+ &from_opd, &is_text, &is_ifunc)) {
/* Check if we've seen this (name,addr) key before. */
key.addr = sym_avma_really;
@@ -785,6 +800,7 @@
elem->size = sym_size;
elem->from_opd = from_opd;
elem->is_text = is_text;
+ elem->is_ifunc = is_ifunc;
VG_(OSetGen_Insert)(oset, elem);
if (di->trace_symtab) {
VG_(printf)(" to-oset [%4ld]: "
@@ -808,11 +824,12 @@
VG_(OSetGen_ResetIter)( oset );
while ( (elem = VG_(OSetGen_Next)(oset)) ) {
- risym.addr = elem->key.addr;
- risym.size = elem->size;
- risym.name = ML_(addStr) ( di, elem->key.name, -1 );
- risym.tocptr = elem->tocptr;
- risym.isText = elem->is_text;
+ risym.addr = elem->key.addr;
+ risym.size = elem->size;
+ risym.name = ML_(addStr) ( di, elem->key.name, -1 );
+ risym.tocptr = elem->tocptr;
+ risym.isText = elem->is_text;
+ risym.isIFunc = elem->is_ifunc;
vg_assert(risym.name != NULL);
ML_(addSym) ( di, &risym );
Modified: trunk/coregrind/m_redir.c
===================================================================
--- trunk/coregrind/m_redir.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/m_redir.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -268,12 +268,15 @@
TopSpec* parent_spec; /* the TopSpec which supplied the Spec */
TopSpec* parent_sym; /* the TopSpec which supplied the symbol */
Bool isWrap; /* wrap or replacement? */
+ Bool isIFunc; /* indirect function? */
}
Active;
/* The active set is a fast lookup table */
static OSet* activeSet = NULL;
+/* Wrapper routine for indirect functions */
+static Addr iFuncWrapper;
/*------------------------------------------------------------*/
/*--- FWDses ---*/
@@ -350,8 +353,8 @@
nsyms = VG_(DebugInfo_syms_howmany)( newsi );
for (i = 0; i < nsyms; i++) {
- VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
- NULL, &sym_name, &isText );
+ VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
+ NULL, &sym_name, &isText, NULL );
ok = VG_(maybe_Z_demangle)( sym_name, demangled_sopatt, N_DEMANGLED,
demangled_fnpatt, N_DEMANGLED, &isWrap );
/* ignore data symbols */
@@ -388,8 +391,8 @@
if (check_ppcTOCs) {
for (i = 0; i < nsyms; i++) {
- VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
- NULL, &sym_name, &isText );
+ VG_(DebugInfo_syms_getidx)( newsi, i, &sym_addr, &sym_toc,
+ NULL, &sym_name, &isText, NULL );
ok = isText
&& VG_(maybe_Z_demangle)(
sym_name, demangled_sopatt, N_DEMANGLED,
@@ -470,7 +473,31 @@
#undef N_DEMANGLED
+/* Add a new target for an indirect function. Adds a new redirection
+ for the indirection function with address old_from that redirects
+ the ordinary function with address new_from to the target address
+ of the original redirection. */
+void VG_(redir_add_ifunc_target)( Addr old_from, Addr new_from )
+{
+ Active *old, new;
+
+ old = VG_(OSetGen_Lookup)(activeSet, &old_from);
+ vg_assert(old);
+ vg_assert(old->isIFunc);
+
+ new = *old;
+ new.from_addr = new_from;
+ new.isIFunc = False;
+ maybe_add_active (new);
+
+ if (VG_(clo_trace_redir)) {
+ VG_(message)( Vg_DebugMsg,
+ "Adding redirect for indirect function 0x%llx from 0x%llx -> 0x%llx\n",
+ (ULong)old_from, (ULong)new_from, (ULong)new.to_addr );
+ }
+}
+
/* Do one element of the basic cross product: add to the active set,
all matches resulting from comparing all the given specs against
all the symbols in the given seginfo. If a conflicting binding
@@ -487,7 +514,7 @@
)
{
Spec* sp;
- Bool anyMark, isText;
+ Bool anyMark, isText, isIFunc;
Active act;
Int nsyms, i;
Addr sym_addr;
@@ -513,7 +540,7 @@
nsyms = VG_(DebugInfo_syms_howmany)( di );
for (i = 0; i < nsyms; i++) {
VG_(DebugInfo_syms_getidx)( di, i, &sym_addr, NULL, NULL,
- &sym_name, &isText );
+ &sym_name, &isText, &isIFunc );
/* ignore data symbols */
if (!isText)
@@ -539,6 +566,7 @@
act.parent_spec = parent_spec;
act.parent_sym = parent_sym;
act.isWrap = sp->isWrap;
+ act.isIFunc = isIFunc;
sp->done = True;
maybe_add_active( act );
}
@@ -780,7 +808,9 @@
vg_assert(r->to_addr != 0);
if (isWrap)
- *isWrap = r->isWrap;
+ *isWrap = r->isWrap || r->isIFunc;
+ if (r->isIFunc)
+ return iFuncWrapper;
return r->to_addr;
}
@@ -1096,6 +1126,8 @@
if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(freeres))) == 0)
VG_(client___libc_freeres_wrapper) = addr;
+ else if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(ifunc_wrapper))) == 0)
+ iFuncWrapper = addr;
else
vg_assert2(0, "unrecognised load notification function: %s", symbol);
}
Modified: trunk/coregrind/m_scheduler/scheduler.c
===================================================================
--- trunk/coregrind/m_scheduler/scheduler.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/m_scheduler/scheduler.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -89,6 +89,7 @@
#include "pub_core_debuginfo.h" // VG_(di_notify_pdb_debuginfo)
#include "priv_sema.h"
#include "pub_core_scheduler.h" // self
+#include "pub_core_redir.h"
/* ---------------------------------------------------------------------
@@ -1399,6 +1400,11 @@
SET_CLREQ_RETVAL( tid, count );
break; }
+ case VG_USERREQ__ADD_IFUNC_TARGET: {
+ VG_(redir_add_ifunc_target)( arg[1], arg[2] );
+ SET_CLREQ_RETVAL( tid, 0);
+ break; }
+
case VG_USERREQ__PRINTF_BACKTRACE: {
Int count =
VG_(vmessage)( Vg_ClientMsg, (char *)arg[1], (void*)arg[2] );
Modified: trunk/coregrind/pub_core_clreq.h
===================================================================
--- trunk/coregrind/pub_core_clreq.h 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/pub_core_clreq.h 2009-10-29 09:27:11 UTC (rev 10920)
@@ -50,6 +50,9 @@
/* Internal equivalent of VALGRIND_PRINTF . */
VG_USERREQ__INTERNAL_PRINTF = 0x3103,
+ /* Add a target for an indirect function redirection. */
+ VG_USERREQ__ADD_IFUNC_TARGET = 0x3104,
+
} Vg_InternalClientRequest;
// Function for printing from code within Valgrind, but which runs on the
Modified: trunk/coregrind/pub_core_redir.h
===================================================================
--- trunk/coregrind/pub_core_redir.h 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/pub_core_redir.h 2009-10-29 09:27:11 UTC (rev 10920)
@@ -58,6 +58,8 @@
/* Initialise the module, and load initial "hardwired" redirects. */
extern void VG_(redir_initialise)( void );
+/* Notify the module of a new target for an indirect function. */
+extern void VG_(redir_add_ifunc_target)( Addr old_from, Addr new_from );
//--------------------------------------------------------------------
// Queries
Modified: trunk/coregrind/vg_preloaded.c
===================================================================
--- trunk/coregrind/vg_preloaded.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/coregrind/vg_preloaded.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -47,12 +47,12 @@
#include "pub_core_debuginfo.h" // Needed for pub_core_redir.h
#include "pub_core_redir.h" // For VG_NOTIFY_ON_LOAD
+#if defined(VGO_linux) || defined(VGO_aix5)
+
/* ---------------------------------------------------------------------
Hook for running __libc_freeres once the program exits.
------------------------------------------------------------------ */
-#if defined(VGO_linux) || defined(VGO_aix5)
-
void VG_NOTIFY_ON_LOAD(freeres)( void );
void VG_NOTIFY_ON_LOAD(freeres)( void )
{
@@ -68,6 +68,31 @@
*(int *)0 = 'x';
}
+/* ---------------------------------------------------------------------
+ Wrapper for indirect functions which need to be redirected.
+ ------------------------------------------------------------------ */
+
+void * VG_NOTIFY_ON_LOAD(ifunc_wrapper) (void);
+void * VG_NOTIFY_ON_LOAD(ifunc_wrapper) (void)
+{
+ OrigFn fn;
+ Addr result = 0;
+ int res;
+
+ /* Call the original indirect function and get it's result */
+ VALGRIND_GET_ORIG_FN(fn);
+ CALL_FN_W_v(result, fn);
+
+ /* Ask the valgrind core running on the real CPU (as opposed to this
+ code which runs on the emulated CPU) to update the redirection that
+ led to this function. This client request eventually gives control to
+ the function VG_(redir_add_ifunc_target) in m_redir.c */
+ VALGRIND_DO_CLIENT_REQUEST(res, 0,
+ VG_USERREQ__ADD_IFUNC_TARGET,
+ fn.nraddr, result, 0, 0, 0);
+ return result;
+}
+
#elif defined(VGO_darwin)
/* ---------------------------------------------------------------------
Modified: trunk/include/pub_tool_debuginfo.h
===================================================================
--- trunk/include/pub_tool_debuginfo.h 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/include/pub_tool_debuginfo.h 2009-10-29 09:27:11 UTC (rev 10920)
@@ -212,7 +212,8 @@
/*OUT*/Addr* tocptr,
/*OUT*/UInt* size,
/*OUT*/HChar** name,
- /*OUT*/Bool* isText );
+ /*OUT*/Bool* isText,
+ /*OUT*/Bool* isIFunc );
/* A simple enumeration to describe the 'kind' of various kinds of
segments that arise from the mapping of object files. */
Modified: trunk/memcheck/mc_replace_strmem.c
===================================================================
--- trunk/memcheck/mc_replace_strmem.c 2009-10-29 09:01:39 UTC (rev 10919)
+++ trunk/memcheck/mc_replace_strmem.c 2009-10-29 09:27:11 UTC (rev 10920)
@@ -116,6 +116,7 @@
STRRCHR(VG_Z_LIBC_SONAME, strrchr)
STRRCHR(VG_Z_LIBC_SONAME, rindex)
#if defined(VGO_linux)
+STRRCHR(VG_Z_LIBC_SONAME, __GI_strrchr)
STRRCHR(VG_Z_LD_LINUX_SO_2, rindex)
#elif defined(VGO_darwin)
STRRCHR(VG_Z_DYLD, strrchr)
@@ -140,6 +141,7 @@
STRCHR(VG_Z_LIBC_SONAME, strchr)
STRCHR(VG_Z_LIBC_SONAME, index)
#if defined(VGO_linux)
+STRCHR(VG_Z_LIBC_SONAME, __GI_strchr)
STRCHR(VG_Z_LD_LINUX_SO_2, strchr)
STRCHR(VG_Z_LD_LINUX_SO_2, index)
STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, strchr)
@@ -172,8 +174,10 @@
}
STRCAT(VG_Z_LIBC_SONAME, strcat)
+#if defined(VGO_linux)
+STRCAT(VG_Z_LIBC_SONAME, __GI_strcat)
+#endif
-
#define STRNCAT(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( char* dst, const char* src, SizeT n ); \
@@ -257,6 +261,9 @@
}
STRNLEN(VG_Z_LIBC_SONAME, strnlen)
+#if defined(VGO_linux)
+STRNLEN(VG_Z_LIBC_SONAME, __GI_strnlen)
+#endif
// Note that this replacement often doesn't get used because gcc inlines
@@ -274,6 +281,7 @@
STRLEN(VG_Z_LIBC_SONAME, strlen)
#if defined(VGO_linux)
+STRLEN(VG_Z_LIBC_SONAME, __GI_strlen)
STRLEN(VG_Z_LD_LINUX_SO_2, strlen)
STRLEN(VG_Z_LD_LINUX_X86_64_SO_2, strlen)
#endif
@@ -301,7 +309,9 @@
}
STRCPY(VG_Z_LIBC_SONAME, strcpy)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRCPY(VG_Z_LIBC_SONAME, __GI_strcpy)
+#elif defined(VGO_darwin)
STRCPY(VG_Z_DYLD, strcpy)
#endif
@@ -327,7 +337,9 @@
}
STRNCPY(VG_Z_LIBC_SONAME, strncpy)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRNCPY(VG_Z_LIBC_SONAME, __GI_strncpy)
+#elif defined(VGO_darwin)
STRNCPY(VG_Z_DYLD, strncpy)
#endif
@@ -384,7 +396,9 @@
}
STRNCMP(VG_Z_LIBC_SONAME, strncmp)
-#if defined(VGO_darwin)
+#if defined(VGO_linux)
+STRNCMP(VG_Z_LIBC_SONAME, __GI_strncmp)
+#elif defined(VGO_darwin)
STRNCMP(VG_Z_DYLD, strncmp)
#endif
@@ -411,6 +425,7 @@
STRCMP(VG_Z_LIBC_SONAME, strcmp)
#if defined(VGO_linux)
+STRCMP(VG_Z_LIBC_SONAME, __GI_strcmp)
STRCMP(VG_Z_LD_LINUX_X86_64_SO_2, strcmp)
STRCMP(VG_Z_LD64_SO_1, strcmp)
#endif
@@ -557,6 +572,7 @@
STPCPY(VG_Z_LIBC_SONAME, stpcpy)
#if defined(VGO_linux)
+STPCPY(VG_Z_LIBC_SONAME, __GI_stpcpy)
STPCPY(VG_Z_LD_LINUX_SO_2, stpcpy)
STPCPY(VG_Z_LD_LINUX_X86_64_SO_2, stpcpy)
#elif defined(VGO_darwin)
@@ -709,8 +725,10 @@
}
GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, rawmemchr)
+#if defined (VGO_linux)
+GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, __GI___rawmemchr)
+#endif
-
/* glibc variant of strcpy that checks the dest is big enough.
Copied from glibc-2.5/debug/test-strcpy_chk.c. */
#define GLIBC25___STRCPY_CHK(soname,fnname) \
|