blob: 845507702fe19e77c1c57363023acfe3f98ad8b8 [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"
njn24a6efb2005-06-20 03:36:51 +000034#include "pub_core_aspacemgr.h" // Needed for pub_core_redir.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"
njnaf1d7df2005-06-11 01:31:52 +000038#include "pub_core_mallocfree.h"
njn20242342005-05-16 23:31:24 +000039#include "pub_core_options.h"
njnd1af0032005-05-29 17:01:48 +000040#include "pub_core_redir.h"
njnaf1d7df2005-06-11 01:31:52 +000041#include "pub_core_skiplist.h"
njna7598f62005-06-18 03:27:58 +000042#include "pub_core_trampoline.h"
njn8bddf582005-05-13 23:40:55 +000043#include "pub_core_transtab.h"
njn24a6efb2005-06-20 03:36:51 +000044#include "m_debuginfo/priv_symtab.h" // XXX: bad! For SegInfo internals
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 */
sewardjcbdddcf2005-03-10 23:23:45 +000076 Addr to_addr; /* new addr */
77
78 /* used for wrapping */
79 const FuncWrapper *wrapper;
80
81 CodeRedirect *next; /* next pointer on unresolved list */
82};
83
84static Char *straddr(void *p)
85{
86 static Char buf[16];
87
88 VG_(sprintf)(buf, "%p", *(Addr *)p);
89
90 return buf;
91}
92
njnbe91aae2005-03-27 01:42:41 +000093static SkipList sk_resolved_redir = VG_SKIPLIST_INIT(CodeRedirect, from_addr,
sewardjcbdddcf2005-03-10 23:23:45 +000094 VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
95static CodeRedirect *unresolved_redir = NULL;
96
97static Bool match_lib(const Char *pattern, const SegInfo *si)
98{
99 /* pattern == NULL matches everything, otherwise use globbing
100
101 If the pattern starts with:
102 file:, then match filename
103 soname:, then match soname
104 something else, match filename
105 */
106 const Char *name = si->filename;
107
108 if (pattern == NULL)
109 return True;
110
111 if (VG_(strncmp)(pattern, "file:", 5) == 0) {
112 pattern += 5;
113 name = si->filename;
114 }
115 if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
116 pattern += 7;
117 name = si->soname;
118 }
119
120 if (name == NULL)
121 return False;
122
123 return VG_(string_match)(pattern, name);
124}
125
126static inline Bool from_resolved(const CodeRedirect *redir)
127{
128 return redir->from_addr != 0;
129}
130
sewardjcbdddcf2005-03-10 23:23:45 +0000131Bool VG_(is_resolved)(const CodeRedirect *redir)
132{
njn136b83b2005-06-19 05:14:03 +0000133 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000134}
135
tom748a1312005-04-02 15:53:01 +0000136static void add_resolved(CodeRedirect *redir)
137{
138 switch(redir->type) {
139 case R_REDIRECT:
140 if (VG_(clo_trace_redir)) {
141 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
142 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000143 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000144 }
145
146 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
147 /* For some given (from, to) redir, the "from" function got
148 called before the .so containing "to" became available. We
149 know this because there is already a translation for the
150 entry point of the original "from". So the redirect will
151 never actually take effect unless that translation is
152 discarded.
153
154 Note, we only really need to discard the first bb of the
155 old entry point, and so we avoid the problem of having to
156 figure out how big that bb was -- since it is at least 1
157 byte of original code, we can just pass 1 as the original
158 size to invalidate_translations() and it will indeed get
159 rid of the translation.
160
161 Note, this is potentially expensive -- discarding
162 translations causes complete unchaining.
163 */
164 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
165 VG_(message)(Vg_UserMsg,
166 "Discarding translation due to redirect of already called function" );
167 VG_(message)(Vg_UserMsg,
168 " %s (%p -> %p)",
169 redir->from_sym, redir->from_addr, redir->to_addr );
170 }
171 VG_(discard_translations)((Addr64)redir->from_addr, 1);
172 }
173
174 {
175 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
176
177 if (r == NULL)
178 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
179 else {
180 /* XXX leak redir */
181 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000182 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000183 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000184 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000185 }
186 }
187 break;
188
189 case R_WRAPPER:
190 if (VG_(clo_trace_redir)) {
191 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
192 redir->from_lib, redir->from_sym, redir->from_addr);
193 }
194
195 /* XXX redir leaked */
196 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
197 break;
198
199 case R_CLIENT_WRAPPER:
200 VG_(core_panic)("not implemented");
201 break;
202 }
203}
204
sewardjcbdddcf2005-03-10 23:23:45 +0000205/* Resolve a redir using si if possible, and add it to the resolved
206 list */
njn17250122005-05-14 17:18:12 +0000207static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000208{
209 Bool resolved;
210
211 vg_assert(si != NULL);
212 vg_assert(si->seg != NULL);
213
214 /* no redirection from Valgrind segments */
215 if (si->seg->flags & SF_VALGRIND)
216 return False;
217
218 resolved = VG_(is_resolved)(redir);
219
sewardjcbdddcf2005-03-10 23:23:45 +0000220 vg_assert(!resolved);
221
222 if (!from_resolved(redir)) {
223 vg_assert(redir->from_sym != NULL);
224
225 if (match_lib(redir->from_lib, si)) {
226 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
227 if (VG_(clo_trace_redir) && redir->from_addr != 0)
228 VG_(printf)(" bind FROM: %p = %s:%s\n",
229 redir->from_addr,redir->from_lib, redir->from_sym );
230 }
231 }
232
njn136b83b2005-06-19 05:14:03 +0000233 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000234
235 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000236 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000237 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000238 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000239
tom748a1312005-04-02 15:53:01 +0000240 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000241
242 return resolved;
243}
244
njnbf7ca332005-05-14 17:11:06 +0000245static Bool resolve_redir_allsegs(CodeRedirect *redir)
246{
247 const SegInfo *si;
248
249 for(si = VG_(next_seginfo)(NULL);
250 si != NULL;
251 si = VG_(next_seginfo)(si))
252 {
njn17250122005-05-14 17:18:12 +0000253 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000254 return True;
255 }
256 return False;
257}
258
sewardjcbdddcf2005-03-10 23:23:45 +0000259/* Go through the complete redir list, resolving as much as possible with this SegInfo.
260
261 This should be called when a new SegInfo symtab is loaded.
262 */
263void VG_(resolve_seg_redirs)(SegInfo *si)
264{
265 CodeRedirect **prevp = &unresolved_redir;
266 CodeRedirect *redir, *next;
267
268 if (VG_(clo_trace_redir))
269 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
270 si->filename, si->soname);
271
272 /* visit each unresolved redir - if it becomes resolved, then
273 remove it from the unresolved list */
274 for(redir = unresolved_redir; redir != NULL; redir = next) {
275 next = redir->next;
276
njn17250122005-05-14 17:18:12 +0000277 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000278 *prevp = next;
279 redir->next = NULL;
280 } else
281 prevp = &redir->next;
282 }
283}
284
njn136b83b2005-06-19 05:14:03 +0000285static void add_redirect_X_to_addr(
286 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000287)
sewardjcbdddcf2005-03-10 23:23:45 +0000288{
289 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
290
291 redir->type = R_REDIRECT;
292
njn16eeb4e2005-06-16 03:56:58 +0000293 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000294 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000295 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000296 else redir->from_sym = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000297 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000298
njn136b83b2005-06-19 05:14:03 +0000299 vg_assert(0 != to_addr);
300 redir->to_addr = to_addr;
301 redir->wrapper = 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000302
303 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
304 VG_(message)(Vg_UserMsg,
njn136b83b2005-06-19 05:14:03 +0000305 "REDIRECT %s:%s(%p) to %p",
306 from_lib, from_sym, from_addr, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000307
308 /* Check against all existing segments to see if this redirection
309 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000310 if (VG_(is_resolved)(redir)) {
311 add_resolved(redir);
312 }
313 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000314 /* nope, add to list */
315 redir->next = unresolved_redir;
316 unresolved_redir = redir;
317 }
318}
319
njn16eeb4e2005-06-16 03:56:58 +0000320/* Redirect a lib/symbol reference to a function at addr */
321static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
322 Addr to_addr)
323{
njn136b83b2005-06-19 05:14:03 +0000324 add_redirect_X_to_addr(from_lib, from_sym, 0, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000325}
326
tom748a1312005-04-02 15:53:01 +0000327/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000328__attribute__((unused)) // It is used, but not on all platforms...
329static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000330{
njn136b83b2005-06-19 05:14:03 +0000331 add_redirect_X_to_addr(NULL, NULL, from_addr, to_addr);
tom748a1312005-04-02 15:53:01 +0000332}
333
sewardjcbdddcf2005-03-10 23:23:45 +0000334CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
335 const FuncWrapper *wrapper)
336{
337 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
338
339 if (0)
340 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
341 from_lib, from_sym, wrapper->before, wrapper->after);
342
343 redir->type = R_WRAPPER;
344
njn136b83b2005-06-19 05:14:03 +0000345 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
346 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000347 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000348 redir->to_addr = 0;
349 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000350
351 /* Check against all existing segments to see if this redirection
352 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000353 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000354 /* nope, add to list */
355 redir->next = unresolved_redir;
356 unresolved_redir = redir;
357 }
358
359 return redir;
360}
361
sewardjcbdddcf2005-03-10 23:23:45 +0000362/* If address 'a' is being redirected, return the redirected-to
363 address. */
364Addr VG_(code_redirect)(Addr a)
365{
366 CodeRedirect* r;
367
sewardjcbdddcf2005-03-10 23:23:45 +0000368 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
369 if (r == NULL)
370 return a;
371
372 vg_assert(r->to_addr != 0);
373
374 return r->to_addr;
375}
376
377void VG_(setup_code_redirect_table) ( void )
378{
njnd1af0032005-05-29 17:01:48 +0000379#if defined(VGP_x86_linux)
380 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
381 routine, to the routine in our trampoline page so that the
382 special sysinfo unwind hack in m_stacktrace.c will kick in. */
njn16eeb4e2005-06-16 03:56:58 +0000383 add_redirect_sym_to_addr("soname:ld-linux.so.2", "_dl_sysinfo_int80",
384 VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
njnd1af0032005-05-29 17:01:48 +0000385#elif defined(VGP_amd64_linux)
386 /* Redirect vsyscalls to local versions */
njn16eeb4e2005-06-16 03:56:58 +0000387 add_redirect_addr_to_addr(0xFFFFFFFFFF600000ULL,
388 VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
389 add_redirect_addr_to_addr(0xFFFFFFFFFF600400ULL,
390 VG_(client_trampoline_code)+VG_(tramp_time_offset));
cerion85665ca2005-06-20 15:51:07 +0000391#elif defined(VGP_ppc32_linux)
392//CAB: TODO
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
njnbc6d84d2005-06-19 18:58:03 +0000515static Addr __libc_freeres_wrapper = 0;
516
517Addr VG_(get_libc_freeres_wrapper)(void)
518{
519 return __libc_freeres_wrapper;
520}
521
njn16eeb4e2005-06-16 03:56:58 +0000522// This is specifically for stringifying VG_(x) function names. We
523// need to do two macroexpansions to get the VG_ macro expanded before
524// stringifying.
525#define _STR(x) #x
526#define STR(x) _STR(x)
527
528static void handle_load_notifier( Char* symbol, Addr addr )
529{
530 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000531 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000532// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
533// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
534 else
535 vg_assert2(0, "unrecognised load notification function: %s", symbol);
536}
537
538static Bool is_replacement_function(Char* s)
539{
540 return (0 == VG_(strncmp)(s,
541 VG_REPLACE_FUNCTION_PREFIX,
542 VG_REPLACE_FUNCTION_PREFIX_LEN));
543}
544
545static Bool is_load_notifier(Char* s)
546{
547 return (0 == VG_(strncmp)(s,
548 VG_NOTIFY_ON_LOAD_PREFIX,
549 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
550}
551
552// Call this for each symbol loaded. It determines if we need to do
553// anything special with it. It can modify 'symbol' in-place.
554void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
555{
556 if (is_replacement_function(symbol))
557 handle_replacement_function(symbol, addr);
558 else
559 if (is_load_notifier(symbol))
560 handle_load_notifier(symbol, addr);
561}
562
563
sewardjcbdddcf2005-03-10 23:23:45 +0000564//:: /*------------------------------------------------------------*/
565//:: /*--- General function wrapping. ---*/
566//:: /*------------------------------------------------------------*/
567//::
568//:: /*
569//:: TODO:
570//:: - hook into the symtab machinery
571//:: - client-side wrappers?
572//:: - better interfaces for before() functions to get to arguments
573//:: - handle munmap of code (dlclose())
574//:: - handle thread exit
575//:: - handle longjmp
576//:: */
577//:: struct callkey {
578//:: ThreadId tid; /* calling thread */
579//:: Addr esp; /* address of args on stack */
580//:: Addr eip; /* return address */
581//:: };
582//::
583//:: struct call_instance {
584//:: struct callkey key;
585//::
586//:: const FuncWrapper *wrapper;
587//:: void *nonce;
588//:: };
589//::
590//:: static inline Addr addrcmp(Addr a, Addr b)
591//:: {
592//:: if (a < b)
593//:: return -1;
594//:: else if (a > b)
595//:: return 1;
596//:: else
597//:: return 0;
598//:: }
599//::
600//:: static inline Int cmp(UInt a, UInt b)
601//:: {
602//:: if (a < b)
603//:: return -1;
604//:: else if (a > b)
605//:: return 1;
606//:: else
607//:: return 0;
608//:: }
609//::
610//:: static Int keycmp(const void *pa, const void *pb)
611//:: {
612//:: const struct callkey *a = (const struct callkey *)pa;
613//:: const struct callkey *b = (const struct callkey *)pb;
614//:: Int ret;
615//::
616//:: if ((ret = cmp(a->tid, b->tid)))
617//:: return ret;
618//::
619//:: if ((ret = addrcmp(a->esp, b->esp)))
620//:: return ret;
621//::
622//:: return addrcmp(a->eip, b->eip);
623//:: }
624//::
625//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000626//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000627//:: NULL, VG_AR_SYMTAB);
628//::
629//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
630//:: {
631//:: struct callkey key = { tid, argsp, retaddr };
632//::
633//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
634//:: }
635//::
636//:: static void wrapper_return(Addr retaddr);
637//::
638//:: /* Called from generated code via helper */
639//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
640//:: {
njndb9b7732005-03-26 00:32:29 +0000641//:: Addr retaddr = VGA_RETADDR(tst->arch);
642//:: Addr argp = (Addr)&VGA_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000643//:: void *nonce = NULL;
644//:: Bool mf = VG_(my_fault);
645//:: VG_(my_fault) = True;
646//::
647//:: if (wrapper->before) {
njndb9b7732005-03-26 00:32:29 +0000648//:: va_list args = VGA_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000649//:: nonce = (*wrapper->before)(args);
650//:: }
651//::
652//:: if (wrapper->after) {
653//:: /* If there's an after function, make sure it gets called */
654//:: struct call_instance *call;
655//::
656//:: call = find_call(retaddr, argp, tst->tid);
657//::
658//:: if (call != NULL) {
659//:: /* Found a stale outstanding call; clean it up and recycle
660//:: the structure */
661//:: if (call->wrapper->after)
662//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
663//:: } else {
664//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
665//::
666//:: call->key.tid = tst->tid;
667//:: call->key.esp = argp;
668//:: call->key.eip = retaddr;
669//::
670//:: VG_(SkipList_Insert)(&wrapped_frames, call);
671//::
672//:: wrapper_return(retaddr);
673//:: }
674//::
675//:: call->wrapper = wrapper;
676//:: call->nonce = nonce;
677//:: } else
678//:: vg_assert(nonce == NULL);
679//::
680//:: VG_(my_fault) = mf;
681//:: }
682//::
683//:: /* Called from generated code via helper */
684//:: void VG_(wrap_after)(ThreadState *tst)
685//:: {
njn35172bc2005-03-26 00:04:03 +0000686//:: Addr EIP = VGA_INSTR_PTR(tst->arch); /* instruction after call */
687//:: Addr ESP = VGA_STACK_PTR(tst->arch); /* pointer to args */
688//:: Word ret = VGA_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000689//:: struct call_instance *call;
690//:: Bool mf = VG_(my_fault);
691//::
692//:: VG_(my_fault) = True;
693//:: call = find_call(EIP, ESP, tst->tid);
694//::
695//:: if (0)
696//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
697//::
698//:: if (call != NULL) {
699//:: if (call->wrapper->after)
700//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
701//::
702//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
703//:: VG_(SkipNode_Free)(&wrapped_frames, call);
704//:: }
705//:: VG_(my_fault) = mf;
706//:: }
707//::
708//::
709//:: struct wrapped_function {
710//:: Addr eip; /* eip of function entrypoint */
711//:: const FuncWrapper *wrapper;
712//:: };
713//::
714//:: struct wrapper_return {
715//:: Addr eip; /* return address */
716//:: };
717//::
718//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000719//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000720//:: NULL, VG_AR_SYMTAB);
721//::
722//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000723//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000724//:: NULL, VG_AR_SYMTAB);
725//::
726//:: /* Wrap function starting at eip */
727//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
728//:: {
729//:: struct wrapped_function *func;
730//::
731//:: if (0)
732//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
733//::
734//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
735//::
736//:: if (func == NULL) {
737//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
738//:: VG_(invalidate_translations)(eip, 1, True);
739//::
740//:: func->eip = eip;
741//:: VG_(SkipList_Insert)(&wrapped_functions, func);
742//:: }
743//::
744//:: func->wrapper = wrapper;
745//:: }
746//::
747//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
748//:: {
749//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
750//::
751//:: if (func)
752//:: return func->wrapper;
753//:: return NULL;
754//:: }
755//::
756//:: Bool VG_(is_wrapper_return)(Addr eip)
757//:: {
758//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
759//::
760//:: return ret != NULL;
761//:: }
762//::
763//:: /* Mark eip as being the return address of a wrapper, so that the
764//:: codegen will generate the appropriate call. */
765//:: void wrapper_return(Addr eip)
766//:: {
767//:: struct wrapper_return *ret;
768//::
769//:: if (VG_(is_wrapper_return)(eip))
770//:: return;
771//::
772//:: VG_(invalidate_translations)(eip, 1, True);
773//::
774//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
775//:: ret->eip = eip;
776//::
777//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
778//:: }