blob: e7c72304704661e82ca7539ca2b19cd5de7dd465 [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"
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 */
sewardjcbdddcf2005-03-10 23:23:45 +000077 Addr to_addr; /* new addr */
78
79 /* used for wrapping */
80 const FuncWrapper *wrapper;
81
82 CodeRedirect *next; /* next pointer on unresolved list */
83};
84
85static Char *straddr(void *p)
86{
87 static Char buf[16];
88
89 VG_(sprintf)(buf, "%p", *(Addr *)p);
90
91 return buf;
92}
93
njnbe91aae2005-03-27 01:42:41 +000094static SkipList sk_resolved_redir = VG_SKIPLIST_INIT(CodeRedirect, from_addr,
sewardjcbdddcf2005-03-10 23:23:45 +000095 VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
96static CodeRedirect *unresolved_redir = NULL;
97
98static Bool match_lib(const Char *pattern, const SegInfo *si)
99{
100 /* pattern == NULL matches everything, otherwise use globbing
101
102 If the pattern starts with:
103 file:, then match filename
104 soname:, then match soname
105 something else, match filename
106 */
107 const Char *name = si->filename;
108
109 if (pattern == NULL)
110 return True;
111
112 if (VG_(strncmp)(pattern, "file:", 5) == 0) {
113 pattern += 5;
114 name = si->filename;
115 }
116 if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
117 pattern += 7;
118 name = si->soname;
119 }
120
121 if (name == NULL)
122 return False;
123
124 return VG_(string_match)(pattern, name);
125}
126
127static inline Bool from_resolved(const CodeRedirect *redir)
128{
129 return redir->from_addr != 0;
130}
131
sewardjcbdddcf2005-03-10 23:23:45 +0000132Bool VG_(is_resolved)(const CodeRedirect *redir)
133{
njn136b83b2005-06-19 05:14:03 +0000134 return from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000135}
136
tom748a1312005-04-02 15:53:01 +0000137static void add_resolved(CodeRedirect *redir)
138{
139 switch(redir->type) {
140 case R_REDIRECT:
141 if (VG_(clo_trace_redir)) {
142 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
143 redir->from_lib, redir->from_sym, redir->from_addr);
njn136b83b2005-06-19 05:14:03 +0000144 VG_(message)(Vg_DebugMsg, " %p)", redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000145 }
146
147 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
148 /* For some given (from, to) redir, the "from" function got
149 called before the .so containing "to" became available. We
150 know this because there is already a translation for the
151 entry point of the original "from". So the redirect will
152 never actually take effect unless that translation is
153 discarded.
154
155 Note, we only really need to discard the first bb of the
156 old entry point, and so we avoid the problem of having to
157 figure out how big that bb was -- since it is at least 1
158 byte of original code, we can just pass 1 as the original
159 size to invalidate_translations() and it will indeed get
160 rid of the translation.
161
162 Note, this is potentially expensive -- discarding
163 translations causes complete unchaining.
164 */
165 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
166 VG_(message)(Vg_UserMsg,
167 "Discarding translation due to redirect of already called function" );
168 VG_(message)(Vg_UserMsg,
169 " %s (%p -> %p)",
170 redir->from_sym, redir->from_addr, redir->to_addr );
171 }
172 VG_(discard_translations)((Addr64)redir->from_addr, 1);
173 }
174
175 {
176 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
177
178 if (r == NULL)
179 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
180 else {
181 /* XXX leak redir */
182 if (VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000183 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%p duplicated\n",
tom748a1312005-04-02 15:53:01 +0000184 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000185 redir->to_addr);
tom748a1312005-04-02 15:53:01 +0000186 }
187 }
188 break;
189
190 case R_WRAPPER:
191 if (VG_(clo_trace_redir)) {
192 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
193 redir->from_lib, redir->from_sym, redir->from_addr);
194 }
195
196 /* XXX redir leaked */
197 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
198 break;
199
200 case R_CLIENT_WRAPPER:
201 VG_(core_panic)("not implemented");
202 break;
203 }
204}
205
sewardjcbdddcf2005-03-10 23:23:45 +0000206/* Resolve a redir using si if possible, and add it to the resolved
207 list */
njn17250122005-05-14 17:18:12 +0000208static Bool resolve_redir(CodeRedirect *redir, const SegInfo *si)
sewardjcbdddcf2005-03-10 23:23:45 +0000209{
210 Bool resolved;
211
212 vg_assert(si != NULL);
213 vg_assert(si->seg != NULL);
214
215 /* no redirection from Valgrind segments */
216 if (si->seg->flags & SF_VALGRIND)
217 return False;
218
219 resolved = VG_(is_resolved)(redir);
220
sewardjcbdddcf2005-03-10 23:23:45 +0000221 vg_assert(!resolved);
222
223 if (!from_resolved(redir)) {
224 vg_assert(redir->from_sym != NULL);
225
226 if (match_lib(redir->from_lib, si)) {
227 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
228 if (VG_(clo_trace_redir) && redir->from_addr != 0)
229 VG_(printf)(" bind FROM: %p = %s:%s\n",
230 redir->from_addr,redir->from_lib, redir->from_sym );
231 }
232 }
233
njn136b83b2005-06-19 05:14:03 +0000234 resolved = from_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000235
236 if (0 && VG_(clo_trace_redir))
njn136b83b2005-06-19 05:14:03 +0000237 VG_(printf)("resolve_redir: %s:%s from=%p to=%p\n",
sewardjcbdddcf2005-03-10 23:23:45 +0000238 redir->from_lib, redir->from_sym, redir->from_addr,
njn136b83b2005-06-19 05:14:03 +0000239 redir->to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000240
tom748a1312005-04-02 15:53:01 +0000241 if (resolved) add_resolved(redir);
sewardjcbdddcf2005-03-10 23:23:45 +0000242
243 return resolved;
244}
245
njnbf7ca332005-05-14 17:11:06 +0000246static Bool resolve_redir_allsegs(CodeRedirect *redir)
247{
248 const SegInfo *si;
249
250 for(si = VG_(next_seginfo)(NULL);
251 si != NULL;
252 si = VG_(next_seginfo)(si))
253 {
njn17250122005-05-14 17:18:12 +0000254 if (resolve_redir(redir, si))
njnbf7ca332005-05-14 17:11:06 +0000255 return True;
256 }
257 return False;
258}
259
sewardjcbdddcf2005-03-10 23:23:45 +0000260/* Go through the complete redir list, resolving as much as possible with this SegInfo.
261
262 This should be called when a new SegInfo symtab is loaded.
263 */
264void VG_(resolve_seg_redirs)(SegInfo *si)
265{
266 CodeRedirect **prevp = &unresolved_redir;
267 CodeRedirect *redir, *next;
268
269 if (VG_(clo_trace_redir))
270 VG_(printf)("Considering redirs to/from %s(soname=%s)\n",
271 si->filename, si->soname);
272
273 /* visit each unresolved redir - if it becomes resolved, then
274 remove it from the unresolved list */
275 for(redir = unresolved_redir; redir != NULL; redir = next) {
276 next = redir->next;
277
njn17250122005-05-14 17:18:12 +0000278 if (resolve_redir(redir, si)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000279 *prevp = next;
280 redir->next = NULL;
281 } else
282 prevp = &redir->next;
283 }
284}
285
njn136b83b2005-06-19 05:14:03 +0000286static void add_redirect_X_to_addr(
287 const Char *from_lib, const Char *from_sym, Addr from_addr, Addr to_addr
njn16eeb4e2005-06-16 03:56:58 +0000288)
sewardjcbdddcf2005-03-10 23:23:45 +0000289{
290 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
291
292 redir->type = R_REDIRECT;
293
njn16eeb4e2005-06-16 03:56:58 +0000294 if (from_lib) redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
tom3c9b8662005-06-16 09:20:43 +0000295 else redir->from_lib = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000296 if (from_sym) redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
tom3c9b8662005-06-16 09:20:43 +0000297 else redir->from_sym = NULL;
njn16eeb4e2005-06-16 03:56:58 +0000298 redir->from_addr = from_addr;
sewardjcbdddcf2005-03-10 23:23:45 +0000299
njn136b83b2005-06-19 05:14:03 +0000300 vg_assert(0 != to_addr);
301 redir->to_addr = to_addr;
302 redir->wrapper = 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000303
304 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
305 VG_(message)(Vg_UserMsg,
njn136b83b2005-06-19 05:14:03 +0000306 "REDIRECT %s:%s(%p) to %p",
307 from_lib, from_sym, from_addr, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000308
309 /* Check against all existing segments to see if this redirection
310 can be resolved immediately */
tom3c9b8662005-06-16 09:20:43 +0000311 if (VG_(is_resolved)(redir)) {
312 add_resolved(redir);
313 }
314 else if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000315 /* nope, add to list */
316 redir->next = unresolved_redir;
317 unresolved_redir = redir;
318 }
319}
320
njn16eeb4e2005-06-16 03:56:58 +0000321/* Redirect a lib/symbol reference to a function at addr */
322static void add_redirect_sym_to_addr(const Char *from_lib, const Char *from_sym,
323 Addr to_addr)
324{
njn136b83b2005-06-19 05:14:03 +0000325 add_redirect_X_to_addr(from_lib, from_sym, 0, to_addr);
sewardjcbdddcf2005-03-10 23:23:45 +0000326}
327
tom748a1312005-04-02 15:53:01 +0000328/* Redirect a function at from_addr to a function at to_addr */
njn16eeb4e2005-06-16 03:56:58 +0000329__attribute__((unused)) // It is used, but not on all platforms...
330static void add_redirect_addr_to_addr(Addr from_addr, Addr to_addr)
tom748a1312005-04-02 15:53:01 +0000331{
njn136b83b2005-06-19 05:14:03 +0000332 add_redirect_X_to_addr(NULL, NULL, from_addr, to_addr);
tom748a1312005-04-02 15:53:01 +0000333}
334
sewardjcbdddcf2005-03-10 23:23:45 +0000335CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
336 const FuncWrapper *wrapper)
337{
338 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
339
340 if (0)
341 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
342 from_lib, from_sym, wrapper->before, wrapper->after);
343
344 redir->type = R_WRAPPER;
345
njn136b83b2005-06-19 05:14:03 +0000346 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
347 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
sewardjcbdddcf2005-03-10 23:23:45 +0000348 redir->from_addr = 0;
njn136b83b2005-06-19 05:14:03 +0000349 redir->to_addr = 0;
350 redir->wrapper = wrapper;
sewardjcbdddcf2005-03-10 23:23:45 +0000351
352 /* Check against all existing segments to see if this redirection
353 can be resolved immediately */
njnbf7ca332005-05-14 17:11:06 +0000354 if (!resolve_redir_allsegs(redir)) {
sewardjcbdddcf2005-03-10 23:23:45 +0000355 /* nope, add to list */
356 redir->next = unresolved_redir;
357 unresolved_redir = redir;
358 }
359
360 return redir;
361}
362
sewardjcbdddcf2005-03-10 23:23:45 +0000363/* If address 'a' is being redirected, return the redirected-to
364 address. */
365Addr VG_(code_redirect)(Addr a)
366{
367 CodeRedirect* r;
368
sewardjcbdddcf2005-03-10 23:23:45 +0000369 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
370 if (r == NULL)
371 return a;
372
373 vg_assert(r->to_addr != 0);
374
375 return r->to_addr;
376}
377
378void VG_(setup_code_redirect_table) ( void )
379{
njnd1af0032005-05-29 17:01:48 +0000380#if defined(VGP_x86_linux)
381 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
382 routine, to the routine in our trampoline page so that the
383 special sysinfo unwind hack in m_stacktrace.c will kick in. */
njn16eeb4e2005-06-16 03:56:58 +0000384 add_redirect_sym_to_addr("soname:ld-linux.so.2", "_dl_sysinfo_int80",
385 VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
njnd1af0032005-05-29 17:01:48 +0000386#elif defined(VGP_amd64_linux)
387 /* Redirect vsyscalls to local versions */
njn16eeb4e2005-06-16 03:56:58 +0000388 add_redirect_addr_to_addr(0xFFFFFFFFFF600000ULL,
389 VG_(client_trampoline_code)+VG_(tramp_gettimeofday_offset));
390 add_redirect_addr_to_addr(0xFFFFFFFFFF600400ULL,
391 VG_(client_trampoline_code)+VG_(tramp_time_offset));
njnd1af0032005-05-29 17:01:48 +0000392#else
393# error Unknown platform
394#endif
395}
sewardjcbdddcf2005-03-10 23:23:45 +0000396
njn16eeb4e2005-06-16 03:56:58 +0000397/* Z-decode a symbol into library:func form, eg
398
399 _vgi_libcZdsoZd6__ZdlPv --> libc.so.6:_ZdlPv
400
401 Uses the Z-encoding scheme described in pub_core_redir.h.
402 Returns True if demangle OK, False otherwise.
403*/
404static Bool Z_decode(const Char* symbol, Char* result, Int nbytes)
405{
406# define EMIT(ch) \
407 do { \
408 if (j >= nbytes) \
409 result[j-1] = 0; \
410 else \
411 result[j++] = ch; \
412 } while (0)
413
414 Bool error = False;
415 Int i, j = 0;
416 Int len = VG_(strlen)(symbol);
417 if (0) VG_(printf)("idm: %s\n", symbol);
418
419 i = VG_REPLACE_FUNCTION_PREFIX_LEN;
420
421 /* Chew though the Z-encoded soname part. */
422 while (True) {
423
424 if (i >= len)
425 break;
426
427 if (symbol[i] == '_')
428 /* We found the underscore following the Z-encoded soname.
429 Just copy the rest literally. */
430 break;
431
432 if (symbol[i] != 'Z') {
433 EMIT(symbol[i]);
434 i++;
435 continue;
436 }
437
438 /* We've got a Z-escape. Act accordingly. */
439 i++;
440 if (i >= len) {
441 /* Hmm, Z right at the end. Something's wrong. */
442 error = True;
443 EMIT('Z');
444 break;
445 }
446 switch (symbol[i]) {
447 case 'a': EMIT('*'); break;
448 case 'p': EMIT('+'); break;
449 case 'c': EMIT(':'); break;
450 case 'd': EMIT('.'); break;
451 case 'u': EMIT('_'); break;
452 case 'h': EMIT('-'); break;
453 case 's': EMIT(' '); break;
454 case 'Z': EMIT('Z'); break;
455 default: error = True; EMIT('Z'); EMIT(symbol[i]); break;
456 }
457 i++;
458 }
459
460 if (error || i >= len || symbol[i] != '_') {
461 /* Something's wrong. Give up. */
462 VG_(message)(Vg_UserMsg, "intercept: error demangling: %s", symbol);
463 EMIT(0);
464 return False;
465 }
466
467 /* Copy the rest of the string verbatim. */
468 i++;
469 EMIT(':');
470 while (True) {
471 if (i >= len)
472 break;
473 EMIT(symbol[i]);
474 i++;
475 }
476
477 EMIT(0);
478 if (0) VG_(printf)("%s\n", result);
479 return True;
480
481# undef EMIT
482}
483
484// Nb: this can change the string pointed to by 'symbol'.
485static void handle_replacement_function( Char* symbol, Addr addr )
486{
487 Bool ok;
488 Int len = VG_(strlen)(symbol) + 1 - VG_REPLACE_FUNCTION_PREFIX_LEN;
489 Char *lib = VG_(arena_malloc)(VG_AR_SYMTAB, len+8);
490 Char *func;
491
492 // Put "soname:" at the start of lib
493 VG_(strcpy)(lib, "soname:");
494
495 ok = Z_decode(symbol, lib+7, len);
496 if (ok) {
497 // lib is "soname:<libname>:<fnname>". Split the string at the 2nd ':'.
498 func = lib + VG_(strlen)(lib)-1;
499 while(*func != ':') func--;
500 *func = '\0';
501 func++; // Move past the '\0'
502
503 // Now lib is "soname:<libname>" and func is "<fnname>".
504 if (0) VG_(printf)("lib A%sZ, func A%sZ\n", lib, func);
505 add_redirect_sym_to_addr(lib, func, addr);
506
507 // Overwrite the given Z-encoded name with just the fnname.
508 VG_(strcpy)(symbol, func);
509 }
510
511 VG_(arena_free)(VG_AR_SYMTAB, lib);
512}
513
njnbc6d84d2005-06-19 18:58:03 +0000514static Addr __libc_freeres_wrapper = 0;
515
516Addr VG_(get_libc_freeres_wrapper)(void)
517{
518 return __libc_freeres_wrapper;
519}
520
njn16eeb4e2005-06-16 03:56:58 +0000521// This is specifically for stringifying VG_(x) function names. We
522// need to do two macroexpansions to get the VG_ macro expanded before
523// stringifying.
524#define _STR(x) #x
525#define STR(x) _STR(x)
526
527static void handle_load_notifier( Char* symbol, Addr addr )
528{
529 if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
njnbc6d84d2005-06-19 18:58:03 +0000530 __libc_freeres_wrapper = addr;
njn16eeb4e2005-06-16 03:56:58 +0000531// else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
532// VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
533 else
534 vg_assert2(0, "unrecognised load notification function: %s", symbol);
535}
536
537static Bool is_replacement_function(Char* s)
538{
539 return (0 == VG_(strncmp)(s,
540 VG_REPLACE_FUNCTION_PREFIX,
541 VG_REPLACE_FUNCTION_PREFIX_LEN));
542}
543
544static Bool is_load_notifier(Char* s)
545{
546 return (0 == VG_(strncmp)(s,
547 VG_NOTIFY_ON_LOAD_PREFIX,
548 VG_NOTIFY_ON_LOAD_PREFIX_LEN));
549}
550
551// Call this for each symbol loaded. It determines if we need to do
552// anything special with it. It can modify 'symbol' in-place.
553void VG_(maybe_redir_or_notify) ( Char* symbol, Addr addr )
554{
555 if (is_replacement_function(symbol))
556 handle_replacement_function(symbol, addr);
557 else
558 if (is_load_notifier(symbol))
559 handle_load_notifier(symbol, addr);
560}
561
562
sewardjcbdddcf2005-03-10 23:23:45 +0000563//:: /*------------------------------------------------------------*/
564//:: /*--- General function wrapping. ---*/
565//:: /*------------------------------------------------------------*/
566//::
567//:: /*
568//:: TODO:
569//:: - hook into the symtab machinery
570//:: - client-side wrappers?
571//:: - better interfaces for before() functions to get to arguments
572//:: - handle munmap of code (dlclose())
573//:: - handle thread exit
574//:: - handle longjmp
575//:: */
576//:: struct callkey {
577//:: ThreadId tid; /* calling thread */
578//:: Addr esp; /* address of args on stack */
579//:: Addr eip; /* return address */
580//:: };
581//::
582//:: struct call_instance {
583//:: struct callkey key;
584//::
585//:: const FuncWrapper *wrapper;
586//:: void *nonce;
587//:: };
588//::
589//:: static inline Addr addrcmp(Addr a, Addr b)
590//:: {
591//:: if (a < b)
592//:: return -1;
593//:: else if (a > b)
594//:: return 1;
595//:: else
596//:: return 0;
597//:: }
598//::
599//:: static inline Int cmp(UInt a, UInt b)
600//:: {
601//:: if (a < b)
602//:: return -1;
603//:: else if (a > b)
604//:: return 1;
605//:: else
606//:: return 0;
607//:: }
608//::
609//:: static Int keycmp(const void *pa, const void *pb)
610//:: {
611//:: const struct callkey *a = (const struct callkey *)pa;
612//:: const struct callkey *b = (const struct callkey *)pb;
613//:: Int ret;
614//::
615//:: if ((ret = cmp(a->tid, b->tid)))
616//:: return ret;
617//::
618//:: if ((ret = addrcmp(a->esp, b->esp)))
619//:: return ret;
620//::
621//:: return addrcmp(a->eip, b->eip);
622//:: }
623//::
624//:: /* List of wrapped call invocations which are currently active */
njnbe91aae2005-03-27 01:42:41 +0000625//:: static SkipList wrapped_frames = VG_SKIPLIST_INIT(struct call_instance, key, keycmp,
sewardjcbdddcf2005-03-10 23:23:45 +0000626//:: NULL, VG_AR_SYMTAB);
627//::
628//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
629//:: {
630//:: struct callkey key = { tid, argsp, retaddr };
631//::
632//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
633//:: }
634//::
635//:: static void wrapper_return(Addr retaddr);
636//::
637//:: /* Called from generated code via helper */
638//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
639//:: {
njndb9b7732005-03-26 00:32:29 +0000640//:: Addr retaddr = VGA_RETADDR(tst->arch);
641//:: Addr argp = (Addr)&VGA_FUNC_ARG(tst->arch, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000642//:: void *nonce = NULL;
643//:: Bool mf = VG_(my_fault);
644//:: VG_(my_fault) = True;
645//::
646//:: if (wrapper->before) {
njndb9b7732005-03-26 00:32:29 +0000647//:: va_list args = VGA_VA_LIST(tst->arch);
sewardjcbdddcf2005-03-10 23:23:45 +0000648//:: nonce = (*wrapper->before)(args);
649//:: }
650//::
651//:: if (wrapper->after) {
652//:: /* If there's an after function, make sure it gets called */
653//:: struct call_instance *call;
654//::
655//:: call = find_call(retaddr, argp, tst->tid);
656//::
657//:: if (call != NULL) {
658//:: /* Found a stale outstanding call; clean it up and recycle
659//:: the structure */
660//:: if (call->wrapper->after)
661//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
662//:: } else {
663//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
664//::
665//:: call->key.tid = tst->tid;
666//:: call->key.esp = argp;
667//:: call->key.eip = retaddr;
668//::
669//:: VG_(SkipList_Insert)(&wrapped_frames, call);
670//::
671//:: wrapper_return(retaddr);
672//:: }
673//::
674//:: call->wrapper = wrapper;
675//:: call->nonce = nonce;
676//:: } else
677//:: vg_assert(nonce == NULL);
678//::
679//:: VG_(my_fault) = mf;
680//:: }
681//::
682//:: /* Called from generated code via helper */
683//:: void VG_(wrap_after)(ThreadState *tst)
684//:: {
njn35172bc2005-03-26 00:04:03 +0000685//:: Addr EIP = VGA_INSTR_PTR(tst->arch); /* instruction after call */
686//:: Addr ESP = VGA_STACK_PTR(tst->arch); /* pointer to args */
687//:: Word ret = VGA_RETVAL(tst->arch); /* return value */
sewardjcbdddcf2005-03-10 23:23:45 +0000688//:: struct call_instance *call;
689//:: Bool mf = VG_(my_fault);
690//::
691//:: VG_(my_fault) = True;
692//:: call = find_call(EIP, ESP, tst->tid);
693//::
694//:: if (0)
695//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
696//::
697//:: if (call != NULL) {
698//:: if (call->wrapper->after)
699//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
700//::
701//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
702//:: VG_(SkipNode_Free)(&wrapped_frames, call);
703//:: }
704//:: VG_(my_fault) = mf;
705//:: }
706//::
707//::
708//:: struct wrapped_function {
709//:: Addr eip; /* eip of function entrypoint */
710//:: const FuncWrapper *wrapper;
711//:: };
712//::
713//:: struct wrapper_return {
714//:: Addr eip; /* return address */
715//:: };
716//::
717//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
njnbe91aae2005-03-27 01:42:41 +0000718//:: static SkipList wrapped_functions = VG_SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000719//:: NULL, VG_AR_SYMTAB);
720//::
721//:: /* A set of EIPs which are return addresses for wrapped functions */
njnbe91aae2005-03-27 01:42:41 +0000722//:: static SkipList wrapper_returns = VG_SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
sewardjcbdddcf2005-03-10 23:23:45 +0000723//:: NULL, VG_AR_SYMTAB);
724//::
725//:: /* Wrap function starting at eip */
726//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
727//:: {
728//:: struct wrapped_function *func;
729//::
730//:: if (0)
731//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
732//::
733//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
734//::
735//:: if (func == NULL) {
736//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
737//:: VG_(invalidate_translations)(eip, 1, True);
738//::
739//:: func->eip = eip;
740//:: VG_(SkipList_Insert)(&wrapped_functions, func);
741//:: }
742//::
743//:: func->wrapper = wrapper;
744//:: }
745//::
746//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
747//:: {
748//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
749//::
750//:: if (func)
751//:: return func->wrapper;
752//:: return NULL;
753//:: }
754//::
755//:: Bool VG_(is_wrapper_return)(Addr eip)
756//:: {
757//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
758//::
759//:: return ret != NULL;
760//:: }
761//::
762//:: /* Mark eip as being the return address of a wrapper, so that the
763//:: codegen will generate the appropriate call. */
764//:: void wrapper_return(Addr eip)
765//:: {
766//:: struct wrapper_return *ret;
767//::
768//:: if (VG_(is_wrapper_return)(eip))
769//:: return;
770//::
771//:: VG_(invalidate_translations)(eip, 1, True);
772//::
773//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
774//:: ret->eip = eip;
775//::
776//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
777//:: }