blob: 155812f300291ff283b7551f71a3d1d047109ec1 [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"
34#include "pub_core_threadstate.h" // needed for pub_core_main.h
sewardj55f9d1a2005-04-25 11:11:44 +000035#include "pub_core_aspacemgr.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"
njnc7561b92005-06-19 01:24:32 +000039#include "pub_core_main.h" // for VG_(set_libc_freeres_wrapper_addr)
njnaf1d7df2005-06-11 01:31:52 +000040#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000041#include "pub_core_options.h"
njnd1af0032005-05-29 17:01:48 +000042#include "pub_core_redir.h"
njnaf1d7df2005-06-11 01:31:52 +000043#include "pub_core_skiplist.h"
njna7598f62005-06-18 03:27:58 +000044#include "pub_core_trampoline.h"
njn8bddf582005-05-13 23:40:55 +000045#include "pub_core_transtab.h"
njnea27e462005-05-31 02:38:09 +000046#include "m_debuginfo/priv_symtab.h" // XXX: bad!
sewardj55f9d1a2005-04-25 11:11:44 +000047
sewardjcbdddcf2005-03-10 23:23:45 +000048/*------------------------------------------------------------*/
49/*--- General purpose redirection. ---*/
50/*------------------------------------------------------------*/
51
52/*
53 wraps and redirections, indexed by from_addr
54
55 Redirection and wrapping are two distinct mechanisms which Valgrind
56 can use to change the client's control flow.
57
58 Redirection intercepts a call to a client function, and re-points it
59 to a new piece of code (presumably functionally equivalent). The
60 original code is never run.
61
62 Wrapping does call the client's original code, but calls "before"
63 and "after" functions which can inspect (and perhaps modify) the
64 function's arguments and return value.
65 */
66struct _CodeRedirect {
67 enum redir_type {
68 R_REDIRECT, /* plain redirection */
69 R_WRAPPER, /* wrap with valgrind-internal code */
70 R_CLIENT_WRAPPER, /* wrap with client-side code */
71 } type;
72
73 const Char *from_lib; /* library qualifier pattern */
74 const Char *from_sym; /* symbol */
75 Addr from_addr; /* old addr */
76
77 /* used for redirection */
78 const Char *to_lib; /* library qualifier pattern */
79 const Char *to_sym; /* symbol */
80 Addr to_addr; /* new addr */
81
82 /* used for wrapping */
83 const FuncWrapper *wrapper;
84
85 CodeRedirect *next; /* next pointer on unresolved list */
86};
87
88static Char *straddr(void *p)
89{
90 static Char buf[16];
91
92 VG_(sprintf)(buf, "%p", *(Addr *)p);
93
94 return buf;
95}
96
njnbe91aae2005-03-27 01:42:41 +000097static SkipList sk_resolved_redir = VG_SKIPLIST_INIT(CodeRedirect, from_addr,
sewardjcbdddcf2005-03-10 23:23:45 +000098 VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
99static CodeRedirect *unresolved_redir = NULL;
100
101static Bool match_lib(const Char *pattern, const SegInfo *si)
102{
103 /* pattern == NULL matches everything, otherwise use globbing
104
105 If the pattern starts with:
106 file:, then match filename
107 soname:, then match soname
108 something else, match filename
109 */
110 const Char *name = si->filename;
111
112 if (pattern == NULL)
113 return True;
114
115 if (VG_(strncmp)(pattern, "file:", 5) == 0) {
116 pattern += 5;
117 name = si->filename;
118 }
119 if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
120 pattern += 7;
121 name = si->soname;
122 }
123
124 if (name == NULL)
125 return False;
126
127 return VG_(string_match)(pattern, name);
128}
129
130static inline Bool from_resolved(const CodeRedirect *redir)
131{
132 return redir->from_addr != 0;
133}
134
135static inline Bool to_resolved(const CodeRedirect *redir)
136{
137 if (redir->type == R_REDIRECT)
138 return redir->to_addr != 0;
139 vg_assert(redir->wrapper != NULL);
140 return True;
141}
142
143Bool VG_(is_resolved)(const CodeRedirect *redir)
144{
145 return from_resolved(redir) && to_resolved(redir);
146}
147
tom748a1312005-04-02 15:53:01 +0000148static void add_resolved(CodeRedirect *redir)
149{
150 switch(redir->type) {
151 case R_REDIRECT:
152 if (VG_(clo_trace_redir)) {
153 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
154 redir->from_lib, redir->from_sym, redir->from_addr);
155 VG_(message)(Vg_DebugMsg, " %s:%s=%p)",
156 redir->to_lib, redir->to_sym, redir->to_addr);
157 }
158
159 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
160 /* For some given (from, to) redir, the "from" function got
161 called before the .so containing "to" became available. We
162 know this because there is already a translation for the
163 entry point of the original "from". So the redirect will
164 never actually take effect unless that translation is
165 discarded.
166
167 Note, we only really need to discard the first bb of the
168 old entry point, and so we avoid the problem of having to
169 figure out how big that bb was -- since it is at least 1
170 byte of original code, we can just pass 1 as the original
171 size to invalidate_translations() and it will indeed get
172 rid of the translation.
173
174 Note, this is potentially expensive -- discarding
175 translations causes complete unchaining.
176 */
177 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
178 VG_(message)(Vg_UserMsg,
179 "Discarding translation due to redirect of already called function" );
180 VG_(message)(Vg_UserMsg,
181 " %s (%p -> %p)",
182 redir->from_sym, redir->from_addr, redir->to_addr );
183 }
184 VG_(discard_translations)((Addr64)redir->from_addr, 1);
185 }
186
187 {
188 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
189
190 if (r == NULL)
191 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
192 else {
193 /* XXX leak redir */
194 if (VG_(clo_trace_redir))
195 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%s:%s:%p duplicated\n",
196 redir->from_lib, redir->from_sym, redir->from_addr,
197 redir->to_lib, redir->to_sym, redir->to_addr);
198 }
199 }
200 break;
201
202 case R_WRAPPER:
203 if (VG_(clo_trace_redir)) {
204 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
205 redir->from_lib, redir->from_sym, redir->from_addr);
206 }
207
208 /* XXX redir leaked */
209 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
210 break;
211
212 case R_CLIENT_WRAPPER:
213 VG_(core_panic)("not implemented");
214 break;
215 }
216}
217
sewardjcbdddcf2005-03-10 23:23:45 +0000218/* Resolve a redir using si if possible, and add it to the resolved
219 list */
njn17250122005-05-14 17:18:12 +0000220static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000221{
222 Bool resolved;
223
224 vg_assert(si != NULL);
225 vg_assert(si->seg != NULL);
226
227 /* no redirection from Valgrind segments */
228 if (si->seg->flags & SF_VALGRIND)
229 return False;
230
231 resolved = VG_(is_resolved)(redir);
232
233 if (0 && VG_(clo_trace_redir))
234 VG_(printf)(" consider FROM binding %s:%s -> %s:%s in %s(%s)\n",
235 redir->from_lib, redir->from_sym,
236 redir->to_lib, redir->to_sym,
237 si->filename, si->soname);
238
239 vg_assert(!resolved);
240
241 if (!from_resolved(redir)) {
242 vg_assert(redir->from_sym != NULL);
243
244 if (match_lib(redir->from_lib, si)) {
245 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
246 if (VG_(clo_trace_redir) && redir->from_addr != 0)
247 VG_(printf)(" bind FROM: %p = %s:%s\n",
248 redir->from_addr,redir->from_lib, redir->from_sym );
249 }
250 }
251
252 if (!to_resolved(redir)) {
253 vg_assert(redir->type == R_REDIRECT);
254 vg_assert(redir->to_sym != NULL);
255
256 if (match_lib(redir->to_lib, si)) {
257 redir->to_addr = VG_(reverse_search_one_symtab)(si, redir->to_sym);
258 if (VG_(clo_trace_redir) && redir->to_addr != 0)
259 VG_(printf)(" bind TO: %p = %s:%s\n",
260 redir->to_addr,redir->to_lib, redir->to_sym );
261
262 }
263 }
264
265 resolved = from_resolved(redir) && to_resolved(redir);
266
267 if (0 && VG_(clo_trace_redir))
268 VG_(printf)("resolve_redir: %s:%s from=%p %s:%s to=%p\n",
269 redir->from_lib, redir->from_sym, redir->from_addr,
270 redir->to_lib, redir->to_sym, redir->to_addr);
271
tom748a1312005-04-02 15:53:01 +0000272 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000273
274 return resolved;
275}
276
njnbf7ca332005-05-14 17:11:06 +0000277static Bool resolve_redir_allsegs(CodeRedirect *redir)
278{
279 const SegInfo *si;
280
281 for(si = VG_(next_seginfo)(NULL);
282 si != NULL;
283 si = VG_(next_seginfo)(si))
284 {
njn17250122005-05-14 17:18:12 +0000285 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000286 return True;
287 }
288 return False;
289}
290
sewardjcbdddcf2005-03-10 23:23:45 +0000291/* Go through the complete redir list, resolving as much as possible with this SegInfo.
292
293 This should be called when a new SegInfo symtab is loaded.
294 */
295void VG_(resolve_seg_redirs)(SegInfo *si)
296{
297 CodeRedirect **prevp = &unresolved_redir;
298 CodeRedirect *redir, *next;
299
300 if (VG_(clo_trace_redir))
301 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
302 si->filename, si->soname);
303
304 /* visit each unresolved redir - if it becomes resolved, then
305 remove it from the unresolved list */
306 for(redir = unresolved_redir; redir != NULL; redir = next) {
307 next = redir->next;
308
njn17250122005-05-14 17:18:12 +0000309 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000310 *prevp = next;
311 redir->next = NULL;
312 } else
313 prevp = &redir->next;
314 }
315}
316
njn16eeb4e2005-06-16 03:56:58 +0000317static void add_redirect_X_to_X(
318 const Char *from_lib, const Char *from_sym, Addr from_addr,
319 const Char *to_lib, const Char *to_sym, Addr to_addr
320)
sewardjcbdddcf2005-03-10 23:23:45 +0000321{
322 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
323
324 redir->type = R_REDIRECT;
325
njn16eeb4e2005-06-16 03:56:58 +0000326 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000327 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000328 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000329 else redir->from_sym = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000330 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000331
njn16eeb4e2005-06-16 03:56:58 +0000332 if (to_lib) redir->to_lib = VG_(arena_strdup)(VG_AR_SYMTAB, to_lib);
tom3c9b8662005-06-16 09:20:43 +0000333 else redir->to_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000334 if (to_sym) redir->to_sym = VG_(arena_strdup)(VG_AR_SYMTAB, to_sym);
tom3c9b8662005-06-16 09:20:43 +0000335 else redir->to_sym = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000336 redir->to_addr = to_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000337
338 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
339 VG_(message)(Vg_UserMsg,
njn16eeb4e2005-06-16 03:56:58 +0000340 "REDIRECT %s:%s(%p) to %s:%s(%p)",
341 from_lib, from_sym, from_addr, to_lib, to_sym, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000342
343 /* Check against all existing segments to see if this redirection
344 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000345 if (VG_(is_resolved)(redir)) {
346 add_resolved(redir);
347 }
348 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000349 /* nope, add to list */
350 redir->next = unresolved_redir;
351 unresolved_redir = redir;
352 }
353}
354
njn16eeb4e2005-06-16 03:56:58 +0000355/* Redirect a lib/symbol reference to a function at lib/symbol */
356__attribute__((unused))
357static void add_redirect_sym_to_sym(const Char *from_lib, const Char *from_sym,
358 const Char *to_lib, const Char *to_sym)
sewardjcbdddcf2005-03-10 23:23:45 +0000359{
njn16eeb4e2005-06-16 03:56:58 +0000360 add_redirect_X_to_X(from_lib, from_sym, 0, to_lib, to_sym, 0);
361}
sewardjcbdddcf2005-03-10 23:23:45 +0000362
njn16eeb4e2005-06-16 03:56:58 +0000363/* Redirect a lib/symbol reference to a function at addr */
364static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
365 Addr to_addr)
366{
367 add_redirect_X_to_X(from_lib, from_sym, 0, NULL, NULL, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000368}
369
tom748a1312005-04-02 15:53:01 +0000370/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000371__attribute__((unused)) // It is used, but not on all platforms...
372static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000373{
njn16eeb4e2005-06-16 03:56:58 +0000374 add_redirect_X_to_X(NULL, NULL, from_addr, NULL, NULL, to_addr);
tom748a1312005-04-02 15:53:01 +0000375}
376
sewardjcbdddcf2005-03-10 23:23:45 +0000377CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
378 const FuncWrapper *wrapper)
379{
380 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
381
382 if (0)
383 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
384 from_lib, from_sym, wrapper->before, wrapper->after);
385
386 redir->type = R_WRAPPER;
387
388 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
389 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
390 redir->from_addr = 0;
391
392 redir->to_lib = NULL;
393 redir->to_sym = NULL;
394 redir->to_addr = 0;
395
396 redir->wrapper = wrapper;
397
398 /* Check against all existing segments to see if this redirection
399 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000400 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000401 /* nope, add to list */
402 redir->next = unresolved_redir;
403 unresolved_redir = redir;
404 }
405
406 return redir;
407}
408
sewardjcbdddcf2005-03-10 23:23:45 +0000409/* If address 'a' is being redirected, return the redirected-to
410 address. */
411Addr VG_(code_redirect)(Addr a)
412{
413 CodeRedirect* r;
414
sewardjcbdddcf2005-03-10 23:23:45 +0000415 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
416 if (r == NULL)
417 return a;
418
419 vg_assert(r->to_addr != 0);
420
421 return r->to_addr;
422}
423
424void VG_(setup_code_redirect_table) ( void )
425{
njnd1af0032005-05-29 17:01:48 +0000426#if defined(VGP_x86_linux)
427 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
428 routine, to the routine in our trampoline page so that the
429 special sysinfo unwind hack in m_stacktrace.c will kick in. */
njn16eeb4e2005-06-16 03:56:58 +0000430 add_redirect_sym_to_addr("soname:ld-linux.so.2", "_dl_sysinfo_int80",
431 VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
njnd1af0032005-05-29 17:01:48 +0000432#elif defined(VGP_amd64_linux)
433 /* Redirect vsyscalls to local versions */
njn16eeb4e2005-06-16 03:56:58 +0000434 add_redirect_addr_to_addr(0xFFFFFFFFFF600000ULL,
435 VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
436 add_redirect_addr_to_addr(0xFFFFFFFFFF600400ULL,
437 VG_(client_trampoline_code)+VG_(tramp_time_offset));
njnd1af0032005-05-29 17:01:48 +0000438#else
439# error Unknown platform
440#endif
441}
sewardjcbdddcf2005-03-10 23:23:45 +0000442
njn16eeb4e2005-06-16 03:56:58 +0000443/* Z-decode a symbol into library:func form, eg
444
445 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
446
447 Uses the Z-encoding scheme described in pub_core_redir.h.
448 Returns True if demangle OK, False otherwise.
449*/
450static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
451{
452# define EMIT(ch) \
453 do { \
454 if (j >= nbytes) \
455 result[j-1] = 0; \
456 else \
457 result[j++] = ch; \
458 } while (0)
459
460 Bool error = False;
461 Int i, j = 0;
462 Int len = VG_(strlen)(symbol);
463 if (0) VG_(printf)("idm: %s\n", symbol);
464
465 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
466
467 /* Chew though the Z-encoded soname part. */
468 while (True) {
469
470 if (i >= len)
471 break;
472
473 if (symbol[i] == '_')
474 /* We found the underscore following the Z-encoded soname.
475 Just copy the rest literally. */
476 break;
477
478 if (symbol[i] != 'Z') {
479 EMIT(symbol[i]);
480 i++;
481 continue;
482 }
483
484 /* We've got a Z-escape. Act accordingly. */
485 i++;
486 if (i >= len) {
487 /* Hmm, Z right at the end. Something's wrong. */
488 error = True;
489 EMIT('Z');
490 break;
491 }
492 switch (symbol[i]) {
493 case 'a': EMIT('*'); break;
494 case 'p': EMIT('+'); break;
495 case 'c': EMIT(':'); break;
496 case 'd': EMIT('.'); break;
497 case 'u': EMIT('_'); break;
498 case 'h': EMIT('-'); break;
499 case 's': EMIT(' '); break;
500 case 'Z': EMIT('Z'); break;
501 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
502 }
503 i++;
504 }
505
506 if (error || i >= len || symbol[i] != '_') {
507 /* Something's wrong. Give up. */
508 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
509 EMIT(0);
510 return False;
511 }
512
513 /* Copy the rest of the string verbatim. */
514 i++;
515 EMIT(':');
516 while (True) {
517 if (i >= len)
518 break;
519 EMIT(symbol[i]);
520 i++;
521 }
522
523 EMIT(0);
524 if (0) VG_(printf)("%s\n", result);
525 return True;
526
527# undef EMIT
528}
529
530// Nb: this can change the string pointed to by 'symbol'.
531static void handle_replacement_function( Char* symbol, Addr addr )
532{
533 Bool ok;
534 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
535 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
536 Char *func;
537
538 // Put "soname:" at the start of lib
539 VG_(strcpy)(lib, "soname:");
540
541 ok = Z_decode(symbol, lib+7, len);
542 if (ok) {
543 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
544 func = lib + VG_(strlen)(lib)-1;
545 while(*func != ':') func--;
546 *func = '\0';
547 func++; // Move past the '\0'
548
549 // Now lib is "soname:<libname>" and func is "<fnname>".
550 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
551 add_redirect_sym_to_addr(lib, func, addr);
552
553 // Overwrite the given Z-encoded name with just the fnname.
554 VG_(strcpy)(symbol, func);
555 }
556
557 VG_(arena_free)(VG_AR_SYMTAB, lib);
558}
559
560// This is specifically for stringifying VG_(x) function names. We
561// need to do two macroexpansions to get the VG_ macro expanded before
562// stringifying.
563#define _STR(x) #x
564#define STR(x) _STR(x)
565
566static void handle_load_notifier( Char* symbol, Addr addr )
567{
568 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
569 VG_(set_libc_freeres_wrapper_addr)(addr);
570// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
571// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
572 else
573 vg_assert2(0, "unrecognised load notification function: %s", symbol);
574}
575
576static Bool is_replacement_function(Char* s)
577{
578 return (0 == VG_(strncmp)(s,
579 VG_REPLACE_FUNCTION_PREFIX,
580 VG_REPLACE_FUNCTION_PREFIX_LEN));
581}
582
583static Bool is_load_notifier(Char* s)
584{
585 return (0 == VG_(strncmp)(s,
586 VG_NOTIFY_ON_LOAD_PREFIX,
587 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
588}
589
590// Call this for each symbol loaded. It determines if we need to do
591// anything special with it. It can modify 'symbol' in-place.
592void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
593{
594 if (is_replacement_function(symbol))
595 handle_replacement_function(symbol, addr);
596 else
597 if (is_load_notifier(symbol))
598 handle_load_notifier(symbol, addr);
599}
600
601
sewardjcbdddcf2005-03-10 23:23:45 +0000602//:: /*------------------------------------------------------------*/
603//:: /*--- General function wrapping. ---*/
604//:: /*------------------------------------------------------------*/
605//::
606//:: /*
607//:: TODO:
608//:: - hook into the symtab machinery
609//:: - client-side wrappers?
610//:: - better interfaces for before() functions to get to arguments
611//:: - handle munmap of code (dlclose())
612//:: - handle thread exit
613//:: - handle longjmp
614//:: */
615//:: struct callkey {
616//:: ThreadId tid; /* calling thread */
617//:: Addr esp; /* address of args on stack */
618//:: Addr eip; /* return address */
619//:: };
620//::
621//:: struct call_instance {
622//:: struct callkey key;
623//::
624//:: const FuncWrapper *wrapper;
625//:: void *nonce;
626//:: };
627//::
628//:: static inline Addr addrcmp(Addr a, Addr b)
629//:: {
630//:: if (a < b)
631//:: return -1;
632//:: else if (a > b)
633//:: return 1;
634//:: else
635//:: return 0;
636//:: }
637//::
638//:: static inline Int cmp(UInt a, UInt b)
639//:: {
640//:: if (a < b)
641//:: return -1;
642//:: else if (a > b)
643//:: return 1;
644//:: else
645//:: return 0;
646//:: }
647//::
648//:: static Int keycmp(const void *pa, const void *pb)
649//:: {
650//:: const struct callkey *a = (const struct callkey *)pa;
651//:: const struct callkey *b = (const struct callkey *)pb;
652//:: Int ret;
653//::
654//:: if ((ret = cmp(a->tid, b->tid)))
655//:: return ret;
656//::
657//:: if ((ret = addrcmp(a->esp, b->esp)))
658//:: return ret;
659//::
660//:: return addrcmp(a->eip, b->eip);
661//:: }
662//::
663//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000664//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000665//:: NULL, VG_AR_SYMTAB);
666//::
667//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
668//:: {
669//:: struct callkey key = { tid, argsp, retaddr };
670//::
671//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
672//:: }
673//::
674//:: static void wrapper_return(Addr retaddr);
675//::
676//:: /* Called from generated code via helper */
677//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
678//:: {
njndb9b7732005-03-26 00:32:29 +0000679//:: Addr retaddr = VGA_RETADDR(tst->arch);
680//:: Addr argp = (Addr)&VGA_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000681//:: void *nonce = NULL;
682//:: Bool mf = VG_(my_fault);
683//:: VG_(my_fault) = True;
684//::
685//:: if (wrapper->before) {
njndb9b7732005-03-26 00:32:29 +0000686//:: va_list args = VGA_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000687//:: nonce = (*wrapper->before)(args);
688//:: }
689//::
690//:: if (wrapper->after) {
691//:: /* If there's an after function, make sure it gets called */
692//:: struct call_instance *call;
693//::
694//:: call = find_call(retaddr, argp, tst->tid);
695//::
696//:: if (call != NULL) {
697//:: /* Found a stale outstanding call; clean it up and recycle
698//:: the structure */
699//:: if (call->wrapper->after)
700//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
701//:: } else {
702//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
703//::
704//:: call->key.tid = tst->tid;
705//:: call->key.esp = argp;
706//:: call->key.eip = retaddr;
707//::
708//:: VG_(SkipList_Insert)(&wrapped_frames, call);
709//::
710//:: wrapper_return(retaddr);
711//:: }
712//::
713//:: call->wrapper = wrapper;
714//:: call->nonce = nonce;
715//:: } else
716//:: vg_assert(nonce == NULL);
717//::
718//:: VG_(my_fault) = mf;
719//:: }
720//::
721//:: /* Called from generated code via helper */
722//:: void VG_(wrap_after)(ThreadState *tst)
723//:: {
njn35172bc2005-03-26 00:04:03 +0000724//:: Addr EIP = VGA_INSTR_PTR(tst->arch); /* instruction after call */
725//:: Addr ESP = VGA_STACK_PTR(tst->arch); /* pointer to args */
726//:: Word ret = VGA_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000727//:: struct call_instance *call;
728//:: Bool mf = VG_(my_fault);
729//::
730//:: VG_(my_fault) = True;
731//:: call = find_call(EIP, ESP, tst->tid);
732//::
733//:: if (0)
734//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
735//::
736//:: if (call != NULL) {
737//:: if (call->wrapper->after)
738//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
739//::
740//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
741//:: VG_(SkipNode_Free)(&wrapped_frames, call);
742//:: }
743//:: VG_(my_fault) = mf;
744//:: }
745//::
746//::
747//:: struct wrapped_function {
748//:: Addr eip; /* eip of function entrypoint */
749//:: const FuncWrapper *wrapper;
750//:: };
751//::
752//:: struct wrapper_return {
753//:: Addr eip; /* return address */
754//:: };
755//::
756//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000757//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000758//:: NULL, VG_AR_SYMTAB);
759//::
760//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000761//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000762//:: NULL, VG_AR_SYMTAB);
763//::
764//:: /* Wrap function starting at eip */
765//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
766//:: {
767//:: struct wrapped_function *func;
768//::
769//:: if (0)
770//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
771//::
772//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
773//::
774//:: if (func == NULL) {
775//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
776//:: VG_(invalidate_translations)(eip, 1, True);
777//::
778//:: func->eip = eip;
779//:: VG_(SkipList_Insert)(&wrapped_functions, func);
780//:: }
781//::
782//:: func->wrapper = wrapper;
783//:: }
784//::
785//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
786//:: {
787//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
788//::
789//:: if (func)
790//:: return func->wrapper;
791//:: return NULL;
792//:: }
793//::
794//:: Bool VG_(is_wrapper_return)(Addr eip)
795//:: {
796//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
797//::
798//:: return ret != NULL;
799//:: }
800//::
801//:: /* Mark eip as being the return address of a wrapper, so that the
802//:: codegen will generate the appropriate call. */
803//:: void wrapper_return(Addr eip)
804//:: {
805//:: struct wrapper_return *ret;
806//::
807//:: if (VG_(is_wrapper_return)(eip))
808//:: return;
809//::
810//:: VG_(invalidate_translations)(eip, 1, True);
811//::
812//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
813//:: ret->eip = eip;
814//::
815//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
816//:: }