Splitting up the symbol database code to make it easier to work with.
This commit is contained in:
parent
63f0785ecf
commit
a020072ed7
|
@ -10,221 +10,9 @@
|
||||||
#ifndef XENIA_CPU_SDB_H_
|
#ifndef XENIA_CPU_SDB_H_
|
||||||
#define XENIA_CPU_SDB_H_
|
#define XENIA_CPU_SDB_H_
|
||||||
|
|
||||||
#include <xenia/core.h>
|
#include <xenia/cpu/sdb/raw_symbol_database.h>
|
||||||
|
#include <xenia/cpu/sdb/symbol.h>
|
||||||
#include <list>
|
#include <xenia/cpu/sdb/symbol_database.h>
|
||||||
#include <map>
|
#include <xenia/cpu/sdb/xex_symbol_database.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <xenia/kernel/export.h>
|
|
||||||
#include <xenia/kernel/xex2.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace cpu {
|
|
||||||
namespace sdb {
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionSymbol;
|
|
||||||
class VariableSymbol;
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionCall {
|
|
||||||
public:
|
|
||||||
uint32_t address;
|
|
||||||
FunctionSymbol* source;
|
|
||||||
FunctionSymbol* target;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariableAccess {
|
|
||||||
public:
|
|
||||||
uint32_t address;
|
|
||||||
FunctionSymbol* source;
|
|
||||||
VariableSymbol* target;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Symbol {
|
|
||||||
public:
|
|
||||||
enum SymbolType {
|
|
||||||
Function = 0,
|
|
||||||
Variable = 1,
|
|
||||||
ExceptionEntry = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~Symbol() {}
|
|
||||||
|
|
||||||
SymbolType symbol_type;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Symbol(SymbolType type) : symbol_type(type) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExceptionEntrySymbol;
|
|
||||||
|
|
||||||
class FunctionBlock {
|
|
||||||
public:
|
|
||||||
enum TargetType {
|
|
||||||
kTargetUnknown = 0,
|
|
||||||
kTargetBlock = 1,
|
|
||||||
kTargetFunction = 2,
|
|
||||||
kTargetLR = 3,
|
|
||||||
kTargetCTR = 4,
|
|
||||||
kTargetNone = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
FunctionBlock();
|
|
||||||
|
|
||||||
uint32_t start_address;
|
|
||||||
uint32_t end_address;
|
|
||||||
|
|
||||||
std::vector<FunctionBlock*> incoming_blocks;
|
|
||||||
|
|
||||||
TargetType outgoing_type;
|
|
||||||
uint32_t outgoing_address;
|
|
||||||
union {
|
|
||||||
FunctionSymbol* outgoing_function;
|
|
||||||
FunctionBlock* outgoing_block;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class FunctionSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
enum FunctionType {
|
|
||||||
Unknown = 0,
|
|
||||||
Kernel = 1,
|
|
||||||
User = 2,
|
|
||||||
};
|
|
||||||
enum Flags {
|
|
||||||
kFlagSaveGprLr = 1 << 1,
|
|
||||||
kFlagRestGprLr = 1 << 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
FunctionSymbol();
|
|
||||||
virtual ~FunctionSymbol();
|
|
||||||
|
|
||||||
FunctionBlock* GetBlock(uint32_t address);
|
|
||||||
FunctionBlock* SplitBlock(uint32_t address);
|
|
||||||
|
|
||||||
uint32_t start_address;
|
|
||||||
uint32_t end_address;
|
|
||||||
char* name;
|
|
||||||
FunctionType type;
|
|
||||||
uint32_t flags;
|
|
||||||
|
|
||||||
kernel::KernelExport* kernel_export;
|
|
||||||
ExceptionEntrySymbol* ee;
|
|
||||||
|
|
||||||
std::vector<FunctionCall*> incoming_calls;
|
|
||||||
std::vector<FunctionCall*> outgoing_calls;
|
|
||||||
std::vector<VariableAccess*> variable_accesses;
|
|
||||||
|
|
||||||
std::map<uint32_t, FunctionBlock*> blocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariableSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
VariableSymbol();
|
|
||||||
virtual ~VariableSymbol();
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
char* name;
|
|
||||||
|
|
||||||
kernel::KernelExport* kernel_export;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExceptionEntrySymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
ExceptionEntrySymbol();
|
|
||||||
virtual ~ExceptionEntrySymbol() {}
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
FunctionSymbol* function;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class SymbolDatabase {
|
|
||||||
public:
|
|
||||||
SymbolDatabase(xe_memory_ref memory, kernel::ExportResolver* export_resolver);
|
|
||||||
virtual ~SymbolDatabase();
|
|
||||||
|
|
||||||
virtual int Analyze();
|
|
||||||
|
|
||||||
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
|
|
||||||
FunctionSymbol* GetOrInsertFunction(uint32_t address);
|
|
||||||
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
|
||||||
FunctionSymbol* GetFunction(uint32_t address);
|
|
||||||
VariableSymbol* GetVariable(uint32_t address);
|
|
||||||
Symbol* GetSymbol(uint32_t address);
|
|
||||||
|
|
||||||
int GetAllVariables(std::vector<VariableSymbol*>& variables);
|
|
||||||
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
|
|
||||||
|
|
||||||
void Write(const char* file_name);
|
|
||||||
void Dump();
|
|
||||||
void DumpFunctionBlocks(FunctionSymbol* fn);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
typedef std::tr1::unordered_map<uint32_t, Symbol*> SymbolMap;
|
|
||||||
typedef std::list<FunctionSymbol*> FunctionList;
|
|
||||||
|
|
||||||
int AnalyzeFunction(FunctionSymbol* fn);
|
|
||||||
int CompleteFunctionGraph(FunctionSymbol* fn);
|
|
||||||
bool FillHoles();
|
|
||||||
int FlushQueue();
|
|
||||||
|
|
||||||
bool IsRestGprLr(uint32_t addr);
|
|
||||||
virtual uint32_t GetEntryPoint() = 0;
|
|
||||||
virtual bool IsValueInTextRange(uint32_t value) = 0;
|
|
||||||
|
|
||||||
xe_memory_ref memory_;
|
|
||||||
kernel::ExportResolver* export_resolver_;
|
|
||||||
size_t function_count_;
|
|
||||||
size_t variable_count_;
|
|
||||||
SymbolMap symbols_;
|
|
||||||
FunctionList scan_queue_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class RawSymbolDatabase : public SymbolDatabase {
|
|
||||||
public:
|
|
||||||
RawSymbolDatabase(xe_memory_ref memory,
|
|
||||||
kernel::ExportResolver* export_resolver,
|
|
||||||
uint32_t start_address, uint32_t end_address);
|
|
||||||
virtual ~RawSymbolDatabase();
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual uint32_t GetEntryPoint();
|
|
||||||
virtual bool IsValueInTextRange(uint32_t value);
|
|
||||||
|
|
||||||
uint32_t start_address_;
|
|
||||||
uint32_t end_address_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class XexSymbolDatabase : public SymbolDatabase {
|
|
||||||
public:
|
|
||||||
XexSymbolDatabase(xe_memory_ref memory,
|
|
||||||
kernel::ExportResolver* export_resolver,
|
|
||||||
xe_xex2_ref xex);
|
|
||||||
virtual ~XexSymbolDatabase();
|
|
||||||
|
|
||||||
virtual int Analyze();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int FindGplr();
|
|
||||||
int AddImports(const xe_xex2_import_library_t *library);
|
|
||||||
int AddMethodHints();
|
|
||||||
|
|
||||||
virtual uint32_t GetEntryPoint();
|
|
||||||
virtual bool IsValueInTextRange(uint32_t value);
|
|
||||||
|
|
||||||
xe_xex2_ref xex_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace sdb
|
|
||||||
} // namespace cpu
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_CPU_SDB_H_
|
#endif // XENIA_CPU_SDB_H_
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
||||||
|
#define XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
||||||
|
|
||||||
|
#include <xenia/cpu/sdb/symbol_database.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace cpu {
|
||||||
|
namespace sdb {
|
||||||
|
|
||||||
|
|
||||||
|
class RawSymbolDatabase : public SymbolDatabase {
|
||||||
|
public:
|
||||||
|
RawSymbolDatabase(xe_memory_ref memory,
|
||||||
|
kernel::ExportResolver* export_resolver,
|
||||||
|
uint32_t start_address, uint32_t end_address);
|
||||||
|
virtual ~RawSymbolDatabase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual uint32_t GetEntryPoint();
|
||||||
|
virtual bool IsValueInTextRange(uint32_t value);
|
||||||
|
|
||||||
|
uint32_t start_address_;
|
||||||
|
uint32_t end_address_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace sdb
|
||||||
|
} // namespace cpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_CPU_SDB_SYMBOL_H_
|
||||||
|
#define XENIA_CPU_SDB_SYMBOL_H_
|
||||||
|
|
||||||
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <xenia/kernel/export.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace cpu {
|
||||||
|
namespace sdb {
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionSymbol;
|
||||||
|
class VariableSymbol;
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionCall {
|
||||||
|
public:
|
||||||
|
uint32_t address;
|
||||||
|
FunctionSymbol* source;
|
||||||
|
FunctionSymbol* target;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VariableAccess {
|
||||||
|
public:
|
||||||
|
uint32_t address;
|
||||||
|
FunctionSymbol* source;
|
||||||
|
VariableSymbol* target;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Symbol {
|
||||||
|
public:
|
||||||
|
enum SymbolType {
|
||||||
|
Function = 0,
|
||||||
|
Variable = 1,
|
||||||
|
ExceptionEntry = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~Symbol() {}
|
||||||
|
|
||||||
|
SymbolType symbol_type;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Symbol(SymbolType type) : symbol_type(type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExceptionEntrySymbol;
|
||||||
|
|
||||||
|
class FunctionBlock {
|
||||||
|
public:
|
||||||
|
enum TargetType {
|
||||||
|
kTargetUnknown = 0,
|
||||||
|
kTargetBlock = 1,
|
||||||
|
kTargetFunction = 2,
|
||||||
|
kTargetLR = 3,
|
||||||
|
kTargetCTR = 4,
|
||||||
|
kTargetNone = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
FunctionBlock();
|
||||||
|
|
||||||
|
uint32_t start_address;
|
||||||
|
uint32_t end_address;
|
||||||
|
|
||||||
|
std::vector<FunctionBlock*> incoming_blocks;
|
||||||
|
|
||||||
|
TargetType outgoing_type;
|
||||||
|
uint32_t outgoing_address;
|
||||||
|
union {
|
||||||
|
FunctionSymbol* outgoing_function;
|
||||||
|
FunctionBlock* outgoing_block;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
enum FunctionType {
|
||||||
|
Unknown = 0,
|
||||||
|
Kernel = 1,
|
||||||
|
User = 2,
|
||||||
|
};
|
||||||
|
enum Flags {
|
||||||
|
kFlagSaveGprLr = 1 << 1,
|
||||||
|
kFlagRestGprLr = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
FunctionSymbol();
|
||||||
|
virtual ~FunctionSymbol();
|
||||||
|
|
||||||
|
FunctionBlock* GetBlock(uint32_t address);
|
||||||
|
FunctionBlock* SplitBlock(uint32_t address);
|
||||||
|
|
||||||
|
uint32_t start_address;
|
||||||
|
uint32_t end_address;
|
||||||
|
char* name;
|
||||||
|
FunctionType type;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
kernel::KernelExport* kernel_export;
|
||||||
|
ExceptionEntrySymbol* ee;
|
||||||
|
|
||||||
|
std::vector<FunctionCall*> incoming_calls;
|
||||||
|
std::vector<FunctionCall*> outgoing_calls;
|
||||||
|
std::vector<VariableAccess*> variable_accesses;
|
||||||
|
|
||||||
|
std::map<uint32_t, FunctionBlock*> blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VariableSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
VariableSymbol();
|
||||||
|
virtual ~VariableSymbol();
|
||||||
|
|
||||||
|
uint32_t address;
|
||||||
|
char* name;
|
||||||
|
|
||||||
|
kernel::KernelExport* kernel_export;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExceptionEntrySymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ExceptionEntrySymbol();
|
||||||
|
virtual ~ExceptionEntrySymbol() {}
|
||||||
|
|
||||||
|
uint32_t address;
|
||||||
|
FunctionSymbol* function;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace sdb
|
||||||
|
} // namespace cpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_CPU_SDB_SYMBOL_H_
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
||||||
|
#define XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
||||||
|
|
||||||
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <xenia/kernel/export.h>
|
||||||
|
#include <xenia/cpu/sdb/symbol.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace cpu {
|
||||||
|
namespace sdb {
|
||||||
|
|
||||||
|
|
||||||
|
class SymbolDatabase {
|
||||||
|
public:
|
||||||
|
SymbolDatabase(xe_memory_ref memory, kernel::ExportResolver* export_resolver);
|
||||||
|
virtual ~SymbolDatabase();
|
||||||
|
|
||||||
|
virtual int Analyze();
|
||||||
|
|
||||||
|
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
|
||||||
|
FunctionSymbol* GetOrInsertFunction(uint32_t address);
|
||||||
|
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
||||||
|
FunctionSymbol* GetFunction(uint32_t address);
|
||||||
|
VariableSymbol* GetVariable(uint32_t address);
|
||||||
|
Symbol* GetSymbol(uint32_t address);
|
||||||
|
|
||||||
|
int GetAllVariables(std::vector<VariableSymbol*>& variables);
|
||||||
|
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
|
||||||
|
|
||||||
|
void Write(const char* file_name);
|
||||||
|
void Dump();
|
||||||
|
void DumpFunctionBlocks(FunctionSymbol* fn);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef std::tr1::unordered_map<uint32_t, Symbol*> SymbolMap;
|
||||||
|
typedef std::list<FunctionSymbol*> FunctionList;
|
||||||
|
|
||||||
|
int AnalyzeFunction(FunctionSymbol* fn);
|
||||||
|
int CompleteFunctionGraph(FunctionSymbol* fn);
|
||||||
|
bool FillHoles();
|
||||||
|
int FlushQueue();
|
||||||
|
|
||||||
|
bool IsRestGprLr(uint32_t addr);
|
||||||
|
virtual uint32_t GetEntryPoint() = 0;
|
||||||
|
virtual bool IsValueInTextRange(uint32_t value) = 0;
|
||||||
|
|
||||||
|
xe_memory_ref memory_;
|
||||||
|
kernel::ExportResolver* export_resolver_;
|
||||||
|
size_t function_count_;
|
||||||
|
size_t variable_count_;
|
||||||
|
SymbolMap symbols_;
|
||||||
|
FunctionList scan_queue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace sdb
|
||||||
|
} // namespace cpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
||||||
|
#define XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
||||||
|
|
||||||
|
#include <xenia/cpu/sdb/symbol_database.h>
|
||||||
|
|
||||||
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace cpu {
|
||||||
|
namespace sdb {
|
||||||
|
|
||||||
|
|
||||||
|
class XexSymbolDatabase : public SymbolDatabase {
|
||||||
|
public:
|
||||||
|
XexSymbolDatabase(xe_memory_ref memory,
|
||||||
|
kernel::ExportResolver* export_resolver,
|
||||||
|
xe_xex2_ref xex);
|
||||||
|
virtual ~XexSymbolDatabase();
|
||||||
|
|
||||||
|
virtual int Analyze();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int FindGplr();
|
||||||
|
int AddImports(const xe_xex2_import_library_t *library);
|
||||||
|
int AddMethodHints();
|
||||||
|
|
||||||
|
virtual uint32_t GetEntryPoint();
|
||||||
|
virtual bool IsValueInTextRange(uint32_t value);
|
||||||
|
|
||||||
|
xe_xex2_ref xex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace sdb
|
||||||
|
} // namespace cpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
|
#endif // XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/cpu/sdb/raw_symbol_database.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace xe;
|
||||||
|
using namespace xe::cpu;
|
||||||
|
using namespace xe::cpu::ppc;
|
||||||
|
using namespace xe::cpu::sdb;
|
||||||
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
RawSymbolDatabase::RawSymbolDatabase(
|
||||||
|
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||||
|
uint32_t start_address, uint32_t end_address) :
|
||||||
|
SymbolDatabase(memory, export_resolver) {
|
||||||
|
start_address_ = start_address;
|
||||||
|
end_address_ = end_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawSymbolDatabase::~RawSymbolDatabase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RawSymbolDatabase::GetEntryPoint() {
|
||||||
|
return start_address_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||||
|
return value >= start_address_ && value < end_address_;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
|
{
|
||||||
|
'sources': [
|
||||||
|
'raw_symbol_database.cc',
|
||||||
|
'symbol.cc',
|
||||||
|
'symbol_database.cc',
|
||||||
|
'xex_symbol_database.cc',
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/cpu/sdb/symbol.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace xe;
|
||||||
|
using namespace xe::cpu;
|
||||||
|
using namespace xe::cpu::ppc;
|
||||||
|
using namespace xe::cpu::sdb;
|
||||||
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
FunctionBlock::FunctionBlock() :
|
||||||
|
start_address(0), end_address(0),
|
||||||
|
outgoing_type(kTargetUnknown), outgoing_address(0),
|
||||||
|
outgoing_function(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionSymbol::FunctionSymbol() :
|
||||||
|
Symbol(Function),
|
||||||
|
start_address(0), end_address(0), name(0),
|
||||||
|
type(Unknown), flags(0),
|
||||||
|
kernel_export(0), ee(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionSymbol::~FunctionSymbol() {
|
||||||
|
delete name;
|
||||||
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
|
||||||
|
it != blocks.end(); ++it) {
|
||||||
|
delete it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableSymbol::VariableSymbol() :
|
||||||
|
Symbol(Variable),
|
||||||
|
address(0), name(0),
|
||||||
|
kernel_export(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableSymbol::~VariableSymbol() {
|
||||||
|
delete name;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionEntrySymbol::ExceptionEntrySymbol() :
|
||||||
|
Symbol(ExceptionEntry),
|
||||||
|
address(0), function(0) {
|
||||||
|
}
|
|
@ -7,10 +7,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <xenia/cpu/sdb.h>
|
#include <xenia/cpu/sdb/symbol_database.h>
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include <xenia/cpu/ppc/instr.h>
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
@ -23,110 +20,6 @@ using namespace xe::cpu::sdb;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
|
|
||||||
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
|
|
||||||
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
|
|
||||||
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
|
|
||||||
uint32_t FuncStart; // Virtual address
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
|
|
||||||
uint32_t FuncLen : 22; // # of instructions total (size = x4)
|
|
||||||
uint32_t ThirtyTwoBit : 1; // Always 1
|
|
||||||
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
|
|
||||||
} Flags;
|
|
||||||
uint32_t FlagsValue; // To make byte swapping easier
|
|
||||||
};
|
|
||||||
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
|
|
||||||
|
|
||||||
|
|
||||||
class PEMethodInfo {
|
|
||||||
public:
|
|
||||||
uint32_t address;
|
|
||||||
size_t total_length; // in bytes
|
|
||||||
size_t prolog_length; // in bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FunctionBlock::FunctionBlock() :
|
|
||||||
start_address(0), end_address(0),
|
|
||||||
outgoing_type(kTargetUnknown), outgoing_address(0),
|
|
||||||
outgoing_function(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionSymbol::FunctionSymbol() :
|
|
||||||
Symbol(Function),
|
|
||||||
start_address(0), end_address(0), name(0),
|
|
||||||
type(Unknown), flags(0),
|
|
||||||
kernel_export(0), ee(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionSymbol::~FunctionSymbol() {
|
|
||||||
delete name;
|
|
||||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
|
|
||||||
it != blocks.end(); ++it) {
|
|
||||||
delete it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
VariableSymbol::VariableSymbol() :
|
|
||||||
Symbol(Variable),
|
|
||||||
address(0), name(0),
|
|
||||||
kernel_export(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
VariableSymbol::~VariableSymbol() {
|
|
||||||
delete name;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExceptionEntrySymbol::ExceptionEntrySymbol() :
|
|
||||||
Symbol(ExceptionEntry),
|
|
||||||
address(0), function(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolDatabase::SymbolDatabase(xe_memory_ref memory,
|
SymbolDatabase::SymbolDatabase(xe_memory_ref memory,
|
||||||
ExportResolver* export_resolver) {
|
ExportResolver* export_resolver) {
|
||||||
memory_ = xe_memory_retain(memory);
|
memory_ = xe_memory_retain(memory);
|
||||||
|
@ -766,278 +659,3 @@ bool SymbolDatabase::IsRestGprLr(uint32_t addr) {
|
||||||
FunctionSymbol* fn = GetFunction(addr);
|
FunctionSymbol* fn = GetFunction(addr);
|
||||||
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
|
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
|
||||||
}
|
}
|
||||||
|
|
||||||
RawSymbolDatabase::RawSymbolDatabase(
|
|
||||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
|
||||||
uint32_t start_address, uint32_t end_address) :
|
|
||||||
SymbolDatabase(memory, export_resolver) {
|
|
||||||
start_address_ = start_address;
|
|
||||||
end_address_ = end_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawSymbolDatabase::~RawSymbolDatabase() {
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t RawSymbolDatabase::GetEntryPoint() {
|
|
||||||
return start_address_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
|
||||||
return value >= start_address_ && value < end_address_;
|
|
||||||
}
|
|
||||||
|
|
||||||
XexSymbolDatabase::XexSymbolDatabase(
|
|
||||||
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
|
|
||||||
SymbolDatabase(memory, export_resolver) {
|
|
||||||
xex_ = xe_xex2_retain(xex);
|
|
||||||
}
|
|
||||||
|
|
||||||
XexSymbolDatabase::~XexSymbolDatabase() {
|
|
||||||
xe_xex2_release(xex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int XexSymbolDatabase::Analyze() {
|
|
||||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
|
||||||
|
|
||||||
// Find __savegprlr_* and __restgprlr_*.
|
|
||||||
FindGplr();
|
|
||||||
|
|
||||||
// Add each import thunk.
|
|
||||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
|
||||||
AddImports(&header->import_libraries[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add each export root.
|
|
||||||
// TODO(benvanik): exports.
|
|
||||||
// - insert fn or variable
|
|
||||||
// - queue fn
|
|
||||||
|
|
||||||
// Add method hints, if available.
|
|
||||||
// Not all XEXs have these.
|
|
||||||
AddMethodHints();
|
|
||||||
|
|
||||||
return SymbolDatabase::Analyze();
|
|
||||||
}
|
|
||||||
|
|
||||||
int XexSymbolDatabase::FindGplr() {
|
|
||||||
// Special stack save/restore functions.
|
|
||||||
// __savegprlr_14 to __savegprlr_31
|
|
||||||
// __restgprlr_14 to __restgprlr_31
|
|
||||||
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
|
|
||||||
// It'd be nice to stash these away and mark them as such to allow for
|
|
||||||
// special codegen.
|
|
||||||
static const uint32_t code_values[] = {
|
|
||||||
0x68FFC1F9, // __savegprlr_14
|
|
||||||
0x70FFE1F9, // __savegprlr_15
|
|
||||||
0x78FF01FA, // __savegprlr_16
|
|
||||||
0x80FF21FA, // __savegprlr_17
|
|
||||||
0x88FF41FA, // __savegprlr_18
|
|
||||||
0x90FF61FA, // __savegprlr_19
|
|
||||||
0x98FF81FA, // __savegprlr_20
|
|
||||||
0xA0FFA1FA, // __savegprlr_21
|
|
||||||
0xA8FFC1FA, // __savegprlr_22
|
|
||||||
0xB0FFE1FA, // __savegprlr_23
|
|
||||||
0xB8FF01FB, // __savegprlr_24
|
|
||||||
0xC0FF21FB, // __savegprlr_25
|
|
||||||
0xC8FF41FB, // __savegprlr_26
|
|
||||||
0xD0FF61FB, // __savegprlr_27
|
|
||||||
0xD8FF81FB, // __savegprlr_28
|
|
||||||
0xE0FFA1FB, // __savegprlr_29
|
|
||||||
0xE8FFC1FB, // __savegprlr_30
|
|
||||||
0xF0FFE1FB, // __savegprlr_31
|
|
||||||
0xF8FF8191,
|
|
||||||
0x2000804E,
|
|
||||||
0x68FFC1E9, // __restgprlr_14
|
|
||||||
0x70FFE1E9, // __restgprlr_15
|
|
||||||
0x78FF01EA, // __restgprlr_16
|
|
||||||
0x80FF21EA, // __restgprlr_17
|
|
||||||
0x88FF41EA, // __restgprlr_18
|
|
||||||
0x90FF61EA, // __restgprlr_19
|
|
||||||
0x98FF81EA, // __restgprlr_20
|
|
||||||
0xA0FFA1EA, // __restgprlr_21
|
|
||||||
0xA8FFC1EA, // __restgprlr_22
|
|
||||||
0xB0FFE1EA, // __restgprlr_23
|
|
||||||
0xB8FF01EB, // __restgprlr_24
|
|
||||||
0xC0FF21EB, // __restgprlr_25
|
|
||||||
0xC8FF41EB, // __restgprlr_26
|
|
||||||
0xD0FF61EB, // __restgprlr_27
|
|
||||||
0xD8FF81EB, // __restgprlr_28
|
|
||||||
0xE0FFA1EB, // __restgprlr_29
|
|
||||||
0xE8FFC1EB, // __restgprlr_30
|
|
||||||
0xF0FFE1EB, // __restgprlr_31
|
|
||||||
0xF8FF8181,
|
|
||||||
0xA603887D,
|
|
||||||
0x2000804E,
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t gplr_start = 0;
|
|
||||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
|
||||||
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 (section->info.type == XEX_SECTION_CODE) {
|
|
||||||
gplr_start = xe_memory_search_aligned(
|
|
||||||
memory_, start_address, end_address,
|
|
||||||
code_values, XECOUNT(code_values));
|
|
||||||
if (gplr_start) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += section->info.page_count;
|
|
||||||
}
|
|
||||||
if (!gplr_start) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add function stubs.
|
|
||||||
char name[32];
|
|
||||||
uint32_t address = gplr_start;
|
|
||||||
for (int n = 14; n <= 31; n++) {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "__savegprlr_%d", n);
|
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
|
||||||
fn->end_address = fn->start_address + (31 - n) * 4 + 2 * 4;
|
|
||||||
fn->name = xestrdupa(name);
|
|
||||||
fn->type = FunctionSymbol::User;
|
|
||||||
fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
|
||||||
address += 4;
|
|
||||||
}
|
|
||||||
address = gplr_start + 20 * 4;
|
|
||||||
for (int n = 14; n <= 31; n++) {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "__restgprlr_%d", n);
|
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
|
||||||
fn->end_address = fn->start_address + (31 - n) * 4 + 3 * 4;
|
|
||||||
fn->name = xestrdupa(name);
|
|
||||||
fn->type = FunctionSymbol::User;
|
|
||||||
fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
|
||||||
address += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
|
||||||
xe_xex2_import_info_t* import_infos;
|
|
||||||
size_t import_info_count;
|
|
||||||
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
|
|
||||||
&import_info_count)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char name[128];
|
|
||||||
for (size_t n = 0; n < import_info_count; n++) {
|
|
||||||
const xe_xex2_import_info_t* info = &import_infos[n];
|
|
||||||
|
|
||||||
KernelExport* kernel_export = export_resolver_->GetExportByOrdinal(
|
|
||||||
library->name, info->ordinal);
|
|
||||||
|
|
||||||
VariableSymbol* var = GetOrInsertVariable(info->value_address);
|
|
||||||
if (kernel_export) {
|
|
||||||
if (info->thunk_address) {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "__imp__%s", kernel_export->name);
|
|
||||||
} else {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "__imp__%s_%.3X", library->name,
|
|
||||||
info->ordinal);
|
|
||||||
}
|
|
||||||
var->name = xestrdupa(name);
|
|
||||||
var->kernel_export = kernel_export;
|
|
||||||
if (info->thunk_address) {
|
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
|
||||||
fn->end_address = fn->start_address + 16 - 4;
|
|
||||||
fn->type = FunctionSymbol::Kernel;
|
|
||||||
fn->kernel_export = kernel_export;
|
|
||||||
if (kernel_export) {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
|
||||||
} else {
|
|
||||||
xesnprintfa(name, XECOUNT(name), "__kernel_%s_%.3X", library->name,
|
|
||||||
info->ordinal);
|
|
||||||
}
|
|
||||||
fn->name = xestrdupa(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_free(import_infos);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XexSymbolDatabase::AddMethodHints() {
|
|
||||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
|
||||||
|
|
||||||
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
|
|
||||||
|
|
||||||
// Find pdata, which contains the exception handling entries.
|
|
||||||
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
|
|
||||||
if (!pdata) {
|
|
||||||
// No exception data to go on.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve.
|
|
||||||
const uint8_t* p = mem + pdata->address;
|
|
||||||
|
|
||||||
// Entry count = pdata size / sizeof(entry).
|
|
||||||
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
|
|
||||||
if (!entry_count) {
|
|
||||||
// Empty?
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate output.
|
|
||||||
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
|
|
||||||
entry_count * sizeof(PEMethodInfo));
|
|
||||||
if (!method_infos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse entries.
|
|
||||||
// NOTE: entries are in memory as big endian, so pull them out and swap the
|
|
||||||
// values before using them.
|
|
||||||
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
|
|
||||||
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
|
|
||||||
for (size_t n = 0; n < entry_count; n++, entry++) {
|
|
||||||
PEMethodInfo* method_info = &method_infos[n];
|
|
||||||
method_info->address = XESWAP32BE(entry->FuncStart);
|
|
||||||
|
|
||||||
// The bitfield needs to be swapped by hand.
|
|
||||||
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
|
|
||||||
method_info->total_length = temp_entry.Flags.FuncLen * 4;
|
|
||||||
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t n = 0; n < entry_count; n++) {
|
|
||||||
PEMethodInfo* method_info = &method_infos[n];
|
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
|
||||||
fn->end_address = method_info->address + method_info->total_length - 4;
|
|
||||||
fn->type = FunctionSymbol::User;
|
|
||||||
// TODO(benvanik): something with prolog_length?
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_free(method_infos);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t XexSymbolDatabase::GetEntryPoint() {
|
|
||||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
|
||||||
return header->exe_entry_point;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
|
||||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/cpu/sdb/xex_symbol_database.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace xe;
|
||||||
|
using namespace xe::cpu;
|
||||||
|
using namespace xe::cpu::ppc;
|
||||||
|
using namespace xe::cpu::sdb;
|
||||||
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
|
||||||
|
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
|
||||||
|
uint32_t FuncStart; // Virtual address
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
|
||||||
|
uint32_t FuncLen : 22; // # of instructions total (size = x4)
|
||||||
|
uint32_t ThirtyTwoBit : 1; // Always 1
|
||||||
|
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
|
||||||
|
} Flags;
|
||||||
|
uint32_t FlagsValue; // To make byte swapping easier
|
||||||
|
};
|
||||||
|
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
|
||||||
|
|
||||||
|
|
||||||
|
class PEMethodInfo {
|
||||||
|
public:
|
||||||
|
uint32_t address;
|
||||||
|
size_t total_length; // in bytes
|
||||||
|
size_t prolog_length; // in bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XexSymbolDatabase::XexSymbolDatabase(
|
||||||
|
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
|
||||||
|
SymbolDatabase(memory, export_resolver) {
|
||||||
|
xex_ = xe_xex2_retain(xex);
|
||||||
|
}
|
||||||
|
|
||||||
|
XexSymbolDatabase::~XexSymbolDatabase() {
|
||||||
|
xe_xex2_release(xex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int XexSymbolDatabase::Analyze() {
|
||||||
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
|
||||||
|
// Find __savegprlr_* and __restgprlr_*.
|
||||||
|
FindGplr();
|
||||||
|
|
||||||
|
// Add each import thunk.
|
||||||
|
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||||
|
AddImports(&header->import_libraries[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add each export root.
|
||||||
|
// TODO(benvanik): exports.
|
||||||
|
// - insert fn or variable
|
||||||
|
// - queue fn
|
||||||
|
|
||||||
|
// Add method hints, if available.
|
||||||
|
// Not all XEXs have these.
|
||||||
|
AddMethodHints();
|
||||||
|
|
||||||
|
return SymbolDatabase::Analyze();
|
||||||
|
}
|
||||||
|
|
||||||
|
int XexSymbolDatabase::FindGplr() {
|
||||||
|
// Special stack save/restore functions.
|
||||||
|
// __savegprlr_14 to __savegprlr_31
|
||||||
|
// __restgprlr_14 to __restgprlr_31
|
||||||
|
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
|
||||||
|
// It'd be nice to stash these away and mark them as such to allow for
|
||||||
|
// special codegen.
|
||||||
|
static const uint32_t code_values[] = {
|
||||||
|
0x68FFC1F9, // __savegprlr_14
|
||||||
|
0x70FFE1F9, // __savegprlr_15
|
||||||
|
0x78FF01FA, // __savegprlr_16
|
||||||
|
0x80FF21FA, // __savegprlr_17
|
||||||
|
0x88FF41FA, // __savegprlr_18
|
||||||
|
0x90FF61FA, // __savegprlr_19
|
||||||
|
0x98FF81FA, // __savegprlr_20
|
||||||
|
0xA0FFA1FA, // __savegprlr_21
|
||||||
|
0xA8FFC1FA, // __savegprlr_22
|
||||||
|
0xB0FFE1FA, // __savegprlr_23
|
||||||
|
0xB8FF01FB, // __savegprlr_24
|
||||||
|
0xC0FF21FB, // __savegprlr_25
|
||||||
|
0xC8FF41FB, // __savegprlr_26
|
||||||
|
0xD0FF61FB, // __savegprlr_27
|
||||||
|
0xD8FF81FB, // __savegprlr_28
|
||||||
|
0xE0FFA1FB, // __savegprlr_29
|
||||||
|
0xE8FFC1FB, // __savegprlr_30
|
||||||
|
0xF0FFE1FB, // __savegprlr_31
|
||||||
|
0xF8FF8191,
|
||||||
|
0x2000804E,
|
||||||
|
0x68FFC1E9, // __restgprlr_14
|
||||||
|
0x70FFE1E9, // __restgprlr_15
|
||||||
|
0x78FF01EA, // __restgprlr_16
|
||||||
|
0x80FF21EA, // __restgprlr_17
|
||||||
|
0x88FF41EA, // __restgprlr_18
|
||||||
|
0x90FF61EA, // __restgprlr_19
|
||||||
|
0x98FF81EA, // __restgprlr_20
|
||||||
|
0xA0FFA1EA, // __restgprlr_21
|
||||||
|
0xA8FFC1EA, // __restgprlr_22
|
||||||
|
0xB0FFE1EA, // __restgprlr_23
|
||||||
|
0xB8FF01EB, // __restgprlr_24
|
||||||
|
0xC0FF21EB, // __restgprlr_25
|
||||||
|
0xC8FF41EB, // __restgprlr_26
|
||||||
|
0xD0FF61EB, // __restgprlr_27
|
||||||
|
0xD8FF81EB, // __restgprlr_28
|
||||||
|
0xE0FFA1EB, // __restgprlr_29
|
||||||
|
0xE8FFC1EB, // __restgprlr_30
|
||||||
|
0xF0FFE1EB, // __restgprlr_31
|
||||||
|
0xF8FF8181,
|
||||||
|
0xA603887D,
|
||||||
|
0x2000804E,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t gplr_start = 0;
|
||||||
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
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 (section->info.type == XEX_SECTION_CODE) {
|
||||||
|
gplr_start = xe_memory_search_aligned(
|
||||||
|
memory_, start_address, end_address,
|
||||||
|
code_values, XECOUNT(code_values));
|
||||||
|
if (gplr_start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += section->info.page_count;
|
||||||
|
}
|
||||||
|
if (!gplr_start) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add function stubs.
|
||||||
|
char name[32];
|
||||||
|
uint32_t address = gplr_start;
|
||||||
|
for (int n = 14; n <= 31; n++) {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "__savegprlr_%d", n);
|
||||||
|
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||||
|
fn->end_address = fn->start_address + (31 - n) * 4 + 2 * 4;
|
||||||
|
fn->name = xestrdupa(name);
|
||||||
|
fn->type = FunctionSymbol::User;
|
||||||
|
fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
||||||
|
address += 4;
|
||||||
|
}
|
||||||
|
address = gplr_start + 20 * 4;
|
||||||
|
for (int n = 14; n <= 31; n++) {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "__restgprlr_%d", n);
|
||||||
|
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||||
|
fn->end_address = fn->start_address + (31 - n) * 4 + 3 * 4;
|
||||||
|
fn->name = xestrdupa(name);
|
||||||
|
fn->type = FunctionSymbol::User;
|
||||||
|
fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
||||||
|
address += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||||
|
xe_xex2_import_info_t* import_infos;
|
||||||
|
size_t import_info_count;
|
||||||
|
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
|
||||||
|
&import_info_count)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[128];
|
||||||
|
for (size_t n = 0; n < import_info_count; n++) {
|
||||||
|
const xe_xex2_import_info_t* info = &import_infos[n];
|
||||||
|
|
||||||
|
KernelExport* kernel_export = export_resolver_->GetExportByOrdinal(
|
||||||
|
library->name, info->ordinal);
|
||||||
|
|
||||||
|
VariableSymbol* var = GetOrInsertVariable(info->value_address);
|
||||||
|
if (kernel_export) {
|
||||||
|
if (info->thunk_address) {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "__imp__%s", kernel_export->name);
|
||||||
|
} else {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "__imp__%s_%.3X", library->name,
|
||||||
|
info->ordinal);
|
||||||
|
}
|
||||||
|
var->name = xestrdupa(name);
|
||||||
|
var->kernel_export = kernel_export;
|
||||||
|
if (info->thunk_address) {
|
||||||
|
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
||||||
|
fn->end_address = fn->start_address + 16 - 4;
|
||||||
|
fn->type = FunctionSymbol::Kernel;
|
||||||
|
fn->kernel_export = kernel_export;
|
||||||
|
if (kernel_export) {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
||||||
|
} else {
|
||||||
|
xesnprintfa(name, XECOUNT(name), "__kernel_%s_%.3X", library->name,
|
||||||
|
info->ordinal);
|
||||||
|
}
|
||||||
|
fn->name = xestrdupa(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_free(import_infos);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XexSymbolDatabase::AddMethodHints() {
|
||||||
|
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||||
|
|
||||||
|
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
|
||||||
|
|
||||||
|
// Find pdata, which contains the exception handling entries.
|
||||||
|
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
|
||||||
|
if (!pdata) {
|
||||||
|
// No exception data to go on.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve.
|
||||||
|
const uint8_t* p = mem + pdata->address;
|
||||||
|
|
||||||
|
// Entry count = pdata size / sizeof(entry).
|
||||||
|
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
|
||||||
|
if (!entry_count) {
|
||||||
|
// Empty?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate output.
|
||||||
|
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
|
||||||
|
entry_count * sizeof(PEMethodInfo));
|
||||||
|
if (!method_infos) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse entries.
|
||||||
|
// NOTE: entries are in memory as big endian, so pull them out and swap the
|
||||||
|
// values before using them.
|
||||||
|
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
|
||||||
|
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
|
||||||
|
for (size_t n = 0; n < entry_count; n++, entry++) {
|
||||||
|
PEMethodInfo* method_info = &method_infos[n];
|
||||||
|
method_info->address = XESWAP32BE(entry->FuncStart);
|
||||||
|
|
||||||
|
// The bitfield needs to be swapped by hand.
|
||||||
|
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
|
||||||
|
method_info->total_length = temp_entry.Flags.FuncLen * 4;
|
||||||
|
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t n = 0; n < entry_count; n++) {
|
||||||
|
PEMethodInfo* method_info = &method_infos[n];
|
||||||
|
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
||||||
|
fn->end_address = method_info->address + method_info->total_length - 4;
|
||||||
|
fn->type = FunctionSymbol::User;
|
||||||
|
// TODO(benvanik): something with prolog_length?
|
||||||
|
}
|
||||||
|
|
||||||
|
xe_free(method_infos);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t XexSymbolDatabase::GetEntryPoint() {
|
||||||
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
return header->exe_entry_point;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||||
|
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||||
|
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;
|
||||||
|
}
|
|
@ -5,12 +5,12 @@
|
||||||
'exec_module.cc',
|
'exec_module.cc',
|
||||||
'llvm_exports.cc',
|
'llvm_exports.cc',
|
||||||
'processor.cc',
|
'processor.cc',
|
||||||
'sdb.cc',
|
|
||||||
'thread_state.cc',
|
'thread_state.cc',
|
||||||
],
|
],
|
||||||
|
|
||||||
'includes': [
|
'includes': [
|
||||||
'codegen/sources.gypi',
|
'codegen/sources.gypi',
|
||||||
'ppc/sources.gypi',
|
'ppc/sources.gypi',
|
||||||
|
'sdb/sources.gypi',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue