blob: 4c6d8ff079def31608ef17233137de742607c6e8 [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
njnbe91aae2005-03-27 01:42:41 +000094static SkipList sk_resolved_redir = VG_SKIPLIST_INIT(CodeRedirect, from_addr,
sewardjcbdddcf2005-03-10 23:23:45 +000095 VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
96static CodeRedirect *unresolved_redir = NULL;
97
98static Bool match_lib(const Char *pattern, const SegInfo *si)
99{
100 /* pattern == NULL matches everything, otherwise use globbing
101
102 If the pattern starts with:
103 file:, then match filename
104 soname:, then match soname
105 something else, match filename
106 */
107 const Char *name = si->filename;
108
109 if (pattern == NULL)
110 return True;
111
112 if (VG_(strncmp)(pattern, "file:", 5) == 0) {
113 pattern += 5;
114 name = si->filename;
115 }
116 if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
117 pattern += 7;
118 name = si->soname;
119 }
120
121 if (name == NULL)
122 return False;
123
124 return VG_(string_match)(pattern, name);
125}
126
127static inline Bool from_resolved(const CodeRedirect *redir)
128{
129 return redir->from_addr != 0;
130}
131
sewardjcbdddcf2005-03-10 23:23:45 +0000132Bool VG_(is_resolved)(const CodeRedirect *redir)
133{
njn136b83b2005-06-19 05:14:03 +0000134 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000135}
136
tom748a1312005-04-02 15:53:01 +0000137static void add_resolved(CodeRedirect *redir)
138{
139 switch(redir->type) {
140 case R_REDIRECT:
141 if (VG_(clo_trace_redir)) {
142 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
143 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000144 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000145 }
146
147 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
148 /* For some given (from, to) redir, the "from" function got
149 called before the .so containing "to" became available. We
150 know this because there is already a translation for the
151 entry point of the original "from". So the redirect will
152 never actually take effect unless that translation is
153 discarded.
154
155 Note, we only really need to discard the first bb of the
156 old entry point, and so we avoid the problem of having to
157 figure out how big that bb was -- since it is at least 1
158 byte of original code, we can just pass 1 as the original
159 size to invalidate_translations() and it will indeed get
160 rid of the translation.
161
162 Note, this is potentially expensive -- discarding
163 translations causes complete unchaining.
164 */
165 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
166 VG_(message)(Vg_UserMsg,
167 "Discarding translation due to redirect of already called function" );
168 VG_(message)(Vg_UserMsg,
169 " %s (%p -> %p)",
170 redir->from_sym, redir->from_addr, redir->to_addr );
171 }
172 VG_(discard_translations)((Addr64)redir->from_addr, 1);
173 }
174
175 {
176 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
177
178 if (r == NULL)
179 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
180 else {
181 /* XXX leak redir */
182 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000183 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000184 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000185 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000186 }
187 }
188 break;
189
190 case R_WRAPPER:
191 if (VG_(clo_trace_redir)) {
192 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
193 redir->from_lib, redir->from_sym, redir->from_addr);
194 }
195
196 /* XXX redir leaked */
197 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
198 break;
199
200 case R_CLIENT_WRAPPER:
201 VG_(core_panic)("not implemented");
202 break;
203 }
204}
205
sewardjcbdddcf2005-03-10 23:23:45 +0000206/* Resolve a redir using si if possible, and add it to the resolved
207 list */
njn17250122005-05-14 17:18:12 +0000208static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000209{
210 Bool resolved;
211
212 vg_assert(si != NULL);
213 vg_assert(si->seg != NULL);
214
215 /* no redirection from Valgrind segments */
216 if (si->seg->flags & SF_VALGRIND)
217 return False;
218
219 resolved = VG_(is_resolved)(redir);
220
sewardjcbdddcf2005-03-10 23:23:45 +0000221 vg_assert(!resolved);
222
223 if (!from_resolved(redir)) {
224 vg_assert(redir->from_sym != NULL);
225
226 if (match_lib(redir->from_lib, si)) {
227 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
228 if (VG_(clo_trace_redir) && redir->from_addr != 0)
229 VG_(printf)(" bind FROM: %p = %s:%s\n",
230 redir->from_addr,redir->from_lib, redir->from_sym );
231 }
232 }
233
njn136b83b2005-06-19 05:14:03 +0000234 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000235
236 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000237 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000238 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000239 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000240
tom748a1312005-04-02 15:53:01 +0000241 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000242
243 return resolved;
244}
245
njnbf7ca332005-05-14 17:11:06 +0000246static Bool resolve_redir_allsegs(CodeRedirect *redir)
247{
248 const SegInfo *si;
249
250 for(si = VG_(next_seginfo)(NULL);
251 si != NULL;
252 si = VG_(next_seginfo)(si))
253 {
njn17250122005-05-14 17:18:12 +0000254 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000255 return True;
256 }
257 return False;
258}
259
sewardjcbdddcf2005-03-10 23:23:45 +0000260/* Go through the complete redir list, resolving as much as possible with this SegInfo.
261
262 This should be called when a new SegInfo symtab is loaded.
263 */
264void VG_(resolve_seg_redirs)(SegInfo *si)
265{
266 CodeRedirect **prevp = &unresolved_redir;
267 CodeRedirect *redir, *next;
268
269 if (VG_(clo_trace_redir))
270 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
271 si->filename, si->soname);
272
273 /* visit each unresolved redir - if it becomes resolved, then
274 remove it from the unresolved list */
275 for(redir = unresolved_redir; redir != NULL; redir = next) {
276 next = redir->next;
277
njn17250122005-05-14 17:18:12 +0000278 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000279 *prevp = next;
280 redir->next = NULL;
281 } else
282 prevp = &redir->next;
283 }
284}
285
njn136b83b2005-06-19 05:14:03 +0000286static void add_redirect_X_to_addr(
287 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000288)
sewardjcbdddcf2005-03-10 23:23:45 +0000289{
290 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
291
292 redir->type = R_REDIRECT;
293
njn16eeb4e2005-06-16 03:56:58 +0000294 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000295 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000296 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000297 else redir->from_sym = NULL;
sewardjb9bce632005-06-21 01:41:34 +0000298
299 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000300
njn136b83b2005-06-19 05:14:03 +0000301 vg_assert(0 != to_addr);
302 redir->to_addr = to_addr;
303 redir->wrapper = 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000304
305 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
306 VG_(message)(Vg_UserMsg,
njn136b83b2005-06-19 05:14:03 +0000307 "REDIRECT %s:%s(%p) to %p",
308 from_lib, from_sym, from_addr, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000309
310 /* Check against all existing segments to see if this redirection
311 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000312 if (VG_(is_resolved)(redir)) {
313 add_resolved(redir);
314 }
315 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000316 /* nope, add to list */
317 redir->next = unresolved_redir;
318 unresolved_redir = redir;
319 }
320}
321
njn16eeb4e2005-06-16 03:56:58 +0000322/* Redirect a lib/symbol reference to a function at addr */
323static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
324 Addr to_addr)
325{
njn136b83b2005-06-19 05:14:03 +0000326 add_redirect_X_to_addr(from_lib, from_sym, 0, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000327}
328
tom748a1312005-04-02 15:53:01 +0000329/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000330__attribute__((unused)) // It is used, but not on all platforms...
331static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000332{
njn136b83b2005-06-19 05:14:03 +0000333 add_redirect_X_to_addr(NULL, NULL, from_addr, to_addr);
tom748a1312005-04-02 15:53:01 +0000334}
335
sewardjcbdddcf2005-03-10 23:23:45 +0000336CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
337 const FuncWrapper *wrapper)
338{
339 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
340
341 if (0)
342 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
343 from_lib, from_sym, wrapper->before, wrapper->after);
344
345 redir->type = R_WRAPPER;
346
njn136b83b2005-06-19 05:14:03 +0000347 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
348 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000349 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000350 redir->to_addr = 0;
351 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000352
353 /* Check against all existing segments to see if this redirection
354 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000355 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000356 /* nope, add to list */
357 redir->next = unresolved_redir;
358 unresolved_redir = redir;
359 }
360
361 return redir;
362}
363
sewardjcbdddcf2005-03-10 23:23:45 +0000364/* If address 'a' is being redirected, return the redirected-to
365 address. */
366Addr VG_(code_redirect)(Addr a)
367{
368 CodeRedirect* r;
369
sewardjcbdddcf2005-03-10 23:23:45 +0000370 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
371 if (r == NULL)
372 return a;
373
374 vg_assert(r->to_addr != 0);
375
376 return r->to_addr;
377}
378
379void VG_(setup_code_redirect_table) ( void )
380{
njnd1af0032005-05-29 17:01:48 +0000381#if defined(VGP_x86_linux)
382 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
sewardjb9bce632005-06-21 01:41:34 +0000383 routine, to our copy so that the special sysinfo unwind hack in
384 m_stacktrace.c will kick in. */
385 add_redirect_sym_to_addr(
386 "soname:ld-linux.so.2", "_dl_sysinfo_int80",
387 (Addr)&VG_(x86_linux_REDIR_FOR__dl_sysinfo_int80)
388 );
389
njnd1af0032005-05-29 17:01:48 +0000390#elif defined(VGP_amd64_linux)
sewardjb9bce632005-06-21 01:41:34 +0000391
njnd1af0032005-05-29 17:01:48 +0000392 /* Redirect vsyscalls to local versions */
sewardjb9bce632005-06-21 01:41:34 +0000393 add_redirect_addr_to_addr(
394 0xFFFFFFFFFF600000ULL,
395 (Addr)&VG_(amd64_linux_REDIR_FOR_vgettimeofday)
396 );
397
398 add_redirect_addr_to_addr(
399 0xFFFFFFFFFF600000ULL,
400 (Addr)&VG_(amd64_linux_REDIR_FOR_vtime)
401 );
402
cerion85665ca2005-06-20 15:51:07 +0000403#elif defined(VGP_ppc32_linux)
sewardjb9bce632005-06-21 01:41:34 +0000404
405 //CAB: TODO
406
njnd1af0032005-05-29 17:01:48 +0000407#else
408# error Unknown platform
409#endif
410}
sewardjcbdddcf2005-03-10 23:23:45 +0000411
njn16eeb4e2005-06-16 03:56:58 +0000412/* Z-decode a symbol into library:func form, eg
413
414 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
415
416 Uses the Z-encoding scheme described in pub_core_redir.h.
417 Returns True if demangle OK, False otherwise.
418*/
419static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
420{
421# define EMIT(ch) \
422 do { \
423 if (j >= nbytes) \
424 result[j-1] = 0; \
425 else \
426 result[j++] = ch; \
427 } while (0)
428
429 Bool error = False;
430 Int i, j = 0;
431 Int len = VG_(strlen)(symbol);
432 if (0) VG_(printf)("idm: %s\n", symbol);
433
434 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
435
436 /* Chew though the Z-encoded soname part. */
437 while (True) {
438
439 if (i >= len)
440 break;
441
442 if (symbol[i] == '_')
443 /* We found the underscore following the Z-encoded soname.
444 Just copy the rest literally. */
445 break;
446
447 if (symbol[i] != 'Z') {
448 EMIT(symbol[i]);
449 i++;
450 continue;
451 }
452
453 /* We've got a Z-escape. Act accordingly. */
454 i++;
455 if (i >= len) {
456 /* Hmm, Z right at the end. Something's wrong. */
457 error = True;
458 EMIT('Z');
459 break;
460 }
461 switch (symbol[i]) {
462 case 'a': EMIT('*'); break;
463 case 'p': EMIT('+'); break;
464 case 'c': EMIT(':'); break;
465 case 'd': EMIT('.'); break;
466 case 'u': EMIT('_'); break;
467 case 'h': EMIT('-'); break;
468 case 's': EMIT(' '); break;
469 case 'Z': EMIT('Z'); break;
470 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
471 }
472 i++;
473 }
474
475 if (error || i >= len || symbol[i] != '_') {
476 /* Something's wrong. Give up. */
477 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
478 EMIT(0);
479 return False;
480 }
481
482 /* Copy the rest of the string verbatim. */
483 i++;
484 EMIT(':');
485 while (True) {
486 if (i >= len)
487 break;
488 EMIT(symbol[i]);
489 i++;
490 }
491
492 EMIT(0);
493 if (0) VG_(printf)("%s\n", result);
494 return True;
495
496# undef EMIT
497}
498
499// Nb: this can change the string pointed to by 'symbol'.
500static void handle_replacement_function( Char* symbol, Addr addr )
501{
502 Bool ok;
503 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
504 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
505 Char *func;
506
507 // Put "soname:" at the start of lib
508 VG_(strcpy)(lib, "soname:");
509
510 ok = Z_decode(symbol, lib+7, len);
511 if (ok) {
512 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
513 func = lib + VG_(strlen)(lib)-1;
514 while(*func != ':') func--;
515 *func = '\0';
516 func++; // Move past the '\0'
517
518 // Now lib is "soname:<libname>" and func is "<fnname>".
519 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
520 add_redirect_sym_to_addr(lib, func, addr);
521
522 // Overwrite the given Z-encoded name with just the fnname.
523 VG_(strcpy)(symbol, func);
524 }
525
526 VG_(arena_free)(VG_AR_SYMTAB, lib);
527}
528
njnbc6d84d2005-06-19 18:58:03 +0000529static Addr __libc_freeres_wrapper = 0;
530
531Addr VG_(get_libc_freeres_wrapper)(void)
532{
533 return __libc_freeres_wrapper;
534}
535
njn16eeb4e2005-06-16 03:56:58 +0000536// This is specifically for stringifying VG_(x) function names. We
537// need to do two macroexpansions to get the VG_ macro expanded before
538// stringifying.
539#define _STR(x) #x
540#define STR(x) _STR(x)
541
542static void handle_load_notifier( Char* symbol, Addr addr )
543{
544 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000545 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000546// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
547// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
548 else
549 vg_assert2(0, "unrecognised load notification function: %s", symbol);
550}
551
552static Bool is_replacement_function(Char* s)
553{
554 return (0 == VG_(strncmp)(s,
555 VG_REPLACE_FUNCTION_PREFIX,
556 VG_REPLACE_FUNCTION_PREFIX_LEN));
557}
558
559static Bool is_load_notifier(Char* s)
560{
561 return (0 == VG_(strncmp)(s,
562 VG_NOTIFY_ON_LOAD_PREFIX,
563 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
564}
565
566// Call this for each symbol loaded. It determines if we need to do
567// anything special with it. It can modify 'symbol' in-place.
568void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
569{
570 if (is_replacement_function(symbol))
571 handle_replacement_function(symbol, addr);
572 else
573 if (is_load_notifier(symbol))
574 handle_load_notifier(symbol, addr);
575}
576
577
sewardjcbdddcf2005-03-10 23:23:45 +0000578//:: /*------------------------------------------------------------*/
579//:: /*--- General function wrapping. ---*/
580//:: /*------------------------------------------------------------*/
581//::
582//:: /*
583//:: TODO:
584//:: - hook into the symtab machinery
585//:: - client-side wrappers?
586//:: - better interfaces for before() functions to get to arguments
587//:: - handle munmap of code (dlclose())
588//:: - handle thread exit
589//:: - handle longjmp
590//:: */
591//:: struct callkey {
592//:: ThreadId tid; /* calling thread */
593//:: Addr esp; /* address of args on stack */
594//:: Addr eip; /* return address */
595//:: };
596//::
597//:: struct call_instance {
598//:: struct callkey key;
599//::
600//:: const FuncWrapper *wrapper;
601//:: void *nonce;
602//:: };
603//::
604//:: static inline Addr addrcmp(Addr a, Addr b)
605//:: {
606//:: if (a < b)
607//:: return -1;
608//:: else if (a > b)
609//:: return 1;
610//:: else
611//:: return 0;
612//:: }
613//::
614//:: static inline Int cmp(UInt a, UInt b)
615//:: {
616//:: if (a < b)
617//:: return -1;
618//:: else if (a > b)
619//:: return 1;
620//:: else
621//:: return 0;
622//:: }
623//::
624//:: static Int keycmp(const void *pa, const void *pb)
625//:: {
626//:: const struct callkey *a = (const struct callkey *)pa;
627//:: const struct callkey *b = (const struct callkey *)pb;
628//:: Int ret;
629//::
630//:: if ((ret = cmp(a->tid, b->tid)))
631//:: return ret;
632//::
633//:: if ((ret = addrcmp(a->esp, b->esp)))
634//:: return ret;
635//::
636//:: return addrcmp(a->eip, b->eip);
637//:: }
638//::
639//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000640//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000641//:: NULL, VG_AR_SYMTAB);
642//::
643//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
644//:: {
645//:: struct callkey key = { tid, argsp, retaddr };
646//::
647//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
648//:: }
649//::
650//:: static void wrapper_return(Addr retaddr);
651//::
652//:: /* Called from generated code via helper */
653//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
654//:: {
njndb9b7732005-03-26 00:32:29 +0000655//:: Addr retaddr = VGA_RETADDR(tst->arch);
656//:: Addr argp = (Addr)&VGA_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000657//:: void *nonce = NULL;
658//:: Bool mf = VG_(my_fault);
659//:: VG_(my_fault) = True;
660//::
661//:: if (wrapper->before) {
njndb9b7732005-03-26 00:32:29 +0000662//:: va_list args = VGA_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000663//:: nonce = (*wrapper->before)(args);
664//:: }
665//::
666//:: if (wrapper->after) {
667//:: /* If there's an after function, make sure it gets called */
668//:: struct call_instance *call;
669//::
670//:: call = find_call(retaddr, argp, tst->tid);
671//::
672//:: if (call != NULL) {
673//:: /* Found a stale outstanding call; clean it up and recycle
674//:: the structure */
675//:: if (call->wrapper->after)
676//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
677//:: } else {
678//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
679//::
680//:: call->key.tid = tst->tid;
681//:: call->key.esp = argp;
682//:: call->key.eip = retaddr;
683//::
684//:: VG_(SkipList_Insert)(&wrapped_frames, call);
685//::
686//:: wrapper_return(retaddr);
687//:: }
688//::
689//:: call->wrapper = wrapper;
690//:: call->nonce = nonce;
691//:: } else
692//:: vg_assert(nonce == NULL);
693//::
694//:: VG_(my_fault) = mf;
695//:: }
696//::
697//:: /* Called from generated code via helper */
698//:: void VG_(wrap_after)(ThreadState *tst)
699//:: {
njn35172bc2005-03-26 00:04:03 +0000700//:: Addr EIP = VGA_INSTR_PTR(tst->arch); /* instruction after call */
701//:: Addr ESP = VGA_STACK_PTR(tst->arch); /* pointer to args */
702//:: Word ret = VGA_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000703//:: struct call_instance *call;
704//:: Bool mf = VG_(my_fault);
705//::
706//:: VG_(my_fault) = True;
707//:: call = find_call(EIP, ESP, tst->tid);
708//::
709//:: if (0)
710//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
711//::
712//:: if (call != NULL) {
713//:: if (call->wrapper->after)
714//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
715//::
716//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
717//:: VG_(SkipNode_Free)(&wrapped_frames, call);
718//:: }
719//:: VG_(my_fault) = mf;
720//:: }
721//::
722//::
723//:: struct wrapped_function {
724//:: Addr eip; /* eip of function entrypoint */
725//:: const FuncWrapper *wrapper;
726//:: };
727//::
728//:: struct wrapper_return {
729//:: Addr eip; /* return address */
730//:: };
731//::
732//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000733//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000734//:: NULL, VG_AR_SYMTAB);
735//::
736//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000737//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000738//:: NULL, VG_AR_SYMTAB);
739//::
740//:: /* Wrap function starting at eip */
741//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
742//:: {
743//:: struct wrapped_function *func;
744//::
745//:: if (0)
746//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
747//::
748//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
749//::
750//:: if (func == NULL) {
751//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
752//:: VG_(invalidate_translations)(eip, 1, True);
753//::
754//:: func->eip = eip;
755//:: VG_(SkipList_Insert)(&wrapped_functions, func);
756//:: }
757//::
758//:: func->wrapper = wrapper;
759//:: }
760//::
761//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
762//:: {
763//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
764//::
765//:: if (func)
766//:: return func->wrapper;
767//:: return NULL;
768//:: }
769//::
770//:: Bool VG_(is_wrapper_return)(Addr eip)
771//:: {
772//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
773//::
774//:: return ret != NULL;
775//:: }
776//::
777//:: /* Mark eip as being the return address of a wrapper, so that the
778//:: codegen will generate the appropriate call. */
779//:: void wrapper_return(Addr eip)
780//:: {
781//:: struct wrapper_return *ret;
782//::
783//:: if (VG_(is_wrapper_return)(eip))
784//:: return;
785//::
786//:: VG_(invalidate_translations)(eip, 1, True);
787//::
788//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
789//:: ret->eip = eip;
790//::
791//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
792//:: }