XEX-style exports, dumping exports, and prepping kernel export lookup.
This commit is contained in:
parent
23eb343484
commit
4411a4499d
|
@ -34,7 +34,7 @@ void ExportResolver::RegisterTable(const std::string& library_name,
|
|||
KernelExport* ExportResolver::GetExportByOrdinal(
|
||||
const std::string& library_name, const uint32_t ordinal) {
|
||||
for (const auto& table : tables_) {
|
||||
if (table.name == library_name) {
|
||||
if (table.name == library_name || table.simple_name == library_name) {
|
||||
// TODO(benvanik): binary search?
|
||||
for (size_t n = 0; n < table.count; n++) {
|
||||
if (table.exports[n].ordinal == ordinal) {
|
||||
|
|
|
@ -70,10 +70,18 @@ class ExportResolver {
|
|||
private:
|
||||
struct ExportTable {
|
||||
std::string name;
|
||||
std::string simple_name; // without extension
|
||||
KernelExport* exports;
|
||||
size_t count;
|
||||
ExportTable(const std::string& name, KernelExport* exports, size_t count)
|
||||
: name(name), exports(exports), count(count) {}
|
||||
: name(name), exports(exports), count(count) {
|
||||
auto dot_pos = name.find_last_of('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
simple_name = name.substr(0, dot_pos);
|
||||
} else {
|
||||
simple_name = name;
|
||||
}
|
||||
}
|
||||
};
|
||||
std::vector<ExportTable> tables_;
|
||||
};
|
||||
|
|
|
@ -29,9 +29,35 @@ XKernelModule::XKernelModule(KernelState* kernel_state, const char* path)
|
|||
XKernelModule::~XKernelModule() {}
|
||||
|
||||
uint32_t XKernelModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
// TODO(benvanik): check export tables.
|
||||
XELOGE("GetProcAddressByOrdinal not implemented");
|
||||
return 0;
|
||||
auto export = export_resolver_->GetExportByOrdinal(name(), ordinal);
|
||||
if (!export) {
|
||||
// Export (or its parent library) not found.
|
||||
return 0;
|
||||
}
|
||||
if (export->type == cpu::KernelExport::ExportType::Variable) {
|
||||
if (export->variable_ptr) {
|
||||
return export->variable_ptr;
|
||||
} else {
|
||||
XELOGW(
|
||||
"ERROR: var export referenced GetProcAddressByOrdinal(%.4X(%s)) is "
|
||||
"not implemented",
|
||||
ordinal, export->name);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (export->function_data.shim) {
|
||||
// Implemented. Dynamically generate trampoline.
|
||||
XELOGE("GetProcAddressByOrdinal not implemented");
|
||||
return 0;
|
||||
} else {
|
||||
// Not implemented.
|
||||
XELOGW(
|
||||
"ERROR: fn export referenced GetProcAddressByOrdinal(%.4X(%s)) is "
|
||||
"not implemented",
|
||||
ordinal, export->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t XKernelModule::GetProcAddressByName(const char* name) {
|
||||
|
|
|
@ -143,24 +143,11 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
|
|||
}
|
||||
|
||||
uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
PEExport export;
|
||||
int ret = xe_xex2_lookup_export(xex_, ordinal, export);
|
||||
if (ret) {
|
||||
XELOGE("XUserModule::GetProcAddressByOrdinal(%d) not found", ordinal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return uint32_t(export.addr);
|
||||
return xe_xex2_lookup_export(xex_, ordinal);
|
||||
}
|
||||
|
||||
uint32_t XUserModule::GetProcAddressByName(const char* name) {
|
||||
PEExport export;
|
||||
int ret = xe_xex2_lookup_export(xex_, name, export);
|
||||
if (ret) {
|
||||
XELOGE("XUserModule::GetProcAddressByName(%s) not found", name);
|
||||
return 0;
|
||||
}
|
||||
return uint32_t(export.addr);
|
||||
return xe_xex2_lookup_export(xex_, name);
|
||||
}
|
||||
|
||||
X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||
|
@ -300,6 +287,45 @@ void XUserModule::Dump() {
|
|||
}
|
||||
printf("\n");
|
||||
|
||||
// Exports.
|
||||
printf("Exports:\n");
|
||||
if (header->loader_info.export_table) {
|
||||
printf(" XEX-style Ordinal Exports:\n");
|
||||
auto export_table = reinterpret_cast<const xe_xex2_export_table*>(
|
||||
memory()->TranslateVirtual(header->loader_info.export_table));
|
||||
uint32_t ordinal_count = xe::byte_swap(export_table->count);
|
||||
uint32_t ordinal_base = xe::byte_swap(export_table->base);
|
||||
for (uint32_t i = 0, ordinal = ordinal_base; i < ordinal_count;
|
||||
++i, ++ordinal) {
|
||||
uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]);
|
||||
ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16;
|
||||
printf(" %.8X %.3X (%3d)\n", ordinal_offset, ordinal,
|
||||
ordinal);
|
||||
}
|
||||
}
|
||||
if (header->pe_export_table_offset) {
|
||||
auto e = reinterpret_cast<const IMAGE_EXPORT_DIRECTORY*>(
|
||||
memory()->TranslateVirtual(header->exe_address +
|
||||
header->pe_export_table_offset));
|
||||
uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions);
|
||||
uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames);
|
||||
uint16_t* ordinal_table =
|
||||
(uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals);
|
||||
const char* mod_name = (const char*)((uint64_t)e + e->Name);
|
||||
printf(" PE %s:\n", mod_name);
|
||||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
const char* fn_name = (const char*)((uint64_t)e + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint32_t addr = header->exe_address + function_table[ordinal];
|
||||
printf(" %.8X %.3X (%3d) %s\n", addr, ordinal, ordinal,
|
||||
fn_name);
|
||||
}
|
||||
}
|
||||
if (!header->loader_info.export_table && !header->pe_export_table_offset) {
|
||||
printf(" No exports\n");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Imports.
|
||||
printf("Imports:\n");
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
|
@ -372,11 +398,6 @@ void XUserModule::Dump() {
|
|||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Exports.
|
||||
printf("Exports:\n");
|
||||
printf(" TODO\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -239,7 +239,7 @@ int xe_xex2_read_header(const uint8_t *addr, const size_t length,
|
|||
break;
|
||||
case XEX_HEADER_EXPORTS_BY_NAME: {
|
||||
// IMAGE_DATA_DIRECTORY (w/ offset from PE file base)
|
||||
header->export_table_offset = xe::load_and_swap<uint32_t>(pp);
|
||||
header->pe_export_table_offset = xe::load_and_swap<uint32_t>(pp);
|
||||
// size = xe::load_and_swap<uint32_t>(pp + 0x04);
|
||||
} break;
|
||||
case XEX_HEADER_IMPORT_LIBRARIES: {
|
||||
|
@ -988,27 +988,24 @@ int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int xe_xex2_lookup_export(xe_xex2_ref xex, const char *name,
|
||||
PEExport &peexport) {
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char *name) {
|
||||
auto header = xe_xex2_get_header(xex);
|
||||
|
||||
// no exports :(
|
||||
if (!header->export_table_offset) {
|
||||
return 1;
|
||||
if (!header->pe_export_table_offset) {
|
||||
XELOGE("xe_xex2_lookup_export(%s) failed: no PE export table", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t baseaddr =
|
||||
(uint64_t)xex->memory->TranslateVirtual(header->exe_address);
|
||||
IMAGE_EXPORT_DIRECTORY *e =
|
||||
(PIMAGE_EXPORT_DIRECTORY)(baseaddr + header->export_table_offset);
|
||||
auto e = reinterpret_cast<const IMAGE_EXPORT_DIRECTORY *>(
|
||||
xex->memory->TranslateVirtual(header->exe_address +
|
||||
header->pe_export_table_offset));
|
||||
|
||||
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
|
||||
uint32_t *function_table =
|
||||
(uint32_t *)((uint64_t)e + e->AddressOfFunctions);
|
||||
uint32_t *function_table = (uint32_t *)((uint64_t)e + e->AddressOfFunctions);
|
||||
|
||||
// Names relative to directory
|
||||
uint32_t *name_table =
|
||||
(uint32_t *)((uint64_t)e + e->AddressOfNames);
|
||||
uint32_t *name_table = (uint32_t *)((uint64_t)e + e->AddressOfNames);
|
||||
|
||||
// Table of ordinals (by name)
|
||||
uint16_t *ordinal_table =
|
||||
|
@ -1019,44 +1016,53 @@ int xe_xex2_lookup_export(xe_xex2_ref xex, const char *name,
|
|||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
const char *fn_name = (const char *)((uint64_t)e + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint64_t addr = (uint64_t)(baseaddr + function_table[ordinal]);
|
||||
uint32_t addr = header->exe_address + function_table[ordinal];
|
||||
|
||||
if (!strcmp(name, fn_name)) {
|
||||
// We have a match!
|
||||
peexport.name = fn_name;
|
||||
peexport.addr = addr;
|
||||
peexport.ordinal = ordinal;
|
||||
|
||||
return 0;
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
// No match
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal,
|
||||
PEExport &peexport) {
|
||||
auto header = xe_xex2_get_header(xex);
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) {
|
||||
auto header = xe_xex2_get_header(xex);
|
||||
|
||||
// no exports :(
|
||||
if (!header->export_table_offset) {
|
||||
return 1;
|
||||
// XEX-style export table.
|
||||
if (header->loader_info.export_table) {
|
||||
auto export_table = reinterpret_cast<const xe_xex2_export_table *>(
|
||||
xex->memory->TranslateVirtual(header->loader_info.export_table));
|
||||
uint32_t ordinal_count = xe::byte_swap(export_table->count);
|
||||
uint32_t ordinal_base = xe::byte_swap(export_table->base);
|
||||
if (ordinal > ordinal_count) {
|
||||
XELOGE("xe_xex2_lookup_export: ordinal out of bounds");
|
||||
return 0;
|
||||
}
|
||||
uint32_t i = ordinal - ordinal_base;
|
||||
uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]);
|
||||
ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16;
|
||||
return ordinal_offset;
|
||||
}
|
||||
|
||||
uint64_t baseaddr =
|
||||
(uint64_t)xex->memory->TranslateVirtual(header->exe_address);
|
||||
IMAGE_EXPORT_DIRECTORY *e =
|
||||
(PIMAGE_EXPORT_DIRECTORY)(baseaddr + header->export_table_offset);
|
||||
// Check for PE-style export table.
|
||||
if (!header->pe_export_table_offset) {
|
||||
XELOGE("xe_xex2_lookup_export(%.4X) failed: no XEX or PE export table");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto e = reinterpret_cast<const IMAGE_EXPORT_DIRECTORY *>(
|
||||
xex->memory->TranslateVirtual(header->exe_address +
|
||||
header->pe_export_table_offset));
|
||||
|
||||
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
|
||||
// Functions relative to base
|
||||
uint32_t *function_table =
|
||||
(uint32_t *)((uint64_t)e + e->AddressOfFunctions);
|
||||
uint32_t *function_table = (uint32_t *)((uint64_t)e + e->AddressOfFunctions);
|
||||
|
||||
// Names relative to directory
|
||||
uint32_t *name_table =
|
||||
(uint32_t *)((uint64_t)e + e->AddressOfNames);
|
||||
uint32_t *name_table = (uint32_t *)((uint64_t)e + e->AddressOfNames);
|
||||
|
||||
// Table of ordinals (by name)
|
||||
uint16_t *ordinal_table =
|
||||
|
@ -1064,14 +1070,10 @@ int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal,
|
|||
|
||||
const char *mod_name = (const char *)((uint64_t)e + e->Name);
|
||||
|
||||
if (ordinal < int(e->NumberOfFunctions)) {
|
||||
peexport.name = nullptr; // TODO: Backwards conversion to get this
|
||||
peexport.ordinal = ordinal;
|
||||
peexport.addr = (uint64_t)(baseaddr + function_table[ordinal]);
|
||||
|
||||
return 0;
|
||||
if (ordinal < e->NumberOfFunctions) {
|
||||
return header->exe_address + function_table[ordinal];
|
||||
}
|
||||
|
||||
// No match
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
|
@ -62,9 +62,7 @@ int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
|||
xe_xex2_import_info_t** out_import_infos,
|
||||
size_t* out_import_info_count);
|
||||
|
||||
int xe_xex2_lookup_export(xe_xex2_ref xex, const char* name,
|
||||
PEExport& peexport);
|
||||
int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal,
|
||||
PEExport& peexport);
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char* name);
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal);
|
||||
|
||||
#endif // XENIA_KERNEL_UTIL_XEX2_H_
|
||||
|
|
|
@ -445,7 +445,7 @@ typedef struct {
|
|||
xe_xex2_tls_info_t tls_info;
|
||||
size_t import_library_count;
|
||||
xe_xex2_import_library_t import_libraries[32];
|
||||
size_t export_table_offset; // PE Export Directory
|
||||
uint32_t pe_export_table_offset; // PE Export Directory
|
||||
size_t static_library_count;
|
||||
xe_xex2_static_library_t static_libraries[32];
|
||||
xe_xex2_file_format_info_t file_format_info;
|
||||
|
|
|
@ -252,22 +252,25 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_state,
|
|||
KernelState* state) {
|
||||
uint32_t module_handle = SHIM_GET_ARG_32(0);
|
||||
uint32_t ordinal = SHIM_GET_ARG_32(1);
|
||||
const char* name = (const char*)SHIM_MEM_ADDR(ordinal);
|
||||
uint32_t out_function_ptr = SHIM_GET_ARG_32(2);
|
||||
|
||||
if (ordinal < 0x10000) {
|
||||
// May be entry point?
|
||||
assert_not_zero(ordinal);
|
||||
|
||||
bool is_string_name = (ordinal & 0xFFFF0000) != 0;
|
||||
auto string_name = reinterpret_cast<const char*>(SHIM_MEM_ADDR(ordinal));
|
||||
|
||||
if (is_string_name) {
|
||||
XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle,
|
||||
ordinal, string_name, out_function_ptr);
|
||||
} else {
|
||||
XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", module_handle, ordinal,
|
||||
out_function_ptr);
|
||||
} else {
|
||||
XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle,
|
||||
ordinal, name, out_function_ptr);
|
||||
}
|
||||
|
||||
X_STATUS result = X_STATUS_INVALID_HANDLE;
|
||||
SHIM_SET_MEM_32(out_function_ptr, 0xDEADF00D);
|
||||
|
||||
XModule* module = NULL;
|
||||
|
||||
if (!module_handle) {
|
||||
module = state->GetExecutableModule();
|
||||
} else {
|
||||
|
@ -276,23 +279,19 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_state,
|
|||
}
|
||||
|
||||
if (XSUCCEEDED(result)) {
|
||||
if (ordinal < 0x10000) {
|
||||
// Ordinal.
|
||||
uint32_t ptr = module->GetProcAddressByOrdinal(ordinal);
|
||||
if (ptr) {
|
||||
SHIM_SET_MEM_32(out_function_ptr, ptr);
|
||||
result = X_STATUS_SUCCESS;
|
||||
}
|
||||
uint32_t ptr;
|
||||
if (is_string_name) {
|
||||
ptr = module->GetProcAddressByName(string_name);
|
||||
} else {
|
||||
// It's a name pointer instead.
|
||||
uint32_t ptr = module->GetProcAddressByName(name);
|
||||
|
||||
// FYI: We don't need to generate this function now. It'll
|
||||
// be done automatically by xenia when it gets called.
|
||||
if (ptr) {
|
||||
SHIM_SET_MEM_32(out_function_ptr, ptr);
|
||||
result = X_STATUS_SUCCESS;
|
||||
}
|
||||
ptr = module->GetProcAddressByOrdinal(ordinal);
|
||||
}
|
||||
if (ptr) {
|
||||
SHIM_SET_MEM_32(out_function_ptr, ptr);
|
||||
result = X_STATUS_SUCCESS;
|
||||
} else {
|
||||
XELOGW("ERROR: XexGetProcedureAddress ordinal not found!");
|
||||
SHIM_SET_MEM_32(out_function_ptr, 0);
|
||||
result = X_STATUS_DRIVER_ORDINAL_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ typedef uint32_t X_STATUS;
|
|||
#define X_STATUS_INVALID_PARAMETER_1 ((X_STATUS)0xC00000EFL)
|
||||
#define X_STATUS_INVALID_PARAMETER_2 ((X_STATUS)0xC00000F0L)
|
||||
#define X_STATUS_INVALID_PARAMETER_3 ((X_STATUS)0xC00000F1L)
|
||||
#define X_STATUS_DRIVER_ORDINAL_NOT_FOUND ((X_STATUS)0xC0000262L)
|
||||
#define X_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((X_STATUS)0xC0000263L)
|
||||
|
||||
// HRESULT (ERROR_*)
|
||||
// Adding as needed.
|
||||
|
|
Loading…
Reference in New Issue