mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-18 08:56:05 +00:00
branch hereby becomes inactive. This currently breaks everything except x86; fixes for amd64/ppc32 to follow. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5520
810 lines
30 KiB
C
810 lines
30 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- The JITter proper: register allocation & code improvement ---*/
|
|
/*--- m_translate.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2005 Julian Seward
|
|
jseward@acm.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include "pub_core_basics.h"
|
|
#include "pub_core_aspacemgr.h"
|
|
|
|
#include "pub_core_machine.h" // VG_(fnptr_to_fnentry)
|
|
// VG_(get_SP)
|
|
// VG_(machine_get_VexArchInfo)
|
|
#include "pub_core_libcbase.h"
|
|
#include "pub_core_libcassert.h"
|
|
#include "pub_core_libcprint.h"
|
|
#include "pub_core_options.h"
|
|
|
|
#include "pub_core_debuginfo.h" // VG_(get_fnname_w_offset)
|
|
#include "pub_core_redir.h" // VG_(redir_do_lookup)
|
|
|
|
#include "pub_core_signals.h" // VG_(synth_fault_{perms,mapping}
|
|
#include "pub_core_stacks.h" // VG_(unknown_SP_update)()
|
|
#include "pub_core_tooliface.h" // VG_(tdict)
|
|
|
|
#include "pub_core_translate.h"
|
|
#include "pub_core_transtab.h"
|
|
#include "pub_core_dispatch.h" // VG_(run_innerloop__dispatch_{un}profiled)
|
|
// VG_(run_a_noredir_translation__return_point)
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Stats ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
static UInt n_SP_updates_fast = 0;
|
|
static UInt n_SP_updates_generic_known = 0;
|
|
static UInt n_SP_updates_generic_unknown = 0;
|
|
|
|
void VG_(print_translation_stats) ( void )
|
|
{
|
|
Char buf[6];
|
|
UInt n_SP_updates = n_SP_updates_fast + n_SP_updates_generic_known
|
|
+ n_SP_updates_generic_unknown;
|
|
VG_(percentify)(n_SP_updates_fast, n_SP_updates, 1, 6, buf);
|
|
VG_(message)(Vg_DebugMsg,
|
|
"translate: fast SP updates identified: %,u (%s)",
|
|
n_SP_updates_fast, buf );
|
|
|
|
VG_(percentify)(n_SP_updates_generic_known, n_SP_updates, 1, 6, buf);
|
|
VG_(message)(Vg_DebugMsg,
|
|
"translate: generic_known SP updates identified: %,u (%s)",
|
|
n_SP_updates_generic_known, buf );
|
|
|
|
VG_(percentify)(n_SP_updates_generic_unknown, n_SP_updates, 1, 6, buf);
|
|
VG_(message)(Vg_DebugMsg,
|
|
"translate: generic_unknown SP updates identified: %,u (%s)",
|
|
n_SP_updates_generic_unknown, buf );
|
|
}
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- %SP-update pass ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
static Bool need_to_handle_SP_assignment(void)
|
|
{
|
|
return ( VG_(tdict).track_new_mem_stack_4 ||
|
|
VG_(tdict).track_die_mem_stack_4 ||
|
|
VG_(tdict).track_new_mem_stack_8 ||
|
|
VG_(tdict).track_die_mem_stack_8 ||
|
|
VG_(tdict).track_new_mem_stack_12 ||
|
|
VG_(tdict).track_die_mem_stack_12 ||
|
|
VG_(tdict).track_new_mem_stack_16 ||
|
|
VG_(tdict).track_die_mem_stack_16 ||
|
|
VG_(tdict).track_new_mem_stack_32 ||
|
|
VG_(tdict).track_die_mem_stack_32 ||
|
|
VG_(tdict).track_new_mem_stack ||
|
|
VG_(tdict).track_die_mem_stack );
|
|
}
|
|
|
|
// - The SP aliases are held in an array which is used as a circular buffer.
|
|
// This misses very few constant updates of SP (ie. < 0.1%) while using a
|
|
// small, constant structure that will also never fill up and cause
|
|
// execution to abort.
|
|
// - Unused slots have a .temp value of 'IRTemp_INVALID'.
|
|
// - 'next_SP_alias_slot' is the index where the next alias will be stored.
|
|
// - If the buffer fills, we circle around and start over-writing
|
|
// non-IRTemp_INVALID values. This is rare, and the overwriting of a
|
|
// value that would have subsequently be used is even rarer.
|
|
// - Every slot below next_SP_alias_slot holds a non-IRTemp_INVALID value.
|
|
// The rest either all won't (if we haven't yet circled around) or all
|
|
// will (if we have circled around).
|
|
|
|
typedef
|
|
struct {
|
|
IRTemp temp;
|
|
Long delta;
|
|
}
|
|
SP_Alias;
|
|
|
|
// With 32 slots the buffer fills very rarely -- eg. once in a run of GCC.
|
|
// And I've tested with smaller values and the wrap-around case works ok.
|
|
#define N_ALIASES 32
|
|
static SP_Alias SP_aliases[N_ALIASES];
|
|
static Int next_SP_alias_slot = 0;
|
|
|
|
static void clear_SP_aliases(void)
|
|
{
|
|
Int i;
|
|
for (i = 0; i < N_ALIASES; i++) {
|
|
SP_aliases[i].temp = IRTemp_INVALID;
|
|
SP_aliases[i].delta = 0;
|
|
}
|
|
next_SP_alias_slot = 0;
|
|
}
|
|
|
|
static void add_SP_alias(IRTemp temp, Long delta)
|
|
{
|
|
vg_assert(temp != IRTemp_INVALID);
|
|
SP_aliases[ next_SP_alias_slot ].temp = temp;
|
|
SP_aliases[ next_SP_alias_slot ].delta = delta;
|
|
next_SP_alias_slot++;
|
|
if (N_ALIASES == next_SP_alias_slot) next_SP_alias_slot = 0;
|
|
}
|
|
|
|
static Bool get_SP_delta(IRTemp temp, ULong* delta)
|
|
{
|
|
Int i; // i must be signed!
|
|
vg_assert(IRTemp_INVALID != temp);
|
|
// Search backwards between current buffer position and the start.
|
|
for (i = next_SP_alias_slot-1; i >= 0; i--) {
|
|
if (temp == SP_aliases[i].temp) {
|
|
*delta = SP_aliases[i].delta;
|
|
return True;
|
|
}
|
|
}
|
|
// Search backwards between the end and the current buffer position.
|
|
for (i = N_ALIASES-1; i >= next_SP_alias_slot; i--) {
|
|
if (temp == SP_aliases[i].temp) {
|
|
*delta = SP_aliases[i].delta;
|
|
return True;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static void update_SP_aliases(Long delta)
|
|
{
|
|
Int i;
|
|
for (i = 0; i < N_ALIASES; i++) {
|
|
if (SP_aliases[i].temp == IRTemp_INVALID) {
|
|
return;
|
|
}
|
|
SP_aliases[i].delta += delta;
|
|
}
|
|
}
|
|
|
|
|
|
/* For tools that want to know about SP changes, this pass adds
|
|
in the appropriate hooks. We have to do it after the tool's
|
|
instrumentation, so the tool doesn't have to worry about the C calls
|
|
it adds in, and we must do it before register allocation because
|
|
spilled temps make it much harder to work out the SP deltas.
|
|
This it is done with Vex's "second instrumentation" pass.
|
|
|
|
Basically, we look for GET(SP)/PUT(SP) pairs and track constant
|
|
increments/decrements of SP between them. (This requires tracking one or
|
|
more "aliases", which are not exact aliases but instead are tempregs
|
|
whose value is equal to the SP's plus or minus a known constant.)
|
|
If all the changes to SP leading up to a PUT(SP) are by known, small
|
|
constants, we can do a specific call to eg. new_mem_stack_4, otherwise
|
|
we fall back to the case that handles an unknown SP change.
|
|
*/
|
|
static
|
|
IRBB* vg_SP_update_pass ( IRBB* bb_in,
|
|
VexGuestLayout* layout,
|
|
Addr64 orig_addr_noredir,
|
|
VexGuestExtents* vge,
|
|
IRType gWordTy,
|
|
IRType hWordTy )
|
|
{
|
|
Int i, j, minoff_ST, maxoff_ST, sizeof_SP, offset_SP;
|
|
IRDirty *dcall, *d;
|
|
IRStmt* st;
|
|
IRExpr* e;
|
|
IRArray* descr;
|
|
IRType typeof_SP;
|
|
Long delta, con;
|
|
|
|
/* Set up BB */
|
|
IRBB* bb = emptyIRBB();
|
|
bb->tyenv = dopyIRTypeEnv(bb_in->tyenv);
|
|
bb->next = dopyIRExpr(bb_in->next);
|
|
bb->jumpkind = bb_in->jumpkind;
|
|
|
|
delta = 0;
|
|
|
|
sizeof_SP = layout->sizeof_SP;
|
|
offset_SP = layout->offset_SP;
|
|
typeof_SP = sizeof_SP==4 ? Ity_I32 : Ity_I64;
|
|
vg_assert(sizeof_SP == 4 || sizeof_SP == 8);
|
|
|
|
# define IS_ADD(op) (sizeof_SP==4 ? ((op)==Iop_Add32) : ((op)==Iop_Add64))
|
|
# define IS_SUB(op) (sizeof_SP==4 ? ((op)==Iop_Sub32) : ((op)==Iop_Sub64))
|
|
|
|
# define IS_ADD_OR_SUB(op) (IS_ADD(op) || IS_SUB(op))
|
|
|
|
# define GET_CONST(con) \
|
|
(sizeof_SP==4 ? (Long)(Int)(con->Ico.U32) \
|
|
: (Long)(con->Ico.U64))
|
|
|
|
// XXX: convert this to a function
|
|
# define DO(kind, syze, tmpp) \
|
|
do { \
|
|
if (!VG_(tdict).track_##kind##_mem_stack_##syze) \
|
|
goto generic; \
|
|
\
|
|
/* I don't know if it's really necessary to say that the */ \
|
|
/* call reads the stack pointer. But anyway, we do. */ \
|
|
dcall = unsafeIRDirty_0_N( \
|
|
1/*regparms*/, \
|
|
"track_" #kind "_mem_stack_" #syze, \
|
|
VG_(fnptr_to_fnentry)( \
|
|
VG_(tdict).track_##kind##_mem_stack_##syze ), \
|
|
mkIRExprVec_1(IRExpr_Tmp(tmpp)) \
|
|
); \
|
|
dcall->nFxState = 1; \
|
|
dcall->fxState[0].fx = Ifx_Read; \
|
|
dcall->fxState[0].offset = layout->offset_SP; \
|
|
dcall->fxState[0].size = layout->sizeof_SP; \
|
|
\
|
|
addStmtToIRBB( bb, IRStmt_Dirty(dcall) ); \
|
|
\
|
|
update_SP_aliases(-delta); \
|
|
\
|
|
n_SP_updates_fast++; \
|
|
\
|
|
} while (0)
|
|
|
|
clear_SP_aliases();
|
|
|
|
for (i = 0; i < bb_in->stmts_used; i++) {
|
|
|
|
st = bb_in->stmts[i];
|
|
|
|
/* t = Get(sp): curr = t, delta = 0 */
|
|
if (st->tag != Ist_Tmp) goto case2;
|
|
e = st->Ist.Tmp.data;
|
|
if (e->tag != Iex_Get) goto case2;
|
|
if (e->Iex.Get.offset != offset_SP) goto case2;
|
|
if (e->Iex.Get.ty != typeof_SP) goto case2;
|
|
add_SP_alias(st->Ist.Tmp.tmp, 0);
|
|
addStmtToIRBB( bb, st );
|
|
continue;
|
|
|
|
case2:
|
|
/* t' = curr +/- const: curr = t', delta +=/-= const */
|
|
if (st->tag != Ist_Tmp) goto case3;
|
|
e = st->Ist.Tmp.data;
|
|
if (e->tag != Iex_Binop) goto case3;
|
|
if (e->Iex.Binop.arg1->tag != Iex_Tmp) goto case3;
|
|
if (!get_SP_delta(e->Iex.Binop.arg1->Iex.Tmp.tmp, &delta)) goto case3;
|
|
if (e->Iex.Binop.arg2->tag != Iex_Const) goto case3;
|
|
if (!IS_ADD_OR_SUB(e->Iex.Binop.op)) goto case3;
|
|
con = GET_CONST(e->Iex.Binop.arg2->Iex.Const.con);
|
|
if (IS_ADD(e->Iex.Binop.op)) {
|
|
add_SP_alias(st->Ist.Tmp.tmp, delta + con);
|
|
} else {
|
|
add_SP_alias(st->Ist.Tmp.tmp, delta - con);
|
|
}
|
|
addStmtToIRBB( bb, st );
|
|
continue;
|
|
|
|
case3:
|
|
/* t' = curr: curr = t' */
|
|
if (st->tag != Ist_Tmp) goto case4;
|
|
e = st->Ist.Tmp.data;
|
|
if (e->tag != Iex_Tmp) goto case4;
|
|
if (!get_SP_delta(e->Iex.Tmp.tmp, &delta)) goto case4;
|
|
add_SP_alias(st->Ist.Tmp.tmp, delta);
|
|
addStmtToIRBB( bb, st );
|
|
continue;
|
|
|
|
case4:
|
|
/* Put(sp) = curr */
|
|
if (st->tag != Ist_Put) goto case5;
|
|
if (st->Ist.Put.offset != offset_SP) goto case5;
|
|
if (st->Ist.Put.data->tag != Iex_Tmp) goto case5;
|
|
if (get_SP_delta(st->Ist.Put.data->Iex.Tmp.tmp, &delta)) {
|
|
IRTemp tttmp = st->Ist.Put.data->Iex.Tmp.tmp;
|
|
switch (delta) {
|
|
case 0: addStmtToIRBB(bb,st); continue;
|
|
case 4: DO(die, 4, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case -4: DO(new, 4, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case 8: DO(die, 8, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case -8: DO(new, 8, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case 12: DO(die, 12, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case -12: DO(new, 12, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case 16: DO(die, 16, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case -16: DO(new, 16, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case 32: DO(die, 32, tttmp); addStmtToIRBB(bb,st); continue;
|
|
case -32: DO(new, 32, tttmp); addStmtToIRBB(bb,st); continue;
|
|
default:
|
|
n_SP_updates_generic_known++;
|
|
goto generic;
|
|
}
|
|
} else {
|
|
IRTemp old_SP;
|
|
n_SP_updates_generic_unknown++;
|
|
|
|
// Nb: if all is well, this generic case will typically be
|
|
// called something like every 1000th SP update. If it's more than
|
|
// that, the above code may be missing some cases.
|
|
generic:
|
|
/* Pass both the old and new SP values to this helper. */
|
|
old_SP = newIRTemp(bb->tyenv, typeof_SP);
|
|
addStmtToIRBB(
|
|
bb,
|
|
IRStmt_Tmp( old_SP, IRExpr_Get(offset_SP, typeof_SP) )
|
|
);
|
|
|
|
dcall = unsafeIRDirty_0_N(
|
|
2/*regparms*/,
|
|
"VG_(unknown_SP_update)",
|
|
VG_(fnptr_to_fnentry)( &VG_(unknown_SP_update) ),
|
|
mkIRExprVec_2( IRExpr_Tmp(old_SP), st->Ist.Put.data )
|
|
);
|
|
addStmtToIRBB( bb, IRStmt_Dirty(dcall) );
|
|
|
|
addStmtToIRBB( bb, st );
|
|
|
|
clear_SP_aliases();
|
|
add_SP_alias(st->Ist.Put.data->Iex.Tmp.tmp, 0);
|
|
continue;
|
|
}
|
|
|
|
case5:
|
|
/* PutI or Dirty call which overlaps SP: complain. We can't
|
|
deal with SP changing in weird ways (well, we can, but not at
|
|
this time of night). */
|
|
if (st->tag == Ist_PutI) {
|
|
descr = st->Ist.PutI.descr;
|
|
minoff_ST = descr->base;
|
|
maxoff_ST = descr->base + descr->nElems * sizeofIRType(descr->elemTy) - 1;
|
|
if (!(offset_SP > maxoff_ST || (offset_SP + sizeof_SP - 1) < minoff_ST))
|
|
goto complain;
|
|
}
|
|
if (st->tag == Ist_Dirty) {
|
|
d = st->Ist.Dirty.details;
|
|
for (j = 0; j < d->nFxState; j++) {
|
|
minoff_ST = d->fxState[j].offset;
|
|
maxoff_ST = d->fxState[j].offset + d->fxState[j].size - 1;
|
|
if (d->fxState[j].fx == Ifx_Read || d->fxState[j].fx == Ifx_None)
|
|
continue;
|
|
if (!(offset_SP > maxoff_ST || (offset_SP + sizeof_SP - 1) < minoff_ST))
|
|
goto complain;
|
|
}
|
|
}
|
|
|
|
/* well, not interesting. Just copy and keep going. */
|
|
addStmtToIRBB( bb, st );
|
|
|
|
} /* for (i = 0; i < bb_in->stmts_used; i++) */
|
|
|
|
return bb;
|
|
|
|
complain:
|
|
VG_(core_panic)("vg_SP_update_pass: PutI or Dirty which overlaps SP");
|
|
|
|
}
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Main entry point for the JITter. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Extra comments re self-checking translations and self-modifying
|
|
code. (JRS 14 Oct 05).
|
|
|
|
There are 3 modes:
|
|
(1) no checking: all code assumed to be not self-modifying
|
|
(2) partial: known-problematic situations get a self-check
|
|
(3) full checking: all translations get a self-check
|
|
|
|
As currently implemented, the default is (2). (3) is always safe,
|
|
but very slow. (1) works mostly, but fails for gcc nested-function
|
|
code which uses trampolines on the stack; this situation is
|
|
detected and handled by (2).
|
|
|
|
----------
|
|
|
|
A more robust and transparent solution, which is not currently
|
|
implemented, is a variant of (2): if a translation is made from an
|
|
area which aspacem says does not have 'w' permission, then it can
|
|
be non-self-checking. Otherwise, it needs a self-check.
|
|
|
|
This is complicated by Vex's basic-block chasing. If a self-check
|
|
is requested, then Vex will not chase over basic block boundaries
|
|
(it's too complex). However there is still a problem if it chases
|
|
from a non-'w' area into a 'w' area.
|
|
|
|
I think the right thing to do is:
|
|
|
|
- if a translation request starts in a 'w' area, ask for a
|
|
self-checking translation, and do not allow any chasing (make
|
|
chase_into_ok return False). Note that the latter is redundant
|
|
in the sense that Vex won't chase anyway in this situation.
|
|
|
|
- if a translation request starts in a non-'w' area, do not ask for
|
|
a self-checking translation. However, do not allow chasing (as
|
|
determined by chase_into_ok) to go into a 'w' area.
|
|
|
|
The result of this is that all code inside 'w' areas is self
|
|
checking.
|
|
|
|
To complete the trick, there is a caveat: we must watch the
|
|
client's mprotect calls. If pages are changed from non-'w' to 'w'
|
|
then we should throw away all translations which intersect the
|
|
affected area, so as to force them to be redone with self-checks.
|
|
|
|
----------
|
|
|
|
The above outlines the conditions under which bb chasing is allowed
|
|
from a self-modifying-code point of view. There are other
|
|
situations pertaining to function redirection in which it is
|
|
necessary to disallow chasing, but those fall outside the scope of
|
|
this comment.
|
|
*/
|
|
|
|
/* Vex dumps the final code in here. Then we can copy it off
|
|
wherever we like. */
|
|
#define N_TMPBUF 20000
|
|
static UChar tmpbuf[N_TMPBUF];
|
|
|
|
/* Function pointers we must supply to LibVEX in order that it
|
|
can bomb out and emit messages under Valgrind's control. */
|
|
__attribute__ ((noreturn))
|
|
static
|
|
void failure_exit ( void )
|
|
{
|
|
LibVEX_ShowAllocStats();
|
|
VG_(core_panic)("LibVEX called failure_exit().");
|
|
}
|
|
|
|
static
|
|
void log_bytes ( HChar* bytes, Int nbytes )
|
|
{
|
|
Int i;
|
|
for (i = 0; i < nbytes-3; i += 4)
|
|
VG_(printf)("%c%c%c%c", bytes[i], bytes[i+1], bytes[i+2], bytes[i+3]);
|
|
for (; i < nbytes; i++)
|
|
VG_(printf)("%c", bytes[i]);
|
|
}
|
|
|
|
/* Translate the basic block beginning at orig_addr, and add it to
|
|
the translation cache & translation table. Unless 'debugging' is true,
|
|
in which case the call is being done for debugging purposes, so
|
|
(a) throw away the translation once it is made, and (b) produce a
|
|
load of debugging output.
|
|
|
|
'tid' is the identity of the thread needing this block.
|
|
*/
|
|
|
|
/* Look for reasons to disallow making translations from the given
|
|
segment. */
|
|
|
|
static Bool translations_allowable_from_seg ( NSegment* seg )
|
|
{
|
|
# if defined(VGA_x86)
|
|
Bool allowR = True;
|
|
# else
|
|
Bool allowR = False;
|
|
# endif
|
|
|
|
return seg != NULL
|
|
&& (seg->kind == SkAnonC || seg->kind == SkFileC)
|
|
&& (seg->hasX || (seg->hasR && allowR));
|
|
}
|
|
|
|
|
|
|
|
/* This stops Vex from chasing into function entry points that we wish
|
|
to redirect. Chasing across them obviously defeats the redirect
|
|
mechanism, with bad effects for Memcheck, Addrcheck, and possibly
|
|
others.
|
|
|
|
Also, we must stop Vex chasing into blocks for which we might want
|
|
to self checking.
|
|
|
|
This fn needs to know also the tid of the requesting thread, but
|
|
it can't be passed in as a parameter since this fn is passed to
|
|
Vex and that has no notion of tids. So we clumsily pass it as
|
|
a global, chase_into_ok__CLOSURE_tid.
|
|
*/
|
|
static ThreadId chase_into_ok__CLOSURE_tid;
|
|
static Bool chase_into_ok ( Addr64 addr64 )
|
|
{
|
|
NSegment* seg;
|
|
|
|
/* Work through a list of possibilities why we might not want to
|
|
allow a chase. */
|
|
Addr addr = (Addr)addr64;
|
|
|
|
/* All chasing disallowed if all bbs require self-checks. */
|
|
if (VG_(clo_smc_check) == Vg_SmcAll)
|
|
goto dontchase;
|
|
|
|
/* Check the segment permissions. */
|
|
seg = VG_(am_find_nsegment)(addr);
|
|
if (!translations_allowable_from_seg(seg))
|
|
goto dontchase;
|
|
|
|
/* AAABBBCCC: if default self-checks are in force, reject if we
|
|
would choose to have a self-check for the dest. Note, this must
|
|
match the logic at XXXYYYZZZ below. */
|
|
if (VG_(clo_smc_check) == Vg_SmcStack) {
|
|
ThreadId tid = chase_into_ok__CLOSURE_tid;
|
|
if (seg
|
|
&& (seg->kind == SkAnonC || seg->kind == SkFileC)
|
|
&& seg->start <= VG_(get_SP)(tid)
|
|
&& VG_(get_SP)(tid)+sizeof(Word)-1 <= seg->end)
|
|
goto dontchase;
|
|
}
|
|
|
|
/* Destination is redirected? */
|
|
if (addr != VG_(redir_do_lookup)(addr, NULL))
|
|
goto dontchase;
|
|
|
|
/* well, ok then. go on and chase. */
|
|
return True;
|
|
|
|
vg_assert(0);
|
|
/*NOTREACHED*/
|
|
|
|
dontchase:
|
|
if (0) VG_(printf)("not chasing into 0x%x\n", addr);
|
|
return False;
|
|
}
|
|
|
|
|
|
/* Note: see comments at top of m_redir.c for the Big Picture on how
|
|
redirections are managed. */
|
|
|
|
Bool VG_(translate) ( ThreadId tid,
|
|
Addr64 orig_addr,
|
|
Bool debugging_translation,
|
|
Int debugging_verbosity,
|
|
ULong bbs_done,
|
|
Bool allow_redirection )
|
|
{
|
|
Addr64 redir, orig_addr_noredir = orig_addr;
|
|
Int tmpbuf_used, verbosity, i;
|
|
Bool notrace_until_done, do_self_check;
|
|
Bool did_redirect, isWrap;
|
|
UInt notrace_until_limit = 0;
|
|
NSegment* seg;
|
|
VexArch vex_arch;
|
|
VexArchInfo vex_archinfo;
|
|
VexGuestExtents vge;
|
|
VexTranslateArgs vta;
|
|
VexTranslateResult tres;
|
|
|
|
/* Make sure Vex is initialised right. */
|
|
|
|
static Bool vex_init_done = False;
|
|
|
|
if (!vex_init_done) {
|
|
LibVEX_Init ( &failure_exit, &log_bytes,
|
|
1, /* debug_paranoia */
|
|
False, /* valgrind support */
|
|
&VG_(clo_vex_control) );
|
|
vex_init_done = True;
|
|
}
|
|
|
|
/* Look in the code redirect table to see if we should
|
|
translate an alternative address for orig_addr. */
|
|
isWrap = False;
|
|
if (allow_redirection) {
|
|
redir = VG_(redir_do_lookup)(orig_addr, &isWrap);
|
|
did_redirect = redir != orig_addr;
|
|
} else {
|
|
redir = orig_addr;
|
|
did_redirect = False;
|
|
}
|
|
|
|
if (did_redirect == False) vg_assert(isWrap == False);
|
|
|
|
if (redir != orig_addr
|
|
&& (VG_(clo_verbosity) >= 2 || VG_(clo_trace_redir))) {
|
|
Bool ok;
|
|
Char name1[64] = "";
|
|
Char name2[64] = "";
|
|
name1[0] = name2[0] = 0;
|
|
ok = VG_(get_fnname_w_offset)(orig_addr, name1, 64);
|
|
if (!ok) VG_(strcpy)(name1, "???");
|
|
ok = VG_(get_fnname_w_offset)(redir, name2, 64);
|
|
if (!ok) VG_(strcpy)(name2, "???");
|
|
VG_(message)(Vg_DebugMsg,
|
|
"REDIR: 0x%llx (%s) redirected to 0x%llx (%s)",
|
|
orig_addr, name1,
|
|
redir, name2 );
|
|
}
|
|
orig_addr = redir;
|
|
|
|
/* If codegen tracing, don't start tracing until
|
|
notrace_until_limit blocks have gone by. This avoids printing
|
|
huge amounts of useless junk when all we want to see is the last
|
|
few blocks translated prior to a failure. Set
|
|
notrace_until_limit to be the number of translations to be made
|
|
before --trace-codegen= style printing takes effect. */
|
|
notrace_until_done
|
|
= VG_(get_bbs_translated)() >= notrace_until_limit;
|
|
|
|
if (!debugging_translation)
|
|
VG_TRACK( pre_mem_read, Vg_CoreTranslate,
|
|
tid, "(translator)", orig_addr, 1 );
|
|
|
|
/* If doing any code printing, print a basic block start marker */
|
|
if (VG_(clo_trace_flags) || debugging_translation) {
|
|
Char fnname[64] = "";
|
|
VG_(get_fnname_w_offset)(orig_addr, fnname, 64);
|
|
VG_(printf)(
|
|
"==== BB %d %s(0x%llx) BBs exec'd %lld ====\n",
|
|
VG_(get_bbs_translated)(), fnname, orig_addr,
|
|
bbs_done);
|
|
}
|
|
|
|
/* Are we allowed to translate here? */
|
|
|
|
seg = VG_(am_find_nsegment)(orig_addr);
|
|
|
|
if (!translations_allowable_from_seg(seg)) {
|
|
/* U R busted, sonny. Place your hands on your head and step
|
|
away from the orig_addr. */
|
|
/* Code address is bad - deliver a signal instead */
|
|
if (seg != NULL) {
|
|
/* There's some kind of segment at the requested place, but we
|
|
aren't allowed to execute code here. */
|
|
VG_(synth_fault_perms)(tid, orig_addr);
|
|
} else {
|
|
/* There is no segment at all; we are attempting to execute in
|
|
the middle of nowhere. */
|
|
VG_(synth_fault_mapping)(tid, orig_addr);
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/* Do we want a self-checking translation? */
|
|
do_self_check = False;
|
|
switch (VG_(clo_smc_check)) {
|
|
case Vg_SmcNone: do_self_check = False; break;
|
|
case Vg_SmcAll: do_self_check = True; break;
|
|
case Vg_SmcStack:
|
|
/* XXXYYYZZZ: must match the logic at AAABBBCCC above */
|
|
do_self_check
|
|
/* = seg ? toBool(seg->flags & SF_GROWDOWN) : False; */
|
|
= seg
|
|
? (seg->start <= VG_(get_SP)(tid)
|
|
&& VG_(get_SP)(tid)+sizeof(Word)-1 <= seg->end)
|
|
: False;
|
|
break;
|
|
default:
|
|
vg_assert2(0, "unknown VG_(clo_smc_check) value");
|
|
}
|
|
|
|
/* True if a debug trans., or if bit N set in VG_(clo_trace_codegen). */
|
|
verbosity = 0;
|
|
if (debugging_translation) {
|
|
verbosity = debugging_verbosity;
|
|
}
|
|
else
|
|
if ( (VG_(clo_trace_flags) > 0
|
|
&& VG_(get_bbs_translated)() >= VG_(clo_trace_notbelow) )) {
|
|
verbosity = VG_(clo_trace_flags);
|
|
}
|
|
|
|
/* ------ Actually do the translation. ------ */
|
|
tl_assert2(VG_(tdict).tool_instrument,
|
|
"you forgot to set VgToolInterface function 'tool_instrument'");
|
|
|
|
/* Get the CPU info established at startup. */
|
|
VG_(machine_get_VexArchInfo)( &vex_arch, &vex_archinfo );
|
|
|
|
/* Set up closure arg for "chase_into_ok" */
|
|
chase_into_ok__CLOSURE_tid = tid;
|
|
|
|
/* Set up args for LibVEX_Translate. */
|
|
vta.arch_guest = vex_arch;
|
|
vta.archinfo_guest = vex_archinfo;
|
|
vta.arch_host = vex_arch;
|
|
vta.archinfo_host = vex_archinfo;
|
|
vta.guest_bytes = (UChar*)ULong_to_Ptr(orig_addr);
|
|
vta.guest_bytes_addr = (Addr64)orig_addr;
|
|
vta.guest_bytes_addr_noredir = (Addr64)orig_addr_noredir;
|
|
vta.chase_into_ok = chase_into_ok;
|
|
vta.guest_extents = &vge;
|
|
vta.host_bytes = tmpbuf;
|
|
vta.host_bytes_size = N_TMPBUF;
|
|
vta.host_bytes_used = &tmpbuf_used;
|
|
vta.instrument1 = VG_(tdict).tool_instrument;
|
|
vta.instrument2 = need_to_handle_SP_assignment()
|
|
? vg_SP_update_pass
|
|
: NULL;
|
|
vta.do_self_check = do_self_check;
|
|
/* If this translation started at a redirected address, then we
|
|
need to ask the JIT to generate code to put the non-redirected
|
|
guest address into guest_NRADDR. */
|
|
vta.do_set_NRADDR = isWrap;
|
|
vta.traceflags = verbosity;
|
|
|
|
/* Set up the dispatch-return info. For archs without a link
|
|
register, vex generates a jump back to the specified dispatch
|
|
address. Else, it just generates a branch-to-LR. */
|
|
# if defined(VGA_x86) || defined(VGA_amd64)
|
|
vta.dispatch
|
|
= (!allow_redirection)
|
|
? /* It's a no-redir translation. Will be run with the nonstandard
|
|
dispatcher VG_(run_a_noredir_translation)
|
|
and so needs a nonstandard return point. */
|
|
(void*) &VG_(run_a_noredir_translation__return_point)
|
|
|
|
: /* normal translation. Uses VG_(run_innerloop). Return
|
|
point depends on whether we're profiling bbs or not. */
|
|
VG_(clo_profile_flags) > 0
|
|
? (void*) &VG_(run_innerloop__dispatch_profiled)
|
|
: (void*) &VG_(run_innerloop__dispatch_unprofiled);
|
|
# elif defined(VGA_ppc32) || defined(VGA_ppc64)
|
|
vta.dispatch = NULL;
|
|
# else
|
|
# error "Unknown arch"
|
|
# endif
|
|
|
|
/* Sheesh. Finally, actually _do_ the translation! */
|
|
tres = LibVEX_Translate ( &vta );
|
|
|
|
vg_assert(tres == VexTransOK);
|
|
vg_assert(tmpbuf_used <= N_TMPBUF);
|
|
vg_assert(tmpbuf_used > 0);
|
|
|
|
/* Tell aspacem of all segments that have had translations taken
|
|
from them. Optimisation: don't re-look up vge.base[0] since seg
|
|
should already point to it. */
|
|
|
|
vg_assert( vge.base[0] == (Addr64)orig_addr );
|
|
if (seg->kind == SkFileC || seg->kind == SkAnonC)
|
|
seg->hasT = True; /* has cached code */
|
|
|
|
for (i = 1; i < vge.n_used; i++) {
|
|
seg = VG_(am_find_nsegment)( vge.base[i] );
|
|
if (seg->kind == SkFileC || seg->kind == SkAnonC)
|
|
seg->hasT = True; /* has cached code */
|
|
}
|
|
|
|
/* Copy data at trans_addr into the translation cache. */
|
|
vg_assert(tmpbuf_used > 0 && tmpbuf_used < 65536);
|
|
|
|
// If debugging, don't do anything with the translated block; we
|
|
// only did this for the debugging output produced along the way.
|
|
if (!debugging_translation) {
|
|
|
|
if (allow_redirection) {
|
|
// Put it into the normal TT/TC structures. This is the
|
|
// normal case.
|
|
|
|
// Note that we use orig_addr_noredir, not orig_addr, which
|
|
// might have been changed by the redirection
|
|
VG_(add_to_transtab)( &vge,
|
|
orig_addr_noredir,
|
|
(Addr)(&tmpbuf[0]),
|
|
tmpbuf_used,
|
|
do_self_check );
|
|
} else {
|
|
VG_(add_to_unredir_transtab)( &vge,
|
|
orig_addr_noredir,
|
|
(Addr)(&tmpbuf[0]),
|
|
tmpbuf_used,
|
|
do_self_check );
|
|
}
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|