Skip to content

Commit

Permalink
DebugTools: Scan for functions from the ELF instead of from memory
Browse files Browse the repository at this point in the history
  • Loading branch information
chaoticgd authored and F0bes committed Sep 28, 2024
1 parent 5479ab1 commit 31dcda0
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 68 deletions.
81 changes: 81 additions & 0 deletions pcsx2/DebugTools/DebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,3 +1181,84 @@ std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() co
{
return getIOPThreads();
}

ElfMemoryReader::ElfMemoryReader(const ccc::ElfFile& elf)
: m_elf(elf)
{
}

u32 ElfMemoryReader::read8(u32 address)
{
ccc::Result<u8> result = m_elf.get_object_virtual<u8>(address);
if (!result.success())
return 0;

return *result;
}

u32 ElfMemoryReader::read8(u32 address, bool& valid)
{
ccc::Result<u8> result = m_elf.get_object_virtual<u8>(address);
valid = result.success();
if (!valid)
return 0;

return *result;
}

u32 ElfMemoryReader::read16(u32 address)
{
ccc::Result<u16> result = m_elf.get_object_virtual<u16>(address);
if (!result.success())
return 0;

return *result;
}

u32 ElfMemoryReader::read16(u32 address, bool& valid)
{
ccc::Result<u16> result = m_elf.get_object_virtual<u16>(address);
valid = result.success();
if (!valid)
return 0;

return *result;
}

u32 ElfMemoryReader::read32(u32 address)
{
ccc::Result<u32> result = m_elf.get_object_virtual<u32>(address);
if (!result.success())
return 0;

return *result;
}

u32 ElfMemoryReader::read32(u32 address, bool& valid)
{
ccc::Result<u32> result = m_elf.get_object_virtual<u32>(address);
valid = result.success();
if (!valid)
return 0;

return *result;
}

u64 ElfMemoryReader::read64(u32 address)
{
ccc::Result<u64> result = m_elf.get_object_virtual<u64>(address);
if (!result.success())
return 0;

return *result;
}

u64 ElfMemoryReader::read64(u32 address, bool& valid)
{
ccc::Result<u64> result = m_elf.get_object_virtual<u64>(address);
valid = result.success();
if (!valid)
return 0;

return *result;
}
39 changes: 31 additions & 8 deletions pcsx2/DebugTools/DebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,9 @@ enum BreakPointCpu
BREAKPOINT_IOP_AND_EE = 0x03
};

class DebugInterface
class MemoryReader
{
public:
enum RegisterType
{
NORMAL,
SPECIAL
};

virtual u32 read8(u32 address) = 0;
virtual u32 read8(u32 address, bool& valid) = 0;
virtual u32 read16(u32 address) = 0;
Expand All @@ -49,6 +43,17 @@ class DebugInterface
virtual u32 read32(u32 address, bool& valid) = 0;
virtual u64 read64(u32 address) = 0;
virtual u64 read64(u32 address, bool& valid) = 0;
};

class DebugInterface : public MemoryReader
{
public:
enum RegisterType
{
NORMAL,
SPECIAL
};

virtual u128 read128(u32 address) = 0;
virtual void write8(u32 address, u8 value) = 0;
virtual void write16(u32 address, u16 value) = 0;
Expand Down Expand Up @@ -140,7 +145,6 @@ class R5900DebugInterface : public DebugInterface
BreakPointCpu getCpuType() override;
};


class R3000DebugInterface : public DebugInterface
{
public:
Expand Down Expand Up @@ -183,5 +187,24 @@ class R3000DebugInterface : public DebugInterface
BreakPointCpu getCpuType() override;
};

// Provides access to the loadable segments from the ELF as they are on disk.
class ElfMemoryReader : public MemoryReader
{
public:
ElfMemoryReader(const ccc::ElfFile& elf);

u32 read8(u32 address) override;
u32 read8(u32 address, bool& valid) override;
u32 read16(u32 address) override;
u32 read16(u32 address, bool& valid) override;
u32 read32(u32 address) override;
u32 read32(u32 address, bool& valid) override;
u64 read64(u32 address) override;
u64 read64(u32 address, bool& valid) override;

protected:
const ccc::ElfFile& m_elf;
};

extern R5900DebugInterface r5900Debug;
extern R3000DebugInterface r3000Debug;
56 changes: 30 additions & 26 deletions pcsx2/DebugTools/MIPSAnalyst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@

namespace MIPSAnalyst
{
u32 GetJumpTarget(u32 addr)
u32 GetJumpTarget(u32 addr, MemoryReader& reader)
{
u32 op = r5900Debug.read32(addr);
u32 op = reader.read32(addr);
const R5900::OPCODE& opcode = R5900::GetInstruction(op);

if ((opcode.flags & IS_BRANCH) && (opcode.flags & BRANCHTYPE_MASK) == BRANCHTYPE_JUMP)
Expand All @@ -37,9 +37,9 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}

u32 GetBranchTarget(u32 addr)
u32 GetBranchTarget(u32 addr, MemoryReader& reader)
{
u32 op = r5900Debug.read32(addr);
u32 op = reader.read32(addr);
const R5900::OPCODE& opcode = R5900::GetInstruction(op);

int branchType = (opcode.flags & BRANCHTYPE_MASK);
Expand All @@ -49,9 +49,9 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}

u32 GetBranchTargetNoRA(u32 addr)
u32 GetBranchTargetNoRA(u32 addr, MemoryReader& reader)
{
u32 op = r5900Debug.read32(addr);
u32 op = reader.read32(addr);
const R5900::OPCODE& opcode = R5900::GetInstruction(op);

int branchType = (opcode.flags & BRANCHTYPE_MASK);
Expand All @@ -66,9 +66,9 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}

u32 GetSureBranchTarget(u32 addr)
u32 GetSureBranchTarget(u32 addr, MemoryReader& reader)
{
u32 op = r5900Debug.read32(addr);
u32 op = reader.read32(addr);
const R5900::OPCODE& opcode = R5900::GetInstruction(op);

if ((opcode.flags & IS_BRANCH) && (opcode.flags & BRANCHTYPE_MASK) == BRANCHTYPE_BRANCH)
Expand Down Expand Up @@ -114,7 +114,7 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}

static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd) {
static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd, MemoryReader& reader) {
static const u32 MAX_AHEAD_SCAN = 0x1000;
// Maybe a bit high... just to make sure we don't get confused by recursive tail recursion.
static const u32 MAX_FUNC_SIZE = 0x20000;
Expand All @@ -133,10 +133,10 @@ namespace MIPSAnalyst
u32 furthestJumpbackAddr = INVALIDTARGET;

for (u32 ahead = fromAddr; ahead < fromAddr + MAX_AHEAD_SCAN; ahead += 4) {
u32 aheadOp = r5900Debug.read32(ahead);
u32 target = GetBranchTargetNoRA(ahead);
u32 aheadOp = reader.read32(ahead);
u32 target = GetBranchTargetNoRA(ahead, reader);
if (target == INVALIDTARGET && ((aheadOp & 0xFC000000) == 0x08000000)) {
target = GetJumpTarget(ahead);
target = GetJumpTarget(ahead, reader);
}

if (target != INVALIDTARGET) {
Expand All @@ -157,10 +157,10 @@ namespace MIPSAnalyst

if (closestJumpbackAddr != INVALIDTARGET && furthestJumpbackAddr == INVALIDTARGET) {
for (u32 behind = closestJumpbackTarget; behind < fromAddr; behind += 4) {
u32 behindOp = r5900Debug.read32(behind);
u32 target = GetBranchTargetNoRA(behind);
u32 behindOp = reader.read32(behind);
u32 target = GetBranchTargetNoRA(behind, reader);
if (target == INVALIDTARGET && ((behindOp & 0xFC000000) == 0x08000000)) {
target = GetJumpTarget(behind);
target = GetJumpTarget(behind, reader);
}

if (target != INVALIDTARGET) {
Expand All @@ -174,7 +174,7 @@ namespace MIPSAnalyst
return furthestJumpbackAddr;
}

void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr) {
void ScanForFunctions(ccc::SymbolDatabase& database, MemoryReader& reader, u32 startAddr, u32 endAddr) {
std::vector<MIPSAnalyst::AnalyzedFunction> functions;
AnalyzedFunction currentFunction = {startAddr};

Expand All @@ -199,9 +199,9 @@ namespace MIPSAnalyst
continue;
}

u32 op = r5900Debug.read32(addr);
u32 op = reader.read32(addr);

u32 target = GetBranchTargetNoRA(addr);
u32 target = GetBranchTargetNoRA(addr, reader);
if (target != INVALIDTARGET) {
isStraightLeaf = false;
if (target > furthestBranch) {
Expand All @@ -218,7 +218,7 @@ namespace MIPSAnalyst
}
}
} else if ((op & 0xFC000000) == 0x08000000) {
u32 sureTarget = GetJumpTarget(addr);
u32 sureTarget = GetJumpTarget(addr, reader);
// Check for a tail call. Might not even have a jr ra.
if (sureTarget != INVALIDTARGET && sureTarget < currentFunction.start) {
if (furthestBranch > addr) {
Expand All @@ -230,7 +230,7 @@ namespace MIPSAnalyst
} else if (sureTarget != INVALIDTARGET && sureTarget > addr && sureTarget > furthestBranch) {
// A jump later. Probably tail, but let's check if it jumps back.
u32 knownEnd = furthestBranch == 0 ? addr : furthestBranch;
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd);
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd, reader);
if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) {
furthestBranch = jumpback;
} else {
Expand All @@ -256,10 +256,10 @@ namespace MIPSAnalyst

if (looking) {
if (addr >= furthestBranch) {
u32 sureTarget = GetSureBranchTarget(addr);
u32 sureTarget = GetSureBranchTarget(addr, reader);
// Regular j only, jals are to new funcs.
if (sureTarget == INVALIDTARGET && ((op & 0xFC000000) == 0x08000000)) {
sureTarget = GetJumpTarget(addr);
sureTarget = GetJumpTarget(addr, reader);
}

if (sureTarget != INVALIDTARGET && sureTarget < addr) {
Expand All @@ -268,7 +268,7 @@ namespace MIPSAnalyst
// Okay, we have a downward jump. Might be an else or a tail call...
// If there's a jump back upward in spitting distance of it, it's an else.
u32 knownEnd = furthestBranch == 0 ? addr : furthestBranch;
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd);
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd, reader);
if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) {
furthestBranch = jumpback;
}
Expand All @@ -287,7 +287,7 @@ namespace MIPSAnalyst
// Most functions are aligned to 8 or 16 bytes, so add padding
// to this one unless a symbol exists implying a new function
// follows immediately.
while (next_symbol == nullptr && ((addr+8) % 16) && r5900Debug.read32(addr+8) == 0)
while (next_symbol == nullptr && ((addr+8) % 16) && reader.read32(addr+8) == 0)
addr += 4;

currentFunction.end = addr + 4;
Expand All @@ -309,8 +309,10 @@ namespace MIPSAnalyst
functions.push_back(currentFunction);

ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Analysis");
if(!source->valid())
if (!source.success()) {
Console.Error("MIPSAnalyst: %s", source.error().message.c_str());
return;
}

for (const AnalyzedFunction& function : functions) {
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(function.start);
Expand All @@ -336,8 +338,10 @@ namespace MIPSAnalyst

ccc::Result<ccc::Function*> symbol_result = database.functions.create_symbol(
std::move(name), function.start, *source, nullptr);
if (!symbol_result.success())
if (!symbol_result.success()) {
Console.Error("MIPSAnalyst: %s", symbol_result.error().message.c_str());
return;
}
symbol = *symbol_result;
}

Expand Down
6 changes: 2 additions & 4 deletions pcsx2/DebugTools/MIPSAnalyst.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@

#pragma once

#include "DebugInterface.h"
#include "SymbolGuardian.h"

class DebugInterface;


#define MIPS_GET_OP(op) ((op>>26) & 0x3F)
#define MIPS_GET_FUNC(op) (op & 0x3F)
#define MIPS_GET_SA(op) ((op>>6) & 0x1F)
Expand All @@ -29,7 +27,7 @@ namespace MIPSAnalyst
char name[64];
};

void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr);
void ScanForFunctions(ccc::SymbolDatabase& database, MemoryReader& reader, u32 startAddr, u32 endAddr);

enum LoadStoreLRType { LOADSTORE_NORMAL, LOADSTORE_LEFT, LOADSTORE_RIGHT };

Expand Down
Loading

0 comments on commit 31dcda0

Please sign in to comment.