diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp index 26378d323..e20ea9fcf 100644 --- a/src/core/hle/service/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro.cpp @@ -537,7 +537,7 @@ static void ApplyImportPatches(CROHeader* header, u32 base) { static void ApplyListPatches(CROHeader* header, Patch* first_patch, u32 patch_base) { Patch* current_patch = first_patch; - while (1) { + while (current_patch) { SegmentTableEntry* target_segment = header->GetSegmentTableEntry(current_patch->GetTargetSegment()); ApplyPatch(current_patch, patch_base, target_segment->segment_offset + current_patch->GetSegmentOffset()); @@ -711,7 +711,7 @@ static void LoadExportsTable(CROHeader* header, u32 base) { } } -static u32 GetAddress(CROHeader* header, char* str) { +static u32 FindExportByName(CROHeader* header, char* str) { if (header->export_tree_num) { ExportTreeEntry* first_entry = header->GetExportTreeEntry(0); u32 len = strlen(str); @@ -1037,44 +1037,307 @@ static void LoadExeCRO(Service::Interface* self) { header->always_zero = size - v24; } + for (int i = 0; i < size; ++i) { + u32 mem = Memory::Read32(address + i); + if (mem == 0x400001d9) + __debugbreak(); + } LOG_WARNING(Service_LDR, "Loading CRO address=%08X", address); } -static void UnloadCRO(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 address = cmd_buff[1]; - CROHeader* unload = reinterpret_cast(Memory::GetPointer(address)); - u32 size = unload->file_size; +static void UnlinkCRO(CROHeader* cro, u32 address) { + // TODO(Subv): Verify if this is correct for (auto itr = loaded_cros.begin(); itr != loaded_cros.end(); ++itr) { if (*itr == address) continue; CROHeader* cro = reinterpret_cast(Memory::GetPointer(*itr)); if (cro->next_cro == address) { - cro->next_cro = unload->next_cro; - if (unload->next_cro != 0) { - CROHeader* next = reinterpret_cast(Memory::GetPointer(unload->next_cro)); + cro->next_cro = cro->next_cro; + if (cro->next_cro != 0) { + CROHeader* next = reinterpret_cast(Memory::GetPointer(cro->next_cro)); next->previous_cro = *itr; } } if (cro->previous_cro == address) { - cro->previous_cro = unload->previous_cro; - if (unload->previous_cro != 0) { - CROHeader* prev = reinterpret_cast(Memory::GetPointer(unload->previous_cro)); + cro->previous_cro = cro->previous_cro; + if (cro->previous_cro != 0) { + CROHeader* prev = reinterpret_cast(Memory::GetPointer(cro->previous_cro)); prev->next_cro = *itr; } } } + cro->previous_cro = 0; + cro->next_cro = 0; +} + +static void UnloadImportTablePatches(CROHeader* cro, Patch* first_patch, u32 base_offset) { + Patch* patch = first_patch; + while (patch) { + SegmentTableEntry* target_segment = cro->GetSegmentTableEntry(patch->GetTargetSegment()); + ApplyPatch(patch, base_offset, target_segment->segment_offset + patch->GetSegmentOffset()); + + if (patch->unk) + break; + + patch++; + } + + first_patch->unk2 = 0; +} + +static u32 CalculateBaseOffset(CROHeader* cro) { + u32 base_offset = 0; + + if (cro->GetImportPatchesTargetSegment() < cro->segment_table_num) { + SegmentTableEntry* base_segment = cro->GetSegmentTableEntry(cro->GetImportPatchesTargetSegment()); + if (cro->GetImportPatchesSegmentOffset() < base_segment->segment_size) + base_offset = base_segment->segment_size + cro->GetImportPatchesSegmentOffset(); + } + + return base_offset; +} + +static void UnloadImportTable1Patches(CROHeader* cro, u32 base_offset) { + for (int i = 0; i < cro->import_table1_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable1Entry(i); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(entry->symbol_offset)); + UnloadImportTablePatches(cro, first_patch, base_offset); + } +} + +static void UnloadImportTable2Patches(CROHeader* cro, u32 base_offset) { + for (int i = 0; i < cro->import_table2_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable2Entry(i); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(entry->symbol_offset)); + UnloadImportTablePatches(cro, first_patch, base_offset); + } +} + +static void UnloadImportTable3Patches(CROHeader* cro, u32 base_offset) { + for (int i = 0; i < cro->import_table3_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable3Entry(i); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(entry->symbol_offset)); + UnloadImportTablePatches(cro, first_patch, base_offset); + } +} + +static void ApplyCRSImportTable1UnloadPatches(CROHeader* crs, CROHeader* unload, u32 base_offset) { + for (int i = 0; i < crs->import_table1_num; ++i) { + ImportTableEntry* entry = crs->GetImportTable1Entry(i); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(entry->symbol_offset)); + if (first_patch->unk2) + if (FindExportByName(unload, reinterpret_cast(Memory::GetPointer(entry->name_offset)))) + UnloadImportTablePatches(crs, first_patch, base_offset); + } +} + +static void UnloadUnk2Patches(CROHeader* cro, CROHeader* unload, u32 base_offset) { + char* unload_name = reinterpret_cast(Memory::GetPointer(unload->name_offset)); + for (int i = 0; i < cro->unk2_num; ++i) { + Unk2Patch* entry = cro->GetUnk2PatchEntry(i); + // Find the patch that corresponds to the CRO that is being unloaded + if (strcmp(reinterpret_cast(Memory::GetPointer(entry->string_offset)), unload_name) == 0) { + + // Apply the table 1 patches + for (int j = 0; j < entry->table1_num; ++j) { + Unk2TableEntry* table1_entry = entry->GetTable1Entry(j); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(table1_entry->patches_offset)); + UnloadImportTablePatches(cro, first_patch, base_offset); + } + + // Apply the table 2 patches + for (int j = 0; j < entry->table1_num; ++j) { + Unk2TableEntry* table2_entry = entry->GetTable2Entry(j); + Patch* first_patch = reinterpret_cast(Memory::GetPointer(table2_entry->patches_offset)); + UnloadImportTablePatches(cro, first_patch, base_offset); + } + break; + } + } +} + +static void ApplyCRSUnloadPatches(CROHeader* crs, CROHeader* unload) { + u32 base_offset = CalculateBaseOffset(crs); + + ApplyCRSImportTable1UnloadPatches(crs, unload, base_offset); +} + +static void UnrebaseImportTable3(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->import_table3_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable3Entry(i); + if (entry->symbol_offset) + entry->symbol_offset -= address; + } +} + +static void UnrebaseImportTable2(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->import_table2_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable2Entry(i); + if (entry->symbol_offset) + entry->symbol_offset -= address; + } +} + +static void UnrebaseImportTable1(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->import_table1_num; ++i) { + ImportTableEntry* entry = cro->GetImportTable1Entry(i); + if (entry->name_offset) + entry->name_offset -= address; + if (entry->symbol_offset) + entry->symbol_offset -= address; + } +} + +static void UnrebaseUnk2Patches(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->unk2_num; ++i) { + Unk2Patch* entry = cro->GetUnk2PatchEntry(i); + if (entry->string_offset) + entry->string_offset -= address; + if (entry->table1_offset) + entry->table1_offset -= address; + if (entry->table2_offset) + entry->table2_offset -= address; + } +} + +static void UnrebaseExportsTable(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->export_table_num; ++i) { + ExportTableEntry* entry = cro->GetExportTableEntry(i); + if (entry->name_offset) + entry->name_offset -= address; + } +} + +static void UnrebaseSegments(CROHeader* cro, u32 address) { + for (int i = 0; i < cro->segment_table_num; ++i) { + SegmentTableEntry* entry = cro->GetSegmentTableEntry(i); + if (entry->segment_id == 3) + entry->segment_offset = 0; + else if (entry->segment_id) + entry->segment_offset -= address; + } +} + +static void UnrebaseCRO(CROHeader* cro, u32 address) { + UnrebaseImportTable3(cro, address); + UnrebaseImportTable2(cro, address); + UnrebaseImportTable1(cro, address); + UnrebaseUnk2Patches(cro, address); + UnrebaseExportsTable(cro, address); + UnrebaseSegments(cro, address); + + if (cro->name_offset) + cro->name_offset -= address; + + if (cro->code_offset) + cro->code_offset -= address; + + if (cro->unk_offset) + cro->unk_offset -= address; + + if (cro->module_name_offset) + cro->module_name_offset -= address; + + if (cro->segment_table_offset) + cro->segment_table_offset -= address; + + if (cro->export_table_offset) + cro->export_table_offset -= address; + + if (cro->unk1_offset) + cro->unk1_offset -= address; + + if (cro->export_strings_offset) + cro->export_strings_offset -= address; + + if (cro->export_tree_offset) + cro->export_tree_offset -= address; + + if (cro->unk2_offset) + cro->unk2_offset -= address; + + if (cro->import_patches_offset) + cro->import_patches_offset -= address; + + if (cro->import_table1_offset) + cro->import_table1_offset -= address; + + if (cro->import_table2_offset) + cro->import_table2_offset -= address; + + if (cro->import_table3_offset) + cro->import_table3_offset -= address; + + if (cro->import_strings_offset) + cro->import_strings_offset -= address; + + if (cro->unk3_offset) + cro->unk3_offset -= address; + + if (cro->relocation_patches_offset) + cro->relocation_patches_offset -= address; + + if (cro->unk4_offset) + cro->unk4_offset -= address; +} + +static void UnloadExports(u32 address) { + for (auto itr = loaded_exports.begin(); itr != loaded_exports.end();) { + if (itr->second.cro_base == address) + itr = loaded_exports.erase(itr); + else + ++itr; + } +} + +static ResultCode UnloadCRO(u32 address) { + // If there's only one loaded CRO, it must be the CRS, which can not be unloaded like this + if (loaded_cros.size() == 1) { + return ResultCode(0xD9012C1E); + } + + CROHeader* unload = reinterpret_cast(Memory::GetPointer(address)); + u32 size = unload->file_size; + + UnlinkCRO(unload, address); + + u32 base_offset = CalculateBaseOffset(unload); + + UnloadImportTable1Patches(unload, base_offset); + UnloadImportTable2Patches(unload, base_offset); + UnloadImportTable3Patches(unload, base_offset); + + CROHeader* crs = reinterpret_cast(Memory::GetPointer(loaded_cros.front())); + ApplyCRSUnloadPatches(crs, unload); + + for (u32 base : loaded_cros) { + if (base == address) + continue; + CROHeader* cro = reinterpret_cast(Memory::GetPointer(base)); + base_offset = CalculateBaseOffset(cro); + UnloadUnk2Patches(cro, unload, base_offset); + } + + UnrebaseCRO(unload, address); + unload->always_zero = 0; + loaded_cros.erase(std::remove(loaded_cros.begin(), loaded_cros.end(), address), loaded_cros.end()); - std::memset(Memory::GetPointer(address), 0, size); + UnloadExports(address); + Kernel::g_current_process->vm_manager.UnmapRange(address, size); // TODO(Subv): Unload symbols and unmap memory + return RESULT_SUCCESS; +} - cmd_buff[1] = RESULT_SUCCESS.raw; +static void UnloadCRO(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 address = cmd_buff[1]; + cmd_buff[1] = UnloadCRO(address).raw; LOG_WARNING(Service_LDR, "Unloading CRO address=%08X", address); }