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