232 lines
8.6 KiB
Lua
232 lines
8.6 KiB
Lua
|
|
require("ee-gpr-alias")
|
|
require("utils")
|
|
|
|
MipsInsn = {}
|
|
MipsInsn.IsAddi = function(insn) return (insn & 0xfc000000) == 0x20000000 end -- addi rt,rs,simm
|
|
MipsInsn.IsAddiu = function(insn) return (insn & 0xfc000000) == 0x24000000 end -- addiu rt,rs,simm
|
|
MipsInsn.IsDaddu = function(insn) return (insn & 0xfc0007ff) == 0x0000002d end -- daddu rd,rs,rt
|
|
MipsInsn.IsAddu = function(insn) return (insn & 0xfc0007ff) == 0x00000021 end -- addu rd,rs,rt
|
|
MipsInsn.IsBeq = function(insn) return (insn & 0xfc000000) == 0x10000000 end -- beq rs,rt,off
|
|
MipsInsn.IsJ = function(insn) return (insn & 0xfc000000) == 0x08000000 end -- j target
|
|
MipsInsn.IsJal = function(insn) return (insn & 0xfc000000) == 0x0c000000 end -- jal target
|
|
MipsInsn.IsJr = function(insn) return (insn & 0xfc1fffff) == 0x00000008 end -- jr rs
|
|
MipsInsn.IsLq = function(insn) return (insn & 0xfc000000) == 0x78000000 end -- lq rt,simm(rs)
|
|
MipsInsn.IsLd = function(insn) return (insn & 0xfc000000) == 0xdc000000 end -- ld rt,simm(rs)
|
|
MipsInsn.IsLw = function(insn) return (insn & 0xfc000000) == 0x8c000000 end -- lw rt,simm(rs)
|
|
MipsInsn.IsSq = function(insn) return (insn & 0xfc000000) == 0x7c000000 end -- sq rt,simm(rs)
|
|
MipsInsn.IsSd = function(insn) return (insn & 0xfc000000) == 0xfc000000 end -- sd rt,simm(rs)
|
|
MipsInsn.IsSw = function(insn) return (insn & 0xfc000000) == 0xac000000 end -- sw rt,simm(rs)
|
|
MipsInsn.IsEnd = function(insn) return (insn & 0xfc00003f) == 0x0000000d end -- break [code]
|
|
|
|
MipsInsn.GetRt = function(insn) return (insn >> 16) & 0x1f end
|
|
MipsInsn.GetRs = function(insn) return (insn >> 21) & 0x1f end
|
|
MipsInsn.GetRd = function(insn) return (insn >> 11) & 0x1f end
|
|
--MipsInsn.GetSimm = function(insn) return ((insn << 48) >> 48) end -- this can't create a negative value correctly
|
|
MipsInsn.GetSimm = function(insn)
|
|
-- Lua5.3 shifts are all logical (WHY!?). threfore (insn<<48)>>48 cannot extend the sign.
|
|
-- Instead of using shift, do following
|
|
local bit = 16 -- sign bit place.
|
|
local v = insn & 0xffff
|
|
local m = 1 << (bit - 1)
|
|
v = v & ((1 << bit) - 1)
|
|
return (v ~ m) - m -- '~' is xor in Lua... how strange it is.
|
|
end
|
|
MipsInsn.GetOff = function(insn) return MipsInsn.GetSimm(insn) end
|
|
MipsInsn.GetTarget = function(insn) return insn & 0x3ffffff end
|
|
|
|
local dbgPrintf = function(s,...)
|
|
-- Uncomment to enable debug trace logging of MipsStackTrace.
|
|
-- print (string.format(...))
|
|
end
|
|
|
|
-- return FIFO queue of stack trace
|
|
-- the queue item is { caller-addr, return-from }
|
|
--
|
|
-- example:
|
|
-- print("=== stack trace ===")
|
|
-- local stack_trace = MipsStackTrace(eeObj, eeObj.GetPc()+4, eeObj.GetGpr(gpr.ra), eeObj.GetGpr(gpr.sp))
|
|
-- while not stack_trace:isEmpty() do
|
|
-- local caller = stack_trace:dequeue()
|
|
-- local symName,symPc = debugObj.GetSymbol(caller[1])
|
|
-- print( string.format(" 0x%08x %-48s [will return from : %x]", caller[1], synName, caller[2]) )
|
|
-- end
|
|
--
|
|
-- NOTE: you must +4 against GetPc() if you in a EE/IOP hook.
|
|
-- Because EE/IOP jit executed the instruction at the address already and it might affect $sp or $ra.
|
|
--
|
|
-- obj : eeObj or iopObj
|
|
-- pc : current pc (from GetPC or readout from thread context)
|
|
-- ra : current ra (from GetGpr or readout from thread context)
|
|
-- sp : current sp (from GetGpr or readout from thread context)
|
|
MipsStackTrace = function (obj, pc, ra, sp, depth)
|
|
local max_depth = depth or 10 -- max trace depth
|
|
local n_j = 1
|
|
local jmax = {}
|
|
local depth = 0
|
|
local bdl_count = 0
|
|
local new_pc = 0
|
|
local icount = 0
|
|
|
|
local result = Queue.new()
|
|
local pushed_ra = Queue.new()
|
|
while depth < max_depth and icount < 2048 do
|
|
-- TODO: error checks
|
|
if (pc & 3) ~= 0 then
|
|
return result
|
|
end
|
|
pc = pc & 0x01ffffff
|
|
sp = sp & 0x01ffffff
|
|
local insn = obj.ReadMem32(pc)
|
|
dbgPrintf(string.format("trace .. pc=%x insn=%x", pc, insn))
|
|
-- result:enqueue( { pc, insn } )
|
|
|
|
if MipsInsn.IsJr(insn) and MipsInsn.GetRs(insn) == gpr.ra then
|
|
bdl_count = 1
|
|
depth = depth + 1
|
|
new_pc = ra
|
|
dbgPrintf(string.format("jr ra : ra=%x", ra))
|
|
icount = 0
|
|
dbgPrintf(string.format("enqueue caller pc=%x ret addr=%x", new_pc-8, pc))
|
|
result:enqueue( {new_pc-8, pc} ) -- {return-addr, where-from}
|
|
elseif MipsInsn.IsAddiu(insn) and MipsInsn.GetRt(insn) == gpr.sp and MipsInsn.GetRs(insn) == gpr.sp then
|
|
sp = sp + MipsInsn.GetSimm(insn) -- ((insn<<48) >>48)
|
|
dbgPrintf(string.format("addiu sp,sp,** : new sp = %x", sp))
|
|
elseif (MipsInsn.IsAddu(insn) or MipsInsn.IsDaddu(insn)) and MipsInsn.GetRd(insn) == gpr.sp then
|
|
dbgPrintf(string.format("addiu sp,**,** ... we can't get new sp value..."))
|
|
return result
|
|
elseif MipsInsn.IsLq(insn) or MipsInsn.IsLd(insn) or MipsInsn.IsLw(insn) then
|
|
if MipsInsn.GetRt(insn) == gpr.ra and MipsInsn.GetRs(insn) == gpr.sp then
|
|
-- the code might push $ra on the stack after start pc.
|
|
-- in such case, we must not retrieve $ra value from the memory.
|
|
if pushed_ra:isEmpty() then
|
|
local imm = MipsInsn.GetSimm(insn) -- ((insn<<48) >>48)
|
|
dbgPrintf(string.format("retrieve ra from stack(%x) : sp=%x imm=%x", sp+imm, sp, imm))
|
|
ra = obj.ReadMem32(sp + imm)
|
|
dbgPrintf(string.format("load ra,%x(sp) : sp = %x+%x, ra = %x", imm, sp, imm, ra))
|
|
else
|
|
dbgPrintf(string.format("retrieve ra from pushed one"))
|
|
pushed_ra:dequeue()
|
|
end
|
|
end
|
|
elseif MipsInsn.IsSq(insn) or MipsInsn.IsSd(insn) or MipsInsn.IsSw(insn) then
|
|
if MipsInsn.GetRt(insn) == gpr.ra and MipsInsn.GetRs(insn) == gpr.sp then
|
|
pushed_ra:enqueue(pc);
|
|
end
|
|
elseif MipsInsn.IsJ(insn) then -- j **
|
|
local imm = MipsInsn.GetTarget(insn)
|
|
imm = imm << 2
|
|
if pc == imm then
|
|
-- jump to self? maybe we can ignore it.
|
|
else
|
|
new_pc = imm
|
|
dbgPrintf(string.format("j ** : new_pc = %x", new_pc))
|
|
bdl_count = 1
|
|
for t=1, n_j do
|
|
if jmax[t] == new_pc then
|
|
return result -- closed loop
|
|
end
|
|
end
|
|
if n_j > 1024 then
|
|
return result -- jump buffer overflow
|
|
end
|
|
jmax[n_j] = new_pc
|
|
n_j = n_j + 1
|
|
end
|
|
elseif MipsInsn.IsBeq(insn) and MipsInsn.GetRs(insn) == gpr.zero then -- beq zero,**
|
|
local offset = MipsInsn.GetOff(insn) -- ((insn<<48) >> 48)
|
|
offset = offset << 2
|
|
new_pc = pc + 4 + offset
|
|
dbgPrintf(string.format("beq zero,** : new_pc=%x", new_pc))
|
|
if offset < 0 then
|
|
-- it's ... back loop. ignore
|
|
else
|
|
if pc == new_pc then
|
|
-- jump to self? maybe we can ignore it
|
|
else
|
|
-- print(string.format("beq zero,** : new_pc=%x", new_pc))
|
|
bdl_count = 1
|
|
for t = 1, n_j do
|
|
if jmax[t] == new_pc then
|
|
return result
|
|
end
|
|
end
|
|
if n_j > 1024 then
|
|
return result
|
|
end
|
|
jmax[n_j] = new_pc
|
|
n_j = n_j + 1
|
|
end
|
|
end
|
|
elseif MipsInsn.IsEnd(insn) then -- end
|
|
dbgPrintf(string.format("end"))
|
|
-- Ignore 'end/break'
|
|
-- Most use cases of end/break are 'unreachable' debug code in titles.
|
|
-- If ignoring this is a problem on some specific title, then perhaps analysis of
|
|
-- end/break should be instead controllable via parameter. --jstine
|
|
-- return result
|
|
elseif MipsInsn.IsJal(insn) then -- jal **
|
|
local imm = insn & 0x03ffffff
|
|
imm = imm << 2
|
|
dbgPrintf(string.format("jal ** : addr = %x", imm))
|
|
-- call
|
|
end
|
|
|
|
icount = icount + 1
|
|
pc = pc + 4
|
|
|
|
-- dbgPrintf(string.format("bdl_count=%d", bdl_count))
|
|
if bdl_count > 0 then
|
|
if bdl_count == 2 then
|
|
pc = new_pc
|
|
bdl_count = 0
|
|
else
|
|
bdl_count = bdl_count + 1
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
PS2 = {}
|
|
PS2.GetCurrentThread = function(eeObj) return eeObj.ReadMem32(0x12fac) end
|
|
PS2.GetThreads = function(eeObj)
|
|
local EE_THREAD_BASE = 0x18000
|
|
local EE_NUM_THREADS = 0x100
|
|
local th = EE_THREAD_BASE
|
|
local result = Queue.new()
|
|
for t = 0, EE_NUM_THREADS-1 do
|
|
-- 0 : node_prev
|
|
-- 4 : node_next
|
|
-- 8 : status
|
|
-- 12: pc
|
|
-- 16: sp
|
|
-- 20: gp
|
|
-- 24: init_pri
|
|
-- 26: curr_pri
|
|
-- 28: wstat
|
|
-- 32: waitId
|
|
-- 36: wakeupCount
|
|
-- 40: attr
|
|
-- 44: option
|
|
-- 48: func
|
|
-- 52: argc
|
|
-- 56: args
|
|
-- 60: stack
|
|
-- 64: size
|
|
-- 68: root
|
|
-- 72: endOfHeap
|
|
local status = eeObj.ReadMem32(th + 8)
|
|
if status ~= 0 then
|
|
local id = t
|
|
local pri = eeObj.ReadMem16(th + 26)
|
|
local gp = eeObj.ReadMem32(th + 20)
|
|
local pc = eeObj.ReadMem32(th + 12)
|
|
local sp = eeObj.ReadMem32(th + 16)
|
|
result:enqueue( {id=id, status=status, pri=pri, gp=gp, pc=pc, sp=sp} )
|
|
end
|
|
th = th + 76
|
|
end
|
|
return result
|
|
end
|