Rewrite XexModule to drop dependency on old xex2 headers for imports

This commit is contained in:
Dr. Chat 2015-07-02 21:58:47 -05:00
parent be5f8d3aa4
commit 362a521c79
3 changed files with 162 additions and 115 deletions

View File

@ -12,6 +12,7 @@
#include <cstdint>
#include "xenia/base/assert.h"
#include "xenia/base/platform.h"
#if XE_PLATFORM_MAC

View File

@ -82,6 +82,12 @@ bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const {
return XexModule::GetOptHeader(xex_header_, key, out_ptr);
}
const xex2_security_info* XexModule::GetSecurityInfo(
const xex2_header* header) {
return reinterpret_cast<const xex2_security_info*>((uint8_t*)header +
header->security_offset);
}
uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
return xe_xex2_lookup_export(xex_, ordinal);
}
@ -130,16 +136,17 @@ bool XexModule::Load(const std::string& name, const std::string& path,
bool XexModule::Load(const std::string& name, const std::string& path,
xe_xex2_ref xex) {
xex_ = xex;
const xe_xex2_header_t* header = xe_xex2_get_header(xex);
auto header = xex_header_;
auto old_header = xe_xex2_get_header(xex_);
// Scan and find the low/high addresses.
// All code sections are continuous, so this should be easy.
low_address_ = UINT_MAX;
high_address_ = 0;
for (uint32_t n = 0, i = 0; n < header->section_count; n++) {
const xe_xex2_section_t* section = &header->sections[n];
for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) {
const xe_xex2_section_t* section = &old_header->sections[n];
const uint32_t start_address =
header->exe_address + (i * section->page_size);
old_header->exe_address + (i * section->page_size);
const uint32_t end_address =
start_address + (section->info.page_count * section->page_size);
if (section->info.type == XEX_SECTION_CODE) {
@ -153,12 +160,37 @@ bool XexModule::Load(const std::string& name, const std::string& path,
processor_->backend()->CommitExecutableRange(low_address_, high_address_);
// Add all imports (variables/functions).
for (size_t n = 0; n < header->import_library_count; n++) {
if (!SetupLibraryImports(&header->import_libraries[n])) {
return false;
xex2_opt_import_libraries* opt_import_header = nullptr;
GetOptHeader(XEX_HEADER_IMPORT_LIBRARIES, &opt_import_header);
assert_not_null(opt_import_header);
// FIXME: Don't know if 32 is the actual limit, but haven't seen more than 2.
const char* string_table[32];
std::memset(string_table, 0, sizeof(string_table));
// Parse the string table
for (size_t i = 0, j = 0; i < opt_import_header->string_table_size; j++) {
const char* str = opt_import_header->string_table + i;
string_table[j] = str;
i += std::strlen(str) + 1;
// Padding
if ((i % 4) != 0) {
i += 4 - (i % 4);
}
}
auto libraries =
(uint8_t*)opt_import_header + opt_import_header->string_table_size + 12;
uint32_t library_offset = 0;
for (uint32_t i = 0; i < opt_import_header->library_count; i++) {
auto library = reinterpret_cast<xex2_import_library*>((uint8_t*)libraries + library_offset);
SetupLibraryImports(string_table[library->name_index], library);
library_offset += library->size;
}
// Find __savegprlr_* and __restgprlr_* and the others.
// We can flag these for special handling (inlining/etc).
if (!FindSaveRest()) {
@ -180,121 +212,103 @@ bool XexModule::Load(const std::string& name, const std::string& path,
return true;
}
bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
ExportResolver* export_resolver = processor_->export_resolver();
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 false;
bool XexModule::SetupLibraryImports(const char* name,
const xex2_import_library* library) {
ExportResolver* kernel_resolver = nullptr;
if (kernel_state_->IsKernelModule(name)) {
kernel_resolver = processor_->export_resolver();
}
char name[128];
for (size_t n = 0; n < import_info_count; n++) {
const xe_xex2_import_info_t* info = &import_infos[n];
auto user_module = kernel_state_->GetModule(name);
// Strip off the extension (for the symbol name)
std::string libname = library->name;
auto dot = libname.find_last_of('.');
if (dot != libname.npos) {
libname = libname.substr(0, dot);
}
std::string libbasename = name;
auto dot = libbasename.find_last_of('.');
if (dot != libbasename.npos) {
libbasename = libbasename.substr(0, dot);
}
Export* kernel_export = nullptr; // kernel export info
uint32_t user_export_addr = 0; // user export address
// Imports are stored as {import descriptor, thunk addr, import desc, ...}
for (uint32_t i = 0; i < library->count; i++) {
uint32_t record_addr = library->import_table[i];
assert_not_zero(record_addr);
if (kernel_state_->IsKernelModule(library->name)) {
kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
auto record_slot =
memory()->TranslateVirtual<xe::be<uint32_t>*>(record_addr);
uint32_t record_value = *record_slot;
uint16_t record_type = (record_value & 0xFF000000) >> 24;
uint16_t ordinal = record_value & 0xFFFF;
Export* kernel_export = nullptr;
uint32_t user_export_addr = 0;
if (kernel_resolver) {
kernel_export = kernel_resolver->GetExportByOrdinal(name, ordinal);
} else {
auto module = kernel_state_->GetModule(library->name);
if (module) {
user_export_addr = module->GetProcAddressByOrdinal(info->ordinal);
}
user_export_addr = user_module->GetProcAddressByOrdinal(ordinal);
}
if (kernel_export) {
if (info->thunk_address) {
snprintf(name, xe::countof(name), "__imp_%s", kernel_export->name);
} else {
snprintf(name, xe::countof(name), "%s", kernel_export->name);
}
} else {
snprintf(name, xe::countof(name), "__imp_%s_%.3X", libname.c_str(),
info->ordinal);
// Import not resolved?
assert_not_zero(kernel_export || user_export_addr);
if (!kernel_export && !user_export_addr) {
XELOGW(
"WARNING: an import variable was not resolved! (import lib: %s, "
"ordinal: %.3X)",
name, ordinal);
}
VariableInfo* var_info;
DeclareVariable(info->value_address, &var_info);
// var->set_name(name);
var_info->set_status(SymbolStatus::kDeclared);
DefineVariable(var_info);
// var->kernel_export = kernel_export;
var_info->set_status(SymbolStatus::kDefined);
// Grab, if available.
auto slot = memory_->TranslateVirtual<uint32_t*>(info->value_address);
if (kernel_export) {
if (kernel_export->type == Export::Type::kFunction) {
// Not exactly sure what this should be...
if (info->thunk_address) {
*slot = xe::byte_swap(info->thunk_address);
} else {
// TODO(benvanik): find out what import variables are.
XELOGW("kernel import variable not defined %.8X %s",
info->value_address, kernel_export->name);
*slot = xe::byte_swap(0xF00DF00D);
}
} else {
if (kernel_export->is_implemented()) {
// Implemented - replace with pointer.
xe::store_and_swap<uint32_t>(slot, kernel_export->variable_ptr);
} else {
// Not implemented - write with a dummy value.
xe::store_and_swap<uint32_t>(
slot, 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16);
XELOGCPU("WARNING: imported a variable with no value: %s",
kernel_export->name);
}
}
} else if (user_export_addr) {
xe::store_and_swap<uint32_t>(slot, user_export_addr);
} else {
// No module found.
XELOGE("kernel import not found: %s", name);
if (info->thunk_address) {
*slot = xe::byte_swap(info->thunk_address);
} else {
*slot = xe::byte_swap(0xF00DF00D);
}
}
if (info->thunk_address) {
StringBuffer import_name;
if (record_type == 0) {
// Variable.
import_name.AppendFormat("__imp__");
if (kernel_export) {
snprintf(name, xe::countof(name), "%s", kernel_export->name);
import_name.AppendFormat("%s", kernel_export->name);
} else if (user_export_addr) {
snprintf(name, xe::countof(name), "__%s_%.3X", libname.c_str(),
info->ordinal);
} else {
snprintf(name, xe::countof(name), "__kernel_%s_%.3X", libname.c_str(),
info->ordinal);
import_name.AppendFormat("%s_%.3X", libbasename.c_str(), ordinal);
}
if (user_export_addr) {
// Rewrite PPC code to set r11 to the target address
// So we'll have:
// lis r11, user_export_addr
// ori r11, r11, user_export_addr
// mtspr CTR, r11
// bctr
uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF;
uint16_t low_addr = user_export_addr & 0xFFFF;
if (kernel_export) {
if (kernel_export->type == Export::Type::kFunction) {
// Not exactly sure what this should be...
// Appears to be ignored.
*record_slot = 0xDEADC0DE;
} else if (kernel_export->type == Export::Type::kVariable) {
// Kernel import variable
if (kernel_export->is_implemented()) {
// Implemented - replace with pointer.
*record_slot = kernel_export->variable_ptr;
} else {
// Not implemented - write with a dummy value.
*record_slot = 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16;
XELOGCPU("WARNING: imported a variable with no value: %s",
kernel_export->name);
}
}
} else if (user_export_addr) {
*record_slot = user_export_addr;
}
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
xe::store_and_swap<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
xe::store_and_swap<uint32_t>(p + 0x4, 0x616B0000 | low_addr);
} else {
// Setup a variable and define it.
VariableInfo* var_info;
DeclareVariable(record_addr, &var_info);
var_info->set_name(import_name.GetString());
var_info->set_status(SymbolStatus::kDeclared);
DefineVariable(var_info);
var_info->set_status(SymbolStatus::kDefined);
} else if (record_type == 1) {
// Thunk.
if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name);
} else if (user_export_addr) {
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
}
FunctionInfo* fn_info;
DeclareFunction(record_addr, &fn_info);
fn_info->set_end_address(record_addr + 16 - 4);
fn_info->set_name(import_name.GetString());
if (kernel_export) {
// On load we have something like this in memory:
// li r3, 0
// li r4, 0x1F5
@ -309,7 +323,7 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
// blr
// nop
// nop
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
uint8_t* p = memory()->TranslateVirtual(record_addr);
xe::store_and_swap<uint32_t>(p + 0x0, 0x44000002);
xe::store_and_swap<uint32_t>(p + 0x4, 0x4E800020);
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
@ -328,13 +342,26 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
handler = UndefinedImport;
}
FunctionInfo* fn_info;
DeclareFunction(info->thunk_address, &fn_info);
fn_info->set_end_address(info->thunk_address + 16 - 4);
fn_info->set_name(name);
fn_info->SetupExtern(handler);
fn_info->set_status(SymbolStatus::kDeclared);
} else if (user_export_addr) {
// Rewrite PPC code to set r11 to the target address
// So we'll have:
// lis r11, user_export_addr
// ori r11, r11, user_export_addr
// mtspr CTR, r11
// bctr
uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF;
uint16_t low_addr = user_export_addr & 0xFFFF;
uint8_t* p = memory()->TranslateVirtual(record_addr);
xe::store_and_swap<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
xe::store_and_swap<uint32_t>(p + 0x4, 0x616B0000 | low_addr);
}
fn_info->set_status(SymbolStatus::kDeclared);
} else {
// Bad.
assert_always();
}
}

View File

@ -34,6 +34,9 @@ class XexModule : public xe::cpu::Module {
xe_xex2_ref xex() const { return xex_; }
const xex2_header* xex_header() const { return xex_header_; }
const xex2_security_info* xex_security_info() const {
return GetSecurityInfo(xex_header_);
}
// Gets an optional header. Returns NULL if not found.
// Special case: if key & 0xFF == 0x00, this function will return the value,
@ -42,6 +45,22 @@ class XexModule : public xe::cpu::Module {
void** out_ptr);
bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const;
// Ultra-cool templated version
// Special case: if key & 0xFF == 0x00, this function will return the value,
// not a pointer!
template <typename T>
static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
T* out_ptr) {
return GetOptHeader(header, key, reinterpret_cast<void**>(out_ptr));
}
template <typename T>
bool GetOptHeader(xe_xex2_header_keys key, T* out_ptr) const {
return GetOptHeader(key, reinterpret_cast<void**>(out_ptr));
}
static const xex2_security_info* GetSecurityInfo(const xex2_header* header);
uint32_t GetProcAddress(uint16_t ordinal) const;
uint32_t GetProcAddress(const char* name) const;
@ -55,8 +74,8 @@ class XexModule : public xe::cpu::Module {
bool ContainsAddress(uint32_t address) override;
private:
bool SetupImports(xe_xex2_ref xex);
bool SetupLibraryImports(const xe_xex2_import_library_t* library);
bool SetupLibraryImports(const char* name,
const xex2_import_library* library);
bool FindSaveRest();
private: