Properly relocate all offsets at the start.

Handle some errors.
This commit is contained in:
Subv 2015-12-25 22:13:54 -05:00
parent bab307b1f4
commit 2e4730a3cc

View File

@ -101,7 +101,9 @@ struct CROHeader {
u32 next_cro;
u32 previous_cro;
u32 file_size;
INSERT_PADDING_WORDS(0x6);
u32 unk_size1;
u32 always_zero;
INSERT_PADDING_WORDS(0x4);
u32 segment_offset;
u32 code_offset;
u32 code_size;
@ -141,11 +143,11 @@ struct CROHeader {
u8 GetImportPatchesTargetSegment() { return segment_offset & 0xF; }
u32 GetImportPatchesSegmentOffset() { return segment_offset >> 4; }
SegmentTableEntry* GetSegmentTableEntry(u32 index, bool relocated = false);
void RelocateSegmentsTable(u32 base, u32 data_section0, u32 data_section1, u32& prev_data_section0);
SegmentTableEntry* GetSegmentTableEntry(u32 index);
ResultCode RelocateSegmentsTable(u32 base, u32 size, u32 data_section0, u32 data_section1, u32& prev_data_section0);
ExportTableEntry* GetExportTableEntry(u32 index);
void RelocateExportsTable(u32 base);
ResultCode RelocateExportsTable(u32 base);
ExportTreeEntry* GetExportTreeEntry(u32 index);
@ -170,20 +172,19 @@ struct CROHeader {
void RelocateUnk2Patches(u32 base);
void RelocateOffsets(u32 base);
bool VerifyAndRelocateOffsets(u32 base, u32 size);
};
static std::unordered_map<std::string, ExportedSymbol> loaded_exports;
static std::vector<u32> loaded_cros;
SegmentTableEntry* CROHeader::GetSegmentTableEntry(u32 index, bool relocated) {
if (relocated)
return reinterpret_cast<SegmentTableEntry*>(Memory::GetPointer(segment_table_offset + sizeof(SegmentTableEntry) * index));
return reinterpret_cast<SegmentTableEntry*>(reinterpret_cast<u8*>(this) + segment_table_offset + sizeof(SegmentTableEntry) * index);
SegmentTableEntry* CROHeader::GetSegmentTableEntry(u32 index) {
return reinterpret_cast<SegmentTableEntry*>(Memory::GetPointer(segment_table_offset + sizeof(SegmentTableEntry) * index));
}
void CROHeader::RelocateSegmentsTable(u32 base, u32 data_section0, u32 data_section1, u32& prev_data_section0) {
ResultCode CROHeader::RelocateSegmentsTable(u32 base, u32 size, u32 data_section0, u32 data_section1, u32& prev_data_section0) {
u32 cro_end = base + size;
prev_data_section0 = 0;
for (int i = 0; i < segment_table_num; ++i) {
SegmentTableEntry* entry = GetSegmentTableEntry(i);
@ -194,44 +195,54 @@ void CROHeader::RelocateSegmentsTable(u32 base, u32 data_section0, u32 data_sect
entry->segment_offset = data_section1;
} else if (entry->segment_offset) {
entry->segment_offset += base;
if (entry->segment_offset > cro_end)
return ResultCode(0xD9012C19);
}
}
return RESULT_SUCCESS;
}
ExportTreeEntry* CROHeader::GetExportTreeEntry(u32 index) {
return reinterpret_cast<ExportTreeEntry*>(reinterpret_cast<u8*>(this) + export_tree_offset + sizeof(ExportTreeEntry) * index);
return reinterpret_cast<ExportTreeEntry*>(Memory::GetPointer(export_tree_offset + sizeof(ExportTreeEntry) * index));
}
u32 CROHeader::GetUnk1TableEntry(u32 index) {
return *reinterpret_cast<u32*>(reinterpret_cast<u8*>(this) + unk1_offset + sizeof(u32) * index);
return *reinterpret_cast<u32*>(Memory::GetPointer(unk1_offset + sizeof(u32) * index));
}
ExportTableEntry* CROHeader::GetExportTableEntry(u32 index) {
return reinterpret_cast<ExportTableEntry*>(reinterpret_cast<u8*>(this) + export_table_offset + sizeof(ExportTableEntry) * index);
return reinterpret_cast<ExportTableEntry*>(Memory::GetPointer(export_table_offset + sizeof(ExportTableEntry) * index));
}
void CROHeader::RelocateExportsTable(u32 base) {
ResultCode CROHeader::RelocateExportsTable(u32 base) {
for (int i = 0; i < export_table_num; ++i) {
ExportTableEntry* entry = GetExportTableEntry(i);
if (entry->name_offset)
entry->name_offset += base;
if (entry->name_offset < export_strings_offset ||
entry->name_offset > export_strings_offset + export_strings_num)
return ResultCode(0xD9012C11);
}
return RESULT_SUCCESS;
}
Patch* CROHeader::GetImportPatch(u32 index) {
return reinterpret_cast<Patch*>(reinterpret_cast<u8*>(this) + import_patches_offset + sizeof(Patch) * index);
return reinterpret_cast<Patch*>(Memory::GetPointer(import_patches_offset + sizeof(Patch) * index));
}
ImportTableEntry* CROHeader::GetImportTable1Entry(u32 index) {
return reinterpret_cast<ImportTableEntry*>(reinterpret_cast<u8*>(this) + import_table1_offset + sizeof(ImportTableEntry) * index);
return reinterpret_cast<ImportTableEntry*>(Memory::GetPointer(import_table1_offset + sizeof(ImportTableEntry) * index));
}
ImportTableEntry* CROHeader::GetImportTable2Entry(u32 index) {
return reinterpret_cast<ImportTableEntry*>(reinterpret_cast<u8*>(this) + import_table2_offset + sizeof(ImportTableEntry) * index);
return reinterpret_cast<ImportTableEntry*>(Memory::GetPointer(import_table2_offset + sizeof(ImportTableEntry) * index));
}
ImportTableEntry* CROHeader::GetImportTable3Entry(u32 index) {
return reinterpret_cast<ImportTableEntry*>(reinterpret_cast<u8*>(this) + import_table3_offset + sizeof(ImportTableEntry) * index);
return reinterpret_cast<ImportTableEntry*>(Memory::GetPointer(import_table3_offset + sizeof(ImportTableEntry) * index));
}
void CROHeader::RelocateImportTable1(u32 base) {
@ -273,25 +284,219 @@ void CROHeader::RelocateUnk2Patches(u32 base) {
}
Unk3Patch* CROHeader::GetUnk3PatchEntry(u32 index) {
return reinterpret_cast<Unk3Patch*>(reinterpret_cast<u8*>(this) + unk3_offset * sizeof(Unk3Patch) * index);
return reinterpret_cast<Unk3Patch*>(Memory::GetPointer(unk3_offset * sizeof(Unk3Patch) * index));
}
Patch* CROHeader::GetRelocationPatchEntry(u32 index) {
return reinterpret_cast<Patch*>(reinterpret_cast<u8*>(this) + relocation_patches_offset + sizeof(Patch) * index);
return reinterpret_cast<Patch*>(Memory::GetPointer(relocation_patches_offset + sizeof(Patch) * index));
}
Unk2Patch* CROHeader::GetUnk2PatchEntry(u32 index) {
return reinterpret_cast<Unk2Patch*>(reinterpret_cast<u8*>(this) + unk2_offset + sizeof(Unk2Patch) * index);
return reinterpret_cast<Unk2Patch*>(Memory::GetPointer(unk2_offset + sizeof(Unk2Patch) * index));
}
void CROHeader::RelocateOffsets(u32 base) {
memcpy(magic, "FIXD", 4);
segment_table_offset += base;
export_strings_offset += base;
export_table_offset += base;
export_tree_offset += base;
name_offset += base;
unk_offset += base;
bool CROHeader::VerifyAndRelocateOffsets(u32 base, u32 size) {
u32 end = base + size;
// Error if the magic is invalid
if (strcmp(magic, "CRO0") != 0)
return false;
// If these values are set the game might be trying to load the same CRO multiple times
if (next_cro || previous_cro)
return false;
// This seems to be a hard limit set by the RO module
if (file_size >= 0x10000000 || unk_size1 >= 0x10000000)
return false;
if (always_zero != 0)
return false;
// Hard limit set by the RO module, it is unknown what the value means
if (code_offset < 0x138)
return false;
if (module_name_offset < code_offset)
return false;
if (module_name_offset > segment_table_offset)
return false;
if (export_table_offset < segment_table_offset)
return false;
if (export_table_offset > export_tree_offset)
return false;
if (unk1_offset < export_tree_offset)
return false;
if (unk1_offset > export_strings_offset)
return false;
if (unk2_offset < export_strings_offset)
return false;
if (unk2_offset > import_patches_offset)
return false;
if (import_table1_offset < import_patches_offset)
return false;
if (import_table1_offset > import_table2_offset)
return false;
if (import_table3_offset < import_table2_offset)
return false;
if (import_table3_offset > import_strings_offset)
return false;
if (unk3_offset < import_strings_offset)
return false;
if (unk3_offset > relocation_patches_offset)
return false;
if (unk4_offset < relocation_patches_offset)
return false;
if (unk4_offset > unk_offset)
return false;
if (unk_offset > file_size)
return false;
if (name_offset) {
name_offset += base;
if (name_offset >= end)
return false;
}
if (code_offset) {
code_offset += base;
if (code_offset >= end)
return false;
}
if (unk_offset) {
unk_offset += base;
if (unk_offset >= end)
return false;
}
if (module_name_offset) {
module_name_offset += base;
if (module_name_offset >= end)
return false;
}
if (segment_table_offset) {
segment_table_offset += base;
if (segment_table_offset >= end)
return false;
}
if (export_table_offset) {
export_table_offset += base;
if (export_table_offset >= end)
return false;
}
if (unk1_offset) {
unk1_offset += base;
if (unk1_offset >= end)
return false;
}
if (export_strings_offset) {
export_strings_offset += base;
if (export_strings_offset >= end)
return false;
}
if (export_tree_offset) {
export_tree_offset += base;
if (export_tree_offset >= end)
return false;
}
if (unk2_offset) {
unk2_offset += base;
if (unk2_offset >= end)
return false;
}
if (import_patches_offset) {
import_patches_offset += base;
if (import_patches_offset >= end)
return false;
}
if (import_table1_offset) {
import_table1_offset += base;
if (import_table1_offset >= end)
return false;
}
if (import_table2_offset) {
import_table2_offset += base;
if (import_table2_offset >= end)
return false;
}
if (import_table3_offset) {
import_table3_offset += base;
if (import_table3_offset >= end)
return false;
}
if (import_strings_offset) {
import_strings_offset += base;
if (import_strings_offset >= end)
return false;
}
if (unk3_offset) {
unk3_offset += base;
if (unk3_offset >= end)
return false;
}
if (relocation_patches_offset) {
relocation_patches_offset += base;
if (relocation_patches_offset >= end)
return false;
}
if (unk4_offset) {
unk4_offset += base;
if (unk4_offset >= end)
return false;
}
if (code_offset + code_size > end ||
unk_offset + unk_size > end ||
module_name_offset + module_name_size > end ||
segment_table_offset + sizeof(SegmentTableEntry) * segment_table_num > end ||
export_table_offset + sizeof(ExportTableEntry) * export_table_num > end ||
unk1_offset + sizeof(u32) * unk1_size > end ||
export_strings_offset + export_strings_num > end ||
export_tree_offset + sizeof(ExportTreeEntry) * export_tree_num > end ||
unk2_offset + sizeof(Unk2Patch) * unk2_num > end ||
import_patches_offset + sizeof(Patch) * import_patches_num ||
import_table1_offset + sizeof(ImportTableEntry) * import_table1_num ||
import_table2_offset + sizeof(ImportTableEntry) * import_table2_num ||
import_table3_offset + sizeof(ImportTableEntry) * import_table3_num ||
import_strings_offset + import_strings_num > end ||
unk3_offset + sizeof(Unk3Patch) * unk3_num > end ||
relocation_patches_offset + sizeof(Patch) * relocation_patches_num > end ||
unk4_offset + 12 * unk4_num > end) {
return false;
}
return true;
}
static void ApplyPatch(Patch* patch, u32 patch_base, u32 patch_address, u32* patch_address1 = nullptr) {
@ -329,11 +534,11 @@ static void ApplyImportPatches(CROHeader* header, u32 base) {
}
}
static void ApplyListPatches(CROHeader* header, Patch* first_patch, u32 patch_base, bool relocated = false) {
static void ApplyListPatches(CROHeader* header, Patch* first_patch, u32 patch_base) {
Patch* current_patch = first_patch;
while (1) {
SegmentTableEntry* target_segment = header->GetSegmentTableEntry(current_patch->GetTargetSegment(), relocated);
SegmentTableEntry* target_segment = header->GetSegmentTableEntry(current_patch->GetTargetSegment());
ApplyPatch(current_patch, patch_base, target_segment->segment_offset + current_patch->GetSegmentOffset());
if (current_patch->unk)
@ -396,7 +601,7 @@ static void ApplyExitPatches(CROHeader* header, u32 base) {
LOG_ERROR(Service_LDR, "Could not find __aeabi_atexit in the CRO imports!");
}
static void ApplyImportTable1Patches(CROHeader* header, u32 base, bool relocated) {
static void ApplyImportTable1Patches(CROHeader* header, u32 base) {
for (int i = 0; i < header->import_table1_num; ++i) {
ImportTableEntry* entry = header->GetImportTable1Entry(i);
Patch* patch = reinterpret_cast<Patch*>(Memory::GetPointer(entry->symbol_offset));
@ -410,7 +615,7 @@ static void ApplyImportTable1Patches(CROHeader* header, u32 base, bool relocated
u32 patch_base = export_->second.cro_offset;
Patch* first_patch = reinterpret_cast<Patch*>(Memory::GetPointer(entry->symbol_offset));
ApplyListPatches(header, first_patch, patch_base, relocated);
ApplyListPatches(header, first_patch, patch_base);
}
}
}
@ -426,7 +631,7 @@ static u32 GetCROBaseByName(char* name) {
return 0;
}
static void ApplyUnk2Patches(CROHeader* header, u32 base, bool relocated) {
static void ApplyUnk2Patches(CROHeader* header, u32 base) {
for (int i = 0; i < header->unk2_num; ++i) {
Unk2Patch* entry = header->GetUnk2PatchEntry(i);
u32 cro_base = GetCROBaseByName(reinterpret_cast<char*>(Memory::GetPointer(entry->string_offset)));
@ -440,10 +645,10 @@ static void ApplyUnk2Patches(CROHeader* header, u32 base, bool relocated) {
u32 unk1_table_entry = patch_cro->GetUnk1TableEntry(table1_entry->offset_or_index);
u32 base_segment_id = unk1_table_entry & 0xF;
u32 base_segment_offset = unk1_table_entry >> 4;
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id, true);
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id);
Patch* first_patch = reinterpret_cast<Patch*>(Memory::GetPointer(base + table1_entry->patches_offset));
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset, relocated);
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset);
}
// Apply the patches from the second table
@ -451,10 +656,10 @@ static void ApplyUnk2Patches(CROHeader* header, u32 base, bool relocated) {
Unk2TableEntry* table2_entry = entry->GetTable2Entry(j);
u32 base_segment_id = table2_entry->offset_or_index & 0xF;
u32 base_segment_offset = table2_entry->offset_or_index >> 4;
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id, true);
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id);
Patch* first_patch = reinterpret_cast<Patch*>(Memory::GetPointer(table2_entry->patches_offset));
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset, relocated);
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset);
}
}
}
@ -475,10 +680,10 @@ static void BackApplyUnk2Patches(CROHeader* header, u32 base, CROHeader* new_hea
u32 unk1_table_entry = patch_cro->GetUnk1TableEntry(table1_entry->offset_or_index);
u32 base_segment_id = unk1_table_entry & 0xF;
u32 base_segment_offset = unk1_table_entry >> 4;
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id, false);
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id);
Patch* first_patch = reinterpret_cast<Patch*>(Memory::GetPointer(base + table1_entry->patches_offset));
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset, true);
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset);
}
// Apply the patches from the second table
@ -486,10 +691,10 @@ static void BackApplyUnk2Patches(CROHeader* header, u32 base, CROHeader* new_hea
Unk2TableEntry* table2_entry = entry->GetTable2Entry(j);
u32 base_segment_id = table2_entry->offset_or_index & 0xF;
u32 base_segment_offset = table2_entry->offset_or_index >> 4;
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id, false);
SegmentTableEntry* base_segment = patch_cro->GetSegmentTableEntry(base_segment_id);
Patch* first_patch = reinterpret_cast<Patch*>(Memory::GetPointer(table2_entry->patches_offset));
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset, true);
ApplyListPatches(header, first_patch, base_segment->segment_offset + base_segment_offset);
}
}
}
@ -546,17 +751,38 @@ static void LinkCROs(CROHeader* new_cro, u32 base) {
}
}
static void LoadCRO(u32 base, u8* cro, bool relocate_segments, u32 data_section0, u32 data_section1, bool crs) {
static ResultCode LoadCRO(u32 base, u32 size, u8* cro, u32 data_section0, u32 data_section1, bool crs) {
CROHeader* header = reinterpret_cast<CROHeader*>(cro);
// Relocate all offsets
if (!header->VerifyAndRelocateOffsets(base, size))
return ResultCode(0xD9012C11);
if (header->module_name_size &&
Memory::Read8(header->module_name_offset + header->module_name_size - 1) != 0) {
// The module name must end with '\0'
return ResultCode(0xD9012C0B);
}
u32 prev_section0 = 0;
if (relocate_segments) {
ResultCode result = RESULT_SUCCESS;
if (!crs) {
// Relocate segments
header->RelocateSegmentsTable(base, data_section0, data_section1, prev_section0);
result = header->RelocateSegmentsTable(base, size, data_section0, data_section1, prev_section0);
if (result.IsError())
return result;
}
// Rebase export table
header->RelocateExportsTable(base);
result = header->RelocateExportsTable(base);
if (result.IsError())
return result;
if (header->export_strings_num &&
Memory::Read8(header->export_strings_offset + header->export_strings_num - 1) != 0)
return ResultCode(0xD9012C0B);
// Rebase unk2
header->RelocateUnk2Patches(base);
@ -581,10 +807,10 @@ static void LoadCRO(u32 base, u8* cro, bool relocate_segments, u32 data_section0
ApplyExitPatches(header, base);
// Import Table 1
ApplyImportTable1Patches(header, base, false);
ApplyImportTable1Patches(header, base);
// Apply unk2 patches
ApplyUnk2Patches(header, base, false);
ApplyUnk2Patches(header, base);
// Load exports
LoadExportsTable(header, base);
@ -594,21 +820,24 @@ static void LoadCRO(u32 base, u8* cro, bool relocate_segments, u32 data_section0
for (auto itr = loaded_cros.rbegin(); itr != loaded_cros.rend(); ++itr) {
u32 cro_base = *itr;
CROHeader* cro_header = reinterpret_cast<CROHeader*>(Memory::GetPointer(cro_base));
ApplyImportTable1Patches(cro_header, cro_base, true);
ApplyImportTable1Patches(cro_header, cro_base);
BackApplyUnk2Patches(cro_header, cro_base, header, base);
}
// Relocate all offsets
header->RelocateOffsets(base);
// Link the CROs
LinkCROs(header, base);
loaded_cros.push_back(base);
memcpy(header->magic, "FIXD", 4);
LOG_WARNING(Service_LDR, "Loaded CRO name %s", reinterpret_cast<char*>(Memory::GetPointer(header->name_offset)));
FileUtil::IOFile file(std::to_string(base) + ".cro", "wb+");
file.WriteBytes(header, header->file_size);
file.Close();
return RESULT_SUCCESS;
}
/**
@ -644,10 +873,8 @@ static void Initialize(Service::Interface* self) {
// TODO(Subv): Check what the real hardware returns for MemoryState
Kernel::g_current_process->vm_manager.MapMemoryBlock(address, cro, 0, crs_size, Kernel::MemoryState::Code);
LoadCRO(address, Memory::GetPointer(address), false, 0, 0, true);
cmd_buff[0] = IPC::MakeHeader(1, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[1] = LoadCRO(address, crs_size, Memory::GetPointer(address), 0, 0, true).raw;
LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X",
crs_buffer_ptr, crs_size, address, value, process);
@ -694,11 +921,10 @@ static void LoadExeCRO(Service::Interface* self) {
// TODO(Subv): Check what the real hardware returns for MemoryState
Kernel::g_current_process->vm_manager.MapMemoryBlock(address, cro, 0, size, Kernel::MemoryState::Code);
LoadCRO(address, Memory::GetPointer(address), true, cmd_buff[4], cmd_buff[7], false);
cmd_buff[0] = IPC::MakeHeader(4, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[1] = LoadCRO(address, size, Memory::GetPointer(address), cmd_buff[4], cmd_buff[7], false).raw;
cmd_buff[2] = 0;
LOG_WARNING(Service_LDR, "Loading CRO address=%08X", address);
}