blob: b78350bed44e9c04b3b1e9343a487c500d9e1a0d [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 */
sewardjcbdddcf2005-03-10 23:23:45 +000078 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
sewardjcbdddcf2005-03-10 23:23:45 +0000133Bool VG_(is_resolved)(const CodeRedirect *redir)
134{
njn136b83b2005-06-19 05:14:03 +0000135 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000136}
137
tom748a1312005-04-02 15:53:01 +0000138static void add_resolved(CodeRedirect *redir)
139{
140 switch(redir->type) {
141 case R_REDIRECT:
142 if (VG_(clo_trace_redir)) {
143 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
144 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000145 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000146 }
147
148 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
149 /* For some given (from, to) redir, the "from" function got
150 called before the .so containing "to" became available. We
151 know this because there is already a translation for the
152 entry point of the original "from". So the redirect will
153 never actually take effect unless that translation is
154 discarded.
155
156 Note, we only really need to discard the first bb of the
157 old entry point, and so we avoid the problem of having to
158 figure out how big that bb was -- since it is at least 1
159 byte of original code, we can just pass 1 as the original
160 size to invalidate_translations() and it will indeed get
161 rid of the translation.
162
163 Note, this is potentially expensive -- discarding
164 translations causes complete unchaining.
165 */
166 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
167 VG_(message)(Vg_UserMsg,
168 "Discarding translation due to redirect of already called function" );
169 VG_(message)(Vg_UserMsg,
170 " %s (%p -> %p)",
171 redir->from_sym, redir->from_addr, redir->to_addr );
172 }
173 VG_(discard_translations)((Addr64)redir->from_addr, 1);
174 }
175
176 {
177 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
178
179 if (r == NULL)
180 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
181 else {
182 /* XXX leak redir */
183 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000184 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000185 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000186 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000187 }
188 }
189 break;
190
191 case R_WRAPPER:
192 if (VG_(clo_trace_redir)) {
193 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
194 redir->from_lib, redir->from_sym, redir->from_addr);
195 }
196
197 /* XXX redir leaked */
198 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
199 break;
200
201 case R_CLIENT_WRAPPER:
202 VG_(core_panic)("not implemented");
203 break;
204 }
205}
206
sewardjcbdddcf2005-03-10 23:23:45 +0000207/* Resolve a redir using si if possible, and add it to the resolved
208 list */
njn17250122005-05-14 17:18:12 +0000209static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000210{
211 Bool resolved;
212
213 vg_assert(si != NULL);
214 vg_assert(si->seg != NULL);
215
216 /* no redirection from Valgrind segments */
217 if (si->seg->flags & SF_VALGRIND)
218 return False;
219
220 resolved = VG_(is_resolved)(redir);
221
sewardjcbdddcf2005-03-10 23:23:45 +0000222 vg_assert(!resolved);
223
224 if (!from_resolved(redir)) {
225 vg_assert(redir->from_sym != NULL);
226
227 if (match_lib(redir->from_lib, si)) {
228 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
229 if (VG_(clo_trace_redir) && redir->from_addr != 0)
230 VG_(printf)(" bind FROM: %p = %s:%s\n",
231 redir->from_addr,redir->from_lib, redir->from_sym );
232 }
233 }
234
njn136b83b2005-06-19 05:14:03 +0000235 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000236
237 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000238 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000239 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000240 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000241
tom748a1312005-04-02 15:53:01 +0000242 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000243
244 return resolved;
245}
246
njnbf7ca332005-05-14 17:11:06 +0000247static Bool resolve_redir_allsegs(CodeRedirect *redir)
248{
249 const SegInfo *si;
250
251 for(si = VG_(next_seginfo)(NULL);
252 si != NULL;
253 si = VG_(next_seginfo)(si))
254 {
njn17250122005-05-14 17:18:12 +0000255 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000256 return True;
257 }
258 return False;
259}
260
sewardjcbdddcf2005-03-10 23:23:45 +0000261/* Go through the complete redir list, resolving as much as possible with this SegInfo.
262
263 This should be called when a new SegInfo symtab is loaded.
264 */
265void VG_(resolve_seg_redirs)(SegInfo *si)
266{
267 CodeRedirect **prevp = &unresolved_redir;
268 CodeRedirect *redir, *next;
269
270 if (VG_(clo_trace_redir))
271 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
272 si->filename, si->soname);
273
274 /* visit each unresolved redir - if it becomes resolved, then
275 remove it from the unresolved list */
276 for(redir = unresolved_redir; redir != NULL; redir = next) {
277 next = redir->next;
278
njn17250122005-05-14 17:18:12 +0000279 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000280 *prevp = next;
281 redir->next = NULL;
282 } else
283 prevp = &redir->next;
284 }
285}
286
njn136b83b2005-06-19 05:14:03 +0000287static void add_redirect_X_to_addr(
288 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000289)
sewardjcbdddcf2005-03-10 23:23:45 +0000290{
291 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
292
293 redir->type = R_REDIRECT;
294
njn16eeb4e2005-06-16 03:56:58 +0000295 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000296 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000297 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000298 else redir->from_sym = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000299 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
383 routine, to the routine in our trampoline page so that the
384 special sysinfo unwind hack in m_stacktrace.c will kick in. */
njn16eeb4e2005-06-16 03:56:58 +0000385 add_redirect_sym_to_addr("soname:ld-linux.so.2", "_dl_sysinfo_int80",
386 VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
njnd1af0032005-05-29 17:01:48 +0000387#elif defined(VGP_amd64_linux)
388 /* Redirect vsyscalls to local versions */
njn16eeb4e2005-06-16 03:56:58 +0000389 add_redirect_addr_to_addr(0xFFFFFFFFFF600000ULL,
390 VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
391 add_redirect_addr_to_addr(0xFFFFFFFFFF600400ULL,
392 VG_(client_trampoline_code)+VG_(tramp_time_offset));
njnd1af0032005-05-29 17:01:48 +0000393#else
394# error Unknown platform
395#endif
396}
sewardjcbdddcf2005-03-10 23:23:45 +0000397
njn16eeb4e2005-06-16 03:56:58 +0000398/* Z-decode a symbol into library:func form, eg
399
400 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
401
402 Uses the Z-encoding scheme described in pub_core_redir.h.
403 Returns True if demangle OK, False otherwise.
404*/
405static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
406{
407# define EMIT(ch) \
408 do { \
409 if (j >= nbytes) \
410 result[j-1] = 0; \
411 else \
412 result[j++] = ch; \
413 } while (0)
414
415 Bool error = False;
416 Int i, j = 0;
417 Int len = VG_(strlen)(symbol);
418 if (0) VG_(printf)("idm: %s\n", symbol);
419
420 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
421
422 /* Chew though the Z-encoded soname part. */
423 while (True) {
424
425 if (i >= len)
426 break;
427
428 if (symbol[i] == '_')
429 /* We found the underscore following the Z-encoded soname.
430 Just copy the rest literally. */
431 break;
432
433 if (symbol[i] != 'Z') {
434 EMIT(symbol[i]);
435 i++;
436 continue;
437 }
438
439 /* We've got a Z-escape. Act accordingly. */
440 i++;
441 if (i >= len) {
442 /* Hmm, Z right at the end. Something's wrong. */
443 error = True;
444 EMIT('Z');
445 break;
446 }
447 switch (symbol[i]) {
448 case 'a': EMIT('*'); break;
449 case 'p': EMIT('+'); break;
450 case 'c': EMIT(':'); break;
451 case 'd': EMIT('.'); break;
452 case 'u': EMIT('_'); break;
453 case 'h': EMIT('-'); break;
454 case 's': EMIT(' '); break;
455 case 'Z': EMIT('Z'); break;
456 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
457 }
458 i++;
459 }
460
461 if (error || i >= len || symbol[i] != '_') {
462 /* Something's wrong. Give up. */
463 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
464 EMIT(0);
465 return False;
466 }
467
468 /* Copy the rest of the string verbatim. */
469 i++;
470 EMIT(':');
471 while (True) {
472 if (i >= len)
473 break;
474 EMIT(symbol[i]);
475 i++;
476 }
477
478 EMIT(0);
479 if (0) VG_(printf)("%s\n", result);
480 return True;
481
482# undef EMIT
483}
484
485// Nb: this can change the string pointed to by 'symbol'.
486static void handle_replacement_function( Char* symbol, Addr addr )
487{
488 Bool ok;
489 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
490 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
491 Char *func;
492
493 // Put "soname:" at the start of lib
494 VG_(strcpy)(lib, "soname:");
495
496 ok = Z_decode(symbol, lib+7, len);
497 if (ok) {
498 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
499 func = lib + VG_(strlen)(lib)-1;
500 while(*func != ':') func--;
501 *func = '\0';
502 func++; // Move past the '\0'
503
504 // Now lib is "soname:<libname>" and func is "<fnname>".
505 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
506 add_redirect_sym_to_addr(lib, func, addr);
507
508 // Overwrite the given Z-encoded name with just the fnname.
509 VG_(strcpy)(symbol, func);
510 }
511
512 VG_(arena_free)(VG_AR_SYMTAB, lib);
513}
514
515// This is specifically for stringifying VG_(x) function names. We
516// need to do two macroexpansions to get the VG_ macro expanded before
517// stringifying.
518#define _STR(x) #x
519#define STR(x) _STR(x)
520
521static void handle_load_notifier( Char* symbol, Addr addr )
522{
523 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
524 VG_(set_libc_freeres_wrapper_addr)(addr);
525// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
526// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
527 else
528 vg_assert2(0, "unrecognised load notification function: %s", symbol);
529}
530
531static Bool is_replacement_function(Char* s)
532{
533 return (0 == VG_(strncmp)(s,
534 VG_REPLACE_FUNCTION_PREFIX,
535 VG_REPLACE_FUNCTION_PREFIX_LEN));
536}
537
538static Bool is_load_notifier(Char* s)
539{
540 return (0 == VG_(strncmp)(s,
541 VG_NOTIFY_ON_LOAD_PREFIX,
542 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
543}
544
545// Call this for each symbol loaded. It determines if we need to do
546// anything special with it. It can modify 'symbol' in-place.
547void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
548{
549 if (is_replacement_function(symbol))
550 handle_replacement_function(symbol, addr);
551 else
552 if (is_load_notifier(symbol))
553 handle_load_notifier(symbol, addr);
554}
555
556
sewardjcbdddcf2005-03-10 23:23:45 +0000557//:: /*------------------------------------------------------------*/
558//:: /*--- General function wrapping. ---*/
559//:: /*------------------------------------------------------------*/
560//::
561//:: /*
562//:: TODO:
563//:: - hook into the symtab machinery
564//:: - client-side wrappers?
565//:: - better interfaces for before() functions to get to arguments
566//:: - handle munmap of code (dlclose())
567//:: - handle thread exit
568//:: - handle longjmp
569//:: */
570//:: struct callkey {
571//:: ThreadId tid; /* calling thread */
572//:: Addr esp; /* address of args on stack */
573//:: Addr eip; /* return address */
574//:: };
575//::
576//:: struct call_instance {
577//:: struct callkey key;
578//::
579//:: const FuncWrapper *wrapper;
580//:: void *nonce;
581//:: };
582//::
583//:: static inline Addr addrcmp(Addr a, Addr b)
584//:: {
585//:: if (a < b)
586//:: return -1;
587//:: else if (a > b)
588//:: return 1;
589//:: else
590//:: return 0;
591//:: }
592//::
593//:: static inline Int cmp(UInt a, UInt b)
594//:: {
595//:: if (a < b)
596//:: return -1;
597//:: else if (a > b)
598//:: return 1;
599//:: else
600//:: return 0;
601//:: }
602//::
603//:: static Int keycmp(const void *pa, const void *pb)
604//:: {
605//:: const struct callkey *a = (const struct callkey *)pa;
606//:: const struct callkey *b = (const struct callkey *)pb;
607//:: Int ret;
608//::
609//:: if ((ret = cmp(a->tid, b->tid)))
610//:: return ret;
611//::
612//:: if ((ret = addrcmp(a->esp, b->esp)))
613//:: return ret;
614//::
615//:: return addrcmp(a->eip, b->eip);
616//:: }
617//::
618//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000619//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000620//:: NULL, VG_AR_SYMTAB);
621//::
622//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
623//:: {
624//:: struct callkey key = { tid, argsp, retaddr };
625//::
626//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
627//:: }
628//::
629//:: static void wrapper_return(Addr retaddr);
630//::
631//:: /* Called from generated code via helper */
632//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
633//:: {
njndb9b7732005-03-26 00:32:29 +0000634//:: Addr retaddr = VGA_RETADDR(tst->arch);
635//:: Addr argp = (Addr)&VGA_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000636//:: void *nonce = NULL;
637//:: Bool mf = VG_(my_fault);
638//:: VG_(my_fault) = True;
639//::
640//:: if (wrapper->before) {
njndb9b7732005-03-26 00:32:29 +0000641//:: va_list args = VGA_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000642//:: nonce = (*wrapper->before)(args);
643//:: }
644//::
645//:: if (wrapper->after) {
646//:: /* If there's an after function, make sure it gets called */
647//:: struct call_instance *call;
648//::
649//:: call = find_call(retaddr, argp, tst->tid);
650//::
651//:: if (call != NULL) {
652//:: /* Found a stale outstanding call; clean it up and recycle
653//:: the structure */
654//:: if (call->wrapper->after)
655//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
656//:: } else {
657//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
658//::
659//:: call->key.tid = tst->tid;
660//:: call->key.esp = argp;
661//:: call->key.eip = retaddr;
662//::
663//:: VG_(SkipList_Insert)(&wrapped_frames, call);
664//::
665//:: wrapper_return(retaddr);
666//:: }
667//::
668//:: call->wrapper = wrapper;
669//:: call->nonce = nonce;
670//:: } else
671//:: vg_assert(nonce == NULL);
672//::
673//:: VG_(my_fault) = mf;
674//:: }
675//::
676//:: /* Called from generated code via helper */
677//:: void VG_(wrap_after)(ThreadState *tst)
678//:: {
njn35172bc2005-03-26 00:04:03 +0000679//:: Addr EIP = VGA_INSTR_PTR(tst->arch); /* instruction after call */
680//:: Addr ESP = VGA_STACK_PTR(tst->arch); /* pointer to args */
681//:: Word ret = VGA_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000682//:: struct call_instance *call;
683//:: Bool mf = VG_(my_fault);
684//::
685//:: VG_(my_fault) = True;
686//:: call = find_call(EIP, ESP, tst->tid);
687//::
688//:: if (0)
689//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
690//::
691//:: if (call != NULL) {
692//:: if (call->wrapper->after)
693//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
694//::
695//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
696//:: VG_(SkipNode_Free)(&wrapped_frames, call);
697//:: }
698//:: VG_(my_fault) = mf;
699//:: }
700//::
701//::
702//:: struct wrapped_function {
703//:: Addr eip; /* eip of function entrypoint */
704//:: const FuncWrapper *wrapper;
705//:: };
706//::
707//:: struct wrapper_return {
708//:: Addr eip; /* return address */
709//:: };
710//::
711//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000712//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000713//:: NULL, VG_AR_SYMTAB);
714//::
715//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000716//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000717//:: NULL, VG_AR_SYMTAB);
718//::
719//:: /* Wrap function starting at eip */
720//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
721//:: {
722//:: struct wrapped_function *func;
723//::
724//:: if (0)
725//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
726//::
727//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
728//::
729//:: if (func == NULL) {
730//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
731//:: VG_(invalidate_translations)(eip, 1, True);
732//::
733//:: func->eip = eip;
734//:: VG_(SkipList_Insert)(&wrapped_functions, func);
735//:: }
736//::
737//:: func->wrapper = wrapper;
738//:: }
739//::
740//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
741//:: {
742//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
743//::
744//:: if (func)
745//:: return func->wrapper;
746//:: return NULL;
747//:: }
748//::
749//:: Bool VG_(is_wrapper_return)(Addr eip)
750//:: {
751//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
752//::
753//:: return ret != NULL;
754//:: }
755//::
756//:: /* Mark eip as being the return address of a wrapper, so that the
757//:: codegen will generate the appropriate call. */
758//:: void wrapper_return(Addr eip)
759//:: {
760//:: struct wrapper_return *ret;
761//::
762//:: if (VG_(is_wrapper_return)(eip))
763//:: return;
764//::
765//:: VG_(invalidate_translations)(eip, 1, True);
766//::
767//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
768//:: ret->eip = eip;
769//::
770//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
771//:: }