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