static int get_isa(Context * ctx, ContextAddress addr, ContextISA * isa) { if (context_get_isa(ctx, addr, isa) < 0) { memset(isa, 0, sizeof(ContextISA)); return -1; } #if ENABLE_MemoryMap if (isa->size == 0) { unsigned i, j; MemoryMap * client_map = NULL; MemoryMap * target_map = NULL; Context * mem = context_get_group(ctx, CONTEXT_GROUP_PROCESS); if (memory_map_get(mem, &client_map, &target_map) == 0) { isa->addr = addr; isa->size = ~addr + 1; for (j = 0; j < 2; j++) { MemoryMap * map = j ? target_map : client_map; for (i = 0; i < map->region_cnt; i++) { MemoryRegion * r = map->regions + i; ContextAddress x = r->addr; ContextAddress y = r->addr + r->size; if (x > addr && (addr + isa->size == 0 || x < addr + isa->size)) isa->size = x - addr; if (y > addr && (addr + isa->size == 0 || y < addr + isa->size)) isa->size = y - addr; } } } } #endif return 0; }
static void command_disassemble(char * token, Channel * c) { int error = 0; Context * ctx = NULL; DisassembleCmdArgs * args = (DisassembleCmdArgs *)loc_alloc_zero(sizeof(DisassembleCmdArgs)); json_read_string(&c->inp, args->id, sizeof(args->id)); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); args->addr = (ContextAddress)json_read_uint64(&c->inp); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); args->size = (ContextAddress)json_read_uint64(&c->inp); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); json_read_struct(&c->inp, read_disassembly_params, args); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); ctx = id2ctx(args->id); if (ctx == NULL) error = ERR_INV_CONTEXT; else if (ctx->exited) error = ERR_ALREADY_EXITED; else if (context_get_group(ctx, CONTEXT_GROUP_PROCESS)->mem_access == 0) error = ERR_INV_CONTEXT; if (error != 0) { write_stringz(&c->out, "R"); write_stringz(&c->out, token); write_errno(&c->out, error); write_stringz(&c->out, "null"); write_stream(&c->out, MARKER_EOM); loc_free(args); } else { channel_lock(args->c = c); strlcpy(args->token, token, sizeof(args->token)); post_safe_event(ctx, safe_event_disassemble, args); } }
int cpu_bp_on_resume(Context * ctx, int * single_step) { ContextExtensionARM * ext = EXT(ctx); ContextExtensionARM * bps = EXT(context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT)); if (ctx->stopped_by_cb != NULL || ext->hw_bps_regs_generation != bps->hw_bps_generation) { if (set_debug_regs(ctx, single_step) < 0) return -1; } return 0; }
int cpu_bp_on_suspend(Context * ctx, int * triggered) { unsigned cb_cnt = 0; ContextExtensionARM * ext = EXT(ctx); ContextExtensionARM * bps = EXT(context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT)); if (ctx->exiting) return 0; if (bps->bp_cnt > 0 || bps->wp_cnt > 0) { int i; ContextAddress pc = 0; if (read_reg(ctx, pc_def, pc_def->size, &pc) < 0) return -1; if (ext->skip_wp_addr != pc) ext->skip_wp_set = 0; if (bps->bp_cnt > 0) { for (i = 0; i < bps->bp_cnt; i++) { ContextBreakpoint * cb = bps->hw_bps[i]; if (cb != NULL && ((uint32_t)cb->address & ~0x1) == pc && (ext->armed & (1u << i))) { ext->triggered_hw_bps[cb_cnt++] = cb; } } } if (bps->wp_cnt > 0) { siginfo_t siginfo; pid_t pid = id2pid(ctx->id, NULL); if (ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo) < 0) return -1; if (siginfo.si_signo == SIGTRAP && (siginfo.si_code & 0xffff) == 0x0004 && siginfo.si_errno < 0) { /* Watchpoint */ for (i = bps->bp_cnt; i < bps->bp_cnt + bps->wp_cnt; i++) { ContextBreakpoint * cb = bps->hw_bps[i]; if (cb != NULL && (ext->armed & (1u << i))) { if (bps->wp_cnt > 1) { ContextAddress addr = (ContextAddress)siginfo.si_addr; if (addr < cb->address || addr >= cb->address + cb->length) continue; } ext->triggered_hw_bps[cb_cnt++] = cb; ext->skip_wp_set |= 1u << i; ext->skip_wp_addr = pc; } } } } if (cb_cnt > 0) { ctx->stopped_by_cb = ext->triggered_hw_bps; ctx->stopped_by_cb[cb_cnt] = NULL; } } *triggered = cb_cnt > 0; return 0; }
int cpu_bp_plant(ContextBreakpoint * bp) { Context * ctx = bp->ctx; ContextExtensionARM * bps = EXT(ctx); assert(bp->access_types); assert(ctx == context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT)); if (get_bp_info(ctx) < 0) return -1; if (bp->access_types & CTX_BP_ACCESS_VIRTUAL) { if (bp->access_types & CTX_BP_ACCESS_INSTRUCTION) { unsigned i; unsigned n = 0; for (i = 0; i < bps->bp_cnt; i++) { assert(bps->hw_bps[i] != bp); if (bps->hw_bps[i] == NULL) { bps->hw_bps[i] = bp; bps->hw_bps_generation++; n++; break; } } if (n == 0) { clear_bp(bp); errno = ERR_UNSUPPORTED; return -1; } } if (bp->access_types & (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE)) { unsigned n = 0; if (bp->length <= bps->wp_size) { unsigned i; for (i = bps->bp_cnt; i < bps->bp_cnt + bps->wp_cnt; i++) { assert(bps->hw_bps[i] != bp); if (bps->hw_bps[i] == NULL) { bps->hw_bps[i] = bp; bps->hw_bps_generation++; n++; break; } } } if (n == 0) { clear_bp(bp); errno = ERR_UNSUPPORTED; return -1; } } return 0; } errno = ERR_UNSUPPORTED; return -1; }
int cpu_bp_get_capabilities(Context * ctx) { int res = 0; ContextExtensionARM * bps = EXT(ctx); if (ctx != context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT)) return 0; if (get_bp_info(ctx) < 0) return 0; if (bps->bp_cnt > 0) { res |= CTX_BP_ACCESS_INSTRUCTION; } if (bps->wp_cnt > 0) { res |= CTX_BP_ACCESS_DATA_READ; res |= CTX_BP_ACCESS_DATA_WRITE; } res |= CTX_BP_ACCESS_VIRTUAL; return res; }
void add_disassembler(Context * ctx, const char * isa, Disassembler disassembler) { DisassemblerInfo * i = NULL; ContextExtensionDS * ext = EXT(ctx); assert(ctx == context_get_group(ctx, CONTEXT_GROUP_CPU)); if ((i = find_disassembler_info(ctx, isa)) == NULL) { if (ext->disassemblers_cnt >= ext->disassemblers_max) { ext->disassemblers_max += 8; ext->disassemblers = (DisassemblerInfo *)loc_realloc(ext->disassemblers, sizeof(DisassemblerInfo) * ext->disassemblers_max); } i = ext->disassemblers + ext->disassemblers_cnt++; i->isa = loc_strdup(isa); } i->disassembler = disassembler; }
static void command_get_capabilities_cache_client(void * x) { int error = 0; Context * ctx = NULL; ContextExtensionDS * ext = NULL; GetCapabilitiesCmdArgs * args = (GetCapabilitiesCmdArgs *)x; Channel * c = cache_channel(); ctx = id2ctx(args->id); if (ctx == NULL) error = ERR_INV_CONTEXT; else if (ctx->exited) error = ERR_ALREADY_EXITED; else ext = EXT(context_get_group(ctx, CONTEXT_GROUP_CPU)); cache_exit(); if (!is_channel_closed(c)) { OutputStream * out = &c->out; write_stringz(out, "R"); write_stringz(out, args->token); write_errno(out, error); write_stream(out, '['); if (ext != NULL) { unsigned i; for (i = 0; i < ext->disassemblers_cnt; i++) { if (i > 0) write_stream(out, ','); write_stream(out, '{'); json_write_string(out, "ISA"); write_stream(out, ':'); json_write_string(out, ext->disassemblers[i].isa); write_stream(out, '}'); } } write_stream(out, ']'); write_stream(out, 0); write_stream(out, MARKER_EOM); } }
static void command_get_capabilities(char * token, Channel * c) { int error = 0; char id[256]; Context * ctx = NULL; ContextExtensionDS * ext = NULL; unsigned i, j; json_read_string(&c->inp, id, sizeof(id)); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); ctx = id2ctx(id); if (ctx == NULL) error = ERR_INV_CONTEXT; else if (ctx->exited) error = ERR_ALREADY_EXITED; ctx = context_get_group(ctx, CONTEXT_GROUP_CPU); ext = EXT(ctx); write_stringz(&c->out, "R"); write_stringz(&c->out, token); write_errno(&c->out, error); write_stream(&c->out, '['); for (i = 0, j = 0; i < ext->disassemblers_cnt; i++) { if (j > 0) write_stream(&c->out, ','); write_stream(&c->out, '{'); json_write_string(&c->out, "ISA"); write_stream(&c->out, ':'); json_write_string(&c->out, ext->disassemblers[i].isa); write_stream(&c->out, '}'); j++; } write_stream(&c->out, ']'); write_stream(&c->out, 0); write_stream(&c->out, MARKER_EOM); }
static int set_debug_regs(Context * ctx, int * step_over_hw_bp) { int i, j; ContextAddress pc = 0; Context * grp = context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT); ContextExtensionARM * ext = EXT(ctx); ContextExtensionARM * bps = EXT(grp); pid_t pid = id2pid(ctx->id, NULL); assert(bps->info_ok); ext->armed = 0; *step_over_hw_bp = 0; if (read_reg(ctx, pc_def, pc_def->size, &pc) < 0) return -1; for (i = 0; i < bps->bp_cnt + bps->wp_cnt; i++) { uint32_t cr = 0; ContextBreakpoint * cb = bps->hw_bps[i]; if (i == 0 && ext->hw_stepping) { uint32_t vr = 0; if (ext->hw_stepping == 1) { vr = (uint32_t)ext->step_addr & ~0x1; cr |= 0x3 << 5; } else { vr = (uint32_t)pc; cr |= 0x1 << 22; cr |= 0xf << 5; } cr |= 0x7u; if (ptrace(PTRACE_SETHBPREGS, pid, 1, &vr) < 0) return -1; } else if (cb != NULL) { if (i < bps->bp_cnt && ((uint32_t)cb->address & ~0x1) == pc) { /* Skipping the breakpoint */ *step_over_hw_bp = 1; } else if (bps->arch >= ARM_DEBUG_ARCH_V7_ECP14 && (ext->skip_wp_set & (1u << i))) { /* Skipping the watchpoint */ assert(i >= bps->bp_cnt); *step_over_hw_bp = 1; } else { uint32_t vr = (uint32_t)cb->address & ~0x1; if (i < bps->bp_cnt) { cr |= 0x3 << 5; } else { vr = (uint32_t)cb->address & ~0x3; for (j = 0; j < 4; j++) { if (vr + j < cb->address) continue; if (vr + j >= cb->address + cb->length) continue; cr |= 1 << (5 + j); } if (cb->access_types & CTX_BP_ACCESS_DATA_READ) cr |= 1 << 3; if (cb->access_types & CTX_BP_ACCESS_DATA_WRITE) cr |= 1 << 4; } cr |= 0x7; if (i < bps->bp_cnt) { if (ptrace(PTRACE_SETHBPREGS, pid, i * 2 + 1, &vr) < 0) return -1; } else { if (ptrace(PTRACE_SETHBPREGS, pid, -(i * 2 + 1), &vr) < 0) return -1; } ext->armed |= 1 << i; } } if (cr == 0) { /* Linux kernel does not allow 0 as Control Register value */ cr |= 0x3u << 1; cr |= 0xfu << 5; if (i >= bps->bp_cnt) { cr |= 1u << 4; } } if (i < bps->bp_cnt) { if (ptrace(PTRACE_SETHBPREGS, pid, i * 2 + 2, &cr) < 0) return -1; } else { if (ptrace(PTRACE_SETHBPREGS, pid, -(i * 2 + 2), &cr) < 0) return -1; } } ext->hw_bps_regs_generation = bps->hw_bps_generation; return 0; }
static Context * get_reset_context(Context * ctx) { ContextExtensionRS * ext = EXT(ctx); if (ext->group > 0) return context_get_group(ctx, ext->group); return context_get_group(ctx, CONTEXT_GROUP_CPU); }
static void write_context(OutputStream * out, Context * ctx) { assert(!ctx->exited); write_stream(out, '{'); json_write_string(out, "ID"); write_stream(out, ':'); json_write_string(out, ctx->id); if (ctx->parent != NULL) { write_stream(out, ','); json_write_string(out, "ParentID"); write_stream(out, ':'); json_write_string(out, ctx->parent->id); } write_stream(out, ','); json_write_string(out, "ProcessID"); write_stream(out, ':'); json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id); if (ctx->name != NULL) { write_stream(out, ','); json_write_string(out, "Name"); write_stream(out, ':'); json_write_string(out, ctx->name); } write_stream(out, ','); json_write_string(out, "BigEndian"); write_stream(out, ':'); json_write_boolean(out, ctx->big_endian); if (ctx->mem_access) { int cnt = 0; write_stream(out, ','); json_write_string(out, "AddressSize"); write_stream(out, ':'); json_write_ulong(out, context_word_size(ctx)); write_stream(out, ','); json_write_string(out, "AccessTypes"); write_stream(out, ':'); write_stream(out, '['); if (ctx->mem_access & MEM_ACCESS_INSTRUCTION) { if (cnt++) write_stream(out, ','); json_write_string(out, "instruction"); } if (ctx->mem_access & MEM_ACCESS_DATA) { if (cnt++) write_stream(out, ','); json_write_string(out, "data"); } if (ctx->mem_access & MEM_ACCESS_IO) { if (cnt++) write_stream(out, ','); json_write_string(out, "io"); } if (ctx->mem_access & MEM_ACCESS_USER) { if (cnt++) write_stream(out, ','); json_write_string(out, "user"); } if (ctx->mem_access & MEM_ACCESS_SUPERVISOR) { if (cnt++) write_stream(out, ','); json_write_string(out, "supervisor"); } if (ctx->mem_access & MEM_ACCESS_HYPERVISOR) { if (cnt++) write_stream(out, ','); json_write_string(out, "hypervisor"); } if (ctx->mem_access & MEM_ACCESS_VIRTUAL) { if (cnt++) write_stream(out, ','); json_write_string(out, "virtual"); } if (ctx->mem_access & MEM_ACCESS_PHYSICAL) { if (cnt++) write_stream(out, ','); json_write_string(out, "physical"); } if (ctx->mem_access & MEM_ACCESS_CACHE) { if (cnt++) write_stream(out, ','); json_write_string(out, "cache"); } if (ctx->mem_access & MEM_ACCESS_TLB) { if (cnt++) write_stream(out, ','); json_write_string(out, "tlb"); } write_stream(out, ']'); } write_stream(out, '}'); }
static void write_context(OutputStream * out, char * id, Context * ctx, int frame, RegisterDefinition * reg_def) { assert(!ctx->exited); write_stream(out, '{'); json_write_string(out, "ID"); write_stream(out, ':'); json_write_string(out, id); write_stream(out, ','); json_write_string(out, "ParentID"); write_stream(out, ':'); if (reg_def->parent != NULL) { json_write_string(out, register2id(ctx, frame, reg_def->parent)); } else if (frame < 0 || is_top_frame(ctx, frame)) { json_write_string(out, ctx->id); } else { json_write_string(out, frame2id(ctx, frame)); } write_stream(out, ','); json_write_string(out, "ProcessID"); write_stream(out, ':'); json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id); write_stream(out, ','); json_write_string(out, "Name"); write_stream(out, ':'); json_write_string(out, reg_def->name); if (reg_def->size > 0) { write_stream(out, ','); json_write_string(out, "Size"); write_stream(out, ':'); json_write_long(out, reg_def->size); } if (reg_def->dwarf_id >= 0) { write_stream(out, ','); json_write_string(out, "DwarfID"); write_stream(out, ':'); json_write_long(out, reg_def->dwarf_id); } if (reg_def->eh_frame_id >= 0) { write_stream(out, ','); json_write_string(out, "EhFrameID"); write_stream(out, ':'); json_write_long(out, reg_def->eh_frame_id); } write_boolean_member(out, "BigEndian", reg_def->big_endian); write_boolean_member(out, "Float", reg_def->fp_value); write_boolean_member(out, "Readable", !reg_def->no_read); write_boolean_member(out, "Writeable", !reg_def->no_write); write_boolean_member(out, "ReadOnce", reg_def->read_once); write_boolean_member(out, "WriteOnce", reg_def->write_once); write_boolean_member(out, "Volatile", reg_def->volatile_value); write_boolean_member(out, "SideEffects", reg_def->side_effects); write_boolean_member(out, "LeftToRight", reg_def->left_to_right); if (reg_def->first_bit > 0) { write_stream(out, ','); json_write_string(out, "FirstBit"); write_stream(out, ':'); json_write_long(out, reg_def->first_bit); } if (reg_def->bits != NULL) { int i = 0; write_stream(out, ','); json_write_string(out, "Bits"); write_stream(out, ':'); write_stream(out, '['); while (reg_def->bits[i] >= 0) { if (i > 0) write_stream(out, ','); json_write_long(out, reg_def->bits[i++]); } write_stream(out, ']'); } if (reg_def->values != NULL) { int i = 0; write_stream(out, ','); json_write_string(out, "Values"); write_stream(out, ':'); write_stream(out, '['); while (reg_def->values[i] != NULL) { NamedRegisterValue * v = reg_def->values[i++]; if (i > 1) write_stream(out, ','); write_stream(out, '{'); json_write_string(out, "Value"); write_stream(out, ':'); json_write_binary(out, v->value, reg_def->size); if (v->name != NULL) { write_stream(out, ','); json_write_string(out, "Name"); write_stream(out, ':'); json_write_string(out, v->name); } if (v->description != NULL) { write_stream(out, ','); json_write_string(out, "Description"); write_stream(out, ':'); json_write_string(out, v->description); } write_stream(out, '}'); } write_stream(out, ']'); } if (reg_def->memory_address > 0) { write_stream(out, ','); json_write_string(out, "MemoryAddress"); write_stream(out, ':'); json_write_uint64(out, reg_def->memory_address); } if (reg_def->memory_context != NULL) { write_stream(out, ','); json_write_string(out, "MemoryContext"); write_stream(out, ':'); json_write_string(out, reg_def->memory_context); } if (reg_def->role != NULL) { write_stream(out, ','); json_write_string(out, "Role"); write_stream(out, ':'); json_write_string(out, reg_def->role); } else if (reg_def == get_PC_definition(ctx)) { write_stream(out, ','); json_write_string(out, "Role"); write_stream(out, ':'); json_write_string(out, "PC"); } if (reg_def->description != NULL) { write_stream(out, ','); json_write_string(out, "Description"); write_stream(out, ':'); json_write_string(out, reg_def->description); } if (reg_def->size > 0) { RegisterDefinition * parent_reg_def = NULL; parent_reg_def = reg_def->parent; while (parent_reg_def != NULL && parent_reg_def->size == 0) parent_reg_def = parent_reg_def->parent; if (parent_reg_def != NULL) { if (reg_def->offset >= parent_reg_def->offset && reg_def->offset + reg_def->size <= parent_reg_def->offset + parent_reg_def->size) { write_stream(out, ','); json_write_string(out, "Offset"); write_stream(out, ':'); json_write_uint64(out, reg_def->offset - parent_reg_def->offset); } } } write_stream(out, '}'); write_stream(out, 0); }
static int disassemble_block(Context * ctx, OutputStream * out, uint8_t * mem_buf, ContextAddress buf_addr, ContextAddress buf_size, ContextAddress mem_size, ContextISA * isa, DisassembleCmdArgs * args) { ContextAddress offs = 0; Disassembler * disassembler = NULL; Context * cpu = context_get_group(ctx, CONTEXT_GROUP_CPU); int disassembler_ok = 0; DisassemblerParams param; param.ctx = ctx; param.big_endian = ctx->big_endian; param.pseudo_instr = args->pseudo_instr; param.simplified = args->simplified; if (args->isa) { isa->isa = args->isa; isa->addr = args->addr; isa->size = args->size; } write_stream(out, '['); while (offs < buf_size && offs < mem_size) { ContextAddress addr = buf_addr + offs; ContextAddress size = mem_size - offs; DisassemblyResult * dr = NULL; if (args->isa == NULL && (addr < isa->addr || (isa->addr + isa->size >= isa->addr && addr >= isa->addr + isa->size))) { if (get_isa(ctx, addr, isa) < 0) return -1; disassembler_ok = 0; } if (!disassembler_ok) { disassembler = find_disassembler(cpu, isa->isa); if (disassembler == NULL) disassembler = find_disassembler(cpu, isa->def); disassembler_ok = 1; } if (disassembler) dr = disassembler(mem_buf + (size_t)offs, addr, size, ¶m); if (dr == NULL) { static char buf[32]; static DisassemblyResult dd; memset(&dd, 0, sizeof(dd)); if (isa->alignment >= 4 && (addr & 0x3) == 0 && offs <= mem_size + 4) { unsigned i; uint32_t v = 0; for (i = 0; i < 4; i++) v |= (uint32_t)mem_buf[offs + i] << (i * 8); snprintf(buf, sizeof(buf), ".word 0x%08x", v); dd.size = 4; } else if (isa->alignment >= 2 && (addr & 0x1) == 0 && offs <= mem_size + 2) { unsigned i; uint16_t v = 0; for (i = 0; i < 2; i++) v |= (uint16_t)mem_buf[offs + i] << (i * 8); snprintf(buf, sizeof(buf), ".half 0x%04x", v); dd.size = 2; } else { snprintf(buf, sizeof(buf), ".byte 0x%02x", mem_buf[offs]); dd.size = 1; } dd.text = buf; dr = ⅆ } assert(dr->size > 0); if (offs > 0) write_stream(out, ','); write_stream(out, '{'); json_write_string(out, "Address"); write_stream(out, ':'); json_write_uint64(out, addr); write_stream(out, ','); json_write_string(out, "Size"); write_stream(out, ':'); json_write_uint64(out, dr->size); write_stream(out, ','); json_write_string(out, "Instruction"); write_stream(out, ':'); write_stream(out, '['); write_stream(out, '{'); json_write_string(out, "Type"); write_stream(out, ':'); json_write_string(out, "String"); write_stream(out, ','); json_write_string(out, "Text"); write_stream(out, ':'); json_write_string(out, dr->text); write_stream(out, '}'); write_stream(out, ']'); if (args->opcode_value) { write_stream(out, ','); json_write_string(out, "OpcodeValue"); write_stream(out, ':'); json_write_binary(out, mem_buf + (size_t)offs, (size_t)dr->size); } write_stream(out, '}'); offs += dr->size; } write_stream(out, ']'); return 0; }