blob: b1a7bf6e7716e9b97f267db9a64481463964e2ae [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
10 Copyright (C) 2000-2004 Julian Seward
11 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"
33#include "vg_symtab2.h"
34
35/*------------------------------------------------------------*/
36/*--- General purpose redirection. ---*/
37/*------------------------------------------------------------*/
38
39/*
40 wraps and redirections, indexed by from_addr
41
42 Redirection and wrapping are two distinct mechanisms which Valgrind
43 can use to change the client's control flow.
44
45 Redirection intercepts a call to a client function, and re-points it
46 to a new piece of code (presumably functionally equivalent). The
47 original code is never run.
48
49 Wrapping does call the client's original code, but calls "before"
50 and "after" functions which can inspect (and perhaps modify) the
51 function's arguments and return value.
52 */
53struct _CodeRedirect {
54 enum redir_type {
55 R_REDIRECT, /* plain redirection */
56 R_WRAPPER, /* wrap with valgrind-internal code */
57 R_CLIENT_WRAPPER, /* wrap with client-side code */
58 } type;
59
60 const Char *from_lib; /* library qualifier pattern */
61 const Char *from_sym; /* symbol */
62 Addr from_addr; /* old addr */
63
64 /* used for redirection */
65 const Char *to_lib; /* library qualifier pattern */
66 const Char *to_sym; /* symbol */
67 Addr to_addr; /* new addr */
68
69 /* used for wrapping */
70 const FuncWrapper *wrapper;
71
72 CodeRedirect *next; /* next pointer on unresolved list */
73};
74
75static Char *straddr(void *p)
76{
77 static Char buf[16];
78
79 VG_(sprintf)(buf, "%p", *(Addr *)p);
80
81 return buf;
82}
83
84static SkipList sk_resolved_redir = SKIPLIST_INIT(CodeRedirect, from_addr,
85 VG_(cmp_Addr), straddr, VG_AR_SYMTAB);
86static CodeRedirect *unresolved_redir = NULL;
87
88static Bool match_lib(const Char *pattern, const SegInfo *si)
89{
90 /* pattern == NULL matches everything, otherwise use globbing
91
92 If the pattern starts with:
93 file:, then match filename
94 soname:, then match soname
95 something else, match filename
96 */
97 const Char *name = si->filename;
98
99 if (pattern == NULL)
100 return True;
101
102 if (VG_(strncmp)(pattern, "file:", 5) == 0) {
103 pattern += 5;
104 name = si->filename;
105 }
106 if (VG_(strncmp)(pattern, "soname:", 7) == 0) {
107 pattern += 7;
108 name = si->soname;
109 }
110
111 if (name == NULL)
112 return False;
113
114 return VG_(string_match)(pattern, name);
115}
116
117static inline Bool from_resolved(const CodeRedirect *redir)
118{
119 return redir->from_addr != 0;
120}
121
122static inline Bool to_resolved(const CodeRedirect *redir)
123{
124 if (redir->type == R_REDIRECT)
125 return redir->to_addr != 0;
126 vg_assert(redir->wrapper != NULL);
127 return True;
128}
129
130Bool VG_(is_resolved)(const CodeRedirect *redir)
131{
132 return from_resolved(redir) && to_resolved(redir);
133}
134
135/* Resolve a redir using si if possible, and add it to the resolved
136 list */
137Bool VG_(resolve_redir)(CodeRedirect *redir, const SegInfo *si)
138{
139 Bool resolved;
140
141 vg_assert(si != NULL);
142 vg_assert(si->seg != NULL);
143
144 /* no redirection from Valgrind segments */
145 if (si->seg->flags & SF_VALGRIND)
146 return False;
147
148 resolved = VG_(is_resolved)(redir);
149
150 if (0 && VG_(clo_trace_redir))
151 VG_(printf)(" consider FROM binding %s:%s -> %s:%s in %s(%s)\n",
152 redir->from_lib, redir->from_sym,
153 redir->to_lib, redir->to_sym,
154 si->filename, si->soname);
155
156 vg_assert(!resolved);
157
158 if (!from_resolved(redir)) {
159 vg_assert(redir->from_sym != NULL);
160
161 if (match_lib(redir->from_lib, si)) {
162 redir->from_addr = VG_(reverse_search_one_symtab)(si, redir->from_sym);
163 if (VG_(clo_trace_redir) && redir->from_addr != 0)
164 VG_(printf)(" bind FROM: %p = %s:%s\n",
165 redir->from_addr,redir->from_lib, redir->from_sym );
166 }
167 }
168
169 if (!to_resolved(redir)) {
170 vg_assert(redir->type == R_REDIRECT);
171 vg_assert(redir->to_sym != NULL);
172
173 if (match_lib(redir->to_lib, si)) {
174 redir->to_addr = VG_(reverse_search_one_symtab)(si, redir->to_sym);
175 if (VG_(clo_trace_redir) && redir->to_addr != 0)
176 VG_(printf)(" bind TO: %p = %s:%s\n",
177 redir->to_addr,redir->to_lib, redir->to_sym );
178
179 }
180 }
181
182 resolved = from_resolved(redir) && to_resolved(redir);
183
184 if (0 && VG_(clo_trace_redir))
185 VG_(printf)("resolve_redir: %s:%s from=%p %s:%s to=%p\n",
186 redir->from_lib, redir->from_sym, redir->from_addr,
187 redir->to_lib, redir->to_sym, redir->to_addr);
188
189 if (resolved) {
190 switch(redir->type) {
191 case R_REDIRECT:
192 if (VG_(clo_trace_redir)) {
193 VG_(message)(Vg_DebugMsg, " redir resolved (%s:%s=%p -> ",
194 redir->from_lib, redir->from_sym, redir->from_addr);
195 VG_(message)(Vg_DebugMsg, " %s:%s=%p)",
196 redir->to_lib, redir->to_sym, redir->to_addr);
197 }
198
199 if (VG_(search_transtab)(NULL, (Addr64)redir->from_addr, False)) {
200 /* For some given (from, to) redir, the "from" function got
201 called before the .so containing "to" became available. We
202 know this because there is already a translation for the
203 entry point of the original "from". So the redirect will
204 never actually take effect unless that translation is
205 discarded.
206
207 Note, we only really need to discard the first bb of the
208 old entry point, and so we avoid the problem of having to
209 figure out how big that bb was -- since it is at least 1
210 byte of original code, we can just pass 1 as the original
211 size to invalidate_translations() and it will indeed get
212 rid of the translation.
213
214 Note, this is potentially expensive -- discarding
215 translations causes complete unchaining.
216 */
217 if (VG_(clo_verbosity) > 2 && VG_(clo_trace_redir)) {
218 VG_(message)(Vg_UserMsg,
219 "Discarding translation due to redirect of already called function" );
220 VG_(message)(Vg_UserMsg,
221 " %s (%p -> %p)",
222 redir->from_sym, redir->from_addr, redir->to_addr );
223 }
224 VG_(discard_translations)((Addr64)redir->from_addr, 1);
225 }
226
227 {
228 CodeRedirect *r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &redir->from_addr);
229
230 if (r == NULL)
231 VG_(SkipList_Insert)(&sk_resolved_redir, redir);
232 else {
233 /* XXX leak redir */
234 if (VG_(clo_trace_redir))
235 VG_(message)(Vg_DebugMsg, " redir %s:%s:%p->%s:%s:%p duplicated\n",
236 redir->from_lib, redir->from_sym, redir->from_addr,
237 redir->to_lib, redir->to_sym, redir->to_addr);
238 }
239 }
240 break;
241
242 case R_WRAPPER:
243 if (VG_(clo_trace_redir)) {
244 VG_(message)(Vg_DebugMsg, " wrapper resolved (%s:%s=%p -> wrapper)",
245 redir->from_lib, redir->from_sym, redir->from_addr);
246 }
247
248 /* XXX redir leaked */
249 //VG_(wrap_function)(redir->from_addr, redir->wrapper);
250 break;
251
252 case R_CLIENT_WRAPPER:
253 VG_(core_panic)("not implemented");
254 break;
255 }
256 }
257
258 return resolved;
259}
260
261/* 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
279 if (VG_(resolve_redir)(redir, si)) {
280 *prevp = next;
281 redir->next = NULL;
282 } else
283 prevp = &redir->next;
284 }
285}
286
287/* Redirect a lib/symbol reference to a function at lib/symbol */
288static void add_redirect_sym(const Char *from_lib, const Char *from_sym,
289 const Char *to_lib, const Char *to_sym)
290{
291 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
292
293 redir->type = R_REDIRECT;
294
295 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
296 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
297 redir->from_addr = 0;
298
299 redir->to_lib = VG_(arena_strdup)(VG_AR_SYMTAB, to_lib);
300 redir->to_sym = VG_(arena_strdup)(VG_AR_SYMTAB, to_sym);
301 redir->to_addr = 0;
302
303 if (VG_(clo_verbosity) >= 2 && VG_(clo_trace_redir))
304 VG_(message)(Vg_UserMsg,
305 "REDIRECT %s(%s) to %s(%s)",
306 from_lib, from_sym, to_lib, to_sym);
307
308 /* Check against all existing segments to see if this redirection
309 can be resolved immediately */
310 if (!VG_(resolve_redir_allsegs)(redir)) {
311 /* nope, add to list */
312 redir->next = unresolved_redir;
313 unresolved_redir = redir;
314 }
315}
316
317/* Redirect a lib/symbol reference to a function at addr */
318void VG_(add_redirect_addr)(const Char *from_lib, const Char *from_sym,
319 Addr to_addr)
320{
321 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
322
323 redir->type = R_REDIRECT;
324
325 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
326 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
327 redir->from_addr = 0;
328
329 redir->to_lib = NULL;
330 redir->to_sym = NULL;
331 redir->to_addr = to_addr;
332
333 /* Check against all existing segments to see if this redirection
334 can be resolved immediately */
335 if (!VG_(resolve_redir_allsegs)(redir)) {
336 /* nope, add to list */
337 redir->next = unresolved_redir;
338 unresolved_redir = redir;
339 }
340}
341
342CodeRedirect *VG_(add_wrapper)(const Char *from_lib, const Char *from_sym,
343 const FuncWrapper *wrapper)
344{
345 CodeRedirect *redir = VG_(SkipNode_Alloc)(&sk_resolved_redir);
346
347 if (0)
348 VG_(printf)("adding wrapper for %s:%s -> (%p,%p)\n",
349 from_lib, from_sym, wrapper->before, wrapper->after);
350
351 redir->type = R_WRAPPER;
352
353 redir->from_lib = VG_(arena_strdup)(VG_AR_SYMTAB, from_lib);
354 redir->from_sym = VG_(arena_strdup)(VG_AR_SYMTAB, from_sym);
355 redir->from_addr = 0;
356
357 redir->to_lib = NULL;
358 redir->to_sym = NULL;
359 redir->to_addr = 0;
360
361 redir->wrapper = wrapper;
362
363 /* Check against all existing segments to see if this redirection
364 can be resolved immediately */
365 if (!VG_(resolve_redir_allsegs)(redir)) {
366 /* nope, add to list */
367 redir->next = unresolved_redir;
368 unresolved_redir = redir;
369 }
370
371 return redir;
372}
373
374/* HACK! This should be done properly (see ~/NOTES.txt) */
375#ifdef __amd64__
376/* Rerouted entry points for __NR_vgettimeofday and __NR_vtime.
377 96 == __NR_gettimeofday
378 201 == __NR_time
379*/
380asm(
381"amd64_linux_rerouted__vgettimeofday:\n"
382" movq $96, %rax\n"
383" syscall\n"
384" ret\n"
385"amd64_linux_rerouted__vtime:\n"
386" movq $201, %rax\n"
387" syscall\n"
388" ret\n"
389);
390#endif
391
392/* If address 'a' is being redirected, return the redirected-to
393 address. */
394Addr VG_(code_redirect)(Addr a)
395{
396 CodeRedirect* r;
397
398#ifdef __amd64__
399 /* HACK. Reroute the amd64-linux vsyscalls. This should be moved
400 out of here into an amd64-linux specific initialisation routine.
401 */
402 extern void amd64_linux_rerouted__vgettimeofday;
403 extern void amd64_linux_rerouted__vtime;
404 if (a == 0xFFFFFFFFFF600000ULL)
405 return (Addr)&amd64_linux_rerouted__vgettimeofday;
406 if (a == 0xFFFFFFFFFF600400ULL)
407 return (Addr)&amd64_linux_rerouted__vtime;
408#endif
409
410 r = VG_(SkipList_Find_Exact)(&sk_resolved_redir, &a);
411 if (r == NULL)
412 return a;
413
414 vg_assert(r->to_addr != 0);
415
416 return r->to_addr;
417}
418
419void VG_(setup_code_redirect_table) ( void )
420{
421 /* Redirect _dl_sysinfo_int80, which is glibc's default system call
422 routine, to the routine in our trampoline page so that the
423 special sysinfo unwind hack in vg_execontext.c will kick in.
424 */
425 VG_(add_redirect_addr)("soname:ld-linux.so.2", "_dl_sysinfo_int80",
426 VG_(client_trampoline_code)+VG_(tramp_syscall_offset));
427
428 /* Overenthusiastic use of PLT bypassing by the glibc people also
429 means we need to patch the following functions to our own
430 implementations of said, in mac_replace_strmem.c.
431 */
432 add_redirect_sym("soname:libc.so.6", "stpcpy",
433 "*vgpreload_memcheck.so*", "stpcpy");
434
435 add_redirect_sym("soname:libc.so.6", "strlen",
436 "*vgpreload_memcheck.so*", "strlen");
437
438 add_redirect_sym("soname:libc.so.6", "strnlen",
439 "*vgpreload_memcheck.so*", "strnlen");
440
441 add_redirect_sym("soname:ld-linux.so.2", "stpcpy",
442 "*vgpreload_memcheck.so*", "stpcpy");
443
444 add_redirect_sym("soname:libc.so.6", "strchr",
445 "*vgpreload_memcheck.so*", "strchr");
446 add_redirect_sym("soname:ld-linux.so.2", "strchr",
447 "*vgpreload_memcheck.so*", "strchr");
448
449 add_redirect_sym("soname:libc.so.6", "strchrnul",
450 "*vgpreload_memcheck.so*", "glibc232_strchrnul");
451
452 add_redirect_sym("soname:libc.so.6", "rawmemchr",
453 "*vgpreload_memcheck.so*", "glibc232_rawmemchr");
454}
455
456
457//:: /*------------------------------------------------------------*/
458//:: /*--- General function wrapping. ---*/
459//:: /*------------------------------------------------------------*/
460//::
461//:: /*
462//:: TODO:
463//:: - hook into the symtab machinery
464//:: - client-side wrappers?
465//:: - better interfaces for before() functions to get to arguments
466//:: - handle munmap of code (dlclose())
467//:: - handle thread exit
468//:: - handle longjmp
469//:: */
470//:: struct callkey {
471//:: ThreadId tid; /* calling thread */
472//:: Addr esp; /* address of args on stack */
473//:: Addr eip; /* return address */
474//:: };
475//::
476//:: struct call_instance {
477//:: struct callkey key;
478//::
479//:: const FuncWrapper *wrapper;
480//:: void *nonce;
481//:: };
482//::
483//:: static inline Addr addrcmp(Addr a, Addr b)
484//:: {
485//:: if (a < b)
486//:: return -1;
487//:: else if (a > b)
488//:: return 1;
489//:: else
490//:: return 0;
491//:: }
492//::
493//:: static inline Int cmp(UInt a, UInt b)
494//:: {
495//:: if (a < b)
496//:: return -1;
497//:: else if (a > b)
498//:: return 1;
499//:: else
500//:: return 0;
501//:: }
502//::
503//:: static Int keycmp(const void *pa, const void *pb)
504//:: {
505//:: const struct callkey *a = (const struct callkey *)pa;
506//:: const struct callkey *b = (const struct callkey *)pb;
507//:: Int ret;
508//::
509//:: if ((ret = cmp(a->tid, b->tid)))
510//:: return ret;
511//::
512//:: if ((ret = addrcmp(a->esp, b->esp)))
513//:: return ret;
514//::
515//:: return addrcmp(a->eip, b->eip);
516//:: }
517//::
518//:: /* List of wrapped call invocations which are currently active */
519//:: static SkipList wrapped_frames = SKIPLIST_INIT(struct call_instance, key, keycmp,
520//:: NULL, VG_AR_SYMTAB);
521//::
522//:: static struct call_instance *find_call(Addr retaddr, Addr argsp, ThreadId tid)
523//:: {
524//:: struct callkey key = { tid, argsp, retaddr };
525//::
526//:: return VG_(SkipList_Find_Exact)(&wrapped_frames, &key);
527//:: }
528//::
529//:: static void wrapper_return(Addr retaddr);
530//::
531//:: /* Called from generated code via helper */
532//:: void VG_(wrap_before)(ThreadState *tst, const FuncWrapper *wrapper)
533//:: {
534//:: Addr retaddr = ARCH_RETADDR(tst->arch);
535//:: Addr argp = (Addr)&ARCH_FUNC_ARG(tst->arch, 0);
536//:: void *nonce = NULL;
537//:: Bool mf = VG_(my_fault);
538//:: VG_(my_fault) = True;
539//::
540//:: if (wrapper->before) {
541//:: va_list args = ARCH_VA_LIST(tst->arch);
542//:: nonce = (*wrapper->before)(args);
543//:: }
544//::
545//:: if (wrapper->after) {
546//:: /* If there's an after function, make sure it gets called */
547//:: struct call_instance *call;
548//::
549//:: call = find_call(retaddr, argp, tst->tid);
550//::
551//:: if (call != NULL) {
552//:: /* Found a stale outstanding call; clean it up and recycle
553//:: the structure */
554//:: if (call->wrapper->after)
555//:: (*call->wrapper->after)(call->nonce, RT_LONGJMP, 0);
556//:: } else {
557//:: call = VG_(SkipNode_Alloc)(&wrapped_frames);
558//::
559//:: call->key.tid = tst->tid;
560//:: call->key.esp = argp;
561//:: call->key.eip = retaddr;
562//::
563//:: VG_(SkipList_Insert)(&wrapped_frames, call);
564//::
565//:: wrapper_return(retaddr);
566//:: }
567//::
568//:: call->wrapper = wrapper;
569//:: call->nonce = nonce;
570//:: } else
571//:: vg_assert(nonce == NULL);
572//::
573//:: VG_(my_fault) = mf;
574//:: }
575//::
576//:: /* Called from generated code via helper */
577//:: void VG_(wrap_after)(ThreadState *tst)
578//:: {
579//:: Addr EIP = ARCH_INSTR_PTR(tst->arch); /* instruction after call */
580//:: Addr ESP = ARCH_STACK_PTR(tst->arch); /* pointer to args */
581//:: Word ret = ARCH_RETVAL(tst->arch); /* return value */
582//:: struct call_instance *call;
583//:: Bool mf = VG_(my_fault);
584//::
585//:: VG_(my_fault) = True;
586//:: call = find_call(EIP, ESP, tst->tid);
587//::
588//:: if (0)
589//:: VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
590//::
591//:: if (call != NULL) {
592//:: if (call->wrapper->after)
593//:: (*call->wrapper->after)(call->nonce, RT_RETURN, ret);
594//::
595//:: VG_(SkipList_Remove)(&wrapped_frames, &call->key);
596//:: VG_(SkipNode_Free)(&wrapped_frames, call);
597//:: }
598//:: VG_(my_fault) = mf;
599//:: }
600//::
601//::
602//:: struct wrapped_function {
603//:: Addr eip; /* eip of function entrypoint */
604//:: const FuncWrapper *wrapper;
605//:: };
606//::
607//:: struct wrapper_return {
608//:: Addr eip; /* return address */
609//:: };
610//::
611//:: /* A mapping from eip of wrapped function entrypoints to actual wrappers */
612//:: static SkipList wrapped_functions = SKIPLIST_INIT(struct wrapped_function, eip, VG_(cmp_Addr),
613//:: NULL, VG_AR_SYMTAB);
614//::
615//:: /* A set of EIPs which are return addresses for wrapped functions */
616//:: static SkipList wrapper_returns = SKIPLIST_INIT(struct wrapper_return, eip, VG_(cmp_Addr),
617//:: NULL, VG_AR_SYMTAB);
618//::
619//:: /* Wrap function starting at eip */
620//:: void VG_(wrap_function)(Addr eip, const FuncWrapper *wrapper)
621//:: {
622//:: struct wrapped_function *func;
623//::
624//:: if (0)
625//:: VG_(printf)("wrapping %p with (%p,%p)\n", eip, wrapper->before, wrapper->after);
626//::
627//:: func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
628//::
629//:: if (func == NULL) {
630//:: func = VG_(SkipNode_Alloc)(&wrapped_functions);
631//:: VG_(invalidate_translations)(eip, 1, True);
632//::
633//:: func->eip = eip;
634//:: VG_(SkipList_Insert)(&wrapped_functions, func);
635//:: }
636//::
637//:: func->wrapper = wrapper;
638//:: }
639//::
640//:: const FuncWrapper *VG_(is_wrapped)(Addr eip)
641//:: {
642//:: struct wrapped_function *func = VG_(SkipList_Find_Exact)(&wrapped_functions, &eip);
643//::
644//:: if (func)
645//:: return func->wrapper;
646//:: return NULL;
647//:: }
648//::
649//:: Bool VG_(is_wrapper_return)(Addr eip)
650//:: {
651//:: struct wrapper_return *ret = VG_(SkipList_Find_Exact)(&wrapper_returns, &eip);
652//::
653//:: return ret != NULL;
654//:: }
655//::
656//:: /* Mark eip as being the return address of a wrapper, so that the
657//:: codegen will generate the appropriate call. */
658//:: void wrapper_return(Addr eip)
659//:: {
660//:: struct wrapper_return *ret;
661//::
662//:: if (VG_(is_wrapper_return)(eip))
663//:: return;
664//::
665//:: VG_(invalidate_translations)(eip, 1, True);
666//::
667//:: ret = VG_(SkipNode_Alloc)(&wrapper_returns);
668//:: ret->eip = eip;
669//::
670//:: VG_(SkipList_Insert)(&wrapper_returns, ret);
671//:: }