blob: 78e29eeef14a546693ceea82ec53e28ea955b60e [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"
njn88c51482005-06-25 20:49:33 +000034#include "pub_core_debuginfo.h"
njn97405b22005-06-02 03:39:33 +000035#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000036#include "pub_core_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +000037#include "pub_core_libcprint.h"
njnaf1d7df2005-06-11 01:31:52 +000038#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000039#include "pub_core_options.h"
njnd1af0032005-05-29 17:01:48 +000040#include "pub_core_redir.h"
njnaf1d7df2005-06-11 01:31:52 +000041#include "pub_core_skiplist.h"
njna7598f62005-06-18 03:27:58 +000042#include "pub_core_trampoline.h"
njn8bddf582005-05-13 23:40:55 +000043#include "pub_core_transtab.h"
sewardj55f9d1a2005-04-25 11:11:44 +000044
sewardjcbdddcf2005-03-10 23:23:45 +000045/*------------------------------------------------------------*/
46/*--- General purpose redirection. ---*/
47/*------------------------------------------------------------*/
48
49/*
50 wraps and redirections, indexed by from_addr
51
52 Redirection and wrapping are two distinct mechanisms which Valgrind
53 can use to change the client's control flow.
54
55 Redirection intercepts a call to a client function, and re-points it
56 to a new piece of code (presumably functionally equivalent). The
57 original code is never run.
58
59 Wrapping does call the client's original code, but calls "before"
60 and "after" functions which can inspect (and perhaps modify) the
61 function's arguments and return value.
62 */
63struct _CodeRedirect {
64 enum redir_type {
65 R_REDIRECT, /* plain redirection */
66 R_WRAPPER, /* wrap with valgrind-internal code */
67 R_CLIENT_WRAPPER, /* wrap with client-side code */
68 } type;
69
70 const Char *from_lib; /* library qualifier pattern */
71 const Char *from_sym; /* symbol */
72 Addr from_addr; /* old addr */
73
74 /* used for redirection */
sewardjcbdddcf2005-03-10 23:23:45 +000075 Addr to_addr; /* new addr */
76
77 /* used for wrapping */
78 const FuncWrapper *wrapper;
79
80 CodeRedirect *next; /* next pointer on unresolved list */
81};
82
83static Char *straddr(void *p)
84{
85 static Char buf[16];
86
87 VG_(sprintf)(buf, "%p", *(Addr *)p);
88
89 return buf;
90}
91
njn41f8e4a2005-06-25 20:13:05 +000092static SkipList sk_resolved_redir =
93 VG_SKIPLIST_INIT(CodeRedirect, from_addr, VG_(cmp_Addr),
94 straddr, VG_AR_SYMTAB);
95
sewardjcbdddcf2005-03-10 23:23:45 +000096static CodeRedirect *unresolved_redir = NULL;
97
njn4f612c22005-06-25 20:22:43 +000098static Bool soname_matches(const Char *pattern, const Char* soname)
sewardjcbdddcf2005-03-10 23:23:45 +000099{
njn41f8e4a2005-06-25 20:13:05 +0000100 // pattern must start with "soname:"
101 vg_assert(NULL != pattern);
102 vg_assert(0 == VG_(strncmp)(pattern, "soname:", 7));
sewardjcbdddcf2005-03-10 23:23:45 +0000103
njn4f612c22005-06-25 20:22:43 +0000104 if (NULL == soname)
sewardjcbdddcf2005-03-10 23:23:45 +0000105 return False;
106
njn4f612c22005-06-25 20:22:43 +0000107 return VG_(string_match)(pattern + 7, soname);
sewardjcbdddcf2005-03-10 23:23:45 +0000108}
109
110static inline Bool from_resolved(const CodeRedirect *redir)
111{
112 return redir->from_addr != 0;
113}
114
sewardjcbdddcf2005-03-10 23:23:45 +0000115Bool VG_(is_resolved)(const CodeRedirect *redir)
116{
njn136b83b2005-06-19 05:14:03 +0000117 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000118}
119
tom748a1312005-04-02 15:53:01 +0000120static void add_resolved(CodeRedirect *redir)
121{
122 switch(redir->type) {
123 case R_REDIRECT:
124 if (VG_(clo_trace_redir)) {
125 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
126 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000127 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000128 }
129
130 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
131 /* For some given (from, to) redir, the "from" function got
132 called before the .so containing "to" became available. We
133 know this because there is already a translation for the
134 entry point of the original "from". So the redirect will
135 never actually take effect unless that translation is
136 discarded.
137
138 Note, we only really need to discard the first bb of the
139 old entry point, and so we avoid the problem of having to
140 figure out how big that bb was -- since it is at least 1
141 byte of original code, we can just pass 1 as the original
142 size to invalidate_translations() and it will indeed get
143 rid of the translation.
144
145 Note, this is potentially expensive -- discarding
146 translations causes complete unchaining.
147 */
148 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
149 VG_(message)(Vg_UserMsg,
150 "Discarding translation due to redirect of already called function" );
151 VG_(message)(Vg_UserMsg,
152 " %s (%p -> %p)",
153 redir->from_sym, redir->from_addr, redir->to_addr );
154 }
155 VG_(discard_translations)((Addr64)redir->from_addr, 1);
156 }
157
158 {
159 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
160
161 if (r == NULL)
162 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
163 else {
164 /* XXX leak redir */
165 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000166 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000167 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000168 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000169 }
170 }
171 break;
172
173 case R_WRAPPER:
174 if (VG_(clo_trace_redir)) {
175 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
176 redir->from_lib, redir->from_sym, redir->from_addr);
177 }
178
179 /* XXX redir leaked */
180 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
181 break;
182
183 case R_CLIENT_WRAPPER:
184 VG_(core_panic)("not implemented");
185 break;
186 }
187}
188
sewardjcbdddcf2005-03-10 23:23:45 +0000189/* Resolve a redir using si if possible, and add it to the resolved
190 list */
njn17250122005-05-14 17:18:12 +0000191static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000192{
193 Bool resolved;
194
195 vg_assert(si != NULL);
sewardjcbdddcf2005-03-10 23:23:45 +0000196
njn748ace42005-06-21 03:36:01 +0000197 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000198 vg_assert(!resolved);
njn748ace42005-06-21 03:36:01 +0000199 vg_assert(redir->from_sym != NULL);
sewardjcbdddcf2005-03-10 23:23:45 +0000200
njn4f612c22005-06-25 20:22:43 +0000201 if (soname_matches(redir->from_lib, VG_(seginfo_soname)(si))) {
njn748ace42005-06-21 03:36:01 +0000202 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
203 if (VG_(clo_trace_redir) && redir->from_addr != 0)
204 VG_(printf)(" bind FROM: %p = %s:%s\n",
205 redir->from_addr,redir->from_lib, redir->from_sym );
sewardjcbdddcf2005-03-10 23:23:45 +0000206 }
207
njn136b83b2005-06-19 05:14:03 +0000208 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000209
210 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000211 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000212 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000213 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000214
tom748a1312005-04-02 15:53:01 +0000215 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000216
217 return resolved;
218}
219
njnbf7ca332005-05-14 17:11:06 +0000220static Bool resolve_redir_allsegs(CodeRedirect *redir)
221{
222 const SegInfo *si;
223
224 for(si = VG_(next_seginfo)(NULL);
225 si != NULL;
226 si = VG_(next_seginfo)(si))
227 {
njn17250122005-05-14 17:18:12 +0000228 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000229 return True;
230 }
231 return False;
232}
233
sewardjcbdddcf2005-03-10 23:23:45 +0000234/* Go through the complete redir list, resolving as much as possible with this SegInfo.
235
236 This should be called when a new SegInfo symtab is loaded.
237 */
238void VG_(resolve_seg_redirs)(SegInfo *si)
239{
240 CodeRedirect **prevp = &unresolved_redir;
241 CodeRedirect *redir, *next;
242
243 if (VG_(clo_trace_redir))
244 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
njn4f612c22005-06-25 20:22:43 +0000245 VG_(seginfo_filename)(si), VG_(seginfo_soname)(si));
sewardjcbdddcf2005-03-10 23:23:45 +0000246
247 /* visit each unresolved redir - if it becomes resolved, then
248 remove it from the unresolved list */
249 for(redir = unresolved_redir; redir != NULL; redir = next) {
250 next = redir->next;
251
njn17250122005-05-14 17:18:12 +0000252 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000253 *prevp = next;
254 redir->next = NULL;
255 } else
256 prevp = &redir->next;
257 }
258}
259
njn136b83b2005-06-19 05:14:03 +0000260static void add_redirect_X_to_addr(
261 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000262)
sewardjcbdddcf2005-03-10 23:23:45 +0000263{
264 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
265
266 redir->type = R_REDIRECT;
267
njn16eeb4e2005-06-16 03:56:58 +0000268 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000269 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000270 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000271 else redir->from_sym = NULL;
sewardjb9bce632005-06-21 01:41:34 +0000272
273 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000274
njn136b83b2005-06-19 05:14:03 +0000275 vg_assert(0 != to_addr);
276 redir->to_addr = to_addr;
277 redir->wrapper = 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000278
279 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
280 VG_(message)(Vg_UserMsg,
njn136b83b2005-06-19 05:14:03 +0000281 "REDIRECT %s:%s(%p) to %p",
282 from_lib, from_sym, from_addr, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000283
284 /* Check against all existing segments to see if this redirection
285 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000286 if (VG_(is_resolved)(redir)) {
287 add_resolved(redir);
288 }
289 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000290 /* nope, add to list */
291 redir->next = unresolved_redir;
292 unresolved_redir = redir;
293 }
294}
295
njn16eeb4e2005-06-16 03:56:58 +0000296/* Redirect a lib/symbol reference to a function at addr */
297static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
298 Addr to_addr)
299{
njn136b83b2005-06-19 05:14:03 +0000300 add_redirect_X_to_addr(from_lib, from_sym, 0, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000301}
302
tom748a1312005-04-02 15:53:01 +0000303/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000304__attribute__((unused)) // It is used, but not on all platforms...
305static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000306{
njn136b83b2005-06-19 05:14:03 +0000307 add_redirect_X_to_addr(NULL, NULL, from_addr, to_addr);
tom748a1312005-04-02 15:53:01 +0000308}
309
sewardjcbdddcf2005-03-10 23:23:45 +0000310CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
311 const FuncWrapper *wrapper)
312{
313 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
314
315 if (0)
316 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
317 from_lib, from_sym, wrapper->before, wrapper->after);
318
319 redir->type = R_WRAPPER;
320
njn136b83b2005-06-19 05:14:03 +0000321 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
322 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000323 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000324 redir->to_addr = 0;
325 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000326
327 /* Check against all existing segments to see if this redirection
328 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000329 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000330 /* nope, add to list */
331 redir->next = unresolved_redir;
332 unresolved_redir = redir;
333 }
334
335 return redir;
336}
337
sewardjcbdddcf2005-03-10 23:23:45 +0000338/* If address 'a' is being redirected, return the redirected-to
339 address. */
340Addr VG_(code_redirect)(Addr a)
341{
342 CodeRedirect* r;
343
sewardjcbdddcf2005-03-10 23:23:45 +0000344 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
345 if (r == NULL)
346 return a;
347
348 vg_assert(r->to_addr != 0);
349
350 return r->to_addr;
351}
352
353void VG_(setup_code_redirect_table) ( void )
354{
njnd1af0032005-05-29 17:01:48 +0000355#if defined(VGP_x86_linux)
356 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
sewardjb9bce632005-06-21 01:41:34 +0000357 routine, to our copy so that the special sysinfo unwind hack in
358 m_stacktrace.c will kick in. */
359 add_redirect_sym_to_addr(
360 "soname:ld-linux.so.2", "_dl_sysinfo_int80",
361 (Addr)&VG_(x86_linux_REDIR_FOR__dl_sysinfo_int80)
362 );
363
njnd1af0032005-05-29 17:01:48 +0000364#elif defined(VGP_amd64_linux)
sewardjb9bce632005-06-21 01:41:34 +0000365
njnd1af0032005-05-29 17:01:48 +0000366 /* Redirect vsyscalls to local versions */
sewardjb9bce632005-06-21 01:41:34 +0000367 add_redirect_addr_to_addr(
368 0xFFFFFFFFFF600000ULL,
369 (Addr)&VG_(amd64_linux_REDIR_FOR_vgettimeofday)
370 );
371
372 add_redirect_addr_to_addr(
tomf5db3b62005-06-21 13:26:17 +0000373 0xFFFFFFFFFF600400ULL,
sewardjb9bce632005-06-21 01:41:34 +0000374 (Addr)&VG_(amd64_linux_REDIR_FOR_vtime)
375 );
376
cerion85665ca2005-06-20 15:51:07 +0000377#elif defined(VGP_ppc32_linux)
sewardjb9bce632005-06-21 01:41:34 +0000378
379 //CAB: TODO
380
njnd1af0032005-05-29 17:01:48 +0000381#else
382# error Unknown platform
383#endif
384}
sewardjcbdddcf2005-03-10 23:23:45 +0000385
njn16eeb4e2005-06-16 03:56:58 +0000386/* Z-decode a symbol into library:func form, eg
387
388 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
389
390 Uses the Z-encoding scheme described in pub_core_redir.h.
391 Returns True if demangle OK, False otherwise.
392*/
393static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
394{
395# define EMIT(ch) \
396 do { \
397 if (j >= nbytes) \
398 result[j-1] = 0; \
399 else \
400 result[j++] = ch; \
401 } while (0)
402
403 Bool error = False;
404 Int i, j = 0;
405 Int len = VG_(strlen)(symbol);
406 if (0) VG_(printf)("idm: %s\n", symbol);
407
408 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
409
410 /* Chew though the Z-encoded soname part. */
411 while (True) {
412
413 if (i >= len)
414 break;
415
416 if (symbol[i] == '_')
417 /* We found the underscore following the Z-encoded soname.
418 Just copy the rest literally. */
419 break;
420
421 if (symbol[i] != 'Z') {
422 EMIT(symbol[i]);
423 i++;
424 continue;
425 }
426
427 /* We've got a Z-escape. Act accordingly. */
428 i++;
429 if (i >= len) {
430 /* Hmm, Z right at the end. Something's wrong. */
431 error = True;
432 EMIT('Z');
433 break;
434 }
435 switch (symbol[i]) {
436 case 'a': EMIT('*'); break;
437 case 'p': EMIT('+'); break;
438 case 'c': EMIT(':'); break;
439 case 'd': EMIT('.'); break;
440 case 'u': EMIT('_'); break;
441 case 'h': EMIT('-'); break;
442 case 's': EMIT(' '); break;
443 case 'Z': EMIT('Z'); break;
444 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
445 }
446 i++;
447 }
448
449 if (error || i >= len || symbol[i] != '_') {
450 /* Something's wrong. Give up. */
451 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
452 EMIT(0);
453 return False;
454 }
455
456 /* Copy the rest of the string verbatim. */
457 i++;
458 EMIT(':');
459 while (True) {
460 if (i >= len)
461 break;
462 EMIT(symbol[i]);
463 i++;
464 }
465
466 EMIT(0);
467 if (0) VG_(printf)("%s\n", result);
468 return True;
469
470# undef EMIT
471}
472
473// Nb: this can change the string pointed to by 'symbol'.
474static void handle_replacement_function( Char* symbol, Addr addr )
475{
476 Bool ok;
477 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
478 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
479 Char *func;
480
481 // Put "soname:" at the start of lib
482 VG_(strcpy)(lib, "soname:");
483
484 ok = Z_decode(symbol, lib+7, len);
485 if (ok) {
486 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
487 func = lib + VG_(strlen)(lib)-1;
488 while(*func != ':') func--;
489 *func = '\0';
490 func++; // Move past the '\0'
491
492 // Now lib is "soname:<libname>" and func is "<fnname>".
493 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
494 add_redirect_sym_to_addr(lib, func, addr);
495
496 // Overwrite the given Z-encoded name with just the fnname.
497 VG_(strcpy)(symbol, func);
498 }
499
500 VG_(arena_free)(VG_AR_SYMTAB, lib);
501}
502
njnbc6d84d2005-06-19 18:58:03 +0000503static Addr __libc_freeres_wrapper = 0;
504
505Addr VG_(get_libc_freeres_wrapper)(void)
506{
507 return __libc_freeres_wrapper;
508}
509
njn16eeb4e2005-06-16 03:56:58 +0000510// This is specifically for stringifying VG_(x) function names. We
511// need to do two macroexpansions to get the VG_ macro expanded before
512// stringifying.
513#define _STR(x) #x
514#define STR(x) _STR(x)
515
516static void handle_load_notifier( Char* symbol, Addr addr )
517{
518 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000519 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000520// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
521// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
522 else
523 vg_assert2(0, "unrecognised load notification function: %s", symbol);
524}
525
526static Bool is_replacement_function(Char* s)
527{
528 return (0 == VG_(strncmp)(s,
529 VG_REPLACE_FUNCTION_PREFIX,
530 VG_REPLACE_FUNCTION_PREFIX_LEN));
531}
532
533static Bool is_load_notifier(Char* s)
534{
535 return (0 == VG_(strncmp)(s,
536 VG_NOTIFY_ON_LOAD_PREFIX,
537 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
538}
539
540// Call this for each symbol loaded. It determines if we need to do
541// anything special with it. It can modify 'symbol' in-place.
542void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
543{
544 if (is_replacement_function(symbol))
545 handle_replacement_function(symbol, addr);
546 else
547 if (is_load_notifier(symbol))
548 handle_load_notifier(symbol, addr);
549}
550
551
sewardjcbdddcf2005-03-10 23:23:45 +0000552//:: /*------------------------------------------------------------*/
553//:: /*--- General function wrapping. ---*/
554//:: /*------------------------------------------------------------*/
555//::
556//:: /*
557//:: TODO:
558//:: - hook into the symtab machinery
559//:: - client-side wrappers?
560//:: - better interfaces for before() functions to get to arguments
561//:: - handle munmap of code (dlclose())
562//:: - handle thread exit
563//:: - handle longjmp
564//:: */
565//:: struct callkey {
566//:: ThreadId tid; /* calling thread */
567//:: Addr esp; /* address of args on stack */
568//:: Addr eip; /* return address */
569//:: };
570//::
571//:: struct call_instance {
572//:: struct callkey key;
573//::
574//:: const FuncWrapper *wrapper;
575//:: void *nonce;
576//:: };
577//::
578//:: static inline Addr addrcmp(Addr a, Addr b)
579//:: {
580//:: if (a < b)
581//:: return -1;
582//:: else if (a > b)
583//:: return 1;
584//:: else
585//:: return 0;
586//:: }
587//::
588//:: static inline Int cmp(UInt a, UInt b)
589//:: {
590//:: if (a < b)
591//:: return -1;
592//:: else if (a > b)
593//:: return 1;
594//:: else
595//:: return 0;
596//:: }
597//::
598//:: static Int keycmp(const void *pa, const void *pb)
599//:: {
600//:: const struct callkey *a = (const struct callkey *)pa;
601//:: const struct callkey *b = (const struct callkey *)pb;
602//:: Int ret;
603//::
604//:: if ((ret = cmp(a->tid, b->tid)))
605//:: return ret;
606//::
607//:: if ((ret = addrcmp(a->esp, b->esp)))
608//:: return ret;
609//::
610//:: return addrcmp(a->eip, b->eip);
611//:: }
612//::
613//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000614//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000615//:: NULL, VG_AR_SYMTAB);
616//::
617//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
618//:: {
619//:: struct callkey key = { tid, argsp, retaddr };
620//::
621//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
622//:: }
623//::
624//:: static void wrapper_return(Addr retaddr);
625//::
626//:: /* Called from generated code via helper */
627//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
628//:: {
njnaf839f52005-06-23 03:27:57 +0000629//:: Addr retaddr = VG_RETADDR(tst->arch);
630//:: Addr argp = (Addr)&VG_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000631//:: void *nonce = NULL;
632//:: Bool mf = VG_(my_fault);
633//:: VG_(my_fault) = True;
634//::
635//:: if (wrapper->before) {
njnaf839f52005-06-23 03:27:57 +0000636//:: va_list args = VG_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000637//:: nonce = (*wrapper->before)(args);
638//:: }
639//::
640//:: if (wrapper->after) {
641//:: /* If there's an after function, make sure it gets called */
642//:: struct call_instance *call;
643//::
644//:: call = find_call(retaddr, argp, tst->tid);
645//::
646//:: if (call != NULL) {
647//:: /* Found a stale outstanding call; clean it up and recycle
648//:: the structure */
649//:: if (call->wrapper->after)
650//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
651//:: } else {
652//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
653//::
654//:: call->key.tid = tst->tid;
655//:: call->key.esp = argp;
656//:: call->key.eip = retaddr;
657//::
658//:: VG_(SkipList_Insert)(&wrapped_frames, call);
659//::
660//:: wrapper_return(retaddr);
661//:: }
662//::
663//:: call->wrapper = wrapper;
664//:: call->nonce = nonce;
665//:: } else
666//:: vg_assert(nonce == NULL);
667//::
668//:: VG_(my_fault) = mf;
669//:: }
670//::
671//:: /* Called from generated code via helper */
672//:: void VG_(wrap_after)(ThreadState *tst)
673//:: {
njnaf839f52005-06-23 03:27:57 +0000674//:: Addr EIP = VG_INSTR_PTR(tst->arch); /* instruction after call */
675//:: Addr ESP = VG_STACK_PTR(tst->arch); /* pointer to args */
676//:: Word ret = VG_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000677//:: struct call_instance *call;
678//:: Bool mf = VG_(my_fault);
679//::
680//:: VG_(my_fault) = True;
681//:: call = find_call(EIP, ESP, tst->tid);
682//::
683//:: if (0)
684//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
685//::
686//:: if (call != NULL) {
687//:: if (call->wrapper->after)
688//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
689//::
690//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
691//:: VG_(SkipNode_Free)(&wrapped_frames, call);
692//:: }
693//:: VG_(my_fault) = mf;
694//:: }
695//::
696//::
697//:: struct wrapped_function {
698//:: Addr eip; /* eip of function entrypoint */
699//:: const FuncWrapper *wrapper;
700//:: };
701//::
702//:: struct wrapper_return {
703//:: Addr eip; /* return address */
704//:: };
705//::
706//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000707//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000708//:: NULL, VG_AR_SYMTAB);
709//::
710//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000711//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000712//:: NULL, VG_AR_SYMTAB);
713//::
714//:: /* Wrap function starting at eip */
715//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
716//:: {
717//:: struct wrapped_function *func;
718//::
719//:: if (0)
720//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
721//::
722//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
723//::
724//:: if (func == NULL) {
725//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
726//:: VG_(invalidate_translations)(eip, 1, True);
727//::
728//:: func->eip = eip;
729//:: VG_(SkipList_Insert)(&wrapped_functions, func);
730//:: }
731//::
732//:: func->wrapper = wrapper;
733//:: }
734//::
735//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
736//:: {
737//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
738//::
739//:: if (func)
740//:: return func->wrapper;
741//:: return NULL;
742//:: }
743//::
744//:: Bool VG_(is_wrapper_return)(Addr eip)
745//:: {
746//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
747//::
748//:: return ret != NULL;
749//:: }
750//::
751//:: /* Mark eip as being the return address of a wrapper, so that the
752//:: codegen will generate the appropriate call. */
753//:: void wrapper_return(Addr eip)
754//:: {
755//:: struct wrapper_return *ret;
756//::
757//:: if (VG_(is_wrapper_return)(eip))
758//:: return;
759//::
760//:: VG_(invalidate_translations)(eip, 1, True);
761//::
762//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
763//:: ret->eip = eip;
764//::
765//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
766//:: }