/* inserts "inst" before "where" ("inst" can be a chain of insts) */ void instrlist_preinsert(instrlist_t *ilist, instr_t *where, instr_t *inst) { instr_t *whereprev; instr_t *top = inst; instr_t *bot; if (where == NULL) { /* if where is NULL there is no inst to send for a "before" */ instrlist_append(ilist, inst); return; } CLIENT_ASSERT(where != NULL, "instrlist_preinsert: where cannot be NULL"); CLIENT_ASSERT(instr_get_prev(inst) == NULL, "instrlist_preinsert: cannot add middle of list"); whereprev = instr_get_prev(where); check_translation(ilist, inst); while (instr_get_next(inst)) { inst = instr_get_next(inst); check_translation(ilist, inst); } bot = inst; if (whereprev) { instr_set_next(whereprev, top); instr_set_prev(top, whereprev); } else { ilist->first = top; } instr_set_next(bot, where); instr_set_prev(where, bot); }
/* inserts "inst" after "where" ("inst" can be a chain of insts) */ void instrlist_postinsert(instrlist_t *ilist, instr_t *where, instr_t *inst) { instr_t *wherenext; instr_t *top = inst; instr_t *bot; if (where == NULL) { /* if where is NULL there is no inst to send for an "after" */ instrlist_prepend(ilist, inst); return; } CLIENT_ASSERT(where != NULL, "instrlist_postinsert: where cannot be NULL"); CLIENT_ASSERT(instr_get_prev(inst) == NULL, "instrlist_postinsert: cannot add middle of list"); wherenext = instr_get_next(where); check_translation(ilist, inst); while (instr_get_next(inst)) { inst = instr_get_next(inst); check_translation(ilist, inst); } bot = inst; instr_set_next(where, top); instr_set_prev(top, where); if (wherenext) { instr_set_next(bot, wherenext); instr_set_prev(wherenext, bot); } else { ilist->last = bot; } }
/* 32-bit addressing forms with the ModR/M Byte (Table 2-2). You call * this routine with the byte following the primary opcode byte when you * know that the operation's next byte is a ModR/M byte. This routine * passes back the size of the Eaddr specification in bytes based on the * following encoding of Table 2-2. * * Mod R/M * 0 1 2 3 4 5 6 7 * 0 1 1 1 1 * 5 1 1 * 1 2 2 2 2 3 2 2 2 * 2 5 5 5 5 6 5 5 5 * 3 1 1 1 1 1 1 1 1 * where (*) is 6 if base==5 and 2 otherwise. */ static int sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) { int l = 0; /* return value for sizeof(eAddr) */ uint modrm = (uint)*pc; int r_m = modrm & 0x7; uint mod = modrm >> 6; uint sib; #ifdef X64 if (rip_rel_pc != NULL && X64_MODE_DC(dcontext) && mod == 0 && r_m == 5) { *rip_rel_pc = pc + 1; /* no sib: next 4 bytes are disp */ } #endif if (addr16 && !X64_MODE_DC(dcontext)) { if (mod == 1) return 2; /* modrm + disp8 */ else if (mod == 2) return 3; /* modrm + disp16 */ else if (mod == 3) return 1; /* just modrm */ else { CLIENT_ASSERT(mod == 0, "internal decoding error on addr16 prefix"); if (r_m == 6) return 3; /* modrm + disp16 */ else return 1; /* just modrm */ } CLIENT_ASSERT(false, "internal decoding error on addr16 prefix"); } /* for x64, addr16 simply truncates the computed address: there is * no change in disp sizes */ if (mod == 3) /* register operand */ return 1; switch (mod) { /* memory or immediate operand */ case 0: l = (r_m == 5) ? 5 : 1; break; case 1: l = 2; break; case 2: l = 5; break; } if (r_m == 4) { l += 1; /* adjust for sib byte */ sib = (uint)(*(pc+1)); if ((sib & 0x7) == 5) { if (mod == 0) l += 4; /* disp32(,index,s) */ } } return l; }
instrlist_t* instrlist_clone(dcontext_t *dcontext, instrlist_t *old) { instr_t *inst, *copy; instrlist_t *newlist = instrlist_create(dcontext); inst = instrlist_first(old); while (inst != NULL) { copy = instr_clone(dcontext, inst); /* to copy instr targets we temporarily clobber note field */ instr_set_note(inst, (void *)copy); instrlist_append(newlist, copy); inst = instr_get_next(inst); } /* Fix up instr src if it is an instr and restore note field */ /* Note: we do not allows instruction update code cache, * which is very dangerous. * So we do not support instr as dst opnd and won't fix up here if any. */ for (inst = instrlist_first(old), copy = instrlist_first(newlist); inst != NULL && copy != NULL; inst = instr_get_next(inst), copy = instr_get_next(copy)) { int i; for (i = 0; i < inst->num_srcs; i++) { instr_t *tgt; opnd_t op = instr_get_src(copy, i); if (!opnd_is_instr(op)) continue; CLIENT_ASSERT(opnd_get_instr(op) != NULL, "instrlist_clone: NULL instr operand"); tgt = (instr_t *) instr_get_note(opnd_get_instr(op)); CLIENT_ASSERT(tgt != NULL, "instrlist_clone: operand instr not in instrlist"); if (opnd_is_far_instr(op)) { instr_set_src(copy, i, opnd_create_far_instr (opnd_get_segment_selector(op), tgt)); } else instr_set_src(copy, i, opnd_create_instr(tgt)); } } for (inst = instrlist_first(old), copy = instrlist_first(newlist); inst != NULL && copy != NULL; inst = instr_get_next(inst), copy = instr_get_next(copy)) { /* restore note field */ instr_set_note(inst, instr_get_note(copy)); } #ifdef CLIENT_INTERFACE newlist->fall_through_bb = old->fall_through_bb; #endif return newlist; }
/* frees the instrlist_t object */ void instrlist_destroy(dcontext_t *dcontext, instrlist_t *ilist) { CLIENT_ASSERT(ilist->first == NULL && ilist->last == NULL, "instrlist_destroy: list not empty"); heap_free(dcontext, ilist, sizeof(instrlist_t) HEAPACCT(ACCT_IR)); }
/* return the branch type of the (branch) inst */ uint instr_branch_type(instr_t *cti_instr) { instr_get_opcode(cti_instr); /* ensure opcode is valid */ if (instr_get_opcode(cti_instr) == OP_blx) { /* To handle the mode switch we go through the ibl. * FIXME i#1551: once we have far linking through stubs we should * remove this and have a faster link through the stub. */ return LINK_INDIRECT|LINK_CALL; } /* We treate a predicated call as a cbr, not a call */ else if (instr_is_cbr_arch(cti_instr) || instr_is_ubr_arch(cti_instr)) return LINK_DIRECT|LINK_JMP; else if (instr_is_call_direct(cti_instr)) return LINK_DIRECT|LINK_CALL; else if (instr_is_call_indirect(cti_instr)) return LINK_INDIRECT|LINK_CALL; else if (instr_is_return(cti_instr)) return LINK_INDIRECT|LINK_RETURN; else if (instr_is_mbr_arch(cti_instr)) return LINK_INDIRECT|LINK_JMP; else CLIENT_ASSERT(false, "instr_branch_type: unknown opcode"); return LINK_INDIRECT; }
/* If has_instr_jmp_targets is true, this routine trashes the note field * of each instr_t to store the offset in order to properly encode * the relative pc for an instr_t jump target */ byte * instrlist_encode_to_copy(dcontext_t *dcontext, instrlist_t *ilist, byte *copy_pc, byte *final_pc, byte *max_pc, bool has_instr_jmp_targets) { instr_t *inst; int len = 0; #ifdef ARM /* XXX i#1734: reset encode state to avoid any stale encode state * or dangling pointer. */ if (instr_get_isa_mode(instrlist_first(ilist)) == DR_ISA_ARM_THUMB) encode_reset_it_block(dcontext); #endif /* Do an extra pass over the instrlist so we can determine if an instr opnd * was erroneously used with has_instr_jmp_targets = false. */ DOCHECK(2, { if (!has_instr_jmp_targets) { for (inst = instrlist_first(ilist); inst; inst = instr_get_next(inst)) { if (TEST(INSTR_OPERANDS_VALID, (inst)->flags)) { int i; for (i = 0; i < instr_num_srcs(inst); ++i) { CLIENT_ASSERT(!opnd_is_instr(instr_get_src(inst, i)), "has_instr_jmp_targets was unset " "but an instr opnd was found"); } } } } });
/* Two-byte opcode map (Tables A-4 and A-5). You use this routine * when you have identified the primary opcode as 0x0f. You pass this * routine the next byte to determine the number of extra bytes in the * entire instruction. * May return 0 size for certain invalid instructions. */ static int sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) { uint opc = (uint)*pc; int sz = escape_fixed_length[opc]; ushort varlen = escape_variable_length[opc]; /* for a valid instr, sz must be > 0 here, but we don't want to assert * since we need graceful failure */ if (varlen == VARLEN_MODRM) return sz + sizeof_modrm(dcontext, pc+1, addr16 _IF_X64(rip_rel_pc)); else if (varlen == VARLEN_3BYTE_38_ESCAPE) { opc = *(++pc); /* so far all 3-byte instrs have modrm bytes */ /* to be robust for future additions we don't actually * use the threebyte_38_fixed_length[opc] entry and assume 1 */ return sz + 1 + sizeof_modrm(dcontext, pc+1, addr16 _IF_X64(rip_rel_pc)); } else if (varlen == VARLEN_3BYTE_3A_ESCAPE) { opc = *(++pc); /* so far all 0f 3a 3-byte instrs have modrm bytes and 1-byte immeds */ /* to be robust for future additions we don't actually * use the threebyte_3a_fixed_length[opc] entry and assume 1 */ return sz + 1 + sizeof_modrm(dcontext, pc+1, addr16 _IF_X64(rip_rel_pc)) + 1; } else CLIENT_ASSERT(varlen == VARLEN_NONE, "internal decoding error"); return sz; }
uint opnd_immed_float_arch(uint opcode) { /* FIXME i#1551: NYI */ CLIENT_ASSERT(false, "NYI"); return 0; }
bool instr_is_mov(instr_t *instr) { /* FIXME i#1551: NYI */ CLIENT_ASSERT(false, "NYI"); return false; }
/* replace oldinst with newinst, remove oldinst from ilist, and return oldinst (newinst can be a chain of insts) */ instr_t * instrlist_replace(instrlist_t *ilist, instr_t *oldinst, instr_t *newinst) { instr_t *where; CLIENT_ASSERT(oldinst != NULL, "instrlist_replace: oldinst cannot be NULL"); CLIENT_ASSERT(instr_get_prev(newinst) == NULL, "instrlist_replace: cannot add middle of list"); where = instr_get_prev(oldinst); instrlist_remove(ilist, oldinst); if (where) instrlist_postinsert(ilist, where, newinst); else instrlist_prepend(ilist, newinst); return oldinst; }
/* returns an empty instrlist_t object */ instrlist_t * instrlist_create(dcontext_t *dcontext) { instrlist_t *ilist = (instrlist_t *)heap_alloc(dcontext, sizeof(instrlist_t) HEAPACCT(ACCT_IR)); CLIENT_ASSERT(ilist != NULL, "instrlist_create: allocation error"); instrlist_init(ilist); return ilist; }
/* initializes an instrlist_t object */ void instrlist_init(instrlist_t *ilist) { CLIENT_ASSERT(ilist != NULL, "instrlist_create: NULL parameter"); ilist->first = ilist->last = NULL; ilist->flags = 0; /* no flags set */ ilist->translation_target = NULL; #ifdef CLIENT_INTERFACE ilist->fall_through_bb = NULL; #endif }
static inline void check_translation(instrlist_t *ilist, instr_t *inst) { if (ilist->translation_target != NULL && instr_get_translation(inst) == NULL) { instr_set_translation(inst, ilist->translation_target); } if (instrlist_get_our_mangling(ilist)) instr_set_our_mangling(inst, true); #if defined(CLIENT_INTERFACE) && defined(ARM) if (instr_is_meta(inst)) { dr_pred_type_t auto_pred = ilist->auto_pred; if (instr_predicate_is_cond(auto_pred)) { CLIENT_ASSERT(!instr_is_cti(inst), "auto-predication does not support cti's"); CLIENT_ASSERT( !TESTANY(EFLAGS_WRITE_NZCV, instr_get_arith_flags(inst, DR_QUERY_INCLUDE_COND_SRCS)), "cannot auto predicate a meta-inst that writes to NZCV"); if (!instr_is_predicated(inst)) instr_set_predicate(inst, auto_pred); } } #endif }
void loader_init(void) { uint i; privmod_t *mod; acquire_recursive_lock(&privload_lock); VMVECTOR_ALLOC_VECTOR(modlist_areas, GLOBAL_DCONTEXT, VECTOR_SHARED | VECTOR_NEVER_MERGE /* protected by privload_lock */ | VECTOR_NO_LOCK, modlist_areas); /* os specific loader initialization prologue before finalize the load */ os_loader_init_prologue(); /* Process client libs we loaded early but did not finalize */ for (i = 0; i < privmod_static_idx; i++) { /* Transfer to real list so we can do normal processing */ char name_copy[MAXIMUM_PATH]; mod = privload_insert(NULL, privmod_static[i].base, privmod_static[i].size, privmod_static[i].name, privmod_static[i].path); LOG(GLOBAL, LOG_LOADER, 1, "%s: processing imports for %s\n", __FUNCTION__, mod->name); /* save a copy for error msg, b/c mod will be unloaded (i#643) */ snprintf(name_copy, BUFFER_SIZE_ELEMENTS(name_copy), "%s", mod->name); NULL_TERMINATE_BUFFER(name_copy); if (!privload_load_finalize(mod)) { mod = NULL; /* it's been unloaded! */ #ifdef CLIENT_INTERFACE SYSLOG(SYSLOG_ERROR, CLIENT_LIBRARY_UNLOADABLE, 4, get_application_name(), get_application_pid(), name_copy, ".\n\tUnable to process imports of client library"); #endif CLIENT_ASSERT(false, "failure to process imports of client library"); } } /* os specific loader initialization epilogue after finalize the load */ os_loader_init_epilogue(); /* FIXME i#338: call loader_thread_init here once get * loader_init called after dynamo_thread_init but in a way that * works with Windows */ release_recursive_lock(&privload_lock); }
static void set_cache_size(uint val, uint *dst) { // COOMPLETEDD #254 set_cache_size CLIENT_ASSERT(dst != NULL, "invalid internal param"); switch (val) { case 8: *dst = CACHE_SIZE_8_KB; break; case 16: *dst = CACHE_SIZE_16_KB; break; case 32: *dst = CACHE_SIZE_32_KB; break; case 64: *dst = CACHE_SIZE_64_KB; break; case 128: *dst = CACHE_SIZE_128_KB; break; case 256: *dst = CACHE_SIZE_256_KB; break; case 512: *dst = CACHE_SIZE_512_KB; break; case 1024: *dst = CACHE_SIZE_1_MB; break; case 2048: *dst = CACHE_SIZE_2_MB; break; default: SYSLOG_INTERNAL_ERROR("Unknown processor cache size"); break; } }
const char * proc_get_cache_size_str(cache_size_t size) { // COMPLETEDD #255 proc_get_cache_size_str printf("Starting proc_get_cache_size_str\n"); static const char *strings[] = { "8 KB", "16 KB", "32 KB", "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "unknown" }; CLIENT_ASSERT(size <= CACHE_SIZE_UNKNOWN, "proc_get_cache_size_str: invalid size"); return strings[size]; }
/* prepends inst to the list ("inst" can be a chain of insts) */ void instrlist_prepend(instrlist_t *ilist, instr_t *inst) { instr_t *top = inst; instr_t *bot; CLIENT_ASSERT(instr_get_prev(inst) == NULL, "instrlist_prepend: cannot add middle of list"); check_translation(ilist, inst); while (instr_get_next(inst)) { inst = instr_get_next(inst); check_translation(ilist, inst); } bot = inst; if (ilist->first) { instr_set_next(bot, ilist->first); instr_set_prev(ilist->first, bot); ilist->first = top; } else { ilist->first = top; ilist->last = bot; } }
int opnd_get_reg_dcontext_offs(reg_id_t reg) { switch (reg) { case DR_REG_R0: return R0_OFFSET; case DR_REG_R1: return R1_OFFSET; case DR_REG_R2: return R2_OFFSET; case DR_REG_R3: return R3_OFFSET; case DR_REG_R4: return R4_OFFSET; case DR_REG_R5: return R5_OFFSET; case DR_REG_R6: return R6_OFFSET; case DR_REG_R7: return R7_OFFSET; case DR_REG_R8: return R8_OFFSET; case DR_REG_R9: return R9_OFFSET; case DR_REG_R10: return R10_OFFSET; case DR_REG_R11: return R11_OFFSET; case DR_REG_R12: return R12_OFFSET; case DR_REG_R13: return R13_OFFSET; case DR_REG_R14: return R14_OFFSET; case DR_REG_R15: return PC_OFFSET; default: CLIENT_ASSERT(false, "opnd_get_reg_dcontext_offs: invalid reg"); return -1; } }