uint32_t findFunctionEnd(uint32_t start) { static const uint32_t MaxScannedBytes = 0x400u; auto fnStart = start; auto fnMax = start; auto fnEnd = uint32_t { 0xFFFFFFFFu }; for (auto addr = start; addr < start + MaxScannedBytes; addr += 4) { auto instr = mem::read<espresso::Instruction>(addr); auto data = espresso::decodeInstruction(instr); if (!data) { // If we can't decode this instruction, then we gone done f****d up break; } if (addr > fnMax) { fnMax = addr; } if (isBranchInstr(data)) { auto meta = getBranchMeta(addr, instr, data, nullptr); // Ignore call instructions if (!meta.isCall) { if (meta.isVariable) { // We hit a variable non-call instruction, we can't scan // any further than this. If we don't have any instructions // further down, this is the final. if (fnMax > addr) { addr = fnMax; continue; } else { fnEnd = fnMax + 4; break; } } else { if (addr == fnMax && !meta.isConditional) { if (meta.target >= fnStart && meta.target < addr) { // If we are the last instruction, and this instruction unconditionally // branches backwards, that means that we must be at the end of the func. fnEnd = fnMax + 4; break; } } // We cannot follow unconditional branches outside of the function body // that we have already determined, this is because we don't want to follow // tail calls! if (meta.target > fnMax && meta.isConditional) { fnMax = meta.target; } } } } } return fnEnd; }
void analyse(uint32_t start, uint32_t end) { auto testStart = 0x025B81F4u; auto testEnd = 0x25B8374u + 4; // Scan through all our symbols and mark them as functions. // We do this first as they will not receive names if they are // detected by the analysis pass due to its naming system. kernel::loader::lockLoader(); const auto &modules = kernel::loader::getLoadedModules(); for (auto &mod : modules) { auto codeRangeStart = 0u; auto codeRangeEnd = 0u; for (auto &sec : mod.second->sections) { if (sec.name.compare(".text") == 0) { codeRangeStart = sec.start; codeRangeEnd = sec.end; break; } } for (auto &sym : mod.second->symbols) { if (sym.second.type == kernel::loader::SymbolType::Function) { if (sym.second.address >= codeRangeStart && sym.second.address < codeRangeEnd) { markAsFunction(sym.second.address, sym.first); } } } } kernel::loader::unlockLoader(); for (auto addr = start; addr < end; addr += 4) { auto instr = mem::read<espresso::Instruction>(addr); auto data = espresso::decodeInstruction(instr); if (!data) { continue; } if (isBranchInstr(data)) { auto meta = getBranchMeta(addr, instr, data, nullptr); if (!meta.isCall && !meta.isVariable) { sInstrData[meta.target].sourceBranches.push_back(addr); } // If this is a call, and its not variable, we should mark // the target as a function, since it likely is... if (meta.isCall && !meta.isVariable) { markAsFunction(meta.target); } } } }
static int cop1Emulate(int xcptno, struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) { mips_instruction ir; vaddr_t emulpc; vaddr_t contpc; unsigned int cond; int err = 0; ir = mips_get_word(xcp, REG_TO_VA xcp->cp0_epc, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } /* XXX NEC Vr54xx bug workaround */ if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir)) xcp->cp0_cause &= ~CAUSEF_BD; if (xcp->cp0_cause & CAUSEF_BD) { /* * The instruction to be emulated is in a branch delay slot * which means that we have to emulate the branch instruction * BEFORE we do the cop1 instruction. * * This branch could be a COP1 branch, but in that case we * would have had a trap for that instruction, and would not * come through this route. * * Linux MIPS branch emulator operates on context, updating the * cp0_epc. */ emulpc = REG_TO_VA(xcp->cp0_epc + 4); /* Snapshot emulation target */ if (__compute_return_epc(xcp)) { #ifdef CP1DBG printk("failed to emulate branch at %p\n", REG_TO_VA(xcp->cp0_epc)); #endif return SIGILL;; } ir = mips_get_word(xcp, emulpc, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } contpc = REG_TO_VA xcp->cp0_epc; } else { emulpc = REG_TO_VA xcp->cp0_epc; contpc = REG_TO_VA xcp->cp0_epc + 4; } emul: fpuemuprivate.stats.emulated++; switch (MIPSInst_OPCODE(ir)) { #ifdef CP0_STATUS_FR_SUPPORT /* R4000+ 64-bit fpu registers */ #ifndef SINGLE_ONLY_FPU case ldc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); int ft = MIPSInst_RT(ir); if (!(xcp->cp0_status & ST0_FR)) ft &= ~1; ctx->regs[ft] = mips_get_dword(xcp, va, &err); fpuemuprivate.stats.loads++; if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case sdc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); int ft = MIPSInst_RT(ir); if (!(xcp->cp0_status & ST0_FR)) ft &= ~1; fpuemuprivate.stats.stores++; if (mips_put_dword(xcp, va, ctx->regs[ft])) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; #endif case lwc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); fpureg_t val; int ft = MIPSInst_RT(ir); fpuemuprivate.stats.loads++; val = mips_get_word(xcp, va, &err); if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } if (xcp->cp0_status & ST0_FR) { /* load whole register */ ctx->regs[ft] = val; } else if (ft & 1) { /* load to m.s. 32 bits */ #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else ctx->regs[(ft & ~1)] &= 0xffffffff; ctx->regs[(ft & ~1)] |= val << 32; #endif } else { /* load to l.s. 32 bits */ ctx->regs[ft] &= ~0xffffffffLL; ctx->regs[ft] |= val; } } break; case swc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int val; int ft = MIPSInst_RT(ir); fpuemuprivate.stats.stores++; if (xcp->cp0_status & ST0_FR) { /* store whole register */ val = ctx->regs[ft]; } else if (ft & 1) { #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else /* store from m.s. 32 bits */ val = ctx->regs[(ft & ~1)] >> 32; #endif } else { /* store from l.s. 32 bits */ val = ctx->regs[ft]; } if (mips_put_word(xcp, va, val)) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; #else /* old 32-bit fpu registers */ case lwc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); ctx->regs[MIPSInst_RT(ir)] = mips_get_word(xcp, va, &err); fpuemuprivate.stats.loads++; if (err) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case swc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); fpuemuprivate.stats.stores++; if (mips_put_word (xcp, va, ctx->regs[MIPSInst_RT(ir)])) { fpuemuprivate.stats.errors++; return SIGBUS; } } break; case ldc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int rt = MIPSInst_RT(ir) & ~1; int errs = 0; fpuemuprivate.stats.loads++; #if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) ctx->regs[rt + 1] = mips_get_word(xcp, va + 0, &err); errs += err; ctx->regs[rt + 0] = mips_get_word(xcp, va + 4, &err); errs += err; #else ctx->regs[rt + 0] = mips_get_word(xcp, va + 0, &err); errs += err; ctx->regs[rt + 1] = mips_get_word(xcp, va + 4, &err); errs += err; #endif if (err) return SIGBUS; } break; case sdc1_op: { void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) + MIPSInst_SIMM(ir); unsigned int rt = MIPSInst_RT(ir) & ~1; fpuemuprivate.stats.stores++; #if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) if (mips_put_word(xcp, va + 0, ctx->regs[rt + 1])) return SIGBUS; if (mips_put_word(xcp, va + 4, ctx->regs[rt + 0])) return SIGBUS; #else if (mips_put_word(xcp, va + 0, ctx->regs[rt + 0])) return SIGBUS; if (mips_put_word(xcp, va + 4, ctx->regs[rt + 1])) return SIGBUS; #endif } break; #endif case cop1_op: switch (MIPSInst_RS(ir)) { #ifdef CP0_STATUS_FR_SUPPORT #if __mips64 && !defined(SINGLE_ONLY_FPU) case dmfc_op: /* copregister fs -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { int fs = MIPSInst_RD(ir); if (!(xcp->cp0_status & ST0_FR)) fs &= ~1; xcp->regs[MIPSInst_RT(ir)] = ctx->regs[fs]; } break; case dmtc_op: /* copregister fs <- rt */ { fpureg_t value; int fs = MIPSInst_RD(ir); if (!(xcp->cp0_status & ST0_FR)) fs &= ~1; value = (MIPSInst_RT(ir) == 0) ? 0 : xcp->regs[MIPSInst_RT(ir)]; ctx->regs[fs] = value; } break; #endif case mfc_op: /* copregister rd -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { /* default value from l.s. 32 bits */ int value = ctx->regs[MIPSInst_RD(ir)]; if (MIPSInst_RD(ir) & 1) { #ifdef SINGLE_ONLY_FPU /* illegal register in single-float mode */ return SIGILL; #else if (!(xcp->cp0_status & ST0_FR)) { /* move from m.s. 32 bits */ value = ctx-> regs[MIPSInst_RD(ir) & ~1] >> 32; } #endif } xcp->regs[MIPSInst_RT(ir)] = value; }