blob: b69572fa4268e0f0309a7c36274243c2c39e8bae [file] [log] [blame]
njnc0ae7052005-08-25 22:55:19 +00001
sewardjcbdddcf2005-03-10 23:23:45 +00002/*--------------------------------------------------------------------*/
njnc0ae7052005-08-25 22:55:19 +00003/*--- Function replacement and wrapping. m_redir.c ---*/
sewardjcbdddcf2005-03-10 23:23:45 +00004/*--------------------------------------------------------------------*/
5
6/*
njnc0ae7052005-08-25 22:55:19 +00007 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
sewardjcbdddcf2005-03-10 23:23:45 +00009
njn53612422005-03-12 16:22:54 +000010 Copyright (C) 2000-2005 Julian Seward
sewardjcbdddcf2005-03-10 23:23:45 +000011 jseward@acm.org
12 Copyright (C) 2003-2005 Jeremy Fitzhardinge
13 jeremy@goop.org
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of the
18 License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 02111-1307, USA.
29
30 The GNU General Public License is contained in the file COPYING.
31*/
sewardjcbdddcf2005-03-10 23:23:45 +000032
njnc7561b92005-06-19 01:24:32 +000033#include "pub_core_basics.h"
sewardj45f4e7c2005-09-27 19:20:21 +000034#include "pub_core_debuglog.h"
njn88c51482005-06-25 20:49:33 +000035#include "pub_core_debuginfo.h"
njn97405b22005-06-02 03:39:33 +000036#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000037#include "pub_core_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +000038#include "pub_core_libcprint.h"
njnaf1d7df2005-06-11 01:31:52 +000039#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000040#include "pub_core_options.h"
njnd1af0032005-05-29 17:01:48 +000041#include "pub_core_redir.h"
njnaf1d7df2005-06-11 01:31:52 +000042#include "pub_core_skiplist.h"
njna7598f62005-06-18 03:27:58 +000043#include "pub_core_trampoline.h"
njn8bddf582005-05-13 23:40:55 +000044#include "pub_core_transtab.h"
sewardj55f9d1a2005-04-25 11:11:44 +000045
sewardjcbdddcf2005-03-10 23:23:45 +000046/*------------------------------------------------------------*/
47/*--- General purpose redirection. ---*/
48/*------------------------------------------------------------*/
49
njnd9109c62005-06-26 04:49:25 +000050#define TRACE_REDIR(format, args...) \
51 if (VG_(clo_trace_redir)) { VG_(message)(Vg_DebugMsg, format, ## args); }
52
sewardjcbdddcf2005-03-10 23:23:45 +000053/*
54 wraps and redirections, indexed by from_addr
55
56 Redirection and wrapping are two distinct mechanisms which Valgrind
57 can use to change the client's control flow.
58
59 Redirection intercepts a call to a client function, and re-points it
60 to a new piece of code (presumably functionally equivalent). The
61 original code is never run.
62
63 Wrapping does call the client's original code, but calls "before"
64 and "after" functions which can inspect (and perhaps modify) the
65 function's arguments and return value.
66 */
67struct _CodeRedirect {
68 enum redir_type {
69 R_REDIRECT, /* plain redirection */
70 R_WRAPPER, /* wrap with valgrind-internal code */
71 R_CLIENT_WRAPPER, /* wrap with client-side code */
72 } type;
73
74 const Char *from_lib; /* library qualifier pattern */
75 const Char *from_sym; /* symbol */
76 Addr from_addr; /* old addr */
77
njnd9109c62005-06-26 04:49:25 +000078 Addr to_addr; /* used for redirection -- new addr */
79 const FuncWrapper *wrapper; /* used for wrapping */
sewardjcbdddcf2005-03-10 23:23:45 +000080
njnd9109c62005-06-26 04:49:25 +000081 CodeRedirect *next; /* next pointer on unresolved list */
sewardjcbdddcf2005-03-10 23:23:45 +000082};
83
84static Char *straddr(void *p)
85{
86 static Char buf[16];
sewardjcbdddcf2005-03-10 23:23:45 +000087 VG_(sprintf)(buf, "%p", *(Addr *)p);
sewardjcbdddcf2005-03-10 23:23:45 +000088 return buf;
89}
90
njnd9109c62005-06-26 04:49:25 +000091static SkipList sk_resolved_redirs =
njn41f8e4a2005-06-25 20:13:05 +000092 VG_SKIPLIST_INIT(CodeRedirect, from_addr, VG_(cmp_Addr),
93 straddr, VG_AR_SYMTAB);
94
njnd9109c62005-06-26 04:49:25 +000095static CodeRedirect *unresolved_redirs = NULL;
sewardjcbdddcf2005-03-10 23:23:45 +000096
njn4f612c22005-06-25 20:22:43 +000097static Bool soname_matches(const Char *pattern, const Char* soname)
sewardjcbdddcf2005-03-10 23:23:45 +000098{
njn41f8e4a2005-06-25 20:13:05 +000099 // pattern must start with "soname:"
100 vg_assert(NULL != pattern);
101 vg_assert(0 == VG_(strncmp)(pattern, "soname:", 7));
sewardjcbdddcf2005-03-10 23:23:45 +0000102
njn4f612c22005-06-25 20:22:43 +0000103 if (NULL == soname)
sewardjcbdddcf2005-03-10 23:23:45 +0000104 return False;
105
njn4f612c22005-06-25 20:22:43 +0000106 return VG_(string_match)(pattern + 7, soname);
sewardjcbdddcf2005-03-10 23:23:45 +0000107}
108
njnd9109c62005-06-26 04:49:25 +0000109Bool VG_(is_resolved)(const CodeRedirect *redir)
sewardjcbdddcf2005-03-10 23:23:45 +0000110{
111 return redir->from_addr != 0;
112}
113
njnd9109c62005-06-26 04:49:25 +0000114// Prepends redir to the unresolved list.
115static void add_redir_to_unresolved_list(CodeRedirect *redir)
sewardjcbdddcf2005-03-10 23:23:45 +0000116{
njnd9109c62005-06-26 04:49:25 +0000117 redir->next = unresolved_redirs;
118 unresolved_redirs = redir;
sewardjcbdddcf2005-03-10 23:23:45 +0000119}
120
sewardjf9a82d12005-07-23 09:52:21 +0000121static void add_redir_to_resolved_list(CodeRedirect *redir, Bool need_discard)
tom748a1312005-04-02 15:53:01 +0000122{
njnd9109c62005-06-26 04:49:25 +0000123 vg_assert(redir->from_addr);
124
125 switch (redir->type) {
126 case R_REDIRECT: {
127 CodeRedirect* r;
128
129 TRACE_REDIR(" redir resolved (%s:%s=%p -> %p)",
130 redir->from_lib, redir->from_sym, redir->from_addr,
131 redir->to_addr);
132
133 vg_assert(redir->to_addr != 0);
tom748a1312005-04-02 15:53:01 +0000134
sewardjf9a82d12005-07-23 09:52:21 +0000135 if (need_discard) {
tom748a1312005-04-02 15:53:01 +0000136 /* For some given (from, to) redir, the "from" function got
sewardjf9a82d12005-07-23 09:52:21 +0000137 loaded before the .so containing "to" became available so
138 we need to discard any existing translations involving
139 the "from" function.
tom748a1312005-04-02 15:53:01 +0000140
141 Note, we only really need to discard the first bb of the
142 old entry point, and so we avoid the problem of having to
143 figure out how big that bb was -- since it is at least 1
144 byte of original code, we can just pass 1 as the original
145 size to invalidate_translations() and it will indeed get
146 rid of the translation.
147
148 Note, this is potentially expensive -- discarding
sewardjeccb2d82005-07-23 11:36:03 +0000149 translations requires a complete search through all of
150 them.
tom748a1312005-04-02 15:53:01 +0000151 */
sewardjf9a82d12005-07-23 09:52:21 +0000152 TRACE_REDIR("Discarding translation due to redirect of already loaded function" );
njnd9109c62005-06-26 04:49:25 +0000153 TRACE_REDIR(" %s:%s(%p) -> %p)", redir->from_lib, redir->from_sym,
154 redir->from_addr, redir->to_addr );
sewardj45f4e7c2005-09-27 19:20:21 +0000155 VG_(discard_translations)((Addr64)redir->from_addr, 1,
156 "add_redir_to_resolved_list");
tom748a1312005-04-02 15:53:01 +0000157 }
158
njnd9109c62005-06-26 04:49:25 +0000159 r = VG_(SkipList_Find_Exact)(&sk_resolved_redirs, &redir->from_addr);
tom748a1312005-04-02 15:53:01 +0000160
njnd9109c62005-06-26 04:49:25 +0000161 if (r == NULL) {
162 VG_(SkipList_Insert)(&sk_resolved_redirs, redir);
163 } else {
164 /* XXX leak redir */
165 TRACE_REDIR(" redir %s:%s:%p->%p duplicated\n",
166 redir->from_lib, redir->from_sym, redir->from_addr,
167 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000168 }
169 break;
njnd9109c62005-06-26 04:49:25 +0000170 }
tom748a1312005-04-02 15:53:01 +0000171
172 case R_WRAPPER:
njnd9109c62005-06-26 04:49:25 +0000173 TRACE_REDIR(" wrapper resolved (%s:%s=%p -> wrapper)",
174 redir->from_lib, redir->from_sym, redir->from_addr);
175
176 vg_assert(redir->wrapper);
tom748a1312005-04-02 15:53:01 +0000177
178 /* XXX redir leaked */
179 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
180 break;
181
182 case R_CLIENT_WRAPPER:
njnd9109c62005-06-26 04:49:25 +0000183 vg_assert(redir->wrapper);
tom748a1312005-04-02 15:53:01 +0000184 VG_(core_panic)("not implemented");
185 break;
186 }
187}
188
njnd9109c62005-06-26 04:49:25 +0000189// Resolve a redir using si if possible. Returns True if it succeeded.
190static Bool resolve_redir_with_seginfo(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000191{
njnd9109c62005-06-26 04:49:25 +0000192 Bool ok;
sewardjcbdddcf2005-03-10 23:23:45 +0000193
194 vg_assert(si != NULL);
njnd9109c62005-06-26 04:49:25 +0000195 vg_assert(redir->from_addr == 0 );
196 vg_assert(redir->from_sym != NULL);
sewardjcbdddcf2005-03-10 23:23:45 +0000197
njnd9109c62005-06-26 04:49:25 +0000198 // Resolved if the soname matches and we find the symbol.
199 ok = soname_matches(redir->from_lib, VG_(seginfo_soname)(si));
200 if (ok) {
njn748ace42005-06-21 03:36:01 +0000201 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
njnd9109c62005-06-26 04:49:25 +0000202 ok = ( redir->from_addr == 0 ? False : True );
sewardjcbdddcf2005-03-10 23:23:45 +0000203 }
njnd9109c62005-06-26 04:49:25 +0000204 return ok;
sewardjcbdddcf2005-03-10 23:23:45 +0000205}
206
njn2025cf92005-06-26 20:44:48 +0000207// Resolve a redir using any SegInfo if possible. This is called whenever
208// a new sym-to-addr redir is created. It covers the case where a
209// replacement function is loaded after its replacee.
njnd9109c62005-06-26 04:49:25 +0000210static Bool resolve_redir_with_existing_seginfos(CodeRedirect *redir)
njnbf7ca332005-05-14 17:11:06 +0000211{
212 const SegInfo *si;
213
njnd9109c62005-06-26 04:49:25 +0000214 for (si = VG_(next_seginfo)(NULL);
215 si != NULL;
216 si = VG_(next_seginfo)(si))
njnbf7ca332005-05-14 17:11:06 +0000217 {
njnd9109c62005-06-26 04:49:25 +0000218 if (resolve_redir_with_seginfo(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000219 return True;
220 }
221 return False;
222}
223
njnd9109c62005-06-26 04:49:25 +0000224// Resolve as many unresolved redirs as possible with this SegInfo. This
njn2025cf92005-06-26 20:44:48 +0000225// should be called when a new SegInfo symtab is loaded. It covers the case
226// where a replacee function is loaded after its replacement function.
njnd9109c62005-06-26 04:49:25 +0000227void VG_(resolve_existing_redirs_with_seginfo)(SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000228{
njnd9109c62005-06-26 04:49:25 +0000229 CodeRedirect **prevp = &unresolved_redirs;
sewardjcbdddcf2005-03-10 23:23:45 +0000230 CodeRedirect *redir, *next;
231
njnd9109c62005-06-26 04:49:25 +0000232 TRACE_REDIR("Just loaded %s (soname=%s),",
233 VG_(seginfo_filename)(si), VG_(seginfo_soname)(si));
234 TRACE_REDIR(" resolving any unresolved redirs with it");
sewardjcbdddcf2005-03-10 23:23:45 +0000235
njnd9109c62005-06-26 04:49:25 +0000236 // Visit each unresolved redir - if it becomes resolved, then
237 // move it from the unresolved list to the resolved list.
238 for (redir = unresolved_redirs; redir != NULL; redir = next) {
sewardjcbdddcf2005-03-10 23:23:45 +0000239 next = redir->next;
240
njnd9109c62005-06-26 04:49:25 +0000241 if (resolve_redir_with_seginfo(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000242 *prevp = next;
243 redir->next = NULL;
sewardjf9a82d12005-07-23 09:52:21 +0000244 add_redir_to_resolved_list(redir, False);
sewardjcbdddcf2005-03-10 23:23:45 +0000245 } else
246 prevp = &redir->next;
247 }
sewardjcbdddcf2005-03-10 23:23:45 +0000248
njnd9109c62005-06-26 04:49:25 +0000249 TRACE_REDIR(" Finished resolving");
sewardjcbdddcf2005-03-10 23:23:45 +0000250}
251
tom748a1312005-04-02 15:53:01 +0000252/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000253__attribute__((unused)) // It is used, but not on all platforms...
njnd9109c62005-06-26 04:49:25 +0000254static void add_redirect_addr_to_addr( Addr from_addr, Addr to_addr )
tom748a1312005-04-02 15:53:01 +0000255{
njnd9109c62005-06-26 04:49:25 +0000256 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redirs);
257
258 vg_assert(0 != from_addr && 0 != to_addr);
259
260 redir->type = R_REDIRECT;
261
262 redir->from_lib = NULL;
263 redir->from_sym = NULL;
264 redir->from_addr = from_addr;
265
266 redir->to_addr = to_addr;
267 redir->wrapper = 0;
268
269 TRACE_REDIR("REDIRECT addr to addr: %p to %p", from_addr, to_addr);
270
271 // This redirection is already resolved, put it straight in the list.
sewardjf9a82d12005-07-23 09:52:21 +0000272 add_redir_to_resolved_list(redir, True);
njnd9109c62005-06-26 04:49:25 +0000273}
274
275/* Redirect a lib/symbol reference to a function at addr */
276static void add_redirect_sym_to_addr(
277 const Char *from_lib, const Char *from_sym, Addr to_addr
278)
279{
280 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redirs);
281
282 vg_assert(from_lib && from_sym && 0 != to_addr);
283
284 redir->type = R_REDIRECT;
285 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
286 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
287 redir->from_addr = 0;
288 redir->to_addr = to_addr;
289 redir->wrapper = 0;
290
291 TRACE_REDIR("REDIR sym to addr: %s:%s to %p", from_lib, from_sym, to_addr);
292
293 // Check against all existing segments to see if this redirection
njn2025cf92005-06-26 20:44:48 +0000294 // can be resolved immediately (as will be the case when the replacement
295 // function is loaded after the replacee). Then add it to the
296 // appropriate list.
njnd9109c62005-06-26 04:49:25 +0000297 if (resolve_redir_with_existing_seginfos(redir)) {
sewardjf9a82d12005-07-23 09:52:21 +0000298 add_redir_to_resolved_list(redir, True);
njnd9109c62005-06-26 04:49:25 +0000299 } else {
300 add_redir_to_unresolved_list(redir);
301 }
tom748a1312005-04-02 15:53:01 +0000302}
303
sewardjcbdddcf2005-03-10 23:23:45 +0000304CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
305 const FuncWrapper *wrapper)
306{
njnd9109c62005-06-26 04:49:25 +0000307 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redirs);
sewardjcbdddcf2005-03-10 23:23:45 +0000308
njnd9109c62005-06-26 04:49:25 +0000309 redir->type = R_WRAPPER;
njn136b83b2005-06-19 05:14:03 +0000310 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
311 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000312 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000313 redir->to_addr = 0;
314 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000315
njnd9109c62005-06-26 04:49:25 +0000316 TRACE_REDIR("REDIR sym to wrapper: %s:%s to (%p,%p)",
317 from_lib, from_sym, wrapper->before, wrapper->after);
318
319 // Check against all existing segments to see if this redirection
320 // can be resolved immediately. Then add it to the appropriate list.
321 if (resolve_redir_with_existing_seginfos(redir)) {
sewardjf9a82d12005-07-23 09:52:21 +0000322 add_redir_to_resolved_list(redir, True);
njnd9109c62005-06-26 04:49:25 +0000323 } else {
324 add_redir_to_unresolved_list(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000325 }
326
327 return redir;
328}
329
sewardjcbdddcf2005-03-10 23:23:45 +0000330/* If address 'a' is being redirected, return the redirected-to
331 address. */
332Addr VG_(code_redirect)(Addr a)
333{
334 CodeRedirect* r;
335
njnd9109c62005-06-26 04:49:25 +0000336 r = VG_(SkipList_Find_Exact)(&sk_resolved_redirs, &a);
sewardjcbdddcf2005-03-10 23:23:45 +0000337 if (r == NULL)
338 return a;
339
340 vg_assert(r->to_addr != 0);
341
342 return r->to_addr;
343}
344
345void VG_(setup_code_redirect_table) ( void )
346{
njnd1af0032005-05-29 17:01:48 +0000347#if defined(VGP_x86_linux)
348 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
sewardjb9bce632005-06-21 01:41:34 +0000349 routine, to our copy so that the special sysinfo unwind hack in
350 m_stacktrace.c will kick in. */
351 add_redirect_sym_to_addr(
352 "soname:ld-linux.so.2", "_dl_sysinfo_int80",
353 (Addr)&VG_(x86_linux_REDIR_FOR__dl_sysinfo_int80)
354 );
355
njnd1af0032005-05-29 17:01:48 +0000356#elif defined(VGP_amd64_linux)
sewardjb9bce632005-06-21 01:41:34 +0000357
njnd1af0032005-05-29 17:01:48 +0000358 /* Redirect vsyscalls to local versions */
sewardjb9bce632005-06-21 01:41:34 +0000359 add_redirect_addr_to_addr(
360 0xFFFFFFFFFF600000ULL,
361 (Addr)&VG_(amd64_linux_REDIR_FOR_vgettimeofday)
362 );
363
364 add_redirect_addr_to_addr(
tomf5db3b62005-06-21 13:26:17 +0000365 0xFFFFFFFFFF600400ULL,
sewardjb9bce632005-06-21 01:41:34 +0000366 (Addr)&VG_(amd64_linux_REDIR_FOR_vtime)
367 );
368
cerion85665ca2005-06-20 15:51:07 +0000369#elif defined(VGP_ppc32_linux)
sewardjb9bce632005-06-21 01:41:34 +0000370
sewardjad50be32005-08-18 11:54:30 +0000371 add_redirect_sym_to_addr(
372 "soname:ld.so.1", "strlen",
373 (Addr)&VG_(ppc32_linux_REDIR_FOR_strlen)
374 );
sewardjb9bce632005-06-21 01:41:34 +0000375
njnd1af0032005-05-29 17:01:48 +0000376#else
377# error Unknown platform
378#endif
379}
sewardjcbdddcf2005-03-10 23:23:45 +0000380
njn16eeb4e2005-06-16 03:56:58 +0000381/* Z-decode a symbol into library:func form, eg
382
383 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
384
385 Uses the Z-encoding scheme described in pub_core_redir.h.
386 Returns True if demangle OK, False otherwise.
387*/
388static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
389{
390# define EMIT(ch) \
391 do { \
392 if (j >= nbytes) \
393 result[j-1] = 0; \
394 else \
395 result[j++] = ch; \
396 } while (0)
397
398 Bool error = False;
399 Int i, j = 0;
400 Int len = VG_(strlen)(symbol);
401 if (0) VG_(printf)("idm: %s\n", symbol);
402
403 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
404
405 /* Chew though the Z-encoded soname part. */
406 while (True) {
407
408 if (i >= len)
409 break;
410
411 if (symbol[i] == '_')
412 /* We found the underscore following the Z-encoded soname.
413 Just copy the rest literally. */
414 break;
415
416 if (symbol[i] != 'Z') {
417 EMIT(symbol[i]);
418 i++;
419 continue;
420 }
421
422 /* We've got a Z-escape. Act accordingly. */
423 i++;
424 if (i >= len) {
425 /* Hmm, Z right at the end. Something's wrong. */
426 error = True;
427 EMIT('Z');
428 break;
429 }
430 switch (symbol[i]) {
431 case 'a': EMIT('*'); break;
432 case 'p': EMIT('+'); break;
433 case 'c': EMIT(':'); break;
434 case 'd': EMIT('.'); break;
435 case 'u': EMIT('_'); break;
436 case 'h': EMIT('-'); break;
437 case 's': EMIT(' '); break;
438 case 'Z': EMIT('Z'); break;
439 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
440 }
441 i++;
442 }
443
444 if (error || i >= len || symbol[i] != '_') {
445 /* Something's wrong. Give up. */
446 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
447 EMIT(0);
448 return False;
449 }
450
451 /* Copy the rest of the string verbatim. */
452 i++;
453 EMIT(':');
454 while (True) {
455 if (i >= len)
456 break;
457 EMIT(symbol[i]);
458 i++;
459 }
460
461 EMIT(0);
462 if (0) VG_(printf)("%s\n", result);
463 return True;
464
465# undef EMIT
466}
467
468// Nb: this can change the string pointed to by 'symbol'.
469static void handle_replacement_function( Char* symbol, Addr addr )
470{
471 Bool ok;
472 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
473 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
474 Char *func;
475
476 // Put "soname:" at the start of lib
477 VG_(strcpy)(lib, "soname:");
478
479 ok = Z_decode(symbol, lib+7, len);
480 if (ok) {
481 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
482 func = lib + VG_(strlen)(lib)-1;
483 while(*func != ':') func--;
484 *func = '\0';
485 func++; // Move past the '\0'
486
487 // Now lib is "soname:<libname>" and func is "<fnname>".
488 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
489 add_redirect_sym_to_addr(lib, func, addr);
490
491 // Overwrite the given Z-encoded name with just the fnname.
492 VG_(strcpy)(symbol, func);
493 }
494
495 VG_(arena_free)(VG_AR_SYMTAB, lib);
496}
497
njnbc6d84d2005-06-19 18:58:03 +0000498static Addr __libc_freeres_wrapper = 0;
499
500Addr VG_(get_libc_freeres_wrapper)(void)
501{
502 return __libc_freeres_wrapper;
503}
504
njn16eeb4e2005-06-16 03:56:58 +0000505// This is specifically for stringifying VG_(x) function names. We
506// need to do two macroexpansions to get the VG_ macro expanded before
507// stringifying.
508#define _STR(x) #x
509#define STR(x) _STR(x)
510
511static void handle_load_notifier( Char* symbol, Addr addr )
512{
513 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000514 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000515// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
516// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
517 else
518 vg_assert2(0, "unrecognised load notification function: %s", symbol);
519}
520
521static Bool is_replacement_function(Char* s)
522{
523 return (0 == VG_(strncmp)(s,
524 VG_REPLACE_FUNCTION_PREFIX,
525 VG_REPLACE_FUNCTION_PREFIX_LEN));
526}
527
528static Bool is_load_notifier(Char* s)
529{
530 return (0 == VG_(strncmp)(s,
531 VG_NOTIFY_ON_LOAD_PREFIX,
532 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
533}
534
535// Call this for each symbol loaded. It determines if we need to do
536// anything special with it. It can modify 'symbol' in-place.
537void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
538{
539 if (is_replacement_function(symbol))
540 handle_replacement_function(symbol, addr);
541 else
542 if (is_load_notifier(symbol))
543 handle_load_notifier(symbol, addr);
544}
545
546
sewardjcbdddcf2005-03-10 23:23:45 +0000547//:: /*------------------------------------------------------------*/
548//:: /*--- General function wrapping. ---*/
549//:: /*------------------------------------------------------------*/
550//::
551//:: /*
552//:: TODO:
553//:: - hook into the symtab machinery
554//:: - client-side wrappers?
555//:: - better interfaces for before() functions to get to arguments
556//:: - handle munmap of code (dlclose())
557//:: - handle thread exit
558//:: - handle longjmp
559//:: */
560//:: struct callkey {
561//:: ThreadId tid; /* calling thread */
562//:: Addr esp; /* address of args on stack */
563//:: Addr eip; /* return address */
564//:: };
565//::
566//:: struct call_instance {
567//:: struct callkey key;
568//::
569//:: const FuncWrapper *wrapper;
570//:: void *nonce;
571//:: };
572//::
573//:: static inline Addr addrcmp(Addr a, Addr b)
574//:: {
575//:: if (a < b)
576//:: return -1;
577//:: else if (a > b)
578//:: return 1;
579//:: else
580//:: return 0;
581//:: }
582//::
583//:: static inline Int cmp(UInt a, UInt b)
584//:: {
585//:: if (a < b)
586//:: return -1;
587//:: else if (a > b)
588//:: return 1;
589//:: else
590//:: return 0;
591//:: }
592//::
593//:: static Int keycmp(const void *pa, const void *pb)
594//:: {
595//:: const struct callkey *a = (const struct callkey *)pa;
596//:: const struct callkey *b = (const struct callkey *)pb;
597//:: Int ret;
598//::
599//:: if ((ret = cmp(a->tid, b->tid)))
600//:: return ret;
601//::
602//:: if ((ret = addrcmp(a->esp, b->esp)))
603//:: return ret;
604//::
605//:: return addrcmp(a->eip, b->eip);
606//:: }
607//::
608//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000609//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000610//:: NULL, VG_AR_SYMTAB);
611//::
612//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
613//:: {
614//:: struct callkey key = { tid, argsp, retaddr };
615//::
616//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
617//:: }
618//::
619//:: static void wrapper_return(Addr retaddr);
620//::
621//:: /* Called from generated code via helper */
622//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
623//:: {
njnaf839f52005-06-23 03:27:57 +0000624//:: Addr retaddr = VG_RETADDR(tst->arch);
625//:: Addr argp = (Addr)&VG_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000626//:: void *nonce = NULL;
627//:: Bool mf = VG_(my_fault);
628//:: VG_(my_fault) = True;
629//::
630//:: if (wrapper->before) {
njnaf839f52005-06-23 03:27:57 +0000631//:: va_list args = VG_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000632//:: nonce = (*wrapper->before)(args);
633//:: }
634//::
635//:: if (wrapper->after) {
636//:: /* If there's an after function, make sure it gets called */
637//:: struct call_instance *call;
638//::
639//:: call = find_call(retaddr, argp, tst->tid);
640//::
641//:: if (call != NULL) {
642//:: /* Found a stale outstanding call; clean it up and recycle
643//:: the structure */
644//:: if (call->wrapper->after)
645//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
646//:: } else {
647//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
648//::
649//:: call->key.tid = tst->tid;
650//:: call->key.esp = argp;
651//:: call->key.eip = retaddr;
652//::
653//:: VG_(SkipList_Insert)(&wrapped_frames, call);
654//::
655//:: wrapper_return(retaddr);
656//:: }
657//::
658//:: call->wrapper = wrapper;
659//:: call->nonce = nonce;
660//:: } else
661//:: vg_assert(nonce == NULL);
662//::
663//:: VG_(my_fault) = mf;
664//:: }
665//::
666//:: /* Called from generated code via helper */
667//:: void VG_(wrap_after)(ThreadState *tst)
668//:: {
njnaf839f52005-06-23 03:27:57 +0000669//:: Addr EIP = VG_INSTR_PTR(tst->arch); /* instruction after call */
670//:: Addr ESP = VG_STACK_PTR(tst->arch); /* pointer to args */
671//:: Word ret = VG_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000672//:: struct call_instance *call;
673//:: Bool mf = VG_(my_fault);
674//::
675//:: VG_(my_fault) = True;
676//:: call = find_call(EIP, ESP, tst->tid);
677//::
678//:: if (0)
679//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
680//::
681//:: if (call != NULL) {
682//:: if (call->wrapper->after)
683//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
684//::
685//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
686//:: VG_(SkipNode_Free)(&wrapped_frames, call);
687//:: }
688//:: VG_(my_fault) = mf;
689//:: }
690//::
691//::
692//:: struct wrapped_function {
693//:: Addr eip; /* eip of function entrypoint */
694//:: const FuncWrapper *wrapper;
695//:: };
696//::
697//:: struct wrapper_return {
698//:: Addr eip; /* return address */
699//:: };
700//::
701//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000702//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000703//:: NULL, VG_AR_SYMTAB);
704//::
705//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000706//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000707//:: NULL, VG_AR_SYMTAB);
708//::
709//:: /* Wrap function starting at eip */
710//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
711//:: {
712//:: struct wrapped_function *func;
713//::
714//:: if (0)
715//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
716//::
717//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
718//::
719//:: if (func == NULL) {
720//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
721//:: VG_(invalidate_translations)(eip, 1, True);
722//::
723//:: func->eip = eip;
724//:: VG_(SkipList_Insert)(&wrapped_functions, func);
725//:: }
726//::
727//:: func->wrapper = wrapper;
728//:: }
729//::
730//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
731//:: {
732//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
733//::
734//:: if (func)
735//:: return func->wrapper;
736//:: return NULL;
737//:: }
738//::
739//:: Bool VG_(is_wrapper_return)(Addr eip)
740//:: {
741//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
742//::
743//:: return ret != NULL;
744//:: }
745//::
746//:: /* Mark eip as being the return address of a wrapper, so that the
747//:: codegen will generate the appropriate call. */
748//:: void wrapper_return(Addr eip)
749//:: {
750//:: struct wrapper_return *ret;
751//::
752//:: if (VG_(is_wrapper_return)(eip))
753//:: return;
754//::
755//:: VG_(invalidate_translations)(eip, 1, True);
756//::
757//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
758//:: ret->eip = eip;
759//::
760//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
761//:: }
njnc0ae7052005-08-25 22:55:19 +0000762
763/*--------------------------------------------------------------------*/
764/*--- end ---*/
765/*--------------------------------------------------------------------*/