blob: 9556e0884ee9a73f5a05285216b2af37c89520b5 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2010-2014 Google, Inc. All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2000-2001 Hewlett-Packard Company */
/*
* translate.c - fault translation
*/
#include "../globals.h"
#include "../link.h"
#include "../fragment.h"
#include "arch.h"
#include "instr.h"
#include "instr_create.h"
#include "decode.h"
#include "decode_fast.h"
#include "../fcache.h"
#include "proc.h"
#include "instrument.h"
#include <string.h> /* for memcpy */
#if defined(DEBUG) || defined(INTERNAL)
# include "disassemble.h"
#endif
/***************************************************************************
* FAULT TRANSLATION
*
* Current status:
* After PR 214962, PR 267260, PR 263407, PR 268372, and PR 267764/i398, we
* properly translate indirect branch mangling and client modifications.
* FIXME: However, we still do not properly translate for:
* - PR 303413: properly translate native_exec and windows sysenter mangling faults
* - PR 208037/i#399: flushed fragments (need -safe_translate_flushed)
* - PR 213251: hot patch fragments (b/c nudge can change whether patched =>
* should store translations for all hot patch fragments)
* - PR 372021: restore eflags if within window of ibl or trace-cmp eflags-are-dead
* - i#751: fault translation has not been tested for x86_to_x64
*/
typedef struct _translate_walk_t {
/* The context we're translating */
priv_mcontext_t *mc;
/* The code cache span of the containing fragment */
byte *start_cache;
byte *end_cache;
/* PR 263407: Track registers spilled since the last cti, for
* restoring indirect branch and rip-rel spills
*/
bool reg_spilled[REG_SPILL_NUM];
bool reg_tls[REG_SPILL_NUM];
/* PR 267260: Track our own mangle-inserted pushes and pops, for
* restoring state in the middle of our indirect branch mangling.
* This is the adjustment in the forward direction.
*/
int xsp_adjust;
/* Track whether we've seen an instr for which we can't relocate */
bool unsupported_mangle;
/* Are we currently in a mangle region */
bool in_mangle_region;
/* What is the translation target of the current mangle region */
app_pc translation;
} translate_walk_t;
static void
translate_walk_init(translate_walk_t *walk, byte *start_cache, byte *end_cache,
priv_mcontext_t *mc)
{
memset(walk, 0, sizeof(*walk));
walk->mc = mc;
walk->start_cache = start_cache;
walk->end_cache = end_cache;
}
#ifdef UNIX
static inline bool
instr_is_inline_syscall_jmp(dcontext_t *dcontext, instr_t *inst)
{
if (!instr_is_our_mangling(inst))
return false;
/* Not bothering to check whether there's a nearby syscall instr:
* any label-targeting short jump should be fine to ignore.
*/
# ifdef X86
return (instr_get_opcode(inst) == OP_jmp_short &&
opnd_is_instr(instr_get_target(inst)));
# elif defined(ARM)
/* FIXME i#1551: NYI on ARM */
ASSERT_NOT_IMPLEMENTED(false);
return false;
# endif /* X86/ARM */
}
static inline bool
instr_is_seg_ref_load(dcontext_t *dcontext, instr_t *inst)
{
# ifdef X86
/* This won't fault but we don't want "unsupported mangle instr" message. */
if (!instr_is_our_mangling(inst))
return false;
/* Look for the load of either segment base */
if (instr_is_tls_restore(inst, REG_NULL/*don't care*/,
os_tls_offset(os_get_app_seg_base_offset(SEG_FS))) ||
instr_is_tls_restore(inst, REG_NULL/*don't care*/,
os_tls_offset(os_get_app_seg_base_offset(SEG_GS))))
return true;
/* Look for the lea */
if (instr_get_opcode(inst) == OP_lea) {
opnd_t mem = instr_get_src(inst, 0);
if (opnd_get_scale(mem) == 1 &&
opnd_get_index(mem) == opnd_get_reg(instr_get_dst(inst, 0)))
return true;
}
# endif /* X86 */
return false;
}
#endif /* UNIX */
static void
translate_walk_track(dcontext_t *tdcontext, instr_t *inst, translate_walk_t *walk)
{
#ifdef X86
reg_id_t reg, r;
bool spill, spill_tls;
/* Two mangle regions can be adjacent: distinguish by translation field */
if (walk->in_mangle_region &&
(!instr_is_our_mangling(inst) ||
instr_get_translation(inst) != walk->translation)) {
/* We assume our manglings are local and contiguous: once out of a
* mangling region, we're good to go again */
walk->in_mangle_region = false;
walk->unsupported_mangle = false;
walk->xsp_adjust = 0;
for (r = 0; r < REG_SPILL_NUM; r++) {
/* we should have seen a restore for every spill, unless at
* fragment-ending jump to ibl, which shouldn't come here
*/
ASSERT(!walk->reg_spilled[r]);
walk->reg_spilled[r] = false; /* be paranoid */
}
}
if (instr_is_our_mangling(inst)) {
if (!walk->in_mangle_region) {
walk->in_mangle_region = true;
walk->translation = instr_get_translation(inst);
} else
ASSERT(walk->translation == instr_get_translation(inst));
/* PR 302951: we recognize a clean call by its NULL translation.
* We do not track any stack or spills: we assume we will only
* fault on an argument that references app memory, in which case
* we restore to the priv_mcontext_t on the stack.
*/
if (walk->translation == NULL) {
DOLOG(4, LOG_INTERP, {
loginst(get_thread_private_dcontext(), 4, inst,
"\tin clean call arg region");
});
return;
}
/* PR 263407: track register values that we've spilled. We assume
* that spilling to non-canonical slots only happens in ibl or
* context switch code: never in app code mangling. Since a client
* might add ctis (non-linear code) and its own spills, we track
* register spills only within our own mangling code (for
* post-mangling traces (PR 306163) we require that the client
* handle all translation if it modifies our mangling regions:
* we'll provide a query routine instr_is_DR_mangling()): our
* spills are all local anyway, except
* for selfmod, which we hardcode rep-string support for (non-linear code
* isn't handled by general reg scan). Our trace cmp is the only
* instance (besides selfmod) where we have a cti in our mangling,
* but it doesn't affect our linearity assumption. We assume we
* have no entry points in between a spill and a restore. Our
* mangling goes in last (for regular bbs and traces; see
* comment above for post-mangling traces), and so for local
* spills like rip-rel and ind branches this is fine.
*/
if (instr_is_cti(inst) &&
/* Do not reset for a trace-cmp jecxz or jmp (32-bit) or
* jne (64-bit), since ecx needs to be restored (won't
* fault, but for thread relocation)
*/
((instr_get_opcode(inst) != OP_jecxz &&
instr_get_opcode(inst) != OP_jmp &&
/* x64 trace cmp uses jne for exit */
instr_get_opcode(inst) != OP_jne) ||
/* Rather than check for trace, just ignore exit jumps, which
* won't mess up linearity here. For stored translation info we
* don't have meta-flags so we can't use instr_is_exit_cti(). */
((instr_get_opcode(inst) == OP_jmp ||
/* x64 trace cmp uses jne for exit */
instr_get_opcode(inst) == OP_jne) &&
(!opnd_is_pc(instr_get_target(inst)) ||
(opnd_get_pc(instr_get_target(inst)) >= walk->start_cache &&
opnd_get_pc(instr_get_target(inst)) < walk->end_cache))))) {
/* reset for non-exit non-trace-jecxz cti (i.e., selfmod cti) */
for (r = 0; r < REG_SPILL_NUM; r++)
walk->reg_spilled[r] = false;
}
if (instr_is_reg_spill_or_restore(tdcontext, inst, &spill_tls, &spill, &reg)) {
r = reg - REG_START_SPILL;
/* if a restore whose spill was before a cti, ignore */
if (spill || walk->reg_spilled[r]) {
/* ensure restores and spills are properly paired up */
ASSERT((spill && !walk->reg_spilled[r]) ||
(!spill && walk->reg_spilled[r]));
ASSERT(spill || walk->reg_tls[r] == spill_tls);
walk->reg_spilled[r] = spill;
walk->reg_tls[r] = spill_tls;
LOG(THREAD_GET, LOG_INTERP, 5,
"\tspill update: %s %s %s\n", spill ? "spill" : "restore",
spill_tls ? "tls" : "mcontext", reg_names[reg]);
}
}
/* PR 267260: Track our own mangle-inserted pushes and pops, for
* restoring state on an app fault in the middle of our indirect
* branch mangling. We only need to support instrs added up until
* the last one that could have an app fault, as we can fail when
* called to translate for thread relocation: thus we ignore
* syscall mangling.
*
* The main scenarios are:
*
* 1) call*: "spill ecx; mov->ecx; push retaddr":
* ecx restore handled above
* 2) far direct call: "push cs; push retaddr"
* if fail on 2nd push need to undo 1st push
* 3) far call*: "spill ecx; tgt->ecx; push cs; push retaddr"
* if fail on 1st push, restore ecx (above); 2nd push, also undo 1st push
* 4) iret: "pop eip; pop cs; pop eflags; (pop rsp; pop ss)"
* if fail on non-initial pop, undo earlier pops
* 5) lret: "pop eip; pop cs"
* if fail on non-initial pop, undo earlier pops
*
* FIXME: some of these push/pops are simulated (we simply adjust
* esp or do nothing), so we're not truly fault-transparent.
*/
else if (instr_check_xsp_mangling(tdcontext, inst, &walk->xsp_adjust)) {
/* walk->xsp_adjust is now adjusted */
}
else if (instr_is_trace_cmp(tdcontext, inst)) {
/* nothing to do */
/* We don't support restoring a fault in the middle, but we
* identify here to avoid "unsupported mangle instr" message
*/
}
#ifdef UNIX
else if (instr_is_inline_syscall_jmp(tdcontext, inst)) {
/* nothing to do */
}
else if (instr_is_seg_ref_load(tdcontext, inst)) {
/* nothing to do */
}
#endif
else if (instr_is_app(inst)) {
/* To have reg spill+restore in the same mangle region, we mark
* the (modified) app instr for rip-rel and for segment mangling as
* "our mangling". There's nothing specific to do for it.
*/
}
/* We do not support restoring state at arbitrary points for thread
* relocation (a performance issue, not a correctness one): if not a
* spill, restore, push, or pop, we will not properly translate.
* For an exit jmp for a simple ret we could relocate: but better not to
* for a call, since we've modified the stack w/ a push, so we fail on
* all exit jmps.
*/
else {
DOLOG(4, LOG_INTERP,
loginst(get_thread_private_dcontext(), 4,
inst, "unsupported mangle instr"););
walk->unsupported_mangle = true;
}
}
#elif defined(ARM)
/* FIXME i#1551: NYI on ARM.
* Also, we may want to split these out into arch/{x86,arm}/ files.
*/
ASSERT_NOT_IMPLEMENTED(false);
#endif
}
static bool
translate_walk_good_state(dcontext_t *tdcontext, translate_walk_t *walk,
app_pc translate_pc)
{
return (!walk->unsupported_mangle ||
/* If we're at the instr AFTER the mangle region, we're ok */
(walk->in_mangle_region && translate_pc != walk->translation));
}
static void
translate_walk_restore(dcontext_t *tdcontext, translate_walk_t *walk,
app_pc translate_pc)
{
reg_id_t r;
if (translate_pc != walk->translation) {
/* When we walk we update only each instr we pass. If we're
* now sitting at the instr AFTER the mangle region, we do
* NOT want to adjust xsp, since we're not translating to
* before that instr. We should not have any outstanding spills.
*/
LOG(THREAD_GET, LOG_INTERP, 2,
"\ttranslation "PFX" is post-walk "PFX" so not fixing xsp\n",
translate_pc, walk->translation);
DOCHECK(1, {
for (r = 0; r < REG_SPILL_NUM; r++)
ASSERT(!walk->reg_spilled[r]);
});
return;
}
/* PR 263407: restore register values that are currently in spill slots
* for ind branches or rip-rel mangling.
* FIXME: for rip-rel loads, we may have clobbered the destination
* already, and won't be able to restore it: but that's a minor issue.
*/
for (r = 0; r < REG_SPILL_NUM; r++) {
if (walk->reg_spilled[r]) {
reg_id_t reg = r + REG_START_SPILL;
reg_t value;
if (walk->reg_tls[r]) {
value = *(reg_t *)(((byte*)&tdcontext->local_state->spill_space) +
reg_spill_tls_offs(reg));
} else {
value = reg_get_value_priv(reg, get_mcontext(tdcontext));
}
LOG(THREAD_GET, LOG_INTERP, 2,
"\trestoring spilled %s to "PFX"\n", reg_names[reg], value);
STATS_INC(recreate_spill_restores);
reg_set_value_priv(reg, walk->mc, value);
}
}
/* PR 267260: Restore stack-adjust mangling of ctis.
* FIXME: we do NOT undo writes to the stack, so we're not completely
* transparent. If we ever do restore memory, we'll want to pass in
* the restore_memory param.
*/
if (walk->xsp_adjust != 0) {
walk->mc->xsp -= walk->xsp_adjust; /* negate to undo */
LOG(THREAD_GET, LOG_INTERP, 2,
"\tundoing push/pop by %d: xsp now "PFX"\n",
walk->xsp_adjust, walk->mc->xsp);
}
}
static void
translate_restore_clean_call(dcontext_t *tdcontext, translate_walk_t *walk)
{
/* PR 302951: we recognize a clean call by its combination of
* our-mangling and NULL translation.
* We restore to the priv_mcontext_t that was pushed on the stack.
*/
LOG(THREAD_GET, LOG_INTERP, 2, "\ttranslating clean call arg crash\n");
dr_get_mcontext_priv(tdcontext, NULL, walk->mc);
/* walk->mc->pc will be fixed up by caller */
/* PR 306410: up to caller to shift signal or SEH frame from dstack
* to app stack. We naturally do that already for linux b/c we always
* have an alternate signal handling stack, but for Windows it takes
* extra work.
*/
}
/* Returns a success code, but makes a best effort regardless.
* If just_pc is true, only recreates pc.
* Modifies mc with the recreated state.
* The caller must ensure tdcontext remains valid.
*/
/* Use THREAD_GET instead of THREAD so log messages go to calling thread */
static recreate_success_t
recreate_app_state_from_info(dcontext_t *tdcontext, const translation_info_t *info,
byte *start_cache, byte *end_cache,
priv_mcontext_t *mc, bool just_pc _IF_DEBUG(uint flags))
{
byte *answer = NULL;
byte *cpc, *prev_cpc;
cache_pc target_cache = mc->pc;
uint i;
bool contig = true, ours = false;
recreate_success_t res = (just_pc ? RECREATE_SUCCESS_PC : RECREATE_SUCCESS_STATE);
instr_t instr;
translate_walk_t walk;
translate_walk_init(&walk, start_cache, end_cache, mc);
instr_init(tdcontext, &instr);
ASSERT(info != NULL);
ASSERT(end_cache >= start_cache);
LOG(THREAD_GET, LOG_INTERP, 3,
"recreate_app : looking for "PFX" in frag @ "PFX" (tag "PFX")\n",
target_cache, start_cache, info->translation[0].app);
DOLOG(3, LOG_INTERP, {
translation_info_print(info, start_cache, THREAD_GET);
});
/* Strategy: walk through cache instrs, updating current app translation
* as we go along from the info table. The table records only
* translations at change points and must interpolate between them, using
* either a stride of 0 if the previous translation entry is marked
* "identical" or a stride equal to the instruction length as we decode
* from the cache if the previous entry is !identical=="contiguous".
*/
cpc = start_cache;
ASSERT(cpc - start_cache == info->translation[0].cache_offs);
i = 0;
while (cpc < end_cache) {
/* we can go beyond the end of the table: then use the last point */
if (i < info->num_entries &&
cpc - start_cache >= info->translation[i].cache_offs) {
/* We hit a change point: new app translation target */
answer = info->translation[i].app;
contig = !TEST(TRANSLATE_IDENTICAL, info->translation[i].flags);
ours = TEST(TRANSLATE_OUR_MANGLING, info->translation[i].flags);
i++;
}
if (cpc >= target_cache) {
/* we found the target to translate */
ASSERT(cpc == target_cache);
if (cpc > target_cache) { /* in debug will hit assert 1st */
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- WARNING: cache pc "PFX" != "PFX"\n",
cpc, target_cache);
res = RECREATE_FAILURE; /* try to restore, but return false */
}
break;
}
/* PR 263407/PR 268372: we need to decode to instr level to track register
* values that we've spilled, and watch for ctis. So far we don't need
* enough to justify a full decode_fragment().
*/
instr_reset(tdcontext, &instr);
prev_cpc = cpc;
cpc = decode(tdcontext, cpc, &instr);
instr_set_our_mangling(&instr, ours);
translate_walk_track(tdcontext, &instr, &walk);
/* advance translation by the stride: either instr length or 0 */
if (contig)
answer += (cpc - prev_cpc);
/* else, answer stays put */
}
/* should always find xlation */
ASSERT(cpc < end_cache);
instr_free(tdcontext, &instr);
if (answer == NULL || !translate_walk_good_state(tdcontext, &walk, answer)) {
/* PR 214962: we're either in client meta-code (NULL translation) or
* post-app-fault in our own manglings: we shouldn't get an app
* fault in either case, so it's ok to fail, and neither is a safe
* spot for thread relocation. For client meta-code we could split
* synch view (since we can get the app state consistent, just not
* the client state) from synch relocate, but that would require
* synchall re-architecting and may not be a noticeable perf win
* (should spend enough time at syscalls that will hit safe spot in
* reasonable time).
*/
/* PR 302951: our clean calls do show up here and have full state */
if (answer == NULL && ours)
translate_restore_clean_call(tdcontext, &walk);
else
res = RECREATE_SUCCESS_PC; /* failed on full state, but pc good */
/* should only happen for thread synch, not a fault */
DOCHECK(1, {
if (!(res == RECREATE_SUCCESS_STATE /* clean call */ ||
tdcontext != get_thread_private_dcontext() ||
INTERNAL_OPTION(stress_recreate_pc) ||
/* we can currently fail for flushed code (PR 208037/i#399)
* (and hotpatch, native_exec, and sysenter: but too rare to check) */
TEST(FRAG_SELFMOD_SANDBOXED, flags) ||
TEST(FRAG_WAS_DELETED, flags))) {
CLIENT_ASSERT(false, "meta-instr faulted? must set translation"
" field and handle fault!");
}
});
if (answer == NULL) {
/* use next instr's translation. skip any further meta-instrs regions. */
for (; i < info->num_entries; i++) {
if (info->translation[i].app != NULL)
break;
}
ASSERT(i < info->num_entries);
if (i < info->num_entries)
answer = info->translation[i].app;;
ASSERT(answer != NULL);
}
}
if (!just_pc)
translate_walk_restore(tdcontext, &walk, answer);
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- found ok pc "PFX"\n", answer);
mc->pc = answer;
return res;
}
/* Returns a success code, but makes a best effort regardless.
* If just_pc is true, only recreates pc.
* Modifies mc with the recreated state.
* The caller must ensure tdcontext remains valid.
*/
/* Use THREAD_GET instead of THREAD so log messages go to calling thread */
static recreate_success_t
recreate_app_state_from_ilist(dcontext_t *tdcontext, instrlist_t *ilist,
byte *start_app, byte *start_cache, byte *end_cache,
priv_mcontext_t *mc, bool just_pc, uint flags)
{
byte *answer = NULL;
byte *cpc, *prev_bytes;
instr_t *inst, *prev_ok;
cache_pc target_cache = mc->pc;
recreate_success_t res = (just_pc ? RECREATE_SUCCESS_PC : RECREATE_SUCCESS_STATE);
translate_walk_t walk;
LOG(THREAD_GET, LOG_INTERP, 3,
"recreate_app : looking for "PFX" in frag @ "PFX" (tag "PFX")\n",
target_cache, start_cache, start_app);
DOLOG(5, LOG_INTERP, {
instrlist_disassemble(tdcontext, 0, ilist, THREAD_GET);
});
/* walk ilist, incrementing cache pc by each instr's length until
* cache pc equals target, then look at original address of
* current instr, which is set by routines in mangle except for
* cti_short_rewrite.
*/
cpc = start_cache;
/* since asking for the length will encode to a buffer, we cannot
* walk backwards at all. thus we keep track of the previous instr
* with valid original bytes.
*/
prev_ok = NULL;
prev_bytes = NULL;
translate_walk_init(&walk, start_cache, end_cache, mc);
for (inst = instrlist_first(ilist); inst; inst = instr_get_next(inst)) {
int len = instr_length(tdcontext, inst);
/* All we care about is that we are not going to skip over a
* bundle of app instructions.
*/
ASSERT(!instr_is_level_0(inst));
/* Case 4531, 4344: raw instructions being up-decoded can have
* their translation fields clobbered so we don't want any of those.
* (We used to have raw jecxz and nop instrs.)
* FIXME: if bb associated with this instr was hot patched, then
* the inserted raw instructions can trigger this assert. Part of
* fix for case 5981. In that case, this would be harmless.
*/
ASSERT_CURIOSITY(instr_operands_valid(inst));
/* PR 332437: skip label instrs. Nobody should expect setting
* a label's translation field to have any effect, and we
* don't need to explicitly split our mangling regions at
* labels so no reason to call translate_walk_track().
*
* We also skip all other length 0 instrs. That would
* include un-encodable instrs, which we wouldn't have output,
* and so we should skip here in case the very next instr that we
* did encode had the real fault.
*/
if (len == 0)
continue;
/* note this will be exercised for all instructions up to the answer */
#ifndef CLIENT_INTERFACE
# ifdef INTERNAL
ASSERT(instr_get_translation(inst) != NULL || DYNAMO_OPTION(optimize));
# else
ASSERT(instr_get_translation(inst) != NULL);
# endif
#endif
LOG(THREAD_GET, LOG_INTERP, 5, "cache pc "PFX" vs "PFX"\n",
cpc, target_cache);
if (cpc >= target_cache) {
if (cpc > target_cache) {
if (cpc == start_cache) {
/* Prefix instructions are not added to recreate_fragment_ilist()
* FIXME: we should do so, and then we can at least restore
* our spills, just in case.
*/
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- cache pc "PFX" != "PFX", "
"assuming a prefix instruction\n", cpc, target_cache);
res = RECREATE_SUCCESS_PC; /* failed on full state, but pc good */
/* should only happen for thread synch, not a fault */
ASSERT(tdcontext != get_thread_private_dcontext() ||
INTERNAL_OPTION(stress_recreate_pc));
} else {
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- WARNING: cache pc "PFX" != "PFX", "
"probably prefix instruction\n", cpc, target_cache);
res = RECREATE_FAILURE; /* try to restore, but return false */
}
}
if (instr_get_translation(inst) == NULL) {
/* Clients are supposed to leave their meta instrs with
* NULL translations. (DR may hit this assert for
* -optimize but we need to fix that by setting translation
* for all our optimizations.) We assume we will never
* get an app fault here, so we fail if asked for full state
* since although we can get full app state we can't relocate
* in the middle of client meta code.
*/
ASSERT(instr_is_meta(inst));
/* PR 302951: our clean calls do show up here and have full state */
if (instr_is_our_mangling(inst))
translate_restore_clean_call(tdcontext, &walk);
else
res = RECREATE_SUCCESS_PC; /* failed on full state, but pc good */
/* should only happen for thread synch, not a fault */
DOCHECK(1, {
if (!(instr_is_our_mangling(inst) /* PR 302951 */ ||
tdcontext != get_thread_private_dcontext() ||
INTERNAL_OPTION(stress_recreate_pc)
IF_CLIENT_INTERFACE(||
tdcontext->client_data->is_translating))) {
CLIENT_ASSERT(false, "meta-instr faulted? must set translation "
"field and handle fault!");
}
});
if (prev_ok == NULL) {
answer = start_app;
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- WARNING: guessing start pc "PFX"\n", answer);
} else {
answer = prev_bytes;
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- WARNING: guessing after prev "
"translation (pc "PFX")\n", answer);
DOLOG(2, LOG_INTERP, loginst(get_thread_private_dcontext(),
2, prev_ok, "\tprev instr"););
}
} else {
answer = instr_get_translation(inst);
if (translate_walk_good_state(tdcontext, &walk, answer)) {
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- found valid state pc "PFX"\n", answer);
} else {
#ifdef X86
int op = instr_get_opcode(inst);
if (TEST(FRAG_SELFMOD_SANDBOXED, flags) &&
(op == OP_rep_ins || op == OP_rep_movs || op == OP_rep_stos)) {
/* i#398: xl8 selfmod: rep string instrs have xbx spilled in
* thread-private slot. We assume no other selfmod mangling
* has a reg spilled at time of app instr execution.
*/
if (!just_pc) {
walk.mc->xbx = get_mcontext(tdcontext)->xbx;
LOG(THREAD_GET, LOG_INTERP, 2,
"\trestoring spilled xbx to "PFX"\n", walk.mc->xbx);
STATS_INC(recreate_spill_restores);
}
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- found valid state pc "PFX"\n", answer);
} else
#endif /* X86 */
{
res = RECREATE_SUCCESS_PC; /* failed on full state, but pc good */
/* should only happen for thread synch, not a fault */
ASSERT(tdcontext != get_thread_private_dcontext() ||
INTERNAL_OPTION(stress_recreate_pc) ||
/* we can currently fail for flushed code (PR 208037)
* (and hotpatch, native_exec, and sysenter: but too
* rare to check) */
TEST(FRAG_SELFMOD_SANDBOXED, flags) ||
TEST(FRAG_WAS_DELETED, flags));
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- not able to fully recreate "
"context, pc is in added instruction from mangling\n");
}
}
}
if (!just_pc)
translate_walk_restore(tdcontext, &walk, answer);
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app -- found ok pc "PFX"\n", answer);
mc->pc = answer;
return res;
}
/* we only use translation pointers, never just raw bit pointers */
if (instr_get_translation(inst) != NULL) {
prev_ok = inst;
DOLOG(5, LOG_INTERP, loginst(get_thread_private_dcontext(),
5, prev_ok, "\tok instr"););
prev_bytes = instr_get_translation(inst);
if (instr_is_app(inst)) {
/* we really want the pc after the translation target since we'll
* use this if we pass up the target without hitting it:
* unless this is a meta instr in which case we assume the
* real instr is ahead (FIXME: there could be cases where
* we want the opposite: how know?)
*/
/* FIXME: do we need to check for readability first?
* in normal usage all translation targets should have been decoded
* already while building the bb ilist
*/
prev_bytes = decode_next_pc(tdcontext, prev_bytes);
}
}
translate_walk_track(tdcontext, inst, &walk);
cpc += len;
}
/* ERROR! */
LOG(THREAD_GET, LOG_INTERP, 1,
"ERROR: recreate_app : looking for "PFX" in frag @ "PFX" "
"(tag "PFX")\n", target_cache, start_cache, start_app);
DOLOG(1, LOG_INTERP, {
instrlist_disassemble(tdcontext, 0, ilist, THREAD_GET);
});
ASSERT_NOT_REACHED();
if (just_pc) {
/* just guess */
mc->pc = answer;
}
return RECREATE_FAILURE;
}
static instrlist_t *
recreate_selfmod_ilist(dcontext_t *dcontext, fragment_t *f)
{
cache_pc selfmod_copy;
instrlist_t *ilist;
instr_t *inst;
ASSERT(TEST(FRAG_SELFMOD_SANDBOXED, f->flags));
/* If f is selfmod, app code may have changed (we see this w/ code
* on the stack later flushed w/ os_thread_stack_exit(), though in that
* case we don't expect it to be executed again), so we do a special
* recreate from the selfmod copy.
* Since selfmod is straight-line code we can rebuild from cache and
* offset each translation entry
*/
selfmod_copy = FRAGMENT_SELFMOD_COPY_PC(f);
ASSERT(!TEST(FRAG_IS_TRACE, f->flags));
ASSERT(!TEST(FRAG_HAS_DIRECT_CTI, f->flags));
/* We must build our ilist w/o calling check_thread_vm_area(), as it will
* freak out that we are decoding DR memory.
*/
/* Be sure to "pretend" the bb is for f->tag, b/c selfmod instru is
* different based on whether pc's are in low 2GB or not.
*/
ilist = recreate_bb_ilist(dcontext, selfmod_copy, (byte *) f->tag,
/* Be sure to limit the size (i#1441) */
selfmod_copy + FRAGMENT_SELFMOD_COPY_CODE_SIZE(f),
FRAG_SELFMOD_SANDBOXED, NULL, NULL,
false/*don't check vm areas!*/, true/*mangle*/, NULL
_IF_CLIENT(true/*call client*/)
_IF_CLIENT(false/*!for_trace*/));
ASSERT(ilist != NULL); /* shouldn't fail: our own code is always readable! */
for (inst = instrlist_first(ilist); inst; inst = instr_get_next(inst)) {
app_pc app = instr_get_translation(inst);
if (app != NULL)
instr_set_translation(inst, app - selfmod_copy + f->tag);
}
return ilist;
}
/* The esp in mcontext must either be valid or NULL (if null will be unable to
* recreate on XP and 03 at vsyscall_after_syscall and on sygate 2k at after syscall).
* Returns true if successful. Whether successful or not, attempts to modify
* mcontext with recreated state. If just_pc only translates the pc
* (this is more likely to succeed)
*/
/* Use THREAD_GET instead of THREAD so log messages go to calling thread */
/* Also see NOTEs at recreate_app_state() about lock usage, and lack of full stack
* translation. */
static recreate_success_t
recreate_app_state_internal(dcontext_t *tdcontext, priv_mcontext_t *mcontext,
bool just_pc, fragment_t *owning_f, bool restore_memory)
{
recreate_success_t res = (just_pc ? RECREATE_SUCCESS_PC : RECREATE_SUCCESS_STATE);
#ifdef WINDOWS
if (get_syscall_method() == SYSCALL_METHOD_SYSENTER &&
mcontext->pc == vsyscall_after_syscall &&
mcontext->xsp != 0) {
ASSERT(get_os_version() >= WINDOWS_VERSION_XP);
/* case 5441 sygate hack means ret addr to after_syscall will be at
* esp+4 (esp will point to ret in ntdll.dll) for sysenter */
/* FIXME - should we check that esp is readable? */
if (is_after_syscall_address(tdcontext, *(cache_pc *)
(mcontext->xsp+
(DYNAMO_OPTION(sygate_sysenter) ? 4 : 0)))) {
/* no translation needed, ignoring sysenter stack hacks */
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app no translation needed (at vsyscall)\n");
return res;
} else {
/* this is a dynamo system call! */
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app at dynamo system call\n");
return RECREATE_FAILURE;
}
}
#else
if (get_syscall_method() == SYSCALL_METHOD_SYSENTER &&
/* Even when the main syscall method is sysenter, we also have a
* do_int_syscall and do_clone_syscall that use int, so check only
* the main syscall routine.
* Note that we don't modify the stack, so once we do sysenter syscalls
* inlined in the cache (PR 288101) we'll need some mechanism to
* distinguish those: but for now if a sysenter instruction is used it
* has to be do_syscall since DR's own syscalls are ints.
*/
(mcontext->pc == vsyscall_sysenter_return_pc ||
is_after_main_do_syscall_addr(tdcontext, mcontext->pc) ||
/* Check for pointing right at sysenter, for i#1145 */
mcontext->pc + SYSENTER_LENGTH == vsyscall_syscall_end_pc ||
is_after_main_do_syscall_addr(tdcontext, mcontext->pc + SYSENTER_LENGTH))) {
# ifdef MACOS
if (!just_pc) {
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app: restoring xdx (at sysenter)\n");
mcontext->xdx = tdcontext->app_xdx;
}
# else
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app no translation needed (at syscall)\n");
# endif
return res;
}
#endif
else if (is_after_syscall_that_rets(tdcontext, mcontext->pc)
/* Check for pointing right at sysenter, for i#1145 */
IF_UNIX(|| is_after_syscall_that_rets(tdcontext,
mcontext->pc + INT_LENGTH))) {
/* suspended inside kernel at syscall
* all registers have app values for syscall */
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app pc = after_syscall, translating\n");
#ifdef WINDOWS
if (DYNAMO_OPTION(sygate_int) &&
get_syscall_method() == SYSCALL_METHOD_INT) {
if ((app_pc)mcontext->xsp == NULL)
return RECREATE_FAILURE;
/* dr system calls will have the same after_syscall address when
* sygate hack are in effect so need to check top of stack to see
* if we are returning to dr or do/share syscall (generated
* routines) */
if (!in_generated_routine(tdcontext, *(app_pc *)mcontext->xsp)) {
/* this must be a dynamo system call! */
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app at dynamo system call\n");
return RECREATE_FAILURE;
}
ASSERT(*(app_pc *)mcontext->xsp ==
after_do_syscall_code(tdcontext) ||
*(app_pc *)mcontext->xsp ==
after_shared_syscall_code(tdcontext));
if (!just_pc) {
/* This is an int system call and since for sygate
* compatibility we redirect those with a call to an ntdll.dll
* int 2e ret 0 we need to pop the stack once to match app. */
mcontext->xsp += XSP_SZ; /* pop the stack */
}
}
#else
if (is_after_syscall_that_rets(tdcontext, mcontext->pc + INT_LENGTH)) {
/* i#1145: preserve syscall re-start point */
mcontext->pc = POST_SYSCALL_PC(tdcontext) - INT_LENGTH;
} else
#endif
mcontext->pc = POST_SYSCALL_PC(tdcontext);
return res;
} else if (mcontext->pc == get_reset_exit_stub(tdcontext)) {
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app at reset exit stub => using next_tag "PFX"\n",
tdcontext->next_tag);
/* context is completely native except the pc */
mcontext->pc = tdcontext->next_tag;
return res;
} else if (in_generated_routine(tdcontext, mcontext->pc)) {
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app state at untranslatable address in "
"generated routines for thread "TIDFMT"\n", tdcontext->owning_thread);
return RECREATE_FAILURE;
} else if (in_fcache(mcontext->pc)) {
/* FIXME: what if pc is in separate direct stub???
* do we have to read the &l from the stub to find linkstub_t and thus
* fragment_t owner?
*/
/* NOTE - only at this point is it safe to grab locks other then the
* fcache_unit_areas.lock */
linkstub_t *l;
cache_pc cti_pc;
instrlist_t *ilist = NULL;
fragment_t *f = owning_f;
bool alloc = false, ok;
dr_isa_mode_t old_mode;
#ifdef CLIENT_INTERFACE
dr_restore_state_info_t client_info;
dr_mcontext_t xl8_mcontext;
dr_mcontext_t raw_mcontext;
dr_mcontext_init(&xl8_mcontext);
dr_mcontext_init(&raw_mcontext);
#endif
/* Rather than storing a mapping table, we re-build the fragment
* containing the code cache pc whenever we can. For pending-deletion
* fragments we can't do that and have to store the info, due to our
* weak consistency flushing where the app code may have changed
* before we get here (case 3559).
*/
/* Check whether we have a fragment w/ stored translations before
* asking to recreate the ilist
*/
if (f == NULL)
f = fragment_pclookup_with_linkstubs(tdcontext, mcontext->pc, &alloc);
/* If the passed-in fragment is fake, we need to get the linkstubs */
if (f != NULL && TEST(FRAG_FAKE, f->flags)) {
ASSERT(TEST(FRAG_COARSE_GRAIN, f->flags));
f = fragment_recreate_with_linkstubs(tdcontext, f);
alloc = true;
}
/* Whether a bb or trace, this routine will recreate the entire ilist. */
if (f == NULL) {
ilist = recreate_fragment_ilist(tdcontext, mcontext->pc, &f, &alloc,
true/*mangle*/ _IF_CLIENT(true/*client*/));
} else if (FRAGMENT_TRANSLATION_INFO(f) == NULL) {
if (TEST(FRAG_SELFMOD_SANDBOXED, f->flags)) {
ilist = recreate_selfmod_ilist(tdcontext, f);
} else {
/* NULL for pc indicates that f is valid */
bool new_alloc;
DEBUG_DECLARE(fragment_t *pre_f = f;)
ilist = recreate_fragment_ilist(tdcontext, NULL, &f, &new_alloc,
true/*mangle*/ _IF_CLIENT(true/*client*/));
ASSERT(owning_f == NULL || f == owning_f ||
(TEST(FRAG_COARSE_GRAIN, owning_f->flags) && f == pre_f));
ASSERT(!new_alloc);
}
}
if (ilist == NULL && (f == NULL || FRAGMENT_TRANSLATION_INFO(f) == NULL)) {
/* It is problematic if this routine fails. Many places assume that
* recreate_app_pc() will work.
*/
ASSERT(!INTERNAL_OPTION(safe_translate_flushed));
res = RECREATE_FAILURE;
goto recreate_app_state_done;
}
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app : pc is in F%d("PFX")%s\n", f->id, f->tag,
((f->flags & FRAG_IS_TRACE) != 0)?" (trace)":"");
DOLOG(2, LOG_SYNCH, {
if (ilist != NULL) {
LOG(THREAD_GET, LOG_SYNCH, 2, "ilist for recreation:\n");
instrlist_disassemble(tdcontext, f->tag, ilist, THREAD_GET);
}
});
/* if pc is in an exit stub, we find the corresponding exit instr */
cti_pc = NULL;
for (l = FRAGMENT_EXIT_STUBS(f); l; l = LINKSTUB_NEXT_EXIT(l)) {
if (EXIT_HAS_LOCAL_STUB(l->flags, f->flags)) {
/* FIXME: as computing the stub pc becomes more expensive,
* should perhaps check fragment_body_end_pc() or something
* that only does one stub check up front, and then find the
* exact stub if pc is beyond the end of the body.
*/
if (mcontext->pc < EXIT_STUB_PC(tdcontext, f, l))
break;
cti_pc = EXIT_CTI_PC(f, l);
}
}
if (cti_pc != NULL) {
/* target is inside an exit stub!
* new target: the exit cti, not its stub
*/
if (!just_pc) {
/* FIXME : translate from exit stub */
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"recreate_app_helper -- can't full recreate state, pc "PFX" "
"is in exit stub\n", mcontext->pc);
res = RECREATE_SUCCESS_PC; /* failed on full state, but pc good */
goto recreate_app_state_done;
}
LOG(THREAD_GET, LOG_INTERP|LOG_SYNCH, 2,
"\ttarget "PFX" is inside an exit stub, looking for its cti "
" "PFX"\n", mcontext->pc, cti_pc);
mcontext->pc = cti_pc;
}
/* Recreate in same mode as original fragment */
ok = dr_set_isa_mode(tdcontext, FRAG_ISA_MODE(f->flags), &old_mode);
ASSERT(ok);
/* now recreate the state */
#ifdef CLIENT_INTERFACE
/* keep a copy of the pre-translation state */
priv_mcontext_to_dr_mcontext(&raw_mcontext, mcontext);
client_info.raw_mcontext = &raw_mcontext;
client_info.raw_mcontext_valid = true;
#endif
if (ilist == NULL) {
ASSERT(f != NULL && FRAGMENT_TRANSLATION_INFO(f) != NULL);
ASSERT(!TEST(FRAG_WAS_DELETED, f->flags) ||
INTERNAL_OPTION(safe_translate_flushed));
res = recreate_app_state_from_info(tdcontext, FRAGMENT_TRANSLATION_INFO(f),
(byte *) f->start_pc,
(byte *) f->start_pc + f->size,
mcontext, just_pc _IF_DEBUG(f->flags));
STATS_INC(recreate_via_stored_info);
} else {
res = recreate_app_state_from_ilist(tdcontext, ilist, (byte *) f->tag,
(byte *) FCACHE_ENTRY_PC(f),
(byte *) f->start_pc + f->size,
mcontext, just_pc, f->flags);
STATS_INC(recreate_via_app_ilist);
}
ok = dr_set_isa_mode(tdcontext, old_mode, NULL);
ASSERT(ok);
#ifdef STEAL_REGISTER
/* FIXME: conflicts w/ PR 263407 reg spill tracking */
ASSERT_NOT_IMPLEMENTED(false && "conflicts w/ reg spill tracking");
if (!just_pc) {
/* get app's value of edi */
mc->xdi = get_mcontext(tdcontext)->xdi;
}
#endif
#ifdef CLIENT_INTERFACE
if (res != RECREATE_FAILURE) {
/* PR 214962: if the client has a restore callback, invoke it to
* fix up the state (and pc).
*/
priv_mcontext_to_dr_mcontext(&xl8_mcontext, mcontext);
client_info.mcontext = &xl8_mcontext;
client_info.fragment_info.tag = (void *) f->tag;
client_info.fragment_info.cache_start_pc = FCACHE_ENTRY_PC(f);
client_info.fragment_info.is_trace = TEST(FRAG_IS_TRACE, f->flags);
client_info.fragment_info.app_code_consistent =
!TESTANY(FRAG_WAS_DELETED|FRAG_SELFMOD_SANDBOXED, f->flags);
/* i#220/PR 480565: client has option of failing the translation */
if (!instrument_restore_state(tdcontext, restore_memory, &client_info))
res = RECREATE_FAILURE;
dr_mcontext_to_priv_mcontext(mcontext, &xl8_mcontext);
}
#endif
recreate_app_state_done:
/* free the instrlist_t elements */
if (ilist != NULL)
instrlist_clear_and_destroy(tdcontext, ilist);
if (alloc) {
ASSERT(f != NULL);
fragment_free(tdcontext, f);
}
return res;
} else {
/* handle any other cases, in DR etc. */
return RECREATE_FAILURE;
}
}
/* Assumes that pc is a pc_recreatable place (i.e. in_fcache(), though could do
* syscalls with esp, also see FIXME about separate stubs in
* recreate_app_state_internal()), ASSERTs otherwise.
* If caller knows which fragment pc belongs to, caller should pass in f
* to avoid work and avoid lock rank issues as pclookup acquires shared_cache_lock;
* else, pass in NULL for f.
* NOTE - If called by a thread other than the tdcontext owner, caller must
* ensure tdcontext remains valid. Caller also must ensure that it is safe to
* allocate memory from tdcontext (for instr routines), i.e. caller owns
* tdcontext or the owner of tdcontext is suspended. Also if tdcontext is
* !couldbelinking then caller must own the thread_initexit_lock in case
* recreate_fragment_ilist() is called.
* NOTE - If this function is unable to translate the pc, but the pc is
* in_fcache() then there is an assert curiosity and the function returns NULL.
* This can happen only from the pc being in a fragment that is pending
* deletion (ref case 3559 others). Most callers don't check the returned
* value and wouldn't have a way to recover even if they did. FIXME
*/
/* Use THREAD_GET instead of THREAD so log messages go to calling thread */
app_pc
recreate_app_pc(dcontext_t *tdcontext, cache_pc pc, fragment_t *f)
{
priv_mcontext_t mc;
recreate_success_t res;
#if defined(CLIENT_INTERFACE) && defined(WINDOWS)
bool swap_peb = false;
if (INTERNAL_OPTION(private_peb) && should_swap_peb_pointer() &&
dr_using_app_state(tdcontext)) {
swap_peb_pointer(tdcontext, true/*to priv*/);
swap_peb = true;
}
#endif
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app_pc -- translating from pc="PFX"\n", pc);
memset(&mc, 0, sizeof(mc)); /* ensures esp is NULL */
mc.pc = pc;
res = recreate_app_state_internal(tdcontext, &mc, true, f, false);
if (res != RECREATE_SUCCESS_PC) {
ASSERT(res != RECREATE_SUCCESS_STATE); /* shouldn't return that for just_pc */
ASSERT(in_fcache(pc)); /* Make sure caller didn't screw up */
/* We were unable to translate the pc, most likely because the
* pc is in a fragment that is pending deletion FIXME, most callers
* aren't able to recover! */
ASSERT_CURIOSITY(res && "Unable to translate pc");
mc.pc = NULL;
}
LOG(THREAD_GET, LOG_INTERP, 2,
"recreate_app_pc -- translation is "PFX"\n", mc.pc);
#if defined(CLIENT_INTERFACE) && defined(WINDOWS)
if (swap_peb)
swap_peb_pointer(tdcontext, false/*to app*/);
#endif
return mc.pc;
}
/* Translates the code cache state in mcontext into what it would look like
* in the original application.
* If it fails altogether, returns RECREATE_FAILURE, but still provides
* a best-effort translation.
* If it fails to restore the full machine state, but does restore the pc,
* returns RECREATE_SUCCESS_PC.
* If it successfully restores the full machine state,
* returns RECREATE_SUCCESS_STATE. Only for full success does it
* consider the restore_memory parameter, which, if true, requests restoration
* of any memory values that were shifted (primarily due to clients) (otherwise,
* only the passed-in mcontext is modified). If restore_memory is
* true, the caller should always relocate the translated thread, as
* it may not execute properly if left at its current location (it
* could be in the middle of client code in the cache).
*
* If caller knows which fragment pc belongs to, caller should pass in f
* to avoid work and avoid lock rank issues as pclookup acquires shared_cache_lock;
* else, pass in NULL for f.
*
* FIXME: does not undo stack mangling for sysenter
*/
/* NOTE - Can be called with a thread suspended at an arbitrary place by synch
* routines so must not call mutex_lock (or call a function that does) unless
* the synch routines have checked that lock. Currently only fcache_unit_areas.lock
* is used (for in_fcache in recreate_app_state_internal())
* (if in_fcache succeeds then assumes other locks won't be a problem).
* NOTE - If called by a thread other than the tdcontext owner, caller must
* ensure tdcontext remains valid. Caller also must ensure that it is safe to
* allocate memory from tdcontext (for instr routines), i.e. caller owns
* tdcontext or the owner of tdcontext is suspended. Also if tdcontext is
* !couldbelinking then caller must own the thread_initexit_lock in case
* recreate_fragment_ilist() is called. We assume that when tdcontext is
* not the calling thread, this is a thread synch request, and is NOT from
* an app fault (PR 267260)!
*/
/* Use THREAD_GET instead of THREAD so log messages go to calling thread */
recreate_success_t
recreate_app_state(dcontext_t *tdcontext, priv_mcontext_t *mcontext,
bool restore_memory, fragment_t *f)
{
recreate_success_t res;
#if defined(CLIENT_INTERFACE) && defined(WINDOWS)
bool swap_peb = false;
if (INTERNAL_OPTION(private_peb) && should_swap_peb_pointer() &&
dr_using_app_state(tdcontext)) {
swap_peb_pointer(tdcontext, true/*to priv*/);
swap_peb = true;
}
#endif
#ifdef DEBUG
if (stats->loglevel >= 2 && (stats->logmask & LOG_SYNCH) != 0) {
LOG(THREAD_GET, LOG_SYNCH, 2,
"recreate_app_state -- translating from:\n");
dump_mcontext(mcontext, THREAD_GET, DUMP_NOT_XML);
}
#endif
res = recreate_app_state_internal(tdcontext, mcontext, false, f, restore_memory);
#ifdef DEBUG
if (res) {
if (stats->loglevel >= 2 && (stats->logmask & LOG_SYNCH) != 0) {
LOG(THREAD_GET, LOG_SYNCH, 2,
"recreate_app_state -- translation is:\n");
dump_mcontext(mcontext, THREAD_GET, DUMP_NOT_XML);
}
} else {
LOG(THREAD_GET, LOG_SYNCH, 2,
"recreate_app_state -- unable to translate\n");
}
#endif
#if defined(CLIENT_INTERFACE) && defined(WINDOWS)
if (swap_peb)
swap_peb_pointer(tdcontext, false/*to app*/);
#endif
return res;
}
static inline uint
translation_info_alloc_size(uint num_entries)
{
return (sizeof(translation_info_t) + sizeof(translation_entry_t)*num_entries);
}
/* we save space by inlining the array with the struct holding the length */
static translation_info_t *
translation_info_alloc(dcontext_t *dcontext, uint num_entries)
{
/* we need to use global heap since pending-delete fragments become
* shared entities
*/
translation_info_t *info =
global_heap_alloc(translation_info_alloc_size(num_entries) HEAPACCT(ACCT_OTHER));
info->num_entries = num_entries;
return info;
}
void
translation_info_free(dcontext_t *dcontext, translation_info_t *info)
{
global_heap_free(info, translation_info_alloc_size(info->num_entries)
HEAPACCT(ACCT_OTHER));
}
static inline void
set_translation(dcontext_t *dcontext, translation_entry_t **entries,
uint *num_entries, uint entry,
ushort cache_offs, app_pc app, bool identical, bool our_mangling)
{
if (entry >= *num_entries) {
/* alloc new arrays 2x as big */
*entries = global_heap_realloc(*entries, *num_entries,
*num_entries*2, sizeof(translation_entry_t)
HEAPACCT(ACCT_OTHER));
*num_entries *= 2;
}
ASSERT(entry < *num_entries);
(*entries)[entry].cache_offs = cache_offs;
(*entries)[entry].app = app;
(*entries)[entry].flags = 0;
if (identical)
(*entries)[entry].flags |= TRANSLATE_IDENTICAL;
if (our_mangling)
(*entries)[entry].flags |= TRANSLATE_OUR_MANGLING;
LOG(THREAD, LOG_FRAGMENT, 4, "\tset_translation: %d +%5d => "PFX" %s%s\n",
entry, cache_offs, app, identical ? "identical" : "contiguous",
our_mangling ? " ours" : "");
}
void
translation_info_print(const translation_info_t *info, cache_pc start, file_t file)
{
uint i;
ASSERT(info != NULL);
ASSERT(file != INVALID_FILE);
print_file(file, "translation info "PFX"\n", info);
for (i=0; i<info->num_entries; i++) {
print_file(file, "\t%d +%5d == "PFX" => "PFX" %s%s\n",
i, info->translation[i].cache_offs,
start + info->translation[i].cache_offs,
info->translation[i].app,
TEST(TRANSLATE_IDENTICAL, info->translation[i].flags) ?
"identical" : "contiguous",
TEST(TRANSLATE_OUR_MANGLING, info->translation[i].flags) ?
" ours" : "");
}
}
/* With our weak flushing consistency we must store translation info
* for any fragment that may outlive its original app code (case
* 3559). Here we store actual translation info. An alternative is
* to store elided jmp information and a copy of the source memory,
* but that takes more memory for all but the smallest fragments. A
* better alternative is to reliably de-mangle, which would require
* only elided jmp information.
*/
translation_info_t *
record_translation_info(dcontext_t *dcontext, fragment_t *f, instrlist_t *existing_ilist)
{
translation_entry_t *entries;
uint num_entries;
translation_info_t *info = NULL;
instrlist_t *ilist;
instr_t *inst;
uint i;
uint last_len = 0;
bool last_contig;
app_pc last_translation = NULL;
cache_pc cpc;
LOG(THREAD, LOG_FRAGMENT, 3, "record_translation_info: F%d("PFX")."PFX"\n",
f->id, f->tag, f->start_pc);
if (existing_ilist != NULL)
ilist = existing_ilist;
else if (TEST(FRAG_SELFMOD_SANDBOXED, f->flags)) {
ilist = recreate_selfmod_ilist(dcontext, f);
} else {
/* Must re-build fragment and record translation info for each instr.
* Whether a bb or trace, this routine will recreate the entire ilist.
*/
ilist = recreate_fragment_ilist(dcontext, NULL, &f, NULL,
true/*mangle*/ _IF_CLIENT(true/*client*/));
}
ASSERT(ilist != NULL);
DOLOG(3, LOG_FRAGMENT, {
LOG(THREAD, LOG_FRAGMENT, 3, "ilist for recreation:\n");
instrlist_disassemble(dcontext, f->tag, ilist, THREAD);
});
/* To avoid two passes we do one pass and store into a large-enough array.
* We then copy the results into a just-right-sized array. A typical bb
* requires 2 entries, one for its body of straight-line code and one for
* the inserted jmp at the end, so we start w/ that to avoid copying in
* the common case. FIXME: optimization: instead of every bb requiring a
* final entry for the inserted jmp, have recreate_ know about it and cut
* in half the typical storage reqts.
*/
# define NUM_INITIAL_TRANSLATIONS 2
num_entries = NUM_INITIAL_TRANSLATIONS;
entries = HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, translation_entry_t,
NUM_INITIAL_TRANSLATIONS, ACCT_OTHER, PROTECTED);
i = 0;
cpc = (byte *) FCACHE_ENTRY_PC(f);
if (fragment_prefix_size(f->flags) > 0) {
ASSERT(f->start_pc < cpc);
set_translation(dcontext, &entries, &num_entries, i, 0,
f->tag, true/*identical*/, true/*our mangling*/);
last_translation = f->tag;
last_contig = false;
i++;
} else {
ASSERT(f->start_pc == cpc);
last_contig = true; /* we create 1st entry on 1st loop iter */
}
for (inst = instrlist_first(ilist); inst; inst = instr_get_next(inst)) {
app_pc app = instr_get_translation(inst);
uint prev_i = i;
#ifndef CLIENT_INTERFACE
# ifdef INTERNAL
ASSERT(app != NULL || DYNAMO_OPTION(optimize));
# else
ASSERT(app != NULL);
# endif
#endif
/* Should only be NULL for meta-code added by a client.
* We preserve the NULL so our translation routines know to not
* let this be a thread relocation point
*/
/* i#739, skip label instr */
if (instr_is_label(inst))
continue;
/* PR 302951: clean call args are instr_is_our_mangling so no assert for that */
ASSERT(app != NULL || instr_is_meta(inst));
/* see whether we need a new entry, or the current stride (contig
* or identical) holds
*/
if (last_contig) {
if ((i == 0 && (app == NULL || instr_is_our_mangling(inst)))
|| app == last_translation) {
/* we are now in an identical region */
/* Our incremental discovery can cause us to add a new
* entry of one type that on the next instr we discover
* can optimally be recorded as the other type. Here we
* hit an app pc shift whose target needs an identical
* entry: so rather than a contig followed by identical,
* we can get away with a single identical.
* Example: "x x+1 y y", where we use an identical for the
* first y instead of the contig that we initially guessed
* at b/c we assumed it was an elision.
*/
if (i > 0 && entries[i-1].cache_offs == cpc - last_len - f->start_pc) {
/* convert prev contig into identical */
ASSERT(!TEST(TRANSLATE_IDENTICAL, entries[i-1].flags));
entries[i-1].flags |= TRANSLATE_IDENTICAL;
LOG(THREAD, LOG_FRAGMENT, 3,
"\tchanging %d to identical\n", i-1);
} else {
set_translation(dcontext, &entries, &num_entries, i,
(ushort) (cpc - f->start_pc),
app, true/*identical*/, instr_is_our_mangling(inst));
i++;
}
last_contig = false;
} else if ((i == 0 && app != NULL && !instr_is_our_mangling(inst))
|| app != last_translation + last_len) {
/* either 1st loop iter w/ app instr & no prefix, or else probably
* a follow-ubr, so create a new contig entry
*/
set_translation(dcontext, &entries, &num_entries, i,
(ushort) (cpc - f->start_pc),
app, false/*contig*/, instr_is_our_mangling(inst));
last_contig = true;
i++;
} /* else, contig continues */
} else {
if (app != last_translation) {
/* no longer in an identical region */
ASSERT(i > 0);
/* If we have translations "x x+1 x+1 x+2 x+3" we can more
* efficiently encode with a new contig entry at the 2nd
* x+1 rather than an identical entry there followed by a
* contig entry for x+2.
*/
if (app == last_translation + last_len &&
entries[i-1].cache_offs == cpc - last_len - f->start_pc) {
/* convert prev identical into contig */
ASSERT(TEST(TRANSLATE_IDENTICAL, entries[i-1].flags));
entries[i-1].flags &= ~TRANSLATE_IDENTICAL;
LOG(THREAD, LOG_FRAGMENT, 3,
"\tchanging %d to contig\n", i-1);
} else {
/* probably a follow-ubr, so create a new contig entry */
set_translation(dcontext, &entries, &num_entries, i,
(ushort) (cpc - f->start_pc), app, false/*contig*/,
instr_is_our_mangling(inst));
last_contig = true;
i++;
}
}
}
last_translation = app;
/* We need to make a new entry if the our-mangling flag changed */
if (i > 0 && i == prev_i && instr_is_our_mangling(inst) !=
TEST(TRANSLATE_OUR_MANGLING, entries[i-1].flags)) {
/* our manglings are usually identical */
bool identical = instr_is_our_mangling(inst);
set_translation(dcontext, &entries, &num_entries, i,
(ushort) (cpc - f->start_pc),
app, identical, instr_is_our_mangling(inst));
last_contig = !identical;
i++;
}
last_len = instr_length(dcontext, inst);
cpc += last_len;
ASSERT(CHECK_TRUNCATE_TYPE_ushort(cpc - f->start_pc));
}
/* exit stubs can be examined after app code is gone, so we don't need
* to store any info on them here
*/
/* free the instrlist_t elements */
if (existing_ilist == NULL)
instrlist_clear_and_destroy(dcontext, ilist);
/* now copy into right-sized array */
info = translation_info_alloc(dcontext, i);
memcpy(info->translation, entries, sizeof(translation_entry_t) * i);
HEAP_ARRAY_FREE(GLOBAL_DCONTEXT, entries, translation_entry_t,
num_entries, ACCT_OTHER, PROTECTED);
STATS_INC(translations_computed);
DOLOG(3, LOG_INTERP, {
translation_info_print(info, f->start_pc, THREAD);
});
return info;
}
#ifdef INTERNAL
void
stress_test_recreate_state(dcontext_t *dcontext, fragment_t *f, instrlist_t *ilist)
{
# ifdef X86
priv_mcontext_t mc;
bool res;
cache_pc cpc;
instr_t *in;
static const reg_t STRESS_XSP_INIT = 0x08000000; /* arbitrary */
bool success_so_far = true;
bool inside_mangle_region = false;
bool spill_xcx_outstanding = false;
reg_id_t reg;
bool spill;
int xsp_adjust = 0;
app_pc mangle_translation = f->tag;
LOG(THREAD, LOG_INTERP, 3, "Testing restoring state fragment #%d\n",
GLOBAL_STAT(num_fragments));
if (TEST(FRAG_IS_TRACE, f->flags)) {
/* decode_fragment() does not set the our-mangling bits, nor the
* translation fields (to distinguish back-to-back mangling
* regions): not ideal to test using part of what we're testing but
* better than nothing
*/
ilist = recreate_fragment_ilist(dcontext, NULL, &f, NULL, true/*mangle*/
_IF_CLIENT(true/*call client*/));
}
cpc = FCACHE_ENTRY_PC(f);
for (in = instrlist_first(ilist); in != NULL;
cpc += instr_length(dcontext, in), in = instr_get_next(in)) {
/* PR 267260: we're only testing mangling regions.
* FIXME: also verify rip-relative mangling translation
*/
if (inside_mangle_region &&
(!instr_is_our_mangling(in) ||
/* handle adjacent mangle regions */
(TEST(FRAG_IS_TRACE, f->flags) /* we have translation only for traces */ &&
mangle_translation != instr_get_translation(in)))) {
/* reset */
LOG(THREAD, LOG_INTERP, 3, " out of mangling region\n");
inside_mangle_region = false;
xsp_adjust = 0;
success_so_far = true;
spill_xcx_outstanding = false;
/* go ahead and fall through and ensure we succeed w/ 0 xsp adjust */
}
if (instr_is_our_mangling(in)) {
if (!inside_mangle_region) {
inside_mangle_region = true;
LOG(THREAD, LOG_INTERP, 3, " entering mangling region\n");
mangle_translation = instr_get_translation(in);
} else {
ASSERT(!TEST(FRAG_IS_TRACE, f->flags) ||
mangle_translation == instr_get_translation(in));
}
mc.xcx = (reg_t) get_tls(os_tls_offset
((ushort)reg_spill_tls_offs(REG_XCX))) + 1;
mc.xsp = STRESS_XSP_INIT;
mc.pc = cpc;
LOG(THREAD, LOG_INTERP, 3,
" restoring cpc="PFX", xsp="PFX"\n", mc.pc, mc.xsp);
res = recreate_app_state(dcontext, &mc, false/*just registers*/, NULL);
LOG(THREAD, LOG_INTERP, 3,
" restored res=%d pc="PFX", xsp="PFX" vs "PFX", xcx="PFX" vs "PFX"\n",
res, mc.pc, mc.xsp, STRESS_XSP_INIT -/*negate*/ xsp_adjust,
mc.xcx, get_tls(os_tls_offset((ushort)reg_spill_tls_offs(REG_XCX))));
/* We should only have failures at tail end of mangle regions.
* No instrs after a failing instr should touch app memory.
*/
ASSERT(success_so_far /* ok to fail */ ||
(!res &&
(instr_is_reg_spill_or_restore(dcontext, in, NULL, NULL, NULL) ||
(!instr_reads_memory(in) && !instr_writes_memory(in)))));
/* check that xsp and xcx are adjusted properly */
ASSERT(mc.xsp == STRESS_XSP_INIT -/*negate*/ xsp_adjust);
ASSERT(!spill_xcx_outstanding ||
mc.xcx == (reg_t)
get_tls(os_tls_offset((ushort)reg_spill_tls_offs(REG_XCX))));
if (success_so_far && !res)
success_so_far = false;
instr_check_xsp_mangling(dcontext, in, &xsp_adjust);
if (xsp_adjust != 0)
LOG(THREAD, LOG_INTERP, 3, " xsp_adjust=%d\n", xsp_adjust);
if (instr_is_reg_spill_or_restore(dcontext, in, NULL, &spill, &reg) &&
reg == REG_XCX)
spill_xcx_outstanding = spill;
}
}
if (TEST(FRAG_IS_TRACE, f->flags)) {
instrlist_clear_and_destroy(dcontext, ilist);
}
# elif defined(ARM)
/* FIXME i#1551: NYI on ARM */
ASSERT_NOT_IMPLEMENTED(false);
# endif /* X86/ARM */
}
#endif /* INTERNAL */
/* END TRANSLATION CODE ***********************************************************/