blob: 960aa7ff99277ed4413aa486fed9c88efd4681f9 [file] [log] [blame]
sewardjcbdddcf2005-03-10 23:23:45 +00001/*--------------------------------------------------------------------*/
2/*--- Management of function redirection and wrapping. ---*/
3/*--- vg_redir.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, an extensible x86 protected-mode
8 emulator for monitoring program execution on x86-Unixes.
9
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"
njn899ce732005-06-21 00:28:11 +000034#include "pub_core_debuginfo.h" // Needed for pub_core_aspacemgr :(
35#include "pub_core_aspacemgr.h" // Needed for pub_core_redir.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"
njn24a6efb2005-06-20 03:36:51 +000045#include "m_debuginfo/priv_symtab.h" // XXX: bad! For SegInfo internals
sewardj55f9d1a2005-04-25 11:11:44 +000046
sewardjcbdddcf2005-03-10 23:23:45 +000047/*------------------------------------------------------------*/
48/*--- General purpose redirection. ---*/
49/*------------------------------------------------------------*/
50
51/*
52 wraps and redirections, indexed by from_addr
53
54 Redirection and wrapping are two distinct mechanisms which Valgrind
55 can use to change the client's control flow.
56
57 Redirection intercepts a call to a client function, and re-points it
58 to a new piece of code (presumably functionally equivalent). The
59 original code is never run.
60
61 Wrapping does call the client's original code, but calls "before"
62 and "after" functions which can inspect (and perhaps modify) the
63 function's arguments and return value.
64 */
65struct _CodeRedirect {
66 enum redir_type {
67 R_REDIRECT, /* plain redirection */
68 R_WRAPPER, /* wrap with valgrind-internal code */
69 R_CLIENT_WRAPPER, /* wrap with client-side code */
70 } type;
71
72 const Char *from_lib; /* library qualifier pattern */
73 const Char *from_sym; /* symbol */
74 Addr from_addr; /* old addr */
75
76 /* used for redirection */
sewardjcbdddcf2005-03-10 23:23:45 +000077 Addr to_addr; /* new addr */
78
79 /* used for wrapping */
80 const FuncWrapper *wrapper;
81
82 CodeRedirect *next; /* next pointer on unresolved list */
83};
84
85static Char *straddr(void *p)
86{
87 static Char buf[16];
88
89 VG_(sprintf)(buf, "%p", *(Addr *)p);
90
91 return buf;
92}
93
njn41f8e4a2005-06-25 20:13:05 +000094static SkipList sk_resolved_redir =
95 VG_SKIPLIST_INIT(CodeRedirect, from_addr, VG_(cmp_Addr),
96 straddr, VG_AR_SYMTAB);
97
sewardjcbdddcf2005-03-10 23:23:45 +000098static CodeRedirect *unresolved_redir = NULL;
99
100static Bool match_lib(const Char *pattern, const SegInfo *si)
101{
njn41f8e4a2005-06-25 20:13:05 +0000102 // pattern must start with "soname:"
103 vg_assert(NULL != pattern);
104 vg_assert(0 == VG_(strncmp)(pattern, "soname:", 7));
sewardjcbdddcf2005-03-10 23:23:45 +0000105
njn41f8e4a2005-06-25 20:13:05 +0000106 if (si->soname == NULL)
sewardjcbdddcf2005-03-10 23:23:45 +0000107 return False;
108
njn41f8e4a2005-06-25 20:13:05 +0000109 return VG_(string_match)(pattern + 7, si->soname);
sewardjcbdddcf2005-03-10 23:23:45 +0000110}
111
112static inline Bool from_resolved(const CodeRedirect *redir)
113{
114 return redir->from_addr != 0;
115}
116
sewardjcbdddcf2005-03-10 23:23:45 +0000117Bool VG_(is_resolved)(const CodeRedirect *redir)
118{
njn136b83b2005-06-19 05:14:03 +0000119 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000120}
121
tom748a1312005-04-02 15:53:01 +0000122static void add_resolved(CodeRedirect *redir)
123{
124 switch(redir->type) {
125 case R_REDIRECT:
126 if (VG_(clo_trace_redir)) {
127 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
128 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000129 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000130 }
131
132 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
133 /* For some given (from, to) redir, the "from" function got
134 called before the .so containing "to" became available. We
135 know this because there is already a translation for the
136 entry point of the original "from". So the redirect will
137 never actually take effect unless that translation is
138 discarded.
139
140 Note, we only really need to discard the first bb of the
141 old entry point, and so we avoid the problem of having to
142 figure out how big that bb was -- since it is at least 1
143 byte of original code, we can just pass 1 as the original
144 size to invalidate_translations() and it will indeed get
145 rid of the translation.
146
147 Note, this is potentially expensive -- discarding
148 translations causes complete unchaining.
149 */
150 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
151 VG_(message)(Vg_UserMsg,
152 "Discarding translation due to redirect of already called function" );
153 VG_(message)(Vg_UserMsg,
154 " %s (%p -> %p)",
155 redir->from_sym, redir->from_addr, redir->to_addr );
156 }
157 VG_(discard_translations)((Addr64)redir->from_addr, 1);
158 }
159
160 {
161 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
162
163 if (r == NULL)
164 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
165 else {
166 /* XXX leak redir */
167 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000168 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000169 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000170 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000171 }
172 }
173 break;
174
175 case R_WRAPPER:
176 if (VG_(clo_trace_redir)) {
177 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
178 redir->from_lib, redir->from_sym, redir->from_addr);
179 }
180
181 /* XXX redir leaked */
182 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
183 break;
184
185 case R_CLIENT_WRAPPER:
186 VG_(core_panic)("not implemented");
187 break;
188 }
189}
190
sewardjcbdddcf2005-03-10 23:23:45 +0000191/* Resolve a redir using si if possible, and add it to the resolved
192 list */
njn17250122005-05-14 17:18:12 +0000193static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000194{
195 Bool resolved;
196
197 vg_assert(si != NULL);
sewardjcbdddcf2005-03-10 23:23:45 +0000198
njn748ace42005-06-21 03:36:01 +0000199 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000200 vg_assert(!resolved);
njn748ace42005-06-21 03:36:01 +0000201 vg_assert(redir->from_sym != NULL);
sewardjcbdddcf2005-03-10 23:23:45 +0000202
njn748ace42005-06-21 03:36:01 +0000203 if (match_lib(redir->from_lib, si)) {
204 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
205 if (VG_(clo_trace_redir) && redir->from_addr != 0)
206 VG_(printf)(" bind FROM: %p = %s:%s\n",
207 redir->from_addr,redir->from_lib, redir->from_sym );
sewardjcbdddcf2005-03-10 23:23:45 +0000208 }
209
njn136b83b2005-06-19 05:14:03 +0000210 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000211
212 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000213 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000214 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000215 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000216
tom748a1312005-04-02 15:53:01 +0000217 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000218
219 return resolved;
220}
221
njnbf7ca332005-05-14 17:11:06 +0000222static Bool resolve_redir_allsegs(CodeRedirect *redir)
223{
224 const SegInfo *si;
225
226 for(si = VG_(next_seginfo)(NULL);
227 si != NULL;
228 si = VG_(next_seginfo)(si))
229 {
njn17250122005-05-14 17:18:12 +0000230 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000231 return True;
232 }
233 return False;
234}
235
sewardjcbdddcf2005-03-10 23:23:45 +0000236/* Go through the complete redir list, resolving as much as possible with this SegInfo.
237
238 This should be called when a new SegInfo symtab is loaded.
239 */
240void VG_(resolve_seg_redirs)(SegInfo *si)
241{
242 CodeRedirect **prevp = &unresolved_redir;
243 CodeRedirect *redir, *next;
244
245 if (VG_(clo_trace_redir))
246 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
247 si->filename, si->soname);
248
249 /* visit each unresolved redir - if it becomes resolved, then
250 remove it from the unresolved list */
251 for(redir = unresolved_redir; redir != NULL; redir = next) {
252 next = redir->next;
253
njn17250122005-05-14 17:18:12 +0000254 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000255 *prevp = next;
256 redir->next = NULL;
257 } else
258 prevp = &redir->next;
259 }
260}
261
njn136b83b2005-06-19 05:14:03 +0000262static void add_redirect_X_to_addr(
263 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000264)
sewardjcbdddcf2005-03-10 23:23:45 +0000265{
266 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
267
268 redir->type = R_REDIRECT;
269
njn16eeb4e2005-06-16 03:56:58 +0000270 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000271 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000272 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000273 else redir->from_sym = NULL;
sewardjb9bce632005-06-21 01:41:34 +0000274
275 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000276
njn136b83b2005-06-19 05:14:03 +0000277 vg_assert(0 != to_addr);
278 redir->to_addr = to_addr;
279 redir->wrapper = 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000280
281 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
282 VG_(message)(Vg_UserMsg,
njn136b83b2005-06-19 05:14:03 +0000283 "REDIRECT %s:%s(%p) to %p",
284 from_lib, from_sym, from_addr, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000285
286 /* Check against all existing segments to see if this redirection
287 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000288 if (VG_(is_resolved)(redir)) {
289 add_resolved(redir);
290 }
291 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000292 /* nope, add to list */
293 redir->next = unresolved_redir;
294 unresolved_redir = redir;
295 }
296}
297
njn16eeb4e2005-06-16 03:56:58 +0000298/* Redirect a lib/symbol reference to a function at addr */
299static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
300 Addr to_addr)
301{
njn136b83b2005-06-19 05:14:03 +0000302 add_redirect_X_to_addr(from_lib, from_sym, 0, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000303}
304
tom748a1312005-04-02 15:53:01 +0000305/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000306__attribute__((unused)) // It is used, but not on all platforms...
307static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000308{
njn136b83b2005-06-19 05:14:03 +0000309 add_redirect_X_to_addr(NULL, NULL, from_addr, to_addr);
tom748a1312005-04-02 15:53:01 +0000310}
311
sewardjcbdddcf2005-03-10 23:23:45 +0000312CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
313 const FuncWrapper *wrapper)
314{
315 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
316
317 if (0)
318 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
319 from_lib, from_sym, wrapper->before, wrapper->after);
320
321 redir->type = R_WRAPPER;
322
njn136b83b2005-06-19 05:14:03 +0000323 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
324 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000325 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000326 redir->to_addr = 0;
327 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000328
329 /* Check against all existing segments to see if this redirection
330 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000331 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000332 /* nope, add to list */
333 redir->next = unresolved_redir;
334 unresolved_redir = redir;
335 }
336
337 return redir;
338}
339
sewardjcbdddcf2005-03-10 23:23:45 +0000340/* If address 'a' is being redirected, return the redirected-to
341 address. */
342Addr VG_(code_redirect)(Addr a)
343{
344 CodeRedirect* r;
345
sewardjcbdddcf2005-03-10 23:23:45 +0000346 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
347 if (r == NULL)
348 return a;
349
350 vg_assert(r->to_addr != 0);
351
352 return r->to_addr;
353}
354
355void VG_(setup_code_redirect_table) ( void )
356{
njnd1af0032005-05-29 17:01:48 +0000357#if defined(VGP_x86_linux)
358 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
sewardjb9bce632005-06-21 01:41:34 +0000359 routine, to our copy so that the special sysinfo unwind hack in
360 m_stacktrace.c will kick in. */
361 add_redirect_sym_to_addr(
362 "soname:ld-linux.so.2", "_dl_sysinfo_int80",
363 (Addr)&VG_(x86_linux_REDIR_FOR__dl_sysinfo_int80)
364 );
365
njnd1af0032005-05-29 17:01:48 +0000366#elif defined(VGP_amd64_linux)
sewardjb9bce632005-06-21 01:41:34 +0000367
njnd1af0032005-05-29 17:01:48 +0000368 /* Redirect vsyscalls to local versions */
sewardjb9bce632005-06-21 01:41:34 +0000369 add_redirect_addr_to_addr(
370 0xFFFFFFFFFF600000ULL,
371 (Addr)&VG_(amd64_linux_REDIR_FOR_vgettimeofday)
372 );
373
374 add_redirect_addr_to_addr(
tomf5db3b62005-06-21 13:26:17 +0000375 0xFFFFFFFFFF600400ULL,
sewardjb9bce632005-06-21 01:41:34 +0000376 (Addr)&VG_(amd64_linux_REDIR_FOR_vtime)
377 );
378
cerion85665ca2005-06-20 15:51:07 +0000379#elif defined(VGP_ppc32_linux)
sewardjb9bce632005-06-21 01:41:34 +0000380
381 //CAB: TODO
382
njnd1af0032005-05-29 17:01:48 +0000383#else
384# error Unknown platform
385#endif
386}
sewardjcbdddcf2005-03-10 23:23:45 +0000387
njn16eeb4e2005-06-16 03:56:58 +0000388/* Z-decode a symbol into library:func form, eg
389
390 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
391
392 Uses the Z-encoding scheme described in pub_core_redir.h.
393 Returns True if demangle OK, False otherwise.
394*/
395static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
396{
397# define EMIT(ch) \
398 do { \
399 if (j >= nbytes) \
400 result[j-1] = 0; \
401 else \
402 result[j++] = ch; \
403 } while (0)
404
405 Bool error = False;
406 Int i, j = 0;
407 Int len = VG_(strlen)(symbol);
408 if (0) VG_(printf)("idm: %s\n", symbol);
409
410 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
411
412 /* Chew though the Z-encoded soname part. */
413 while (True) {
414
415 if (i >= len)
416 break;
417
418 if (symbol[i] == '_')
419 /* We found the underscore following the Z-encoded soname.
420 Just copy the rest literally. */
421 break;
422
423 if (symbol[i] != 'Z') {
424 EMIT(symbol[i]);
425 i++;
426 continue;
427 }
428
429 /* We've got a Z-escape. Act accordingly. */
430 i++;
431 if (i >= len) {
432 /* Hmm, Z right at the end. Something's wrong. */
433 error = True;
434 EMIT('Z');
435 break;
436 }
437 switch (symbol[i]) {
438 case 'a': EMIT('*'); break;
439 case 'p': EMIT('+'); break;
440 case 'c': EMIT(':'); break;
441 case 'd': EMIT('.'); break;
442 case 'u': EMIT('_'); break;
443 case 'h': EMIT('-'); break;
444 case 's': EMIT(' '); break;
445 case 'Z': EMIT('Z'); break;
446 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
447 }
448 i++;
449 }
450
451 if (error || i >= len || symbol[i] != '_') {
452 /* Something's wrong. Give up. */
453 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
454 EMIT(0);
455 return False;
456 }
457
458 /* Copy the rest of the string verbatim. */
459 i++;
460 EMIT(':');
461 while (True) {
462 if (i >= len)
463 break;
464 EMIT(symbol[i]);
465 i++;
466 }
467
468 EMIT(0);
469 if (0) VG_(printf)("%s\n", result);
470 return True;
471
472# undef EMIT
473}
474
475// Nb: this can change the string pointed to by 'symbol'.
476static void handle_replacement_function( Char* symbol, Addr addr )
477{
478 Bool ok;
479 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
480 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
481 Char *func;
482
483 // Put "soname:" at the start of lib
484 VG_(strcpy)(lib, "soname:");
485
486 ok = Z_decode(symbol, lib+7, len);
487 if (ok) {
488 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
489 func = lib + VG_(strlen)(lib)-1;
490 while(*func != ':') func--;
491 *func = '\0';
492 func++; // Move past the '\0'
493
494 // Now lib is "soname:<libname>" and func is "<fnname>".
495 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
496 add_redirect_sym_to_addr(lib, func, addr);
497
498 // Overwrite the given Z-encoded name with just the fnname.
499 VG_(strcpy)(symbol, func);
500 }
501
502 VG_(arena_free)(VG_AR_SYMTAB, lib);
503}
504
njnbc6d84d2005-06-19 18:58:03 +0000505static Addr __libc_freeres_wrapper = 0;
506
507Addr VG_(get_libc_freeres_wrapper)(void)
508{
509 return __libc_freeres_wrapper;
510}
511
njn16eeb4e2005-06-16 03:56:58 +0000512// This is specifically for stringifying VG_(x) function names. We
513// need to do two macroexpansions to get the VG_ macro expanded before
514// stringifying.
515#define _STR(x) #x
516#define STR(x) _STR(x)
517
518static void handle_load_notifier( Char* symbol, Addr addr )
519{
520 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000521 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000522// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
523// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
524 else
525 vg_assert2(0, "unrecognised load notification function: %s", symbol);
526}
527
528static Bool is_replacement_function(Char* s)
529{
530 return (0 == VG_(strncmp)(s,
531 VG_REPLACE_FUNCTION_PREFIX,
532 VG_REPLACE_FUNCTION_PREFIX_LEN));
533}
534
535static Bool is_load_notifier(Char* s)
536{
537 return (0 == VG_(strncmp)(s,
538 VG_NOTIFY_ON_LOAD_PREFIX,
539 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
540}
541
542// Call this for each symbol loaded. It determines if we need to do
543// anything special with it. It can modify 'symbol' in-place.
544void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
545{
546 if (is_replacement_function(symbol))
547 handle_replacement_function(symbol, addr);
548 else
549 if (is_load_notifier(symbol))
550 handle_load_notifier(symbol, addr);
551}
552
553
sewardjcbdddcf2005-03-10 23:23:45 +0000554//:: /*------------------------------------------------------------*/
555//:: /*--- General function wrapping. ---*/
556//:: /*------------------------------------------------------------*/
557//::
558//:: /*
559//:: TODO:
560//:: - hook into the symtab machinery
561//:: - client-side wrappers?
562//:: - better interfaces for before() functions to get to arguments
563//:: - handle munmap of code (dlclose())
564//:: - handle thread exit
565//:: - handle longjmp
566//:: */
567//:: struct callkey {
568//:: ThreadId tid; /* calling thread */
569//:: Addr esp; /* address of args on stack */
570//:: Addr eip; /* return address */
571//:: };
572//::
573//:: struct call_instance {
574//:: struct callkey key;
575//::
576//:: const FuncWrapper *wrapper;
577//:: void *nonce;
578//:: };
579//::
580//:: static inline Addr addrcmp(Addr a, Addr b)
581//:: {
582//:: if (a < b)
583//:: return -1;
584//:: else if (a > b)
585//:: return 1;
586//:: else
587//:: return 0;
588//:: }
589//::
590//:: static inline Int cmp(UInt a, UInt b)
591//:: {
592//:: if (a < b)
593//:: return -1;
594//:: else if (a > b)
595//:: return 1;
596//:: else
597//:: return 0;
598//:: }
599//::
600//:: static Int keycmp(const void *pa, const void *pb)
601//:: {
602//:: const struct callkey *a = (const struct callkey *)pa;
603//:: const struct callkey *b = (const struct callkey *)pb;
604//:: Int ret;
605//::
606//:: if ((ret = cmp(a->tid, b->tid)))
607//:: return ret;
608//::
609//:: if ((ret = addrcmp(a->esp, b->esp)))
610//:: return ret;
611//::
612//:: return addrcmp(a->eip, b->eip);
613//:: }
614//::
615//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000616//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000617//:: NULL, VG_AR_SYMTAB);
618//::
619//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
620//:: {
621//:: struct callkey key = { tid, argsp, retaddr };
622//::
623//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
624//:: }
625//::
626//:: static void wrapper_return(Addr retaddr);
627//::
628//:: /* Called from generated code via helper */
629//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
630//:: {
njnaf839f52005-06-23 03:27:57 +0000631//:: Addr retaddr = VG_RETADDR(tst->arch);
632//:: Addr argp = (Addr)&VG_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000633//:: void *nonce = NULL;
634//:: Bool mf = VG_(my_fault);
635//:: VG_(my_fault) = True;
636//::
637//:: if (wrapper->before) {
njnaf839f52005-06-23 03:27:57 +0000638//:: va_list args = VG_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000639//:: nonce = (*wrapper->before)(args);
640//:: }
641//::
642//:: if (wrapper->after) {
643//:: /* If there's an after function, make sure it gets called */
644//:: struct call_instance *call;
645//::
646//:: call = find_call(retaddr, argp, tst->tid);
647//::
648//:: if (call != NULL) {
649//:: /* Found a stale outstanding call; clean it up and recycle
650//:: the structure */
651//:: if (call->wrapper->after)
652//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
653//:: } else {
654//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
655//::
656//:: call->key.tid = tst->tid;
657//:: call->key.esp = argp;
658//:: call->key.eip = retaddr;
659//::
660//:: VG_(SkipList_Insert)(&wrapped_frames, call);
661//::
662//:: wrapper_return(retaddr);
663//:: }
664//::
665//:: call->wrapper = wrapper;
666//:: call->nonce = nonce;
667//:: } else
668//:: vg_assert(nonce == NULL);
669//::
670//:: VG_(my_fault) = mf;
671//:: }
672//::
673//:: /* Called from generated code via helper */
674//:: void VG_(wrap_after)(ThreadState *tst)
675//:: {
njnaf839f52005-06-23 03:27:57 +0000676//:: Addr EIP = VG_INSTR_PTR(tst->arch); /* instruction after call */
677//:: Addr ESP = VG_STACK_PTR(tst->arch); /* pointer to args */
678//:: Word ret = VG_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000679//:: struct call_instance *call;
680//:: Bool mf = VG_(my_fault);
681//::
682//:: VG_(my_fault) = True;
683//:: call = find_call(EIP, ESP, tst->tid);
684//::
685//:: if (0)
686//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
687//::
688//:: if (call != NULL) {
689//:: if (call->wrapper->after)
690//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
691//::
692//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
693//:: VG_(SkipNode_Free)(&wrapped_frames, call);
694//:: }
695//:: VG_(my_fault) = mf;
696//:: }
697//::
698//::
699//:: struct wrapped_function {
700//:: Addr eip; /* eip of function entrypoint */
701//:: const FuncWrapper *wrapper;
702//:: };
703//::
704//:: struct wrapper_return {
705//:: Addr eip; /* return address */
706//:: };
707//::
708//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000709//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000710//:: NULL, VG_AR_SYMTAB);
711//::
712//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000713//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000714//:: NULL, VG_AR_SYMTAB);
715//::
716//:: /* Wrap function starting at eip */
717//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
718//:: {
719//:: struct wrapped_function *func;
720//::
721//:: if (0)
722//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
723//::
724//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
725//::
726//:: if (func == NULL) {
727//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
728//:: VG_(invalidate_translations)(eip, 1, True);
729//::
730//:: func->eip = eip;
731//:: VG_(SkipList_Insert)(&wrapped_functions, func);
732//:: }
733//::
734//:: func->wrapper = wrapper;
735//:: }
736//::
737//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
738//:: {
739//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
740//::
741//:: if (func)
742//:: return func->wrapper;
743//:: return NULL;
744//:: }
745//::
746//:: Bool VG_(is_wrapper_return)(Addr eip)
747//:: {
748//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
749//::
750//:: return ret != NULL;
751//:: }
752//::
753//:: /* Mark eip as being the return address of a wrapper, so that the
754//:: codegen will generate the appropriate call. */
755//:: void wrapper_return(Addr eip)
756//:: {
757//:: struct wrapper_return *ret;
758//::
759//:: if (VG_(is_wrapper_return)(eip))
760//:: return;
761//::
762//:: VG_(invalidate_translations)(eip, 1, True);
763//::
764//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
765//:: ret->eip = eip;
766//::
767//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
768//:: }