/* code cache to hold the call to "clean_call" and return to DR code cache */ static void code_cache_init(void) { void *drcontext; instrlist_t *ilist; instr_t *where; byte *end; drcontext = dr_get_current_drcontext(); code_cache = dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE | DR_MEMPROT_EXEC); ilist = instrlist_create(drcontext); /* The lean procecure simply performs a clean call, and then jump back */ /* jump back to the DR's code cache */ where = INSTR_CREATE_jmp_ind(drcontext, opnd_create_reg(DR_REG_XCX)); instrlist_meta_append(ilist, where); /* clean call */ dr_insert_clean_call(drcontext, ilist, where, (void *)clean_call_ins_trace, false, 0); /* Encodes the instructions into memory and then cleans up. */ end = instrlist_encode(drcontext, ilist, code_cache, false); DR_ASSERT((end - code_cache) < PAGE_SIZE); instrlist_clear_and_destroy(drcontext, ilist); /* set the memory as just +rx now */ dr_memory_protect(code_cache, PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_EXEC); }
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; }
static void test_instr_as_immed(void) { void *drcontext = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(drcontext); byte *pc; instr_t *ins0, *ins1, *ins2; opnd_t opnd; byte *highmem = PREFERRED_ADDR; pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, highmem); ASSERT(pc == highmem); /* Test push_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); instrlist_insert_push_instr_addr(drcontext, ins0, highmem, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_pop (drcontext, opnd_create_reg(DR_REG_RAX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); /* Test mov_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); /* Beyond TOS, but a convenient mem dest */ opnd = opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, -8, OPSZ_8); instrlist_insert_mov_instr_addr(drcontext, ins0, highmem, opnd, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_RAX), opnd)); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); instrlist_clear_and_destroy(drcontext, ilist); dr_raw_mem_free(highmem, PAGE_SIZE); }
DR_EXPORT void dr_init(client_id_t id) { /* Generate the "slowpath" which just returns to eax. */ void *dc = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(dc); PRE(ilist, NULL, INSTR_CREATE_jmp_ind(dc, opnd_create_reg(DR_REG_XAX))); slowpath = dr_nonheap_alloc(SLOWPATH_SIZE, (DR_MEMPROT_READ| DR_MEMPROT_WRITE| DR_MEMPROT_EXEC)); instrlist_encode(dc, ilist, slowpath, false /*no relative jumps*/); instrlist_clear_and_destroy(dc, ilist); dr_register_bb_event(event_bb); dr_register_exit_event(event_exit); }
static void reachability_test(void) { void *drcontext = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(drcontext); byte *gencode = (byte *) dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC); byte *pc; int res; byte *highmem = PREFERRED_ADDR; pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, highmem); ASSERT(pc == highmem); dr_fprintf(STDERR, " reachability test..."); /* Test auto-magically turning rip-rel that won't reach but targets xax * into absmem. */ instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_rel_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); /* Test auto-magically turning a reachable absmem into a rip-rel. */ instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem + 0x800, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, false); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); *(int*)(highmem + 0x800) = 0x12345678; res = ((int (*)(void))highmem)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); /* Test targeting upper 2GB of low 4GB */ highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, (byte *)0xabcd0000); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); /* Test targeting lower 2GB of low 4GB */ highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, (byte *)0x143d0000); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); instrlist_clear_and_destroy(drcontext, ilist); dr_nonheap_free(gencode, PAGE_SIZE); test_instr_as_immed(); dr_fprintf(STDERR, "success\n"); }
static void test_instr_opnds(void *dc) { /* Verbose disasm looks like this: * 32-bit: * 0x080f1ae0 ff 25 e7 1a 0f 08 jmp 0x080f1ae7 * 0x080f1ae6 b8 ef be ad de mov $0xdeadbeef -> %eax * 0x080f1ae0 a0 e6 1a 0f 08 mov 0x080f1ae6 -> %al * 0x080f1ae5 b8 ef be ad de mov $0xdeadbeef -> %eax * 64-bit: * 0x00000000006b8de0 ff 25 02 00 00 00 jmp <rel> 0x00000000006b8de8 * 0x00000000006b8de6 48 b8 ef be ad de 00 mov $0x00000000deadbeef -> %rax * 00 00 00 * 0x00000000006b8de0 8a 05 02 00 00 00 mov <rel> 0x00000000006b8de8 -> %al * 0x00000000006b8de6 48 b8 ef be ad de 00 mov $0x00000000deadbeef -> %rax * 00 00 00 */ instrlist_t *ilist; instr_t *tgt, *instr; byte *pc; short disp; ilist = instrlist_create(dc); /* test mem instr as ind jmp target */ tgt = INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX), opnd_create_immed_int(0xdeadbeef, OPSZ_PTR)); /* skip rex+opcode */ disp = IF_X64_ELSE(2,1); instrlist_append(ilist, INSTR_CREATE_jmp_ind (dc, opnd_create_mem_instr(tgt, disp, OPSZ_PTR))); instrlist_append(ilist, tgt); pc = instrlist_encode(dc, ilist, buf, true/*instr targets*/); ASSERT(pc != NULL); instrlist_clear(dc, ilist); #if VERBOSE pc = disassemble_with_info(dc, buf, STDOUT, true, true); pc = disassemble_with_info(dc, pc, STDOUT, true, true); #endif pc = buf; instr = instr_create(dc); pc = decode(dc, pc, instr); ASSERT(pc != NULL); ASSERT(instr_get_opcode(instr) == OP_jmp_ind); #ifdef X64 ASSERT(opnd_is_rel_addr(instr_get_src(instr, 0))); ASSERT(opnd_get_addr(instr_get_src(instr, 0)) == pc + disp); #else ASSERT(opnd_is_base_disp(instr_get_src(instr, 0))); ASSERT(opnd_get_base(instr_get_src(instr, 0)) == REG_NULL); ASSERT(opnd_get_index(instr_get_src(instr, 0)) == REG_NULL); ASSERT(opnd_get_disp(instr_get_src(instr, 0)) == (ptr_int_t)pc + disp); #endif /* test mem instr as TYPE_O */ tgt = INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX), opnd_create_immed_int(0xdeadbeef, OPSZ_PTR)); /* skip rex+opcode */ disp = IF_X64_ELSE(2,1); instrlist_append(ilist, INSTR_CREATE_mov_ld (dc, opnd_create_reg(DR_REG_AL), opnd_create_mem_instr(tgt, disp, OPSZ_1))); instrlist_append(ilist, tgt); pc = instrlist_encode(dc, ilist, buf, true/*instr targets*/); ASSERT(pc != NULL); instrlist_clear(dc, ilist); #if VERBOSE pc = disassemble_with_info(dc, buf, STDOUT, true, true); pc = disassemble_with_info(dc, pc, STDOUT, true, true); #endif pc = buf; instr_reset(dc, instr); pc = decode(dc, pc, instr); ASSERT(pc != NULL); ASSERT(instr_get_opcode(instr) == OP_mov_ld); #ifdef X64 ASSERT(opnd_is_rel_addr(instr_get_src(instr, 0))); ASSERT(opnd_get_addr(instr_get_src(instr, 0)) == pc + disp); #else ASSERT(opnd_is_base_disp(instr_get_src(instr, 0))); ASSERT(opnd_get_base(instr_get_src(instr, 0)) == REG_NULL); ASSERT(opnd_get_index(instr_get_src(instr, 0)) == REG_NULL); ASSERT(opnd_get_disp(instr_get_src(instr, 0)) == (ptr_int_t)pc + disp); #endif instr_free(dc, instr); instrlist_destroy(dc, ilist); }
/* make sure the following are consistent (though they could still all be wrong :)) * with respect to instr length and opcode: * - decode_fast * - decode * - INSTR_CREATE_ * - encode */ static void test_all_opcodes(void *dc) { byte *pc, *next_pc; byte *end; instrlist_t *ilist = instrlist_create(dc); instr_t *instr; /* we cannot pass on variadic args as separate args to another * macro, so we must split ours by # args (xref PR 208603) */ # define MEMARG(sz) (opnd_create_base_disp(REG_XCX, REG_NULL, 0, 0x37, sz)) # define IMMARG(sz) opnd_create_immed_int(37, sz) # define TGTARG opnd_create_instr(instrlist_last(ilist)) # define REGARG(reg) opnd_create_reg(REG_##reg) # define X86_ONLY 1 # define X64_ONLY 2 # define OPCODE(opc, icnm, ...) \ int len_##icnm; # include "ir_0args.h" # include "ir_1args.h" # include "ir_2args.h" # include "ir_3args.h" # include "ir_4args.h" # undef OPCODE /* we can encode+fast-decode some instrs cross-platform but we * leave that testing to the regression run on that platform */ # define OPCODE(opc, icnm, flags) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_0args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_1args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_2args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2, arg3) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2, arg3)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_3args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2, arg3, arg4) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2, arg3, arg4)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_4args.h" # undef OPCODE end = instrlist_encode(dc, ilist, buf, false); instr = instr_create(dc); pc = buf; # define OPCODE(opc, icnm, flags, ...) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0 && len_##icnm != 0) { \ instr_reset(dc, instr); \ next_pc = decode(dc, pc, instr); \ ASSERT((next_pc - pc) == decode_sizeof(dc, pc, NULL _IF_X64(NULL))); \ ASSERT((next_pc - pc) == len_##icnm); \ ASSERT(instr_get_opcode(instr) == OP_##opc); \ pc = next_pc; \ } } while (0); # include "ir_0args.h" # include "ir_1args.h" # include "ir_2args.h" # include "ir_3args.h" # include "ir_4args.h" # undef OPCODE #if VERBOSE for (pc = buf; pc < end; ) pc = disassemble_with_info(dc, pc, STDOUT, true, true); #endif instr_destroy(dc, instr); instrlist_clear_and_destroy(dc, ilist); }
void Shade::detour(void *address, void *target, void *&trampoline) { const size_t instr_max = 17; auto list = instrlist_create(dr); byte instr_data[instr_max]; byte *current = (byte *)address; byte *min_pos = (byte *)address + 5; size_t size = 0; while(current < min_pos) { read(current, instr_data, instr_max); auto instr = instr_create(dr); byte *decoded = decode_from_copy(dr, instr_data, current, instr); if(!decoded) error("Unknown instruction"); instrlist_append(list, instr); instr_make_persistent(dr, instr); current += (size_t)(decoded - instr_data); size += instr_length(dr, instr); } auto instr = INSTR_CREATE_jmp(dr, opnd_create_pc(current)); size += instr_length(dr, instr); instrlist_append(list, instr); auto local_trampoline = alloca(size); if(!local_trampoline) error("Out of memory"); void *remote = code_section.allocate(size, 4); if(!instrlist_encode_to_copy(dr, list, (byte *)local_trampoline, (byte *)remote, 0, true)) error("Unable to encode instructions"); instrlist_clear_and_destroy(dr, list); write(remote, local_trampoline, size); trampoline = remote; char code[5]; DWORD offset = (size_t)target - (size_t)address - 5; code[0] = 0xE9; *(DWORD *)(code + 1) = offset; access(address, 5, [&] { write(address, code, 5); }); }
void test_dr_insert_it_instrs_cbr(void *dcontext) { instrlist_t *ilist = instrlist_create(dcontext); instr_t *where = INSTR_CREATE_label(dcontext); instr_t *instr_it1, *instr_it2, *instr_it3; byte buffer[4096]; instrlist_append(ilist, where); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_jump (dcontext, opnd_create_instr(where))); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_jump (dcontext, opnd_create_instr(where))); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_move (dcontext, opnd_create_reg(DR_REG_R1), opnd_create_reg(DR_REG_R2))); instrlist_preinsert(ilist, where, XINST_CREATE_jump (dcontext, opnd_create_instr(where))); /* set them all to be predicated and reinstate it instrs */ for (where = instrlist_first(ilist); where; where = instr_get_next(where)) { bool ok = instr_set_isa_mode(where, DR_ISA_ARM_THUMB); DR_ASSERT(ok); instr_set_predicate(where, DR_PRED_LS); } dr_insert_it_instrs(dcontext, ilist); /* Make sure it was encoded properly, noting that the branches * should *not* be in any IT-block. * it * mov.ls r1, r2 * b.ls @0x47366864 * itt * mov.ls r1, r2 * mov.ls r1, r2 * b.ls @0x47366864 * ittt * mov.ls r1, r2 * mov.ls r1, r2 * mov.ls r1, r2 * b.ls @0x47366864 */ instr_it1 = instrlist_first(ilist); instr_it2 = instr_get_next(instr_get_next(instr_get_next(instr_it1))); instr_it3 = instr_get_next(instr_get_next(instr_get_next(instr_get_next(instr_it2)))); DR_ASSERT(instr_get_opcode(instr_it1) == OP_it); DR_ASSERT(instr_it_block_get_count(instr_it1) == 1); DR_ASSERT(instr_get_opcode(instr_it2) == OP_it); DR_ASSERT(instr_it_block_get_count(instr_it2) == 2); DR_ASSERT(instr_get_opcode(instr_it3) == OP_it); DR_ASSERT(instr_it_block_get_count(instr_it3) == 3); instrlist_encode(dcontext, ilist, buffer, true); }