Fixing up basic block targets and such.
This commit is contained in:
parent
852536ae0a
commit
68cc34bb79
|
@ -62,12 +62,25 @@ class ExceptionEntrySymbol;
|
||||||
|
|
||||||
class FunctionBlock {
|
class FunctionBlock {
|
||||||
public:
|
public:
|
||||||
|
enum TargetType {
|
||||||
|
kTargetUnknown = 0,
|
||||||
|
kTargetBlock = 1,
|
||||||
|
kTargetFunction = 2,
|
||||||
|
kTargetLR = 3,
|
||||||
|
kTargetNone = 4,
|
||||||
|
};
|
||||||
|
|
||||||
uint32_t start_address;
|
uint32_t start_address;
|
||||||
uint32_t end_address;
|
uint32_t end_address;
|
||||||
|
|
||||||
vector<FunctionBlock*> incoming_blocks;
|
vector<FunctionBlock*> incoming_blocks;
|
||||||
FunctionBlock* outgoing_block;
|
|
||||||
uint32_t outgoing_address;
|
TargetType outgoing_type;
|
||||||
|
uint32_t outgoing_address;
|
||||||
|
union {
|
||||||
|
FunctionSymbol* outgoing_function;
|
||||||
|
FunctionBlock* outgoing_block;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class FunctionSymbol : public Symbol {
|
class FunctionSymbol : public Symbol {
|
||||||
|
@ -85,6 +98,9 @@ public:
|
||||||
FunctionSymbol() : Symbol(Function) {}
|
FunctionSymbol() : Symbol(Function) {}
|
||||||
virtual ~FunctionSymbol() {}
|
virtual ~FunctionSymbol() {}
|
||||||
|
|
||||||
|
FunctionBlock* GetBlock(uint32_t address);
|
||||||
|
FunctionBlock* SplitBlock(uint32_t address);
|
||||||
|
|
||||||
uint32_t start_address;
|
uint32_t start_address;
|
||||||
uint32_t end_address;
|
uint32_t end_address;
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -146,6 +162,7 @@ private:
|
||||||
int AddImports(const xe_xex2_import_library_t *library);
|
int AddImports(const xe_xex2_import_library_t *library);
|
||||||
int AddMethodHints();
|
int AddMethodHints();
|
||||||
int AnalyzeFunction(FunctionSymbol* fn);
|
int AnalyzeFunction(FunctionSymbol* fn);
|
||||||
|
int CompleteFunctionGraph(FunctionSymbol* fn);
|
||||||
bool FillHoles();
|
bool FillHoles();
|
||||||
int FlushQueue();
|
int FlushQueue();
|
||||||
|
|
||||||
|
|
145
src/cpu/sdb.cc
145
src/cpu/sdb.cc
|
@ -22,6 +22,44 @@ using namespace xe::cpu::sdb;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
FunctionBlock* FunctionSymbol::GetBlock(uint32_t address) {
|
||||||
|
std::map<uint32_t, FunctionBlock*>::iterator it = blocks.find(address);
|
||||||
|
if (it != blocks.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
|
||||||
|
// Scan to find the block that contains the address.
|
||||||
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
|
||||||
|
it != blocks.end(); ++it) {
|
||||||
|
FunctionBlock* block = it->second;
|
||||||
|
if (address == block->start_address) {
|
||||||
|
// No need for a split.
|
||||||
|
return block;
|
||||||
|
} else if (address >= block->start_address &&
|
||||||
|
address <= block->end_address + 4) {
|
||||||
|
// Inside this block.
|
||||||
|
// Since we know we are starting inside of the block we split downwards.
|
||||||
|
FunctionBlock* new_block = new FunctionBlock();
|
||||||
|
new_block->start_address = address;
|
||||||
|
new_block->end_address = block->end_address;
|
||||||
|
new_block->outgoing_type = block->outgoing_type;
|
||||||
|
new_block->outgoing_address = block->outgoing_address;
|
||||||
|
new_block->outgoing_block = block->outgoing_block;
|
||||||
|
blocks.insert(std::pair<uint32_t, FunctionBlock*>(address, new_block));
|
||||||
|
// Patch up old block.
|
||||||
|
block->end_address = address - 4;
|
||||||
|
block->outgoing_type = FunctionBlock::kTargetNone;
|
||||||
|
block->outgoing_address = 0;
|
||||||
|
block->outgoing_block = NULL;
|
||||||
|
return new_block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
SymbolDatabase::SymbolDatabase(xe_memory_ref memory, UserModule* module) {
|
SymbolDatabase::SymbolDatabase(xe_memory_ref memory, UserModule* module) {
|
||||||
memory_ = xe_memory_retain(memory);
|
memory_ = xe_memory_retain(memory);
|
||||||
module_ = module;
|
module_ = module;
|
||||||
|
@ -74,6 +112,14 @@ int SymbolDatabase::Analyze() {
|
||||||
FlushQueue();
|
FlushQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run a pass over all functions and link up their extended data.
|
||||||
|
// This can only be performed after we have all functions and basic blocks.
|
||||||
|
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||||
|
if (it->second->symbol_type == Symbol::Function) {
|
||||||
|
CompleteFunctionGraph(static_cast<FunctionSymbol*>(it->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,11 +395,6 @@ int SymbolDatabase::AddMethodHints() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolDatabase::IsRestGprLr(uint32_t addr) {
|
|
||||||
FunctionSymbol* fn = GetFunction(addr);
|
|
||||||
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
// Ignore functions already analyzed.
|
// Ignore functions already analyzed.
|
||||||
if (fn->blocks.size()) {
|
if (fn->blocks.size()) {
|
||||||
|
@ -436,6 +477,7 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
if (i.code == 0x4E800020) {
|
if (i.code == 0x4E800020) {
|
||||||
// blr -- unconditional branch to LR.
|
// blr -- unconditional branch to LR.
|
||||||
// This is generally a return.
|
// This is generally a return.
|
||||||
|
block->outgoing_type = FunctionBlock::kTargetLR;
|
||||||
if (furthest_target > addr) {
|
if (furthest_target > addr) {
|
||||||
// Remaining targets within function, not end.
|
// Remaining targets within function, not end.
|
||||||
XELOGSDB("ignoring blr %.8X (branch to %.8X)\n", addr, furthest_target);
|
XELOGSDB("ignoring blr %.8X (branch to %.8X)\n", addr, furthest_target);
|
||||||
|
@ -448,11 +490,13 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
} else if (i.type->opcode == 0x48000000) {
|
} else if (i.type->opcode == 0x48000000) {
|
||||||
// b/ba/bl/bla
|
// b/ba/bl/bla
|
||||||
uint32_t target = XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)addr);
|
uint32_t target = XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)addr);
|
||||||
|
block->outgoing_address = target;
|
||||||
|
|
||||||
if (i.I.LK) {
|
if (i.I.LK) {
|
||||||
XELOGSDB("bl %.8X -> %.8X\n", addr, target);
|
XELOGSDB("bl %.8X -> %.8X\n", addr, target);
|
||||||
|
|
||||||
// Queue target if needed.
|
// Queue call target if needed.
|
||||||
|
GetOrInsertFunction(target);
|
||||||
} else {
|
} else {
|
||||||
XELOGSDB("b %.8X -> %.8X\n", addr, target);
|
XELOGSDB("b %.8X -> %.8X\n", addr, target);
|
||||||
// If the target is back into the function and there's no further target
|
// If the target is back into the function and there's no further target
|
||||||
|
@ -478,6 +522,7 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
} else if (i.type->opcode == 0x40000000) {
|
} else if (i.type->opcode == 0x40000000) {
|
||||||
// bc/bca/bcl/bcla
|
// bc/bca/bcl/bcla
|
||||||
uint32_t target = XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)addr);
|
uint32_t target = XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)addr);
|
||||||
|
block->outgoing_address = target;
|
||||||
if (i.B.LK) {
|
if (i.B.LK) {
|
||||||
XELOGSDB("bcl %.8X -> %.8X\n", addr, target);
|
XELOGSDB("bcl %.8X -> %.8X\n", addr, target);
|
||||||
} else {
|
} else {
|
||||||
|
@ -537,34 +582,43 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SymbolDatabase::FlushQueue() {
|
int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
while (scan_queue_.size()) {
|
// Find variable accesses.
|
||||||
FunctionSymbol* fn = scan_queue_.front();
|
// TODO(benvanik): data analysis to find variable accesses.
|
||||||
scan_queue_.pop_front();
|
|
||||||
if (AnalyzeFunction(fn)) {
|
// For each basic block:
|
||||||
XELOGSDB("Aborting analysis!\n");
|
// - find outgoing target block or function
|
||||||
return 1;
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
|
||||||
|
it != fn->blocks.end(); ++it) {
|
||||||
|
FunctionBlock* block = it->second;
|
||||||
|
|
||||||
|
// If we have some address try to see what it is.
|
||||||
|
if (block->outgoing_address) {
|
||||||
|
if (block->outgoing_address >= fn->start_address &&
|
||||||
|
block->outgoing_address <= fn->end_address) {
|
||||||
|
// Branch into a block in this function.
|
||||||
|
block->outgoing_type = FunctionBlock::kTargetBlock;
|
||||||
|
block->outgoing_block = fn->GetBlock(block->outgoing_address);
|
||||||
|
if (!block->outgoing_block) {
|
||||||
|
// Block target not found - we may need to split.
|
||||||
|
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
|
||||||
|
}
|
||||||
|
if (!block->outgoing_block) {
|
||||||
|
printf("block target not found: %.8X\n", block->outgoing_address);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Function call.
|
||||||
|
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||||
|
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||||
|
if (!block->outgoing_function) {
|
||||||
|
printf("call target not found: %.8X\n", block->outgoing_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolDatabase::IsValueInTextRange(uint32_t value) {
|
|
||||||
const xe_xex2_header_t* header = module_->xex_header();
|
|
||||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
|
||||||
const xe_xex2_section_t* section = &header->sections[n];
|
|
||||||
const size_t start_address =
|
|
||||||
header->exe_address + (i * xe_xex2_section_length);
|
|
||||||
const size_t end_address =
|
|
||||||
start_address + (section->info.page_count * xe_xex2_section_length);
|
|
||||||
if (value >= start_address && value < end_address) {
|
|
||||||
return section->info.type == XEX_SECTION_CODE;
|
|
||||||
}
|
|
||||||
i += section->info.page_count;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t start_address;
|
uint32_t start_address;
|
||||||
uint32_t end_address;
|
uint32_t end_address;
|
||||||
|
@ -648,3 +702,36 @@ bool SymbolDatabase::FillHoles() {
|
||||||
|
|
||||||
return holes.size() > 0;
|
return holes.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SymbolDatabase::FlushQueue() {
|
||||||
|
while (scan_queue_.size()) {
|
||||||
|
FunctionSymbol* fn = scan_queue_.front();
|
||||||
|
scan_queue_.pop_front();
|
||||||
|
if (AnalyzeFunction(fn)) {
|
||||||
|
XELOGSDB("Aborting analysis!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||||
|
const xe_xex2_header_t* header = module_->xex_header();
|
||||||
|
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||||
|
const xe_xex2_section_t* section = &header->sections[n];
|
||||||
|
const size_t start_address =
|
||||||
|
header->exe_address + (i * xe_xex2_section_length);
|
||||||
|
const size_t end_address =
|
||||||
|
start_address + (section->info.page_count * xe_xex2_section_length);
|
||||||
|
if (value >= start_address && value < end_address) {
|
||||||
|
return section->info.type == XEX_SECTION_CODE;
|
||||||
|
}
|
||||||
|
i += section->info.page_count;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolDatabase::IsRestGprLr(uint32_t addr) {
|
||||||
|
FunctionSymbol* fn = GetFunction(addr);
|
||||||
|
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue