mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
236 lines
6 KiB
C++
236 lines
6 KiB
C++
// symbolic information API.
|
|
#include "pch.h"
|
|
|
|
using namespace Debug;
|
|
|
|
// IMPORTANT : EXE loading base must be 0x00400000, for correct HLE.
|
|
// MSVC : Project/Settings/Link/Output/Base Address
|
|
// CW : Edit/** Win32 x86 Settings/Linker/x86 COFF/Base address
|
|
|
|
// all important variables are here
|
|
SYMControl sym; // default workspace
|
|
static SYMControl *work = &sym; // current workspace (in use)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// find first occurency of symbol in list
|
|
static SYM * symfind(const char *symName)
|
|
{
|
|
for (auto it = work->symmap.begin(); it != work->symmap.end(); ++it)
|
|
{
|
|
if (!strcmp(it->second->savedName, symName))
|
|
{
|
|
return it->second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void SYMSetWorkspace(SYMControl *useIt)
|
|
{
|
|
work = useIt;
|
|
}
|
|
|
|
void SYMCompareWorkspaces (
|
|
SYMControl *source,
|
|
SYMControl *dest,
|
|
void (*DiffCallback)(uint32_t ea, char * name)
|
|
)
|
|
{
|
|
for (auto sourceId = source->symmap.begin(); sourceId != source->symmap.end(); ++sourceId)
|
|
{
|
|
bool found = false;
|
|
|
|
for (auto destIt = dest->symmap.begin(); destIt != dest->symmap.end(); ++destIt)
|
|
{
|
|
if (!strcmp(sourceId->second->savedName, destIt->second->savedName))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DiffCallback(sourceId->second->eaddr, sourceId->second->savedName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// get address of symbolic label
|
|
// if label is not specified, return 0
|
|
uint32_t SYMAddress(const char *symName)
|
|
{
|
|
// try to find specified symbol
|
|
SYM *symbol = symfind(symName);
|
|
|
|
if(symbol) return symbol->eaddr;
|
|
else return 0;
|
|
}
|
|
|
|
// get symbolic label by given address
|
|
// if label is not specified, return NULL
|
|
char * SYMName(uint32_t symAddr)
|
|
{
|
|
auto it = work->symmap.find(symAddr);
|
|
|
|
if (it == work->symmap.end())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return it->second->savedName;
|
|
}
|
|
|
|
// Get the symbol closest to the specified address and offset relative to the start of the symbol.
|
|
char* SYMGetNearestName(uint32_t address, size_t& offset)
|
|
{
|
|
int minDelta = INT_MAX;
|
|
SYM* nearestSymbol = nullptr;
|
|
|
|
offset = 0;
|
|
|
|
for (auto it = work->symmap.begin(); it != work->symmap.end(); ++it)
|
|
{
|
|
if (address >= it->first)
|
|
{
|
|
int delta = address - it->first;
|
|
if (delta < minDelta)
|
|
{
|
|
minDelta = delta;
|
|
nearestSymbol = it->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nearestSymbol == nullptr)
|
|
return nullptr;
|
|
|
|
offset = address - nearestSymbol->eaddr;
|
|
return nearestSymbol->savedName;
|
|
}
|
|
|
|
// associate high-level call with symbol
|
|
// (if CPU reaches label, it jumps to HLE call)
|
|
void SYMSetHighlevel(const char *symName, void (*routine)())
|
|
{
|
|
// TODO: Disabled for Linux
|
|
#if _LINUX
|
|
return;
|
|
#endif
|
|
|
|
// try to find specified symbol
|
|
SYM *symbol = symfind(symName);
|
|
|
|
// check address
|
|
// High-level call is too high in memory.
|
|
if (((uint64_t)routine & ~0x03ffffff) != 0)
|
|
{
|
|
throw "High-level call is too high in memory!";
|
|
}
|
|
|
|
// leave, if symbol is not found. add otherwise.
|
|
if(symbol)
|
|
{
|
|
symbol->routine = routine; // overwrite
|
|
|
|
// if first opcode is 'BLR', then just leave it
|
|
uint32_t op;
|
|
Gekko::Gekko->ReadWord(symbol->eaddr, &op);
|
|
if(op != 0x4e800020)
|
|
{
|
|
Gekko::Gekko->WriteWord(
|
|
symbol->eaddr, // add patch
|
|
(uint32_t)((uint64_t)routine & 0x03ffffff) // 000: high-level opcode
|
|
);
|
|
if(!_stricmp(symName, "OSLoadContext"))
|
|
{
|
|
Gekko::Gekko->WriteWord(
|
|
symbol->eaddr + 4, // return to caller
|
|
0x4c000064 // rfi
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Gekko::Gekko->WriteWord(
|
|
symbol->eaddr + 4, // return to caller
|
|
0x4e800020 // blr
|
|
);
|
|
}
|
|
}
|
|
Report(Channel::HLE, "patched API call: %08X %s\n", symbol->eaddr, symName);
|
|
}
|
|
}
|
|
|
|
// save string in memory
|
|
static char * strsave(const char *str)
|
|
{
|
|
size_t len = strlen(str) + 1;
|
|
char *saved = new char[len];
|
|
assert(saved);
|
|
strcpy(saved, str);
|
|
return saved;
|
|
}
|
|
|
|
// add new symbol
|
|
void SYMAddNew(uint32_t addr, const char *name)
|
|
{
|
|
SYM* symbol = symfind(name);
|
|
|
|
if (symbol != nullptr)
|
|
{
|
|
// Replace name
|
|
if (symbol->savedName)
|
|
{
|
|
delete[] symbol->savedName;
|
|
symbol->savedName = nullptr;
|
|
}
|
|
symbol->savedName = strsave(name);
|
|
}
|
|
else
|
|
{
|
|
// Add new
|
|
SYM* sym = new SYM;
|
|
|
|
sym->eaddr = addr;
|
|
sym->savedName = strsave(name);
|
|
sym->routine = nullptr;
|
|
|
|
work->symmap[addr] = sym;
|
|
}
|
|
}
|
|
|
|
// Remove all symbols
|
|
void SYMKill()
|
|
{
|
|
for (auto it = work->symmap.begin(); it != work->symmap.end(); ++it)
|
|
{
|
|
if (it->second->savedName)
|
|
{
|
|
delete[] it->second->savedName;
|
|
}
|
|
delete it->second;
|
|
}
|
|
|
|
work->symmap.clear();
|
|
}
|
|
|
|
// list symbols, matching first occurence of "str".
|
|
// * - all symbols (warning! Zelda has about 20000 symbols).
|
|
void SYMList(const char *str)
|
|
{
|
|
size_t len = strlen(str), cnt = 0;
|
|
Report(Channel::Norm, "<address> symbol\n\n");
|
|
|
|
for (auto it = work->symmap.begin(); it != work->symmap.end(); ++it)
|
|
{
|
|
SYM* symbol = it->second;
|
|
if (((*str == '*') || !_strnicmp(str, symbol->savedName, len)))
|
|
{
|
|
Report(Channel::Norm, "<%08X> %s\n", symbol->eaddr, symbol->savedName);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
Report(Channel::Norm, "%i match\n\n", cnt);
|
|
}
|