diff options
| author | 2016-08-28 22:21:24 -0700 | |
|---|---|---|
| committer | 2016-08-28 22:21:24 -0700 | |
| commit | 474586bc53eb6fda40fb0db23ea1d50d32af28b6 (patch) | |
| tree | 49df9979f3351f3eac26d8a74e25c5181ba35e6c /src | |
| parent | Merge pull request #1987 from Lectem/ipcdescriptors (diff) | |
| parent | LDR: Implement CRO (diff) | |
| download | yuzu-474586bc53eb6fda40fb0db23ea1d50d32af28b6.tar.gz yuzu-474586bc53eb6fda40fb0db23ea1d50d32af28b6.tar.xz yuzu-474586bc53eb6fda40fb0db23ea1d50d32af28b6.zip | |
Merge pull request #1948 from wwylele/cro++
Implemented CRO
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/core/arm/arm_interface.h | 3 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.cpp | 6 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro.cpp | 96 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/cro_helper.cpp | 1477 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/cro_helper.h | 691 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/ldr_ro.cpp | 748 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/ldr_ro.h (renamed from src/core/hle/service/ldr_ro.h) | 0 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/memory_synchronizer.cpp | 46 | ||||
| -rw-r--r-- | src/core/hle/service/ldr_ro/memory_synchronizer.h | 44 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 2 | ||||
| -rw-r--r-- | src/core/memory.cpp | 14 | ||||
| -rw-r--r-- | src/core/memory.h | 3 |
14 files changed, 3041 insertions, 99 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0773339a9..174e9dc79 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -95,7 +95,9 @@ set(SRCS | |||
| 95 | hle/service/ir/ir_rst.cpp | 95 | hle/service/ir/ir_rst.cpp |
| 96 | hle/service/ir/ir_u.cpp | 96 | hle/service/ir/ir_u.cpp |
| 97 | hle/service/ir/ir_user.cpp | 97 | hle/service/ir/ir_user.cpp |
| 98 | hle/service/ldr_ro.cpp | 98 | hle/service/ldr_ro/cro_helper.cpp |
| 99 | hle/service/ldr_ro/ldr_ro.cpp | ||
| 100 | hle/service/ldr_ro/memory_synchronizer.cpp | ||
| 99 | hle/service/mic_u.cpp | 101 | hle/service/mic_u.cpp |
| 100 | hle/service/ndm/ndm.cpp | 102 | hle/service/ndm/ndm.cpp |
| 101 | hle/service/ndm/ndm_u.cpp | 103 | hle/service/ndm/ndm_u.cpp |
| @@ -238,7 +240,9 @@ set(HEADERS | |||
| 238 | hle/service/ir/ir_rst.h | 240 | hle/service/ir/ir_rst.h |
| 239 | hle/service/ir/ir_u.h | 241 | hle/service/ir/ir_u.h |
| 240 | hle/service/ir/ir_user.h | 242 | hle/service/ir/ir_user.h |
| 241 | hle/service/ldr_ro.h | 243 | hle/service/ldr_ro/cro_helper.h |
| 244 | hle/service/ldr_ro/ldr_ro.h | ||
| 245 | hle/service/ldr_ro/memory_synchronizer.h | ||
| 242 | hle/service/mic_u.h | 246 | hle/service/mic_u.h |
| 243 | hle/service/ndm/ndm.h | 247 | hle/service/ndm/ndm.h |
| 244 | hle/service/ndm/ndm_u.h | 248 | hle/service/ndm/ndm_u.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index d8abe5aeb..de5e9c8fa 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -32,6 +32,9 @@ public: | |||
| 32 | Run(1); | 32 | Run(1); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | /// Clear all instruction cache | ||
| 36 | virtual void ClearInstructionCache() = 0; | ||
| 37 | |||
| 35 | /** | 38 | /** |
| 36 | * Set the Program Counter to an address | 39 | * Set the Program Counter to an address |
| 37 | * @param addr Address to set PC to | 40 | * @param addr Address to set PC to |
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 13492a08b..ab77da965 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/arm/dyncom/arm_dyncom.h" | 12 | #include "core/arm/dyncom/arm_dyncom.h" |
| 13 | #include "core/arm/dyncom/arm_dyncom_interpreter.h" | 13 | #include "core/arm/dyncom/arm_dyncom_interpreter.h" |
| 14 | #include "core/arm/dyncom/arm_dyncom_run.h" | 14 | #include "core/arm/dyncom/arm_dyncom_run.h" |
| 15 | #include "core/arm/dyncom/arm_dyncom_trans.h" | ||
| 15 | 16 | ||
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| @@ -23,6 +24,11 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { | |||
| 23 | ARM_DynCom::~ARM_DynCom() { | 24 | ARM_DynCom::~ARM_DynCom() { |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 27 | void ARM_DynCom::ClearInstructionCache() { | ||
| 28 | state->instruction_cache.clear(); | ||
| 29 | trans_cache_buf_top = 0; | ||
| 30 | } | ||
| 31 | |||
| 26 | void ARM_DynCom::SetPC(u32 pc) { | 32 | void ARM_DynCom::SetPC(u32 pc) { |
| 27 | state->Reg[15] = pc; | 33 | state->Reg[15] = pc; |
| 28 | } | 34 | } |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 3664fd728..e763abc24 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -21,6 +21,8 @@ public: | |||
| 21 | ARM_DynCom(PrivilegeMode initial_mode); | 21 | ARM_DynCom(PrivilegeMode initial_mode); |
| 22 | ~ARM_DynCom(); | 22 | ~ARM_DynCom(); |
| 23 | 23 | ||
| 24 | void ClearInstructionCache() override; | ||
| 25 | |||
| 24 | void SetPC(u32 pc) override; | 26 | void SetPC(u32 pc) override; |
| 25 | u32 GetPC() const override; | 27 | u32 GetPC() const override; |
| 26 | u32 GetReg(int index) const override; | 28 | u32 GetReg(int index) const override; |
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp deleted file mode 100644 index ecec2ce32..000000000 --- a/src/core/hle/service/ldr_ro.cpp +++ /dev/null | |||
| @@ -1,96 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | |||
| 8 | #include "core/hle/service/ldr_ro.h" | ||
| 9 | |||
| 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 11 | // Namespace LDR_RO | ||
| 12 | |||
| 13 | namespace LDR_RO { | ||
| 14 | |||
| 15 | /** | ||
| 16 | * LDR_RO::Initialize service function | ||
| 17 | * Inputs: | ||
| 18 | * 1 : CRS buffer pointer | ||
| 19 | * 2 : CRS Size | ||
| 20 | * 3 : Process memory address where the CRS will be mapped | ||
| 21 | * 4 : Value, must be zero | ||
| 22 | * 5 : KProcess handle | ||
| 23 | * Outputs: | ||
| 24 | * 0 : Return header | ||
| 25 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 26 | */ | ||
| 27 | static void Initialize(Service::Interface* self) { | ||
| 28 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 29 | u32 crs_buffer_ptr = cmd_buff[1]; | ||
| 30 | u32 crs_size = cmd_buff[2]; | ||
| 31 | u32 address = cmd_buff[3]; | ||
| 32 | u32 value = cmd_buff[4]; | ||
| 33 | u32 process = cmd_buff[5]; | ||
| 34 | |||
| 35 | if (value != 0) { | ||
| 36 | LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); | ||
| 37 | } | ||
| 38 | |||
| 39 | // TODO(purpasmart96): Verify return header on HW | ||
| 40 | |||
| 41 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 42 | |||
| 43 | LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X", | ||
| 44 | crs_buffer_ptr, crs_size, address, value, process); | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * LDR_RO::LoadCRR service function | ||
| 49 | * Inputs: | ||
| 50 | * 1 : CRS buffer pointer | ||
| 51 | * 2 : CRS Size | ||
| 52 | * 3 : Value, must be zero | ||
| 53 | * 4 : KProcess handle | ||
| 54 | * Outputs: | ||
| 55 | * 0 : Return header | ||
| 56 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 57 | */ | ||
| 58 | static void LoadCRR(Service::Interface* self) { | ||
| 59 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 60 | u32 crs_buffer_ptr = cmd_buff[1]; | ||
| 61 | u32 crs_size = cmd_buff[2]; | ||
| 62 | u32 value = cmd_buff[3]; | ||
| 63 | u32 process = cmd_buff[4]; | ||
| 64 | |||
| 65 | if (value != 0) { | ||
| 66 | LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); | ||
| 67 | } | ||
| 68 | |||
| 69 | // TODO(purpasmart96): Verify return header on HW | ||
| 70 | |||
| 71 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 72 | |||
| 73 | LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, value=0x%08X, process=0x%08X", | ||
| 74 | crs_buffer_ptr, crs_size, value, process); | ||
| 75 | } | ||
| 76 | |||
| 77 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 78 | {0x000100C2, Initialize, "Initialize"}, | ||
| 79 | {0x00020082, LoadCRR, "LoadCRR"}, | ||
| 80 | {0x00030042, nullptr, "UnloadCCR"}, | ||
| 81 | {0x000402C2, nullptr, "LoadExeCRO"}, | ||
| 82 | {0x000500C2, nullptr, "LoadCROSymbols"}, | ||
| 83 | {0x00060042, nullptr, "CRO_Load?"}, | ||
| 84 | {0x00070042, nullptr, "LoadCROSymbols"}, | ||
| 85 | {0x00080042, nullptr, "Shutdown"}, | ||
| 86 | {0x000902C2, nullptr, "LoadExeCRO_New?"}, | ||
| 87 | }; | ||
| 88 | |||
| 89 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 90 | // Interface class | ||
| 91 | |||
| 92 | Interface::Interface() { | ||
| 93 | Register(FunctionTable); | ||
| 94 | } | ||
| 95 | |||
| 96 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp new file mode 100644 index 000000000..3d2a613ee --- /dev/null +++ b/src/core/hle/service/ldr_ro/cro_helper.cpp | |||
| @@ -0,0 +1,1477 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/scope_exit.h" | ||
| 8 | |||
| 9 | #include "core/hle/service/ldr_ro/cro_helper.h" | ||
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // Namespace LDR_RO | ||
| 13 | |||
| 14 | namespace LDR_RO { | ||
| 15 | |||
| 16 | static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F | ||
| 17 | ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 18 | |||
| 19 | static ResultCode CROFormatError(u32 description) { | ||
| 20 | return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 21 | } | ||
| 22 | |||
| 23 | const std::array<int, 17> CROHelper::ENTRY_SIZE {{ | ||
| 24 | 1, // code | ||
| 25 | 1, // data | ||
| 26 | 1, // module name | ||
| 27 | sizeof(SegmentEntry), | ||
| 28 | sizeof(ExportNamedSymbolEntry), | ||
| 29 | sizeof(ExportIndexedSymbolEntry), | ||
| 30 | 1, // export strings | ||
| 31 | sizeof(ExportTreeEntry), | ||
| 32 | sizeof(ImportModuleEntry), | ||
| 33 | sizeof(ExternalRelocationEntry), | ||
| 34 | sizeof(ImportNamedSymbolEntry), | ||
| 35 | sizeof(ImportIndexedSymbolEntry), | ||
| 36 | sizeof(ImportAnonymousSymbolEntry), | ||
| 37 | 1, // import strings | ||
| 38 | sizeof(StaticAnonymousSymbolEntry), | ||
| 39 | sizeof(InternalRelocationEntry), | ||
| 40 | sizeof(StaticRelocationEntry) | ||
| 41 | }}; | ||
| 42 | |||
| 43 | const std::array<CROHelper::HeaderField, 4> CROHelper::FIX_BARRIERS {{ | ||
| 44 | Fix0Barrier, | ||
| 45 | Fix1Barrier, | ||
| 46 | Fix2Barrier, | ||
| 47 | Fix3Barrier | ||
| 48 | }}; | ||
| 49 | |||
| 50 | VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const { | ||
| 51 | u32 segment_num = GetField(SegmentNum); | ||
| 52 | |||
| 53 | if (segment_tag.segment_index >= segment_num) | ||
| 54 | return 0; | ||
| 55 | |||
| 56 | SegmentEntry entry; | ||
| 57 | GetEntry(segment_tag.segment_index, entry); | ||
| 58 | |||
| 59 | if (segment_tag.offset_into_segment >= entry.size) | ||
| 60 | return 0; | ||
| 61 | |||
| 62 | return entry.offset + segment_tag.offset_into_segment; | ||
| 63 | } | ||
| 64 | |||
| 65 | ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType relocation_type, | ||
| 66 | u32 addend, u32 symbol_address, u32 target_future_address) { | ||
| 67 | |||
| 68 | switch (relocation_type) { | ||
| 69 | case RelocationType::Nothing: | ||
| 70 | break; | ||
| 71 | case RelocationType::AbsoluteAddress: | ||
| 72 | case RelocationType::AbsoluteAddress2: | ||
| 73 | Memory::Write32(target_address, symbol_address + addend); | ||
| 74 | break; | ||
| 75 | case RelocationType::RelativeAddress: | ||
| 76 | Memory::Write32(target_address, symbol_address + addend - target_future_address); | ||
| 77 | break; | ||
| 78 | case RelocationType::ThumbBranch: | ||
| 79 | case RelocationType::ArmBranch: | ||
| 80 | case RelocationType::ModifyArmBranch: | ||
| 81 | case RelocationType::AlignedRelativeAddress: | ||
| 82 | // TODO(wwylele): implement other types | ||
| 83 | UNIMPLEMENTED(); | ||
| 84 | break; | ||
| 85 | default: | ||
| 86 | return CROFormatError(0x22); | ||
| 87 | } | ||
| 88 | return RESULT_SUCCESS; | ||
| 89 | } | ||
| 90 | |||
| 91 | ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType relocation_type) { | ||
| 92 | switch (relocation_type) { | ||
| 93 | case RelocationType::Nothing: | ||
| 94 | break; | ||
| 95 | case RelocationType::AbsoluteAddress: | ||
| 96 | case RelocationType::AbsoluteAddress2: | ||
| 97 | case RelocationType::RelativeAddress: | ||
| 98 | Memory::Write32(target_address, 0); | ||
| 99 | break; | ||
| 100 | case RelocationType::ThumbBranch: | ||
| 101 | case RelocationType::ArmBranch: | ||
| 102 | case RelocationType::ModifyArmBranch: | ||
| 103 | case RelocationType::AlignedRelativeAddress: | ||
| 104 | // TODO(wwylele): implement other types | ||
| 105 | UNIMPLEMENTED(); | ||
| 106 | break; | ||
| 107 | default: | ||
| 108 | return CROFormatError(0x22); | ||
| 109 | } | ||
| 110 | return RESULT_SUCCESS; | ||
| 111 | } | ||
| 112 | |||
| 113 | ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset) { | ||
| 114 | if (symbol_address == 0 && !reset) | ||
| 115 | return CROFormatError(0x10); | ||
| 116 | |||
| 117 | VAddr relocation_address = batch; | ||
| 118 | while (true) { | ||
| 119 | RelocationEntry relocation; | ||
| 120 | Memory::ReadBlock(relocation_address, &relocation, sizeof(RelocationEntry)); | ||
| 121 | |||
| 122 | VAddr relocation_target = SegmentTagToAddress(relocation.target_position); | ||
| 123 | if (relocation_target == 0) { | ||
| 124 | return CROFormatError(0x12); | ||
| 125 | } | ||
| 126 | |||
| 127 | ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, symbol_address, relocation_target); | ||
| 128 | if (result.IsError()) { | ||
| 129 | LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); | ||
| 130 | return result; | ||
| 131 | } | ||
| 132 | |||
| 133 | if (relocation.is_batch_end) | ||
| 134 | break; | ||
| 135 | |||
| 136 | relocation_address += sizeof(RelocationEntry); | ||
| 137 | } | ||
| 138 | |||
| 139 | RelocationEntry relocation; | ||
| 140 | Memory::ReadBlock(batch, &relocation, sizeof(RelocationEntry)); | ||
| 141 | relocation.is_batch_resolved = reset ? 0 : 1; | ||
| 142 | Memory::WriteBlock(batch, &relocation, sizeof(RelocationEntry)); | ||
| 143 | return RESULT_SUCCESS; | ||
| 144 | } | ||
| 145 | |||
| 146 | VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const { | ||
| 147 | if (!GetField(ExportTreeNum)) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | std::size_t len = name.size(); | ||
| 151 | ExportTreeEntry entry; | ||
| 152 | GetEntry(0, entry); | ||
| 153 | ExportTreeEntry::Child next; | ||
| 154 | next.raw = entry.left.raw; | ||
| 155 | u32 found_id; | ||
| 156 | |||
| 157 | while (true) { | ||
| 158 | GetEntry(next.next_index, entry); | ||
| 159 | |||
| 160 | if (next.is_end) { | ||
| 161 | found_id = entry.export_table_index; | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | |||
| 165 | u16 test_byte = entry.test_bit >> 3; | ||
| 166 | u16 test_bit_in_byte = entry.test_bit & 7; | ||
| 167 | |||
| 168 | if (test_byte >= len) { | ||
| 169 | next.raw = entry.left.raw; | ||
| 170 | } else if((name[test_byte] >> test_bit_in_byte) & 1) { | ||
| 171 | next.raw = entry.right.raw; | ||
| 172 | } else { | ||
| 173 | next.raw = entry.left.raw; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); | ||
| 178 | |||
| 179 | if (found_id >= export_named_symbol_num) | ||
| 180 | return 0; | ||
| 181 | |||
| 182 | u32 export_strings_size = GetField(ExportStringsSize); | ||
| 183 | ExportNamedSymbolEntry symbol_entry; | ||
| 184 | GetEntry(found_id, symbol_entry); | ||
| 185 | |||
| 186 | if (Memory::ReadCString(symbol_entry.name_offset, export_strings_size) != name) | ||
| 187 | return 0; | ||
| 188 | |||
| 189 | return SegmentTagToAddress(symbol_entry.symbol_position); | ||
| 190 | } | ||
| 191 | |||
| 192 | ResultCode CROHelper::RebaseHeader(u32 cro_size) { | ||
| 193 | ResultCode error = CROFormatError(0x11); | ||
| 194 | |||
| 195 | // verifies magic | ||
| 196 | if (GetField(Magic) != MAGIC_CRO0) | ||
| 197 | return error; | ||
| 198 | |||
| 199 | // verifies not registered | ||
| 200 | if (GetField(NextCRO) != 0 || GetField(PreviousCRO) != 0) | ||
| 201 | return error; | ||
| 202 | |||
| 203 | // This seems to be a hard limit set by the RO module | ||
| 204 | if (GetField(FileSize) > 0x10000000 || GetField(BssSize) > 0x10000000) | ||
| 205 | return error; | ||
| 206 | |||
| 207 | // verifies not fixed | ||
| 208 | if (GetField(FixedSize) != 0) | ||
| 209 | return error; | ||
| 210 | |||
| 211 | if (GetField(CodeOffset) < CRO_HEADER_SIZE) | ||
| 212 | return error; | ||
| 213 | |||
| 214 | // verifies that all offsets are in the correct order | ||
| 215 | constexpr std::array<HeaderField, 18> OFFSET_ORDER = {{ | ||
| 216 | CodeOffset, | ||
| 217 | ModuleNameOffset, | ||
| 218 | SegmentTableOffset, | ||
| 219 | ExportNamedSymbolTableOffset, | ||
| 220 | ExportTreeTableOffset, | ||
| 221 | ExportIndexedSymbolTableOffset, | ||
| 222 | ExportStringsOffset, | ||
| 223 | ImportModuleTableOffset, | ||
| 224 | ExternalRelocationTableOffset, | ||
| 225 | ImportNamedSymbolTableOffset, | ||
| 226 | ImportIndexedSymbolTableOffset, | ||
| 227 | ImportAnonymousSymbolTableOffset, | ||
| 228 | ImportStringsOffset, | ||
| 229 | StaticAnonymousSymbolTableOffset, | ||
| 230 | InternalRelocationTableOffset, | ||
| 231 | StaticRelocationTableOffset, | ||
| 232 | DataOffset, | ||
| 233 | FileSize | ||
| 234 | }}; | ||
| 235 | |||
| 236 | u32 prev_offset = GetField(OFFSET_ORDER[0]); | ||
| 237 | u32 cur_offset; | ||
| 238 | for (std::size_t i = 1; i < OFFSET_ORDER.size(); ++i) { | ||
| 239 | cur_offset = GetField(OFFSET_ORDER[i]); | ||
| 240 | if (cur_offset < prev_offset) | ||
| 241 | return error; | ||
| 242 | prev_offset = cur_offset; | ||
| 243 | } | ||
| 244 | |||
| 245 | // rebases offsets | ||
| 246 | u32 offset = GetField(NameOffset); | ||
| 247 | if (offset != 0) | ||
| 248 | SetField(NameOffset, offset + module_address); | ||
| 249 | |||
| 250 | for (int field = CodeOffset; field < Fix0Barrier; field += 2) { | ||
| 251 | HeaderField header_field = static_cast<HeaderField>(field); | ||
| 252 | offset = GetField(header_field); | ||
| 253 | if (offset != 0) | ||
| 254 | SetField(header_field, offset + module_address); | ||
| 255 | } | ||
| 256 | |||
| 257 | // verifies everything is not beyond the buffer | ||
| 258 | u32 file_end = module_address + cro_size; | ||
| 259 | for (int field = CodeOffset, i = 0; field < Fix0Barrier; field += 2, ++i) { | ||
| 260 | HeaderField offset_field = static_cast<HeaderField>(field); | ||
| 261 | HeaderField size_field = static_cast<HeaderField>(field + 1); | ||
| 262 | if (GetField(offset_field) + GetField(size_field) * ENTRY_SIZE[i] > file_end) | ||
| 263 | return error; | ||
| 264 | } | ||
| 265 | |||
| 266 | return RESULT_SUCCESS; | ||
| 267 | } | ||
| 268 | |||
| 269 | ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, | ||
| 270 | VAddr data_segment_address, u32 data_segment_size, | ||
| 271 | VAddr bss_segment_address, u32 bss_segment_size) { | ||
| 272 | |||
| 273 | u32 prev_data_segment = 0; | ||
| 274 | u32 segment_num = GetField(SegmentNum); | ||
| 275 | for (u32 i = 0; i < segment_num; ++i) { | ||
| 276 | SegmentEntry segment; | ||
| 277 | GetEntry(i, segment); | ||
| 278 | if (segment.type == SegmentType::Data) { | ||
| 279 | if (segment.size != 0) { | ||
| 280 | if (segment.size > data_segment_size) | ||
| 281 | return ERROR_BUFFER_TOO_SMALL; | ||
| 282 | prev_data_segment = segment.offset; | ||
| 283 | segment.offset = data_segment_address; | ||
| 284 | } | ||
| 285 | } else if (segment.type == SegmentType::BSS) { | ||
| 286 | if (segment.size != 0) { | ||
| 287 | if (segment.size > bss_segment_size) | ||
| 288 | return ERROR_BUFFER_TOO_SMALL; | ||
| 289 | segment.offset = bss_segment_address; | ||
| 290 | } | ||
| 291 | } else if (segment.offset != 0) { | ||
| 292 | segment.offset += module_address; | ||
| 293 | if (segment.offset > module_address + cro_size) | ||
| 294 | return CROFormatError(0x19); | ||
| 295 | } | ||
| 296 | SetEntry(i, segment); | ||
| 297 | } | ||
| 298 | return MakeResult<u32>(prev_data_segment + module_address); | ||
| 299 | } | ||
| 300 | |||
| 301 | ResultCode CROHelper::RebaseExportNamedSymbolTable() { | ||
| 302 | VAddr export_strings_offset = GetField(ExportStringsOffset); | ||
| 303 | VAddr export_strings_end = export_strings_offset + GetField(ExportStringsSize); | ||
| 304 | |||
| 305 | u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); | ||
| 306 | for (u32 i = 0; i < export_named_symbol_num; ++i) { | ||
| 307 | ExportNamedSymbolEntry entry; | ||
| 308 | GetEntry(i, entry); | ||
| 309 | |||
| 310 | if (entry.name_offset != 0) { | ||
| 311 | entry.name_offset += module_address; | ||
| 312 | if (entry.name_offset < export_strings_offset | ||
| 313 | || entry.name_offset >= export_strings_end) { | ||
| 314 | return CROFormatError(0x11); | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | SetEntry(i, entry); | ||
| 319 | } | ||
| 320 | return RESULT_SUCCESS; | ||
| 321 | } | ||
| 322 | |||
| 323 | ResultCode CROHelper::VerifyExportTreeTable() const { | ||
| 324 | u32 tree_num = GetField(ExportTreeNum); | ||
| 325 | for (u32 i = 0; i < tree_num; ++i) { | ||
| 326 | ExportTreeEntry entry; | ||
| 327 | GetEntry(i, entry); | ||
| 328 | |||
| 329 | if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) { | ||
| 330 | return CROFormatError(0x11); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | return RESULT_SUCCESS; | ||
| 334 | } | ||
| 335 | |||
| 336 | ResultCode CROHelper::RebaseImportModuleTable() { | ||
| 337 | VAddr import_strings_offset = GetField(ImportStringsOffset); | ||
| 338 | VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize); | ||
| 339 | VAddr import_indexed_symbol_table_offset = GetField(ImportIndexedSymbolTableOffset); | ||
| 340 | VAddr index_import_table_end = import_indexed_symbol_table_offset + GetField(ImportIndexedSymbolNum) * sizeof(ImportIndexedSymbolEntry); | ||
| 341 | VAddr import_anonymous_symbol_table_offset = GetField(ImportAnonymousSymbolTableOffset); | ||
| 342 | VAddr offset_import_table_end = import_anonymous_symbol_table_offset + GetField(ImportAnonymousSymbolNum) * sizeof(ImportAnonymousSymbolEntry); | ||
| 343 | |||
| 344 | u32 module_num = GetField(ImportModuleNum); | ||
| 345 | for (u32 i = 0; i < module_num; ++i) { | ||
| 346 | ImportModuleEntry entry; | ||
| 347 | GetEntry(i, entry); | ||
| 348 | |||
| 349 | if (entry.name_offset != 0) { | ||
| 350 | entry.name_offset += module_address; | ||
| 351 | if (entry.name_offset < import_strings_offset | ||
| 352 | || entry.name_offset >= import_strings_end) { | ||
| 353 | return CROFormatError(0x18); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | if (entry.import_indexed_symbol_table_offset != 0) { | ||
| 358 | entry.import_indexed_symbol_table_offset += module_address; | ||
| 359 | if (entry.import_indexed_symbol_table_offset < import_indexed_symbol_table_offset | ||
| 360 | || entry.import_indexed_symbol_table_offset > index_import_table_end) { | ||
| 361 | return CROFormatError(0x18); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | if (entry.import_anonymous_symbol_table_offset != 0) { | ||
| 366 | entry.import_anonymous_symbol_table_offset += module_address; | ||
| 367 | if (entry.import_anonymous_symbol_table_offset < import_anonymous_symbol_table_offset | ||
| 368 | || entry.import_anonymous_symbol_table_offset > offset_import_table_end) { | ||
| 369 | return CROFormatError(0x18); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | SetEntry(i, entry); | ||
| 374 | } | ||
| 375 | return RESULT_SUCCESS; | ||
| 376 | } | ||
| 377 | |||
| 378 | ResultCode CROHelper::RebaseImportNamedSymbolTable() { | ||
| 379 | VAddr import_strings_offset = GetField(ImportStringsOffset); | ||
| 380 | VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize); | ||
| 381 | VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); | ||
| 382 | VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); | ||
| 383 | |||
| 384 | u32 num = GetField(ImportNamedSymbolNum); | ||
| 385 | for (u32 i = 0; i < num ; ++i) { | ||
| 386 | ImportNamedSymbolEntry entry; | ||
| 387 | GetEntry(i, entry); | ||
| 388 | |||
| 389 | if (entry.name_offset != 0) { | ||
| 390 | entry.name_offset += module_address; | ||
| 391 | if (entry.name_offset < import_strings_offset | ||
| 392 | || entry.name_offset >= import_strings_end) { | ||
| 393 | return CROFormatError(0x1B); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | if (entry.relocation_batch_offset != 0) { | ||
| 398 | entry.relocation_batch_offset += module_address; | ||
| 399 | if (entry.relocation_batch_offset < external_relocation_table_offset | ||
| 400 | || entry.relocation_batch_offset > external_relocation_table_end) { | ||
| 401 | return CROFormatError(0x1B); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | SetEntry(i, entry); | ||
| 406 | } | ||
| 407 | return RESULT_SUCCESS; | ||
| 408 | } | ||
| 409 | |||
| 410 | ResultCode CROHelper::RebaseImportIndexedSymbolTable() { | ||
| 411 | VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); | ||
| 412 | VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); | ||
| 413 | |||
| 414 | u32 num = GetField(ImportIndexedSymbolNum); | ||
| 415 | for (u32 i = 0; i < num ; ++i) { | ||
| 416 | ImportIndexedSymbolEntry entry; | ||
| 417 | GetEntry(i, entry); | ||
| 418 | |||
| 419 | if (entry.relocation_batch_offset != 0) { | ||
| 420 | entry.relocation_batch_offset += module_address; | ||
| 421 | if (entry.relocation_batch_offset < external_relocation_table_offset | ||
| 422 | || entry.relocation_batch_offset > external_relocation_table_end) { | ||
| 423 | return CROFormatError(0x14); | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | SetEntry(i, entry); | ||
| 428 | } | ||
| 429 | return RESULT_SUCCESS; | ||
| 430 | } | ||
| 431 | |||
| 432 | ResultCode CROHelper::RebaseImportAnonymousSymbolTable() { | ||
| 433 | VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset); | ||
| 434 | VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry); | ||
| 435 | |||
| 436 | u32 num = GetField(ImportAnonymousSymbolNum); | ||
| 437 | for (u32 i = 0; i < num ; ++i) { | ||
| 438 | ImportAnonymousSymbolEntry entry; | ||
| 439 | GetEntry(i, entry); | ||
| 440 | |||
| 441 | if (entry.relocation_batch_offset != 0) { | ||
| 442 | entry.relocation_batch_offset += module_address; | ||
| 443 | if (entry.relocation_batch_offset < external_relocation_table_offset | ||
| 444 | || entry.relocation_batch_offset > external_relocation_table_end) { | ||
| 445 | return CROFormatError(0x17); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | SetEntry(i, entry); | ||
| 450 | } | ||
| 451 | return RESULT_SUCCESS; | ||
| 452 | } | ||
| 453 | |||
| 454 | VAddr CROHelper::GetOnUnresolvedAddress() { | ||
| 455 | return SegmentTagToAddress(SegmentTag(GetField(OnUnresolvedSegmentTag))); | ||
| 456 | } | ||
| 457 | |||
| 458 | ResultCode CROHelper::ResetExternalRelocations() { | ||
| 459 | u32 unresolved_symbol = GetOnUnresolvedAddress(); | ||
| 460 | u32 external_relocation_num = GetField(ExternalRelocationNum); | ||
| 461 | ExternalRelocationEntry relocation; | ||
| 462 | |||
| 463 | // Verifies that the last relocation is the end of a batch | ||
| 464 | GetEntry(external_relocation_num - 1, relocation); | ||
| 465 | if (!relocation.is_batch_end) { | ||
| 466 | return CROFormatError(0x12); | ||
| 467 | } | ||
| 468 | |||
| 469 | bool batch_begin = true; | ||
| 470 | for (u32 i = 0; i < external_relocation_num; ++i) { | ||
| 471 | GetEntry(i, relocation); | ||
| 472 | VAddr relocation_target = SegmentTagToAddress(relocation.target_position); | ||
| 473 | |||
| 474 | if (relocation_target == 0) { | ||
| 475 | return CROFormatError(0x12); | ||
| 476 | } | ||
| 477 | |||
| 478 | ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, unresolved_symbol, relocation_target); | ||
| 479 | if (result.IsError()) { | ||
| 480 | LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); | ||
| 481 | return result; | ||
| 482 | } | ||
| 483 | |||
| 484 | if (batch_begin) { | ||
| 485 | // resets to unresolved state | ||
| 486 | relocation.is_batch_resolved = 0; | ||
| 487 | SetEntry(i, relocation); | ||
| 488 | } | ||
| 489 | |||
| 490 | // if current is an end, then the next is a beginning | ||
| 491 | batch_begin = relocation.is_batch_end != 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | return RESULT_SUCCESS; | ||
| 495 | } | ||
| 496 | |||
| 497 | ResultCode CROHelper::ClearExternalRelocations() { | ||
| 498 | u32 external_relocation_num = GetField(ExternalRelocationNum); | ||
| 499 | ExternalRelocationEntry relocation; | ||
| 500 | |||
| 501 | bool batch_begin = true; | ||
| 502 | for (u32 i = 0; i < external_relocation_num; ++i) { | ||
| 503 | GetEntry(i, relocation); | ||
| 504 | VAddr relocation_target = SegmentTagToAddress(relocation.target_position); | ||
| 505 | |||
| 506 | if (relocation_target == 0) { | ||
| 507 | return CROFormatError(0x12); | ||
| 508 | } | ||
| 509 | |||
| 510 | ResultCode result = ClearRelocation(relocation_target, relocation.type); | ||
| 511 | if (result.IsError()) { | ||
| 512 | LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw); | ||
| 513 | return result; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (batch_begin) { | ||
| 517 | // resets to unresolved state | ||
| 518 | relocation.is_batch_resolved = 0; | ||
| 519 | SetEntry(i, relocation); | ||
| 520 | } | ||
| 521 | |||
| 522 | // if current is an end, then the next is a beginning | ||
| 523 | batch_begin = relocation.is_batch_end != 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | return RESULT_SUCCESS; | ||
| 527 | } | ||
| 528 | |||
| 529 | ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) { | ||
| 530 | VAddr static_relocation_table_offset = GetField(StaticRelocationTableOffset); | ||
| 531 | VAddr static_relocation_table_end = static_relocation_table_offset + GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry); | ||
| 532 | |||
| 533 | CROHelper crs(crs_address); | ||
| 534 | u32 offset_export_num = GetField(StaticAnonymousSymbolNum); | ||
| 535 | LOG_INFO(Service_LDR, "CRO \"%s\" exports %d static anonymous symbols", ModuleName().data(), offset_export_num); | ||
| 536 | for (u32 i = 0; i < offset_export_num; ++i) { | ||
| 537 | StaticAnonymousSymbolEntry entry; | ||
| 538 | GetEntry(i, entry); | ||
| 539 | u32 batch_address = entry.relocation_batch_offset + module_address; | ||
| 540 | |||
| 541 | if (batch_address < static_relocation_table_offset | ||
| 542 | || batch_address > static_relocation_table_end) { | ||
| 543 | return CROFormatError(0x16); | ||
| 544 | } | ||
| 545 | |||
| 546 | u32 symbol_address = SegmentTagToAddress(entry.symbol_position); | ||
| 547 | LOG_TRACE(Service_LDR, "CRO \"%s\" exports 0x%08X to the static module", ModuleName().data(), symbol_address); | ||
| 548 | ResultCode result = crs.ApplyRelocationBatch(batch_address, symbol_address); | ||
| 549 | if (result.IsError()) { | ||
| 550 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 551 | return result; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | return RESULT_SUCCESS; | ||
| 555 | } | ||
| 556 | |||
| 557 | ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) { | ||
| 558 | u32 segment_num = GetField(SegmentNum); | ||
| 559 | u32 internal_relocation_num = GetField(InternalRelocationNum); | ||
| 560 | for (u32 i = 0; i < internal_relocation_num; ++i) { | ||
| 561 | InternalRelocationEntry relocation; | ||
| 562 | GetEntry(i, relocation); | ||
| 563 | VAddr target_addressB = SegmentTagToAddress(relocation.target_position); | ||
| 564 | if (target_addressB == 0) { | ||
| 565 | return CROFormatError(0x15); | ||
| 566 | } | ||
| 567 | |||
| 568 | VAddr target_address; | ||
| 569 | SegmentEntry target_segment; | ||
| 570 | GetEntry(relocation.target_position.segment_index, target_segment); | ||
| 571 | |||
| 572 | if (target_segment.type == SegmentType::Data) { | ||
| 573 | // If the relocation is to the .data segment, we need to relocate it in the old buffer | ||
| 574 | target_address = old_data_segment_address + relocation.target_position.offset_into_segment; | ||
| 575 | } else { | ||
| 576 | target_address = target_addressB; | ||
| 577 | } | ||
| 578 | |||
| 579 | if (relocation.symbol_segment >= segment_num) { | ||
| 580 | return CROFormatError(0x15); | ||
| 581 | } | ||
| 582 | |||
| 583 | SegmentEntry symbol_segment; | ||
| 584 | GetEntry(relocation.symbol_segment, symbol_segment); | ||
| 585 | LOG_TRACE(Service_LDR, "Internally relocates 0x%08X with 0x%08X", target_address, symbol_segment.offset); | ||
| 586 | ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend, symbol_segment.offset, target_addressB); | ||
| 587 | if (result.IsError()) { | ||
| 588 | LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw); | ||
| 589 | return result; | ||
| 590 | } | ||
| 591 | } | ||
| 592 | return RESULT_SUCCESS; | ||
| 593 | } | ||
| 594 | |||
| 595 | ResultCode CROHelper::ClearInternalRelocations() { | ||
| 596 | u32 internal_relocation_num = GetField(InternalRelocationNum); | ||
| 597 | for (u32 i = 0; i < internal_relocation_num; ++i) { | ||
| 598 | InternalRelocationEntry relocation; | ||
| 599 | GetEntry(i, relocation); | ||
| 600 | VAddr target_address = SegmentTagToAddress(relocation.target_position); | ||
| 601 | |||
| 602 | if (target_address == 0) { | ||
| 603 | return CROFormatError(0x15); | ||
| 604 | } | ||
| 605 | |||
| 606 | ResultCode result = ClearRelocation(target_address, relocation.type); | ||
| 607 | if (result.IsError()) { | ||
| 608 | LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw); | ||
| 609 | return result; | ||
| 610 | } | ||
| 611 | } | ||
| 612 | return RESULT_SUCCESS; | ||
| 613 | } | ||
| 614 | |||
| 615 | void CROHelper::UnrebaseImportAnonymousSymbolTable() { | ||
| 616 | u32 num = GetField(ImportAnonymousSymbolNum); | ||
| 617 | for (u32 i = 0; i < num; ++i) { | ||
| 618 | ImportAnonymousSymbolEntry entry; | ||
| 619 | GetEntry(i, entry); | ||
| 620 | |||
| 621 | if (entry.relocation_batch_offset != 0) { | ||
| 622 | entry.relocation_batch_offset -= module_address; | ||
| 623 | } | ||
| 624 | |||
| 625 | SetEntry(i, entry); | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 629 | void CROHelper::UnrebaseImportIndexedSymbolTable() { | ||
| 630 | u32 num = GetField(ImportIndexedSymbolNum); | ||
| 631 | for (u32 i = 0; i < num; ++i) { | ||
| 632 | ImportIndexedSymbolEntry entry; | ||
| 633 | GetEntry(i, entry); | ||
| 634 | |||
| 635 | if (entry.relocation_batch_offset != 0) { | ||
| 636 | entry.relocation_batch_offset -= module_address; | ||
| 637 | } | ||
| 638 | |||
| 639 | SetEntry(i, entry); | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | void CROHelper::UnrebaseImportNamedSymbolTable() { | ||
| 644 | u32 num = GetField(ImportNamedSymbolNum); | ||
| 645 | for (u32 i = 0; i < num; ++i) { | ||
| 646 | ImportNamedSymbolEntry entry; | ||
| 647 | GetEntry(i, entry); | ||
| 648 | |||
| 649 | if (entry.name_offset != 0) { | ||
| 650 | entry.name_offset -= module_address; | ||
| 651 | } | ||
| 652 | |||
| 653 | if (entry.relocation_batch_offset) { | ||
| 654 | entry.relocation_batch_offset -= module_address; | ||
| 655 | } | ||
| 656 | |||
| 657 | SetEntry(i, entry); | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | void CROHelper::UnrebaseImportModuleTable() { | ||
| 662 | u32 module_num = GetField(ImportModuleNum); | ||
| 663 | for (u32 i = 0; i < module_num; ++i) { | ||
| 664 | ImportModuleEntry entry; | ||
| 665 | GetEntry(i, entry); | ||
| 666 | |||
| 667 | if (entry.name_offset != 0) { | ||
| 668 | entry.name_offset -= module_address; | ||
| 669 | } | ||
| 670 | |||
| 671 | if (entry.import_indexed_symbol_table_offset) { | ||
| 672 | entry.import_indexed_symbol_table_offset -= module_address; | ||
| 673 | } | ||
| 674 | |||
| 675 | if (entry.import_anonymous_symbol_table_offset) { | ||
| 676 | entry.import_anonymous_symbol_table_offset -= module_address; | ||
| 677 | } | ||
| 678 | |||
| 679 | SetEntry(i, entry); | ||
| 680 | } | ||
| 681 | } | ||
| 682 | |||
| 683 | void CROHelper::UnrebaseExportNamedSymbolTable() { | ||
| 684 | u32 export_named_symbol_num = GetField(ExportNamedSymbolNum); | ||
| 685 | for (u32 i = 0; i < export_named_symbol_num; ++i) { | ||
| 686 | ExportNamedSymbolEntry entry; | ||
| 687 | GetEntry(i, entry); | ||
| 688 | |||
| 689 | if (entry.name_offset != 0) { | ||
| 690 | entry.name_offset -= module_address; | ||
| 691 | } | ||
| 692 | |||
| 693 | SetEntry(i, entry); | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | void CROHelper::UnrebaseSegmentTable() { | ||
| 698 | u32 segment_num = GetField(SegmentNum); | ||
| 699 | for (u32 i = 0; i < segment_num; ++i) { | ||
| 700 | SegmentEntry segment; | ||
| 701 | GetEntry(i, segment); | ||
| 702 | |||
| 703 | if (segment.type == SegmentType::BSS) { | ||
| 704 | segment.offset = 0; | ||
| 705 | } else if (segment.offset != 0) { | ||
| 706 | segment.offset -= module_address; | ||
| 707 | } | ||
| 708 | |||
| 709 | SetEntry(i, segment); | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | void CROHelper::UnrebaseHeader() { | ||
| 714 | u32 offset = GetField(NameOffset); | ||
| 715 | if (offset != 0) | ||
| 716 | SetField(NameOffset, offset - module_address); | ||
| 717 | |||
| 718 | for (int field = CodeOffset; field < Fix0Barrier; field += 2) { | ||
| 719 | HeaderField header_field = static_cast<HeaderField>(field); | ||
| 720 | offset = GetField(header_field); | ||
| 721 | if (offset != 0) | ||
| 722 | SetField(header_field, offset - module_address); | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) { | ||
| 727 | u32 import_strings_size = GetField(ImportStringsSize); | ||
| 728 | u32 symbol_import_num = GetField(ImportNamedSymbolNum); | ||
| 729 | for (u32 i = 0; i < symbol_import_num; ++i) { | ||
| 730 | ImportNamedSymbolEntry entry; | ||
| 731 | GetEntry(i, entry); | ||
| 732 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 733 | ExternalRelocationEntry relocation_entry; | ||
| 734 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 735 | |||
| 736 | if (!relocation_entry.is_batch_resolved) { | ||
| 737 | ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||
| 738 | std::string symbol_name = Memory::ReadCString(entry.name_offset, import_strings_size); | ||
| 739 | u32 symbol_address = source.FindExportNamedSymbol(symbol_name); | ||
| 740 | |||
| 741 | if (symbol_address != 0) { | ||
| 742 | LOG_TRACE(Service_LDR, "CRO \"%s\" imports \"%s\" from \"%s\"", | ||
| 743 | ModuleName().data(), symbol_name.data(), source.ModuleName().data()); | ||
| 744 | |||
| 745 | ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address); | ||
| 746 | if (result.IsError()) { | ||
| 747 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 748 | return result; | ||
| 749 | } | ||
| 750 | |||
| 751 | return MakeResult<bool>(false); | ||
| 752 | } | ||
| 753 | |||
| 754 | return MakeResult<bool>(true); | ||
| 755 | }); | ||
| 756 | if (result.IsError()) { | ||
| 757 | return result; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | } | ||
| 761 | return RESULT_SUCCESS; | ||
| 762 | } | ||
| 763 | |||
| 764 | ResultCode CROHelper::ResetImportNamedSymbol() { | ||
| 765 | u32 unresolved_symbol = GetOnUnresolvedAddress(); | ||
| 766 | |||
| 767 | u32 symbol_import_num = GetField(ImportNamedSymbolNum); | ||
| 768 | for (u32 i = 0; i < symbol_import_num; ++i) { | ||
| 769 | ImportNamedSymbolEntry entry; | ||
| 770 | GetEntry(i, entry); | ||
| 771 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 772 | ExternalRelocationEntry relocation_entry; | ||
| 773 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 774 | |||
| 775 | ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||
| 776 | if (result.IsError()) { | ||
| 777 | LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); | ||
| 778 | return result; | ||
| 779 | } | ||
| 780 | |||
| 781 | } | ||
| 782 | return RESULT_SUCCESS; | ||
| 783 | } | ||
| 784 | |||
| 785 | ResultCode CROHelper::ResetImportIndexedSymbol() { | ||
| 786 | u32 unresolved_symbol = GetOnUnresolvedAddress(); | ||
| 787 | |||
| 788 | u32 import_num = GetField(ImportIndexedSymbolNum); | ||
| 789 | for (u32 i = 0; i < import_num; ++i) { | ||
| 790 | ImportIndexedSymbolEntry entry; | ||
| 791 | GetEntry(i, entry); | ||
| 792 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 793 | ExternalRelocationEntry relocation_entry; | ||
| 794 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 795 | |||
| 796 | ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||
| 797 | if (result.IsError()) { | ||
| 798 | LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); | ||
| 799 | return result; | ||
| 800 | } | ||
| 801 | } | ||
| 802 | return RESULT_SUCCESS; | ||
| 803 | } | ||
| 804 | |||
| 805 | ResultCode CROHelper::ResetImportAnonymousSymbol() { | ||
| 806 | u32 unresolved_symbol = GetOnUnresolvedAddress(); | ||
| 807 | |||
| 808 | u32 import_num = GetField(ImportAnonymousSymbolNum); | ||
| 809 | for (u32 i = 0; i < import_num; ++i) { | ||
| 810 | ImportAnonymousSymbolEntry entry; | ||
| 811 | GetEntry(i, entry); | ||
| 812 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 813 | ExternalRelocationEntry relocation_entry; | ||
| 814 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 815 | |||
| 816 | ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||
| 817 | if (result.IsError()) { | ||
| 818 | LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw); | ||
| 819 | return result; | ||
| 820 | } | ||
| 821 | } | ||
| 822 | return RESULT_SUCCESS; | ||
| 823 | } | ||
| 824 | |||
| 825 | ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) { | ||
| 826 | u32 import_strings_size = GetField(ImportStringsSize); | ||
| 827 | |||
| 828 | u32 import_module_num = GetField(ImportModuleNum); | ||
| 829 | for (u32 i = 0; i < import_module_num; ++i) { | ||
| 830 | ImportModuleEntry entry; | ||
| 831 | GetEntry(i, entry); | ||
| 832 | std::string want_cro_name = Memory::ReadCString(entry.name_offset, import_strings_size); | ||
| 833 | |||
| 834 | ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||
| 835 | if (want_cro_name == source.ModuleName()) { | ||
| 836 | LOG_INFO(Service_LDR, "CRO \"%s\" imports %d indexed symbols from \"%s\"", | ||
| 837 | ModuleName().data(), entry.import_indexed_symbol_num, source.ModuleName().data()); | ||
| 838 | for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||
| 839 | ImportIndexedSymbolEntry im; | ||
| 840 | entry.GetImportIndexedSymbolEntry(j, im); | ||
| 841 | ExportIndexedSymbolEntry ex; | ||
| 842 | source.GetEntry(im.index, ex); | ||
| 843 | u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position); | ||
| 844 | LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address); | ||
| 845 | ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); | ||
| 846 | if (result.IsError()) { | ||
| 847 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 848 | return result; | ||
| 849 | } | ||
| 850 | } | ||
| 851 | LOG_INFO(Service_LDR, "CRO \"%s\" imports %d anonymous symbols from \"%s\"", | ||
| 852 | ModuleName().data(), entry.import_anonymous_symbol_num, source.ModuleName().data()); | ||
| 853 | for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||
| 854 | ImportAnonymousSymbolEntry im; | ||
| 855 | entry.GetImportAnonymousSymbolEntry(j, im); | ||
| 856 | u32 symbol_address = source.SegmentTagToAddress(im.symbol_position); | ||
| 857 | LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address); | ||
| 858 | ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); | ||
| 859 | if (result.IsError()) { | ||
| 860 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 861 | return result; | ||
| 862 | } | ||
| 863 | } | ||
| 864 | return MakeResult<bool>(false); | ||
| 865 | } | ||
| 866 | return MakeResult<bool>(true); | ||
| 867 | }); | ||
| 868 | if (result.IsError()) { | ||
| 869 | return result; | ||
| 870 | } | ||
| 871 | } | ||
| 872 | return RESULT_SUCCESS; | ||
| 873 | } | ||
| 874 | |||
| 875 | ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) { | ||
| 876 | LOG_DEBUG(Service_LDR, "CRO \"%s\" exports named symbols to \"%s\"", | ||
| 877 | ModuleName().data(), target.ModuleName().data()); | ||
| 878 | u32 target_import_strings_size = target.GetField(ImportStringsSize); | ||
| 879 | u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); | ||
| 880 | for (u32 i = 0; i < target_symbol_import_num; ++i) { | ||
| 881 | ImportNamedSymbolEntry entry; | ||
| 882 | target.GetEntry(i, entry); | ||
| 883 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 884 | ExternalRelocationEntry relocation_entry; | ||
| 885 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 886 | |||
| 887 | if (!relocation_entry.is_batch_resolved) { | ||
| 888 | std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size); | ||
| 889 | u32 symbol_address = FindExportNamedSymbol(symbol_name); | ||
| 890 | if (symbol_address != 0) { | ||
| 891 | LOG_TRACE(Service_LDR, " exports symbol \"%s\"", symbol_name.data()); | ||
| 892 | ResultCode result = target.ApplyRelocationBatch(relocation_addr, symbol_address); | ||
| 893 | if (result.IsError()) { | ||
| 894 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 895 | return result; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | } | ||
| 899 | } | ||
| 900 | return RESULT_SUCCESS; | ||
| 901 | } | ||
| 902 | |||
| 903 | ResultCode CROHelper::ResetExportNamedSymbol(CROHelper target) { | ||
| 904 | LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports named symbols to \"%s\"", | ||
| 905 | ModuleName().data(), target.ModuleName().data()); | ||
| 906 | u32 unresolved_symbol = target.GetOnUnresolvedAddress(); | ||
| 907 | u32 target_import_strings_size = target.GetField(ImportStringsSize); | ||
| 908 | u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum); | ||
| 909 | for (u32 i = 0; i < target_symbol_import_num; ++i) { | ||
| 910 | ImportNamedSymbolEntry entry; | ||
| 911 | target.GetEntry(i, entry); | ||
| 912 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 913 | ExternalRelocationEntry relocation_entry; | ||
| 914 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 915 | |||
| 916 | if (relocation_entry.is_batch_resolved) { | ||
| 917 | std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size); | ||
| 918 | u32 symbol_address = FindExportNamedSymbol(symbol_name); | ||
| 919 | if (symbol_address != 0) { | ||
| 920 | LOG_TRACE(Service_LDR, " unexports symbol \"%s\"", symbol_name.data()); | ||
| 921 | ResultCode result = target.ApplyRelocationBatch(relocation_addr, unresolved_symbol, true); | ||
| 922 | if (result.IsError()) { | ||
| 923 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 924 | return result; | ||
| 925 | } | ||
| 926 | } | ||
| 927 | } | ||
| 928 | } | ||
| 929 | return RESULT_SUCCESS; | ||
| 930 | } | ||
| 931 | |||
| 932 | ResultCode CROHelper::ApplyModuleExport(CROHelper target) { | ||
| 933 | std::string module_name = ModuleName(); | ||
| 934 | u32 target_import_string_size = target.GetField(ImportStringsSize); | ||
| 935 | u32 target_import_module_num = target.GetField(ImportModuleNum); | ||
| 936 | for (u32 i = 0; i < target_import_module_num; ++i) { | ||
| 937 | ImportModuleEntry entry; | ||
| 938 | target.GetEntry(i, entry); | ||
| 939 | |||
| 940 | if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name) | ||
| 941 | continue; | ||
| 942 | |||
| 943 | LOG_INFO(Service_LDR, "CRO \"%s\" exports %d indexed symbols to \"%s\"", | ||
| 944 | module_name.data(), entry.import_indexed_symbol_num, target.ModuleName().data()); | ||
| 945 | for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||
| 946 | ImportIndexedSymbolEntry im; | ||
| 947 | entry.GetImportIndexedSymbolEntry(j, im); | ||
| 948 | ExportIndexedSymbolEntry ex; | ||
| 949 | GetEntry(im.index, ex); | ||
| 950 | u32 symbol_address = SegmentTagToAddress(ex.symbol_position); | ||
| 951 | LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address); | ||
| 952 | ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); | ||
| 953 | if (result.IsError()) { | ||
| 954 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 955 | return result; | ||
| 956 | } | ||
| 957 | } | ||
| 958 | |||
| 959 | LOG_INFO(Service_LDR, "CRO \"%s\" exports %d anonymous symbols to \"%s\"", | ||
| 960 | module_name.data(), entry.import_anonymous_symbol_num, target.ModuleName().data()); | ||
| 961 | for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||
| 962 | ImportAnonymousSymbolEntry im; | ||
| 963 | entry.GetImportAnonymousSymbolEntry(j, im); | ||
| 964 | u32 symbol_address = SegmentTagToAddress(im.symbol_position); | ||
| 965 | LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address); | ||
| 966 | ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address); | ||
| 967 | if (result.IsError()) { | ||
| 968 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 969 | return result; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | return RESULT_SUCCESS; | ||
| 975 | } | ||
| 976 | |||
| 977 | ResultCode CROHelper::ResetModuleExport(CROHelper target) { | ||
| 978 | u32 unresolved_symbol = target.GetOnUnresolvedAddress(); | ||
| 979 | |||
| 980 | std::string module_name = ModuleName(); | ||
| 981 | u32 target_import_string_size = target.GetField(ImportStringsSize); | ||
| 982 | u32 target_import_module_num = target.GetField(ImportModuleNum); | ||
| 983 | for (u32 i = 0; i < target_import_module_num; ++i) { | ||
| 984 | ImportModuleEntry entry; | ||
| 985 | target.GetEntry(i, entry); | ||
| 986 | |||
| 987 | if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name) | ||
| 988 | continue; | ||
| 989 | |||
| 990 | LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports indexed symbols to \"%s\"", | ||
| 991 | module_name.data(), target.ModuleName().data()); | ||
| 992 | for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) { | ||
| 993 | ImportIndexedSymbolEntry im; | ||
| 994 | entry.GetImportIndexedSymbolEntry(j, im); | ||
| 995 | ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); | ||
| 996 | if (result.IsError()) { | ||
| 997 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 998 | return result; | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports anonymous symbols to \"%s\"", | ||
| 1003 | module_name.data(), target.ModuleName().data()); | ||
| 1004 | for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) { | ||
| 1005 | ImportAnonymousSymbolEntry im; | ||
| 1006 | entry.GetImportAnonymousSymbolEntry(j, im); | ||
| 1007 | ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true); | ||
| 1008 | if (result.IsError()) { | ||
| 1009 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 1010 | return result; | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | return RESULT_SUCCESS; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) { | ||
| 1019 | u32 import_strings_size = GetField(ImportStringsSize); | ||
| 1020 | u32 symbol_import_num = GetField(ImportNamedSymbolNum); | ||
| 1021 | for (u32 i = 0; i < symbol_import_num; ++i) { | ||
| 1022 | ImportNamedSymbolEntry entry; | ||
| 1023 | GetEntry(i, entry); | ||
| 1024 | VAddr relocation_addr = entry.relocation_batch_offset; | ||
| 1025 | ExternalRelocationEntry relocation_entry; | ||
| 1026 | Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry)); | ||
| 1027 | |||
| 1028 | if (Memory::ReadCString(entry.name_offset, import_strings_size) == "__aeabi_atexit"){ | ||
| 1029 | ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> { | ||
| 1030 | u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_"); | ||
| 1031 | |||
| 1032 | if (symbol_address != 0) { | ||
| 1033 | LOG_DEBUG(Service_LDR, "CRO \"%s\" import exit function from \"%s\"", | ||
| 1034 | ModuleName().data(), source.ModuleName().data()); | ||
| 1035 | |||
| 1036 | ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address); | ||
| 1037 | if (result.IsError()) { | ||
| 1038 | LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw); | ||
| 1039 | return result; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | return MakeResult<bool>(false); | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | return MakeResult<bool>(true); | ||
| 1046 | }); | ||
| 1047 | if (result.IsError()) { | ||
| 1048 | LOG_ERROR(Service_LDR, "Error applying exit relocation %08X", result.raw); | ||
| 1049 | return result; | ||
| 1050 | } | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | return RESULT_SUCCESS; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | /** | ||
| 1057 | * Verifies a string or a string table matching a predicted size (i.e. terminated by 0) | ||
| 1058 | * if it is not empty. There can be many other nulls in the string table because | ||
| 1059 | * they are composed by many sub strings. This function is to check whether the | ||
| 1060 | * whole string (table) is terminated properly, despite that it is not actually one string. | ||
| 1061 | * @param address the virtual address of the string (table) | ||
| 1062 | * @param size the size of the string (table), including the terminating 0 | ||
| 1063 | * @returns ResultCode RESULT_SUCCESS if the size matches, otherwise error code. | ||
| 1064 | */ | ||
| 1065 | static ResultCode VerifyStringTableLength(VAddr address, u32 size) { | ||
| 1066 | if (size != 0) { | ||
| 1067 | if (Memory::Read8(address + size - 1) != 0) | ||
| 1068 | return CROFormatError(0x0B); | ||
| 1069 | } | ||
| 1070 | return RESULT_SUCCESS; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, | ||
| 1074 | VAddr data_segment_addresss, u32 data_segment_size, | ||
| 1075 | VAddr bss_segment_address, u32 bss_segment_size, bool is_crs) { | ||
| 1076 | |||
| 1077 | ResultCode result = RebaseHeader(cro_size); | ||
| 1078 | if (result.IsError()) { | ||
| 1079 | LOG_ERROR(Service_LDR, "Error rebasing header %08X", result.raw); | ||
| 1080 | return result; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | result = VerifyStringTableLength(GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||
| 1084 | if (result.IsError()) { | ||
| 1085 | LOG_ERROR(Service_LDR, "Error verifying module name %08X", result.raw); | ||
| 1086 | return result; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | u32 prev_data_segment_address = 0; | ||
| 1090 | if (!is_crs) { | ||
| 1091 | auto result_val = RebaseSegmentTable(cro_size, | ||
| 1092 | data_segment_addresss, data_segment_size, | ||
| 1093 | bss_segment_address, bss_segment_size); | ||
| 1094 | if (result_val.Failed()) { | ||
| 1095 | LOG_ERROR(Service_LDR, "Error rebasing segment table %08X", result_val.Code().raw); | ||
| 1096 | return result_val.Code(); | ||
| 1097 | } | ||
| 1098 | prev_data_segment_address = *result_val; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | result = RebaseExportNamedSymbolTable(); | ||
| 1102 | if (result.IsError()) { | ||
| 1103 | LOG_ERROR(Service_LDR, "Error rebasing symbol export table %08X", result.raw); | ||
| 1104 | return result; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | result = VerifyExportTreeTable(); | ||
| 1108 | if (result.IsError()) { | ||
| 1109 | LOG_ERROR(Service_LDR, "Error verifying export tree %08X", result.raw); | ||
| 1110 | return result; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | result = VerifyStringTableLength(GetField(ExportStringsOffset), GetField(ExportStringsSize)); | ||
| 1114 | if (result.IsError()) { | ||
| 1115 | LOG_ERROR(Service_LDR, "Error verifying export strings %08X", result.raw); | ||
| 1116 | return result; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | result = RebaseImportModuleTable(); | ||
| 1120 | if (result.IsError()) { | ||
| 1121 | LOG_ERROR(Service_LDR, "Error rebasing object table %08X", result.raw); | ||
| 1122 | return result; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | result = ResetExternalRelocations(); | ||
| 1126 | if (result.IsError()) { | ||
| 1127 | LOG_ERROR(Service_LDR, "Error resetting all external relocations %08X", result.raw); | ||
| 1128 | return result; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | result = RebaseImportNamedSymbolTable(); | ||
| 1132 | if (result.IsError()) { | ||
| 1133 | LOG_ERROR(Service_LDR, "Error rebasing symbol import table %08X", result.raw); | ||
| 1134 | return result; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | result = RebaseImportIndexedSymbolTable(); | ||
| 1138 | if (result.IsError()) { | ||
| 1139 | LOG_ERROR(Service_LDR, "Error rebasing index import table %08X", result.raw); | ||
| 1140 | return result; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | result = RebaseImportAnonymousSymbolTable(); | ||
| 1144 | if (result.IsError()) { | ||
| 1145 | LOG_ERROR(Service_LDR, "Error rebasing offset import table %08X", result.raw); | ||
| 1146 | return result; | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | result = VerifyStringTableLength(GetField(ImportStringsOffset), GetField(ImportStringsSize)); | ||
| 1150 | if (result.IsError()) { | ||
| 1151 | LOG_ERROR(Service_LDR, "Error verifying import strings %08X", result.raw); | ||
| 1152 | return result; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | if (!is_crs) { | ||
| 1156 | result = ApplyStaticAnonymousSymbolToCRS(crs_address); | ||
| 1157 | if (result.IsError()) { | ||
| 1158 | LOG_ERROR(Service_LDR, "Error applying offset export to CRS %08X", result.raw); | ||
| 1159 | return result; | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | result = ApplyInternalRelocations(prev_data_segment_address); | ||
| 1164 | if (result.IsError()) { | ||
| 1165 | LOG_ERROR(Service_LDR, "Error applying internal relocations %08X", result.raw); | ||
| 1166 | return result; | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | if (!is_crs) { | ||
| 1170 | result = ApplyExitRelocations(crs_address); | ||
| 1171 | if (result.IsError()) { | ||
| 1172 | LOG_ERROR(Service_LDR, "Error applying exit relocations %08X", result.raw); | ||
| 1173 | return result; | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | return RESULT_SUCCESS; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | void CROHelper::Unrebase(bool is_crs) { | ||
| 1181 | UnrebaseImportAnonymousSymbolTable(); | ||
| 1182 | UnrebaseImportIndexedSymbolTable(); | ||
| 1183 | UnrebaseImportNamedSymbolTable(); | ||
| 1184 | UnrebaseImportModuleTable(); | ||
| 1185 | UnrebaseExportNamedSymbolTable(); | ||
| 1186 | |||
| 1187 | if (!is_crs) | ||
| 1188 | UnrebaseSegmentTable(); | ||
| 1189 | |||
| 1190 | SetNextModule(0); | ||
| 1191 | SetPreviousModule(0); | ||
| 1192 | |||
| 1193 | SetField(FixedSize, 0); | ||
| 1194 | |||
| 1195 | UnrebaseHeader(); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | ResultCode CROHelper::VerifyHash(u32 cro_size, VAddr crr) const { | ||
| 1199 | // TODO(wwylele): actually verify the hash | ||
| 1200 | return RESULT_SUCCESS; | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) { | ||
| 1204 | ResultCode result = RESULT_SUCCESS; | ||
| 1205 | |||
| 1206 | { | ||
| 1207 | VAddr data_segment_address; | ||
| 1208 | if (link_on_load_bug_fix) { | ||
| 1209 | // this is a bug fix introduced by 7.2.0-17's LoadCRO_New | ||
| 1210 | // The bug itself is: | ||
| 1211 | // If a relocation target is in .data segment, it will relocate to the | ||
| 1212 | // user-specified buffer. But if this is linking during loading, | ||
| 1213 | // the .data segment hasn't been tranfer from CRO to the buffer, | ||
| 1214 | // thus the relocation will be overwritten by data transfer. | ||
| 1215 | // To fix this bug, we need temporarily restore the old .data segment | ||
| 1216 | // offset and apply imported symbols. | ||
| 1217 | |||
| 1218 | // RO service seems assuming segment_index == segment_type, | ||
| 1219 | // so we do the same | ||
| 1220 | if (GetField(SegmentNum) >= 2) { // means we have .data segment | ||
| 1221 | SegmentEntry entry; | ||
| 1222 | GetEntry(2, entry); | ||
| 1223 | ASSERT(entry.type == SegmentType::Data); | ||
| 1224 | data_segment_address = entry.offset; | ||
| 1225 | entry.offset = GetField(DataOffset); | ||
| 1226 | SetEntry(2, entry); | ||
| 1227 | } | ||
| 1228 | } | ||
| 1229 | SCOPE_EXIT({ | ||
| 1230 | // Restore the new .data segment address after importing | ||
| 1231 | if (link_on_load_bug_fix) { | ||
| 1232 | if (GetField(SegmentNum) >= 2) { | ||
| 1233 | SegmentEntry entry; | ||
| 1234 | GetEntry(2, entry); | ||
| 1235 | entry.offset = data_segment_address; | ||
| 1236 | SetEntry(2, entry); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | }); | ||
| 1240 | |||
| 1241 | // Imports named symbols from other modules | ||
| 1242 | result = ApplyImportNamedSymbol(crs_address); | ||
| 1243 | if (result.IsError()) { | ||
| 1244 | LOG_ERROR(Service_LDR, "Error applying symbol import %08X", result.raw); | ||
| 1245 | return result; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | // Imports indexed and anonymous symbols from other modules | ||
| 1249 | result = ApplyModuleImport(crs_address); | ||
| 1250 | if (result.IsError()) { | ||
| 1251 | LOG_ERROR(Service_LDR, "Error applying module import %08X", result.raw); | ||
| 1252 | return result; | ||
| 1253 | } | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | // Exports symbols to other modules | ||
| 1257 | result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> { | ||
| 1258 | ResultCode result = ApplyExportNamedSymbol(target); | ||
| 1259 | if (result.IsError()) | ||
| 1260 | return result; | ||
| 1261 | |||
| 1262 | result = ApplyModuleExport(target); | ||
| 1263 | if (result.IsError()) | ||
| 1264 | return result; | ||
| 1265 | |||
| 1266 | return MakeResult<bool>(true); | ||
| 1267 | }); | ||
| 1268 | if (result.IsError()) { | ||
| 1269 | LOG_ERROR(Service_LDR, "Error applying export %08X", result.raw); | ||
| 1270 | return result; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | return RESULT_SUCCESS; | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | ResultCode CROHelper::Unlink(VAddr crs_address) { | ||
| 1277 | |||
| 1278 | // Resets all imported named symbols | ||
| 1279 | ResultCode result = ResetImportNamedSymbol(); | ||
| 1280 | if (result.IsError()) { | ||
| 1281 | LOG_ERROR(Service_LDR, "Error resetting symbol import %08X", result.raw); | ||
| 1282 | return result; | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | // Resets all imported indexed symbols | ||
| 1286 | result = ResetImportIndexedSymbol(); | ||
| 1287 | if (result.IsError()) { | ||
| 1288 | LOG_ERROR(Service_LDR, "Error resetting indexed import %08X", result.raw); | ||
| 1289 | return result; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | // Resets all imported anonymous symbols | ||
| 1293 | result = ResetImportAnonymousSymbol(); | ||
| 1294 | if (result.IsError()) { | ||
| 1295 | LOG_ERROR(Service_LDR, "Error resetting anonymous import %08X", result.raw); | ||
| 1296 | return result; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | // Resets all symbols in other modules imported from this module | ||
| 1300 | // Note: the RO service seems only searching in auto-link modules | ||
| 1301 | result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> { | ||
| 1302 | ResultCode result = ResetExportNamedSymbol(target); | ||
| 1303 | if (result.IsError()) | ||
| 1304 | return result; | ||
| 1305 | |||
| 1306 | result = ResetModuleExport(target); | ||
| 1307 | if (result.IsError()) | ||
| 1308 | return result; | ||
| 1309 | |||
| 1310 | return MakeResult<bool>(true); | ||
| 1311 | }); | ||
| 1312 | if (result.IsError()) { | ||
| 1313 | LOG_ERROR(Service_LDR, "Error resetting export %08X", result.raw); | ||
| 1314 | return result; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | return RESULT_SUCCESS; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | ResultCode CROHelper::ClearRelocations() { | ||
| 1321 | ResultCode result = ClearExternalRelocations(); | ||
| 1322 | if (result.IsError()) { | ||
| 1323 | LOG_ERROR(Service_LDR, "Error clearing external relocations %08X", result.raw); | ||
| 1324 | return result; | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | result = ClearInternalRelocations(); | ||
| 1328 | if (result.IsError()) { | ||
| 1329 | LOG_ERROR(Service_LDR, "Error clearing internal relocations %08X", result.raw); | ||
| 1330 | return result; | ||
| 1331 | } | ||
| 1332 | return RESULT_SUCCESS; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | void CROHelper::InitCRS() { | ||
| 1336 | SetNextModule(0); | ||
| 1337 | SetPreviousModule(0); | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | void CROHelper::Register(VAddr crs_address, bool auto_link) { | ||
| 1341 | CROHelper crs(crs_address); | ||
| 1342 | CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule()); | ||
| 1343 | |||
| 1344 | if (head.module_address) { | ||
| 1345 | // there are already CROs registered | ||
| 1346 | // register as the new tail | ||
| 1347 | CROHelper tail(head.PreviousModule()); | ||
| 1348 | |||
| 1349 | // link with the old tail | ||
| 1350 | ASSERT(tail.NextModule() == 0); | ||
| 1351 | SetPreviousModule(tail.module_address); | ||
| 1352 | tail.SetNextModule(module_address); | ||
| 1353 | |||
| 1354 | // set previous of the head pointing to the new tail | ||
| 1355 | head.SetPreviousModule(module_address); | ||
| 1356 | } else { | ||
| 1357 | // register as the first CRO | ||
| 1358 | // set previous to self as tail | ||
| 1359 | SetPreviousModule(module_address); | ||
| 1360 | |||
| 1361 | // set self as head | ||
| 1362 | if (auto_link) | ||
| 1363 | crs.SetNextModule(module_address); | ||
| 1364 | else | ||
| 1365 | crs.SetPreviousModule(module_address); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | // the new one is the tail | ||
| 1369 | SetNextModule(0); | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | void CROHelper::Unregister(VAddr crs_address) { | ||
| 1373 | CROHelper crs(crs_address); | ||
| 1374 | CROHelper next_head(crs.NextModule()), previous_head(crs.PreviousModule()); | ||
| 1375 | CROHelper next(NextModule()), previous(PreviousModule()); | ||
| 1376 | |||
| 1377 | if (module_address == next_head.module_address || module_address == previous_head.module_address) { | ||
| 1378 | // removing head | ||
| 1379 | if (next.module_address) { | ||
| 1380 | // the next is new head | ||
| 1381 | // let its previous point to the tail | ||
| 1382 | next.SetPreviousModule(previous.module_address); | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | // set new head | ||
| 1386 | if (module_address == previous_head.module_address) { | ||
| 1387 | crs.SetPreviousModule(next.module_address); | ||
| 1388 | } else { | ||
| 1389 | crs.SetNextModule(next.module_address); | ||
| 1390 | } | ||
| 1391 | } else if (next.module_address) { | ||
| 1392 | // link previous and next | ||
| 1393 | previous.SetNextModule(next.module_address); | ||
| 1394 | next.SetPreviousModule(previous.module_address); | ||
| 1395 | } else { | ||
| 1396 | // removing tail | ||
| 1397 | // set previous as new tail | ||
| 1398 | previous.SetNextModule(0); | ||
| 1399 | |||
| 1400 | // let head's previous point to the new tail | ||
| 1401 | if (next_head.module_address && next_head.PreviousModule() == module_address) { | ||
| 1402 | next_head.SetPreviousModule(previous.module_address); | ||
| 1403 | } else if (previous_head.module_address && previous_head.PreviousModule() == module_address) { | ||
| 1404 | previous_head.SetPreviousModule(previous.module_address); | ||
| 1405 | } else { | ||
| 1406 | UNREACHABLE(); | ||
| 1407 | } | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | // unlink self | ||
| 1411 | SetNextModule(0); | ||
| 1412 | SetPreviousModule(0); | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | u32 CROHelper::GetFixEnd(u32 fix_level) const { | ||
| 1416 | u32 end = CRO_HEADER_SIZE; | ||
| 1417 | end = std::max<u32>(end, GetField(CodeOffset) + GetField(CodeSize)); | ||
| 1418 | |||
| 1419 | u32 entry_size_i = 2; | ||
| 1420 | int field = ModuleNameOffset; | ||
| 1421 | while (true) { | ||
| 1422 | end = std::max<u32>(end, | ||
| 1423 | GetField(static_cast<HeaderField>(field)) + | ||
| 1424 | GetField(static_cast<HeaderField>(field + 1)) * ENTRY_SIZE[entry_size_i]); | ||
| 1425 | |||
| 1426 | ++entry_size_i; | ||
| 1427 | field += 2; | ||
| 1428 | |||
| 1429 | if (field == FIX_BARRIERS[fix_level]) | ||
| 1430 | return end; | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | u32 CROHelper::Fix(u32 fix_level) { | ||
| 1435 | u32 fix_end = GetFixEnd(fix_level); | ||
| 1436 | |||
| 1437 | if (fix_level != 0) { | ||
| 1438 | SetField(Magic, MAGIC_FIXD); | ||
| 1439 | |||
| 1440 | for (int field = FIX_BARRIERS[fix_level]; field < Fix0Barrier; field += 2) { | ||
| 1441 | SetField(static_cast<HeaderField>(field), fix_end); | ||
| 1442 | SetField(static_cast<HeaderField>(field + 1), 0); | ||
| 1443 | } | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | fix_end = Common::AlignUp(fix_end, Memory::PAGE_SIZE); | ||
| 1447 | |||
| 1448 | u32 fixed_size = fix_end - module_address; | ||
| 1449 | SetField(FixedSize, fixed_size); | ||
| 1450 | return fixed_size; | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | bool CROHelper::IsLoaded() const { | ||
| 1454 | u32 magic = GetField(Magic); | ||
| 1455 | if (magic != MAGIC_CRO0 && magic != MAGIC_FIXD) | ||
| 1456 | return false; | ||
| 1457 | |||
| 1458 | // TODO(wwylele): verify memory state here after memory aliasing is implemented | ||
| 1459 | |||
| 1460 | return true; | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const { | ||
| 1464 | u32 segment_num = GetField(SegmentNum); | ||
| 1465 | for (u32 i = 0; i < segment_num; ++i) { | ||
| 1466 | SegmentEntry entry; | ||
| 1467 | GetEntry(i, entry); | ||
| 1468 | if (entry.type == SegmentType::Code && entry.size != 0) { | ||
| 1469 | VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE); | ||
| 1470 | VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE); | ||
| 1471 | return std::make_tuple(begin, end - begin); | ||
| 1472 | } | ||
| 1473 | } | ||
| 1474 | return std::make_tuple(0, 0); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h new file mode 100644 index 000000000..34e357afd --- /dev/null +++ b/src/core/hle/service/ldr_ro/cro_helper.h | |||
| @@ -0,0 +1,691 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <tuple> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/swap.h" | ||
| 12 | |||
| 13 | #include "core/memory.h" | ||
| 14 | #include "core/hle/result.h" | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Namespace LDR_RO | ||
| 18 | |||
| 19 | namespace LDR_RO { | ||
| 20 | |||
| 21 | // GCC versions < 5.0 do not implement std::is_trivially_copyable. | ||
| 22 | // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. | ||
| 23 | #if (__GNUC__ >= 5) || defined(__clang__) | ||
| 24 | #define ASSERT_CRO_STRUCT(name, size) \ | ||
| 25 | static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ | ||
| 26 | static_assert(std::is_trivially_copyable<name>::value, "CRO structure " #name " isn't trivially copyable"); \ | ||
| 27 | static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) | ||
| 28 | #else | ||
| 29 | #define ASSERT_CRO_STRUCT(name, size) \ | ||
| 30 | static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ | ||
| 31 | static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) | ||
| 32 | #endif | ||
| 33 | |||
| 34 | static constexpr u32 CRO_HEADER_SIZE = 0x138; | ||
| 35 | static constexpr u32 CRO_HASH_SIZE = 0x80; | ||
| 36 | |||
| 37 | /// Represents a loaded module (CRO) with interfaces manipulating it. | ||
| 38 | class CROHelper final { | ||
| 39 | public: | ||
| 40 | explicit CROHelper(VAddr cro_address) : module_address(cro_address) { | ||
| 41 | } | ||
| 42 | |||
| 43 | std::string ModuleName() const { | ||
| 44 | return Memory::ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||
| 45 | } | ||
| 46 | |||
| 47 | u32 GetFileSize() const { | ||
| 48 | return GetField(FileSize); | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Rebases the module according to its address. | ||
| 53 | * @param crs_address the virtual address of the static module | ||
| 54 | * @param cro_size the size of the CRO file | ||
| 55 | * @param data_segment_address buffer address for .data segment | ||
| 56 | * @param data_segment_size the buffer size for .data segment | ||
| 57 | * @param bss_segment_address the buffer address for .bss segment | ||
| 58 | * @param bss_segment_size the buffer size for .bss segment | ||
| 59 | * @param is_crs true if the module itself is the static module | ||
| 60 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 61 | */ | ||
| 62 | ResultCode Rebase(VAddr crs_address, u32 cro_size, | ||
| 63 | VAddr data_segment_addresss, u32 data_segment_size, | ||
| 64 | VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Unrebases the module. | ||
| 68 | * @param is_crs true if the module itself is the static module | ||
| 69 | */ | ||
| 70 | void Unrebase(bool is_crs); | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Verifies module hash by CRR. | ||
| 74 | * @param cro_size the size of the CRO | ||
| 75 | * @param crr the virtual address of the CRR | ||
| 76 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 77 | */ | ||
| 78 | ResultCode VerifyHash(u32 cro_size, VAddr crr) const; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Links this module with all registered auto-link module. | ||
| 82 | * @param crs_address the virtual address of the static module | ||
| 83 | * @param link_on_load_bug_fix true if links when loading and fixes the bug | ||
| 84 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 85 | */ | ||
| 86 | ResultCode Link(VAddr crs_address, bool link_on_load_bug_fix); | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Unlinks this module with other modules. | ||
| 90 | * @param crs_address the virtual address of the static module | ||
| 91 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 92 | */ | ||
| 93 | ResultCode Unlink(VAddr crs_address); | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Clears all relocations to zero. | ||
| 97 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 98 | */ | ||
| 99 | ResultCode ClearRelocations(); | ||
| 100 | |||
| 101 | /// Initialize this module as the static module (CRS) | ||
| 102 | void InitCRS(); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Registers this module and adds it to the module list. | ||
| 106 | * @param crs_address the virtual address of the static module | ||
| 107 | * @auto_link whether to register as an auto link module | ||
| 108 | */ | ||
| 109 | void Register(VAddr crs_address, bool auto_link); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Unregisters this module and removes from the module list. | ||
| 113 | * @param crs_address the virtual address of the static module | ||
| 114 | */ | ||
| 115 | void Unregister(VAddr crs_address); | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Gets the end of reserved data according to the fix level. | ||
| 119 | * @param fix_level fix level from 0 to 3 | ||
| 120 | * @returns the end of reserved data. | ||
| 121 | */ | ||
| 122 | u32 GetFixEnd(u32 fix_level) const; | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Zeros offsets to cropped data according to the fix level and marks as fixed. | ||
| 126 | * @param fix_level fix level from 0 to 3 | ||
| 127 | * @returns page-aligned size of the module after fixing. | ||
| 128 | */ | ||
| 129 | u32 Fix(u32 fix_level); | ||
| 130 | |||
| 131 | bool IsFixed() const { | ||
| 132 | return GetField(Magic) == MAGIC_FIXD; | ||
| 133 | } | ||
| 134 | |||
| 135 | u32 GetFixedSize() const { | ||
| 136 | return GetField(FixedSize); | ||
| 137 | } | ||
| 138 | |||
| 139 | bool IsLoaded() const; | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Gets the page address and size of the code segment. | ||
| 143 | * @returns a tuple of (address, size); (0, 0) if the code segment doesn't exist. | ||
| 144 | */ | ||
| 145 | std::tuple<VAddr, u32> GetExecutablePages() const; | ||
| 146 | |||
| 147 | private: | ||
| 148 | const VAddr module_address; ///< the virtual address of this module | ||
| 149 | |||
| 150 | /** | ||
| 151 | * Each item in this enum represents a u32 field in the header begin from address+0x80, successively. | ||
| 152 | * We don't directly use a struct here, to avoid GetPointer, reinterpret_cast, or Read/WriteBlock repeatedly. | ||
| 153 | */ | ||
| 154 | enum HeaderField { | ||
| 155 | Magic = 0, | ||
| 156 | NameOffset, | ||
| 157 | NextCRO, | ||
| 158 | PreviousCRO, | ||
| 159 | FileSize, | ||
| 160 | BssSize, | ||
| 161 | FixedSize, | ||
| 162 | UnknownZero, | ||
| 163 | UnkSegmentTag, | ||
| 164 | OnLoadSegmentTag, | ||
| 165 | OnExitSegmentTag, | ||
| 166 | OnUnresolvedSegmentTag, | ||
| 167 | |||
| 168 | CodeOffset, | ||
| 169 | CodeSize, | ||
| 170 | DataOffset, | ||
| 171 | DataSize, | ||
| 172 | ModuleNameOffset, | ||
| 173 | ModuleNameSize, | ||
| 174 | SegmentTableOffset, | ||
| 175 | SegmentNum, | ||
| 176 | |||
| 177 | ExportNamedSymbolTableOffset, | ||
| 178 | ExportNamedSymbolNum, | ||
| 179 | ExportIndexedSymbolTableOffset, | ||
| 180 | ExportIndexedSymbolNum, | ||
| 181 | ExportStringsOffset, | ||
| 182 | ExportStringsSize, | ||
| 183 | ExportTreeTableOffset, | ||
| 184 | ExportTreeNum, | ||
| 185 | |||
| 186 | ImportModuleTableOffset, | ||
| 187 | ImportModuleNum, | ||
| 188 | ExternalRelocationTableOffset, | ||
| 189 | ExternalRelocationNum, | ||
| 190 | ImportNamedSymbolTableOffset, | ||
| 191 | ImportNamedSymbolNum, | ||
| 192 | ImportIndexedSymbolTableOffset, | ||
| 193 | ImportIndexedSymbolNum, | ||
| 194 | ImportAnonymousSymbolTableOffset, | ||
| 195 | ImportAnonymousSymbolNum, | ||
| 196 | ImportStringsOffset, | ||
| 197 | ImportStringsSize, | ||
| 198 | |||
| 199 | StaticAnonymousSymbolTableOffset, | ||
| 200 | StaticAnonymousSymbolNum, | ||
| 201 | InternalRelocationTableOffset, | ||
| 202 | InternalRelocationNum, | ||
| 203 | StaticRelocationTableOffset, | ||
| 204 | StaticRelocationNum, | ||
| 205 | Fix0Barrier, | ||
| 206 | |||
| 207 | Fix3Barrier = ExportNamedSymbolTableOffset, | ||
| 208 | Fix2Barrier = ImportModuleTableOffset, | ||
| 209 | Fix1Barrier = StaticAnonymousSymbolTableOffset, | ||
| 210 | }; | ||
| 211 | static_assert(Fix0Barrier == (CRO_HEADER_SIZE - CRO_HASH_SIZE) / 4, "CRO Header fields are wrong!"); | ||
| 212 | |||
| 213 | enum class SegmentType : u32 { | ||
| 214 | Code = 0, | ||
| 215 | ROData = 1, | ||
| 216 | Data = 2, | ||
| 217 | BSS = 3, | ||
| 218 | }; | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Identifies a program location inside of a segment. | ||
| 222 | * Required to refer to program locations because individual segments may be relocated independently of each other. | ||
| 223 | */ | ||
| 224 | union SegmentTag { | ||
| 225 | u32_le raw; | ||
| 226 | BitField<0, 4, u32_le> segment_index; | ||
| 227 | BitField<4, 28, u32_le> offset_into_segment; | ||
| 228 | |||
| 229 | SegmentTag() = default; | ||
| 230 | explicit SegmentTag(u32 raw_) : raw(raw_) {} | ||
| 231 | }; | ||
| 232 | |||
| 233 | /// Information of a segment in this module. | ||
| 234 | struct SegmentEntry { | ||
| 235 | u32_le offset; | ||
| 236 | u32_le size; | ||
| 237 | SegmentType type; | ||
| 238 | |||
| 239 | static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset; | ||
| 240 | }; | ||
| 241 | ASSERT_CRO_STRUCT(SegmentEntry, 12); | ||
| 242 | |||
| 243 | /// Identifies a named symbol exported from this module. | ||
| 244 | struct ExportNamedSymbolEntry { | ||
| 245 | u32_le name_offset; // pointing to a substring in ExportStrings | ||
| 246 | SegmentTag symbol_position; // to self's segment | ||
| 247 | |||
| 248 | static constexpr HeaderField TABLE_OFFSET_FIELD = ExportNamedSymbolTableOffset; | ||
| 249 | }; | ||
| 250 | ASSERT_CRO_STRUCT(ExportNamedSymbolEntry, 8); | ||
| 251 | |||
| 252 | /// Identifies an indexed symbol exported from this module. | ||
| 253 | struct ExportIndexedSymbolEntry { | ||
| 254 | SegmentTag symbol_position; // to self's segment | ||
| 255 | |||
| 256 | static constexpr HeaderField TABLE_OFFSET_FIELD = ExportIndexedSymbolTableOffset; | ||
| 257 | }; | ||
| 258 | ASSERT_CRO_STRUCT(ExportIndexedSymbolEntry, 4); | ||
| 259 | |||
| 260 | /// A tree node in the symbol lookup tree. | ||
| 261 | struct ExportTreeEntry { | ||
| 262 | u16_le test_bit; // bit address into the name to test | ||
| 263 | union Child { | ||
| 264 | u16_le raw; | ||
| 265 | BitField<0, 15, u16_le> next_index; | ||
| 266 | BitField<15, 1, u16_le> is_end; | ||
| 267 | } left, right; | ||
| 268 | u16_le export_table_index; // index of an ExportNamedSymbolEntry | ||
| 269 | |||
| 270 | static constexpr HeaderField TABLE_OFFSET_FIELD = ExportTreeTableOffset; | ||
| 271 | }; | ||
| 272 | ASSERT_CRO_STRUCT(ExportTreeEntry, 8); | ||
| 273 | |||
| 274 | /// Identifies a named symbol imported from another module. | ||
| 275 | struct ImportNamedSymbolEntry { | ||
| 276 | u32_le name_offset; // pointing to a substring in ImportStrings | ||
| 277 | u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable | ||
| 278 | |||
| 279 | static constexpr HeaderField TABLE_OFFSET_FIELD = ImportNamedSymbolTableOffset; | ||
| 280 | }; | ||
| 281 | ASSERT_CRO_STRUCT(ImportNamedSymbolEntry, 8); | ||
| 282 | |||
| 283 | /// Identifies an indexed symbol imported from another module. | ||
| 284 | struct ImportIndexedSymbolEntry { | ||
| 285 | u32_le index; // index of an ExportIndexedSymbolEntry in the exporting module | ||
| 286 | u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable | ||
| 287 | |||
| 288 | static constexpr HeaderField TABLE_OFFSET_FIELD = ImportIndexedSymbolTableOffset; | ||
| 289 | }; | ||
| 290 | ASSERT_CRO_STRUCT(ImportIndexedSymbolEntry, 8); | ||
| 291 | |||
| 292 | /// Identifies an anonymous symbol imported from another module. | ||
| 293 | struct ImportAnonymousSymbolEntry { | ||
| 294 | SegmentTag symbol_position; // in the exporting segment | ||
| 295 | u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable | ||
| 296 | |||
| 297 | static constexpr HeaderField TABLE_OFFSET_FIELD = ImportAnonymousSymbolTableOffset; | ||
| 298 | }; | ||
| 299 | ASSERT_CRO_STRUCT(ImportAnonymousSymbolEntry, 8); | ||
| 300 | |||
| 301 | /// Information of a imported module and symbols imported from it. | ||
| 302 | struct ImportModuleEntry { | ||
| 303 | u32_le name_offset; // pointing to a substring in ImportStrings | ||
| 304 | u32_le import_indexed_symbol_table_offset; // pointing to a subtable in ImportIndexedSymbolTable | ||
| 305 | u32_le import_indexed_symbol_num; | ||
| 306 | u32_le import_anonymous_symbol_table_offset; // pointing to a subtable in ImportAnonymousSymbolTable | ||
| 307 | u32_le import_anonymous_symbol_num; | ||
| 308 | |||
| 309 | static constexpr HeaderField TABLE_OFFSET_FIELD = ImportModuleTableOffset; | ||
| 310 | |||
| 311 | void GetImportIndexedSymbolEntry(u32 index, ImportIndexedSymbolEntry& entry) { | ||
| 312 | Memory::ReadBlock(import_indexed_symbol_table_offset + index * sizeof(ImportIndexedSymbolEntry), | ||
| 313 | &entry, sizeof(ImportIndexedSymbolEntry)); | ||
| 314 | } | ||
| 315 | |||
| 316 | void GetImportAnonymousSymbolEntry(u32 index, ImportAnonymousSymbolEntry& entry) { | ||
| 317 | Memory::ReadBlock(import_anonymous_symbol_table_offset + index * sizeof(ImportAnonymousSymbolEntry), | ||
| 318 | &entry, sizeof(ImportAnonymousSymbolEntry)); | ||
| 319 | } | ||
| 320 | }; | ||
| 321 | ASSERT_CRO_STRUCT(ImportModuleEntry, 20); | ||
| 322 | |||
| 323 | enum class RelocationType : u8 { | ||
| 324 | Nothing = 0, | ||
| 325 | AbsoluteAddress = 2, | ||
| 326 | RelativeAddress = 3, | ||
| 327 | ThumbBranch = 10, | ||
| 328 | ArmBranch = 28, | ||
| 329 | ModifyArmBranch = 29, | ||
| 330 | AbsoluteAddress2 = 38, | ||
| 331 | AlignedRelativeAddress = 42, | ||
| 332 | }; | ||
| 333 | |||
| 334 | struct RelocationEntry { | ||
| 335 | SegmentTag target_position; // to self's segment as an ExternalRelocationEntry; to static module segment as a StaticRelocationEntry | ||
| 336 | RelocationType type; | ||
| 337 | u8 is_batch_end; | ||
| 338 | u8 is_batch_resolved; // set at a batch beginning if the batch is resolved | ||
| 339 | INSERT_PADDING_BYTES(1); | ||
| 340 | u32_le addend; | ||
| 341 | }; | ||
| 342 | |||
| 343 | /// Identifies a normal cross-module relocation. | ||
| 344 | struct ExternalRelocationEntry : RelocationEntry { | ||
| 345 | static constexpr HeaderField TABLE_OFFSET_FIELD = ExternalRelocationTableOffset; | ||
| 346 | }; | ||
| 347 | ASSERT_CRO_STRUCT(ExternalRelocationEntry, 12); | ||
| 348 | |||
| 349 | /// Identifies a special static relocation (no game is known using this). | ||
| 350 | struct StaticRelocationEntry : RelocationEntry { | ||
| 351 | static constexpr HeaderField TABLE_OFFSET_FIELD = StaticRelocationTableOffset; | ||
| 352 | }; | ||
| 353 | ASSERT_CRO_STRUCT(StaticRelocationEntry, 12); | ||
| 354 | |||
| 355 | /// Identifies a in-module relocation. | ||
| 356 | struct InternalRelocationEntry { | ||
| 357 | SegmentTag target_position; // to self's segment | ||
| 358 | RelocationType type; | ||
| 359 | u8 symbol_segment; | ||
| 360 | INSERT_PADDING_BYTES(2); | ||
| 361 | u32_le addend; | ||
| 362 | |||
| 363 | static constexpr HeaderField TABLE_OFFSET_FIELD = InternalRelocationTableOffset; | ||
| 364 | }; | ||
| 365 | ASSERT_CRO_STRUCT(InternalRelocationEntry, 12); | ||
| 366 | |||
| 367 | /// Identifies a special static anonymous symbol (no game is known using this). | ||
| 368 | struct StaticAnonymousSymbolEntry { | ||
| 369 | SegmentTag symbol_position; // to self's segment | ||
| 370 | u32_le relocation_batch_offset; // pointing to a relocation batch in StaticRelocationTable | ||
| 371 | |||
| 372 | static constexpr HeaderField TABLE_OFFSET_FIELD = StaticAnonymousSymbolTableOffset; | ||
| 373 | }; | ||
| 374 | ASSERT_CRO_STRUCT(StaticAnonymousSymbolEntry, 8); | ||
| 375 | |||
| 376 | /** | ||
| 377 | * Entry size of each table, from Code to StaticRelocationTable. | ||
| 378 | * Byte string contents (such as Code) are treated with entries of size 1. | ||
| 379 | * This is used for verifying the size of each table and calculating the fix end. | ||
| 380 | */ | ||
| 381 | static const std::array<int, 17> ENTRY_SIZE; | ||
| 382 | |||
| 383 | /// The offset field of the table where to crop for each fix level | ||
| 384 | static const std::array<HeaderField, 4> FIX_BARRIERS; | ||
| 385 | |||
| 386 | static constexpr u32 MAGIC_CRO0 = 0x304F5243; | ||
| 387 | static constexpr u32 MAGIC_FIXD = 0x44584946; | ||
| 388 | |||
| 389 | VAddr Field(HeaderField field) const { | ||
| 390 | return module_address + CRO_HASH_SIZE + field * 4; | ||
| 391 | } | ||
| 392 | |||
| 393 | u32 GetField(HeaderField field) const { | ||
| 394 | return Memory::Read32(Field(field)); | ||
| 395 | } | ||
| 396 | |||
| 397 | void SetField(HeaderField field, u32 value) { | ||
| 398 | Memory::Write32(Field(field), value); | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Reads an entry in one of module tables. | ||
| 403 | * @param index index of the entry | ||
| 404 | * @param data where to put the read entry | ||
| 405 | * @note the entry type must have the static member TABLE_OFFSET_FIELD | ||
| 406 | * indicating which table the entry is in. | ||
| 407 | */ | ||
| 408 | template <typename T> | ||
| 409 | void GetEntry(std::size_t index, T& data) const { | ||
| 410 | Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); | ||
| 411 | } | ||
| 412 | |||
| 413 | /** | ||
| 414 | * Writes an entry to one of module tables. | ||
| 415 | * @param index index of the entry | ||
| 416 | * @param data the entry data to write | ||
| 417 | * @note the entry type must have the static member TABLE_OFFSET_FIELD | ||
| 418 | * indicating which table the entry is in. | ||
| 419 | */ | ||
| 420 | template <typename T> | ||
| 421 | void SetEntry(std::size_t index, const T& data) { | ||
| 422 | Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); | ||
| 423 | } | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Converts a segment tag to virtual address in this module. | ||
| 427 | * @param segment_tag the segment tag to convert | ||
| 428 | * @returns VAddr the virtual address the segment tag points to; 0 if invalid. | ||
| 429 | */ | ||
| 430 | VAddr SegmentTagToAddress(SegmentTag segment_tag) const; | ||
| 431 | |||
| 432 | VAddr NextModule() const { | ||
| 433 | return GetField(NextCRO); | ||
| 434 | } | ||
| 435 | |||
| 436 | VAddr PreviousModule() const { | ||
| 437 | return GetField(PreviousCRO); | ||
| 438 | } | ||
| 439 | |||
| 440 | void SetNextModule(VAddr next) { | ||
| 441 | SetField(NextCRO, next); | ||
| 442 | } | ||
| 443 | |||
| 444 | void SetPreviousModule(VAddr previous) { | ||
| 445 | SetField(PreviousCRO, previous); | ||
| 446 | } | ||
| 447 | |||
| 448 | /** | ||
| 449 | * A helper function iterating over all registered auto-link modules, including the static module. | ||
| 450 | * @param crs_address the virtual address of the static module | ||
| 451 | * @param func a function object to operate on a module. It accepts one parameter | ||
| 452 | * CROHelper and returns ResultVal<bool>. It should return true to continue the iteration, | ||
| 453 | * false to stop the iteration, or an error code (which will also stop the iteration). | ||
| 454 | * @returns ResultCode indicating the result of the operation, RESULT_SUCCESS if all iteration success, | ||
| 455 | * otherwise error code of the last iteration. | ||
| 456 | */ | ||
| 457 | template <typename FunctionObject> | ||
| 458 | static ResultCode ForEachAutoLinkCRO(VAddr crs_address, FunctionObject func) { | ||
| 459 | VAddr current = crs_address; | ||
| 460 | while (current != 0) { | ||
| 461 | CROHelper cro(current); | ||
| 462 | CASCADE_RESULT(bool next, func(cro)); | ||
| 463 | if (!next) | ||
| 464 | break; | ||
| 465 | current = cro.NextModule(); | ||
| 466 | } | ||
| 467 | return RESULT_SUCCESS; | ||
| 468 | } | ||
| 469 | |||
| 470 | /** | ||
| 471 | * Applies a relocation | ||
| 472 | * @param target_address where to apply the relocation | ||
| 473 | * @param relocation_type the type of the relocation | ||
| 474 | * @param addend address addend applied to the relocated symbol | ||
| 475 | * @param symbol_address the symbol address to be relocated with | ||
| 476 | * @param target_future_address the future address of the target. | ||
| 477 | * Usually equals to target_address, but will be different for a target in .data segment | ||
| 478 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 479 | */ | ||
| 480 | ResultCode ApplyRelocation(VAddr target_address, RelocationType relocation_type, | ||
| 481 | u32 addend, u32 symbol_address, u32 target_future_address); | ||
| 482 | |||
| 483 | /** | ||
| 484 | * Clears a relocation to zero | ||
| 485 | * @param target_address where to apply the relocation | ||
| 486 | * @param relocation_type the type of the relocation | ||
| 487 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 488 | */ | ||
| 489 | ResultCode ClearRelocation(VAddr target_address, RelocationType relocation_type); | ||
| 490 | |||
| 491 | /** | ||
| 492 | * Applies or resets a batch of relocations | ||
| 493 | * @param batch the virtual address of the first relocation in the batch | ||
| 494 | * @param symbol_address the symbol address to be relocated with | ||
| 495 | * @param reset false to set the batch to resolved state, true to reset the batch to unresolved state | ||
| 496 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 497 | */ | ||
| 498 | ResultCode ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset = false); | ||
| 499 | |||
| 500 | /** | ||
| 501 | * Finds an exported named symbol in this module. | ||
| 502 | * @param name the name of the symbol to find | ||
| 503 | * @return VAddr the virtual address of the symbol; 0 if not found. | ||
| 504 | */ | ||
| 505 | VAddr FindExportNamedSymbol(const std::string& name) const; | ||
| 506 | |||
| 507 | /** | ||
| 508 | * Rebases offsets in module header according to module address. | ||
| 509 | * @param cro_size the size of the CRO file | ||
| 510 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 511 | */ | ||
| 512 | ResultCode RebaseHeader(u32 cro_size); | ||
| 513 | |||
| 514 | /** | ||
| 515 | * Rebases offsets in segment table according to module address. | ||
| 516 | * @param cro_size the size of the CRO file | ||
| 517 | * @param data_segment_address the buffer address for .data segment | ||
| 518 | * @param data_segment_size the buffer size for .data segment | ||
| 519 | * @param bss_segment_address the buffer address for .bss segment | ||
| 520 | * @param bss_segment_size the buffer size for .bss segment | ||
| 521 | * @returns ResultVal<VAddr> with the virtual address of .data segment in CRO. | ||
| 522 | */ | ||
| 523 | ResultVal<VAddr> RebaseSegmentTable(u32 cro_size, | ||
| 524 | VAddr data_segment_address, u32 data_segment_size, | ||
| 525 | VAddr bss_segment_address, u32 bss_segment_size); | ||
| 526 | |||
| 527 | /** | ||
| 528 | * Rebases offsets in exported named symbol table according to module address. | ||
| 529 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 530 | */ | ||
| 531 | ResultCode RebaseExportNamedSymbolTable(); | ||
| 532 | |||
| 533 | /** | ||
| 534 | * Verifies indices in export tree table. | ||
| 535 | * @returns ResultCode RESULT_SUCCESS if all indices are verified as valid, otherwise error code. | ||
| 536 | */ | ||
| 537 | ResultCode VerifyExportTreeTable() const; | ||
| 538 | |||
| 539 | /** | ||
| 540 | * Rebases offsets in exported module table according to module address. | ||
| 541 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 542 | */ | ||
| 543 | ResultCode RebaseImportModuleTable(); | ||
| 544 | |||
| 545 | /** | ||
| 546 | * Rebases offsets in imported named symbol table according to module address. | ||
| 547 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 548 | */ | ||
| 549 | ResultCode RebaseImportNamedSymbolTable(); | ||
| 550 | |||
| 551 | /** | ||
| 552 | * Rebases offsets in imported indexed symbol table according to module address. | ||
| 553 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 554 | */ | ||
| 555 | ResultCode RebaseImportIndexedSymbolTable(); | ||
| 556 | |||
| 557 | /** | ||
| 558 | * Rebases offsets in imported anonymous symbol table according to module address. | ||
| 559 | * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||
| 560 | */ | ||
| 561 | ResultCode RebaseImportAnonymousSymbolTable(); | ||
| 562 | |||
| 563 | /** | ||
| 564 | * Gets the address of OnUnresolved function in this module. | ||
| 565 | * Used as the applied symbol for reset relocation. | ||
| 566 | * @returns the virtual address of OnUnresolved. 0 if not provided. | ||
| 567 | */ | ||
| 568 | VAddr GetOnUnresolvedAddress(); | ||
| 569 | |||
| 570 | /** | ||
| 571 | * Resets all external relocations to unresolved state. | ||
| 572 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 573 | */ | ||
| 574 | ResultCode ResetExternalRelocations(); | ||
| 575 | |||
| 576 | /** | ||
| 577 | * Clears all external relocations to zero. | ||
| 578 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 579 | */ | ||
| 580 | ResultCode ClearExternalRelocations(); | ||
| 581 | |||
| 582 | /** | ||
| 583 | * Applies all static anonymous symbol to the static module. | ||
| 584 | * @param crs_address the virtual address of the static module | ||
| 585 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 586 | */ | ||
| 587 | ResultCode ApplyStaticAnonymousSymbolToCRS(VAddr crs_address); | ||
| 588 | |||
| 589 | /** | ||
| 590 | * Applies all internal relocations to the module itself. | ||
| 591 | * @param old_data_segment_address the virtual address of data segment in CRO buffer | ||
| 592 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 593 | */ | ||
| 594 | ResultCode ApplyInternalRelocations(u32 old_data_segment_address); | ||
| 595 | |||
| 596 | /** | ||
| 597 | * Clears all internal relocations to zero. | ||
| 598 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 599 | */ | ||
| 600 | ResultCode ClearInternalRelocations(); | ||
| 601 | |||
| 602 | /// Unrebases offsets in imported anonymous symbol table | ||
| 603 | void UnrebaseImportAnonymousSymbolTable(); | ||
| 604 | |||
| 605 | /// Unrebases offsets in imported indexed symbol table | ||
| 606 | void UnrebaseImportIndexedSymbolTable(); | ||
| 607 | |||
| 608 | /// Unrebases offsets in imported named symbol table | ||
| 609 | void UnrebaseImportNamedSymbolTable(); | ||
| 610 | |||
| 611 | /// Unrebases offsets in imported module table | ||
| 612 | void UnrebaseImportModuleTable(); | ||
| 613 | |||
| 614 | /// Unrebases offsets in exported named symbol table | ||
| 615 | void UnrebaseExportNamedSymbolTable(); | ||
| 616 | |||
| 617 | /// Unrebases offsets in segment table | ||
| 618 | void UnrebaseSegmentTable(); | ||
| 619 | |||
| 620 | /// Unrebases offsets in module header | ||
| 621 | void UnrebaseHeader(); | ||
| 622 | |||
| 623 | /** | ||
| 624 | * Looks up all imported named symbols of this module in all registered auto-link modules, and resolves them if found. | ||
| 625 | * @param crs_address the virtual address of the static module | ||
| 626 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 627 | */ | ||
| 628 | ResultCode ApplyImportNamedSymbol(VAddr crs_address); | ||
| 629 | |||
| 630 | /** | ||
| 631 | * Resets all imported named symbols of this module to unresolved state. | ||
| 632 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 633 | */ | ||
| 634 | ResultCode ResetImportNamedSymbol(); | ||
| 635 | |||
| 636 | /** | ||
| 637 | * Resets all imported indexed symbols of this module to unresolved state. | ||
| 638 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 639 | */ | ||
| 640 | ResultCode ResetImportIndexedSymbol(); | ||
| 641 | |||
| 642 | /** | ||
| 643 | * Resets all imported anonymous symbols of this module to unresolved state. | ||
| 644 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 645 | */ | ||
| 646 | ResultCode ResetImportAnonymousSymbol(); | ||
| 647 | |||
| 648 | /** | ||
| 649 | * Finds registered auto-link modules that this module imports, and resolves indexed and anonymous symbols exported by them. | ||
| 650 | * @param crs_address the virtual address of the static module | ||
| 651 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 652 | */ | ||
| 653 | ResultCode ApplyModuleImport(VAddr crs_address); | ||
| 654 | |||
| 655 | /** | ||
| 656 | * Resolves target module's imported named symbols that exported by this module. | ||
| 657 | * @param target the module to resolve. | ||
| 658 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 659 | */ | ||
| 660 | ResultCode ApplyExportNamedSymbol(CROHelper target); | ||
| 661 | |||
| 662 | /** | ||
| 663 | * Resets target's named symbols imported from this module to unresolved state. | ||
| 664 | * @param target the module to reset. | ||
| 665 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 666 | */ | ||
| 667 | ResultCode ResetExportNamedSymbol(CROHelper target); | ||
| 668 | |||
| 669 | /** | ||
| 670 | * Resolves imported indexed and anonymous symbols in the target module which imports this module. | ||
| 671 | * @param target the module to resolve. | ||
| 672 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 673 | */ | ||
| 674 | ResultCode ApplyModuleExport(CROHelper target); | ||
| 675 | |||
| 676 | /** | ||
| 677 | * Resets target's indexed and anonymous symbol imported from this module to unresolved state. | ||
| 678 | * @param target the module to reset. | ||
| 679 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 680 | */ | ||
| 681 | ResultCode ResetModuleExport(CROHelper target); | ||
| 682 | |||
| 683 | /** | ||
| 684 | * Resolves the exit function in this module | ||
| 685 | * @param crs_address the virtual address of the static module. | ||
| 686 | * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||
| 687 | */ | ||
| 688 | ResultCode ApplyExitRelocations(VAddr crs_address); | ||
| 689 | }; | ||
| 690 | |||
| 691 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp new file mode 100644 index 000000000..8ba73ea8d --- /dev/null +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp | |||
| @@ -0,0 +1,748 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | |||
| 9 | #include "core/arm/arm_interface.h" | ||
| 10 | #include "core/hle/kernel/process.h" | ||
| 11 | #include "core/hle/kernel/vm_manager.h" | ||
| 12 | #include "core/hle/service/ldr_ro/cro_helper.h" | ||
| 13 | #include "core/hle/service/ldr_ro/ldr_ro.h" | ||
| 14 | #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Namespace LDR_RO | ||
| 18 | |||
| 19 | namespace LDR_RO { | ||
| 20 | |||
| 21 | static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9 | ||
| 22 | ResultCode(ErrorDescription::AlreadyInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent); | ||
| 23 | static const ResultCode ERROR_NOT_INITIALIZED = // 0xD9612FF8 | ||
| 24 | ResultCode(ErrorDescription::NotInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent); | ||
| 25 | static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F | ||
| 26 | ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 27 | static const ResultCode ERROR_MISALIGNED_ADDRESS = // 0xD9012FF1 | ||
| 28 | ResultCode(ErrorDescription::MisalignedAddress, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 29 | static const ResultCode ERROR_MISALIGNED_SIZE = // 0xD9012FF2 | ||
| 30 | ResultCode(ErrorDescription::MisalignedSize, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 31 | static const ResultCode ERROR_ILLEGAL_ADDRESS = // 0xE1612C0F | ||
| 32 | ResultCode(static_cast<ErrorDescription>(15), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage); | ||
| 33 | static const ResultCode ERROR_INVALID_MEMORY_STATE = // 0xD8A12C08 | ||
| 34 | ResultCode(static_cast<ErrorDescription>(8), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); | ||
| 35 | static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D | ||
| 36 | ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); | ||
| 37 | static const ResultCode ERROR_INVALID_DESCRIPTOR = // 0xD9001830 | ||
| 38 | ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 39 | |||
| 40 | static MemorySynchronizer memory_synchronizer; | ||
| 41 | |||
| 42 | // TODO(wwylele): this should be in the per-client storage when we implement multi-process | ||
| 43 | static VAddr loaded_crs; ///< the virtual address of the static module | ||
| 44 | |||
| 45 | static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { | ||
| 46 | auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr); | ||
| 47 | return vma != Kernel::g_current_process->vm_manager.vma_map.end() | ||
| 48 | && vma->second.base + vma->second.size >= buffer_ptr + size | ||
| 49 | && vma->second.permissions == Kernel::VMAPermission::ReadWrite | ||
| 50 | && vma->second.meminfo_state == Kernel::MemoryState::Private; | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * LDR_RO::Initialize service function | ||
| 55 | * Inputs: | ||
| 56 | * 0 : 0x000100C2 | ||
| 57 | * 1 : CRS buffer pointer | ||
| 58 | * 2 : CRS Size | ||
| 59 | * 3 : Process memory address where the CRS will be mapped | ||
| 60 | * 4 : handle translation descriptor (zero) | ||
| 61 | * 5 : KProcess handle | ||
| 62 | * Outputs: | ||
| 63 | * 0 : Return header | ||
| 64 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 65 | */ | ||
| 66 | static void Initialize(Service::Interface* self) { | ||
| 67 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 68 | VAddr crs_buffer_ptr = cmd_buff[1]; | ||
| 69 | u32 crs_size = cmd_buff[2]; | ||
| 70 | VAddr crs_address = cmd_buff[3]; | ||
| 71 | u32 descriptor = cmd_buff[4]; | ||
| 72 | u32 process = cmd_buff[5]; | ||
| 73 | |||
| 74 | LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, descriptor=0x%08X, process=0x%08X", | ||
| 75 | crs_buffer_ptr, crs_address, crs_size, descriptor, process); | ||
| 76 | |||
| 77 | if (descriptor != 0) { | ||
| 78 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 79 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 80 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | |||
| 84 | cmd_buff[0] = IPC::MakeHeader(1, 1, 0); | ||
| 85 | |||
| 86 | if (loaded_crs != 0) { | ||
| 87 | LOG_ERROR(Service_LDR, "Already initialized"); | ||
| 88 | cmd_buff[1] = ERROR_ALREADY_INITIALIZED.raw; | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (crs_size < CRO_HEADER_SIZE) { | ||
| 93 | LOG_ERROR(Service_LDR, "CRS is too small"); | ||
| 94 | cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | |||
| 98 | if (crs_buffer_ptr & Memory::PAGE_MASK) { | ||
| 99 | LOG_ERROR(Service_LDR, "CRS original address is not aligned"); | ||
| 100 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (crs_address & Memory::PAGE_MASK) { | ||
| 105 | LOG_ERROR(Service_LDR, "CRS mapping address is not aligned"); | ||
| 106 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 107 | return; | ||
| 108 | } | ||
| 109 | |||
| 110 | if (crs_size & Memory::PAGE_MASK) { | ||
| 111 | LOG_ERROR(Service_LDR, "CRS size is not aligned"); | ||
| 112 | cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { | ||
| 117 | LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); | ||
| 118 | cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | |||
| 122 | if (crs_address < Memory::PROCESS_IMAGE_VADDR || crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) { | ||
| 123 | LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region"); | ||
| 124 | cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | ResultCode result = RESULT_SUCCESS; | ||
| 129 | |||
| 130 | if (crs_buffer_ptr != crs_address) { | ||
| 131 | // TODO(wwylele): should be memory aliasing | ||
| 132 | std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size); | ||
| 133 | Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size); | ||
| 134 | result = Kernel::g_current_process->vm_manager.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code).Code(); | ||
| 135 | if (result.IsError()) { | ||
| 136 | LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); | ||
| 137 | cmd_buff[1] = result.raw; | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | |||
| 141 | result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read); | ||
| 142 | if (result.IsError()) { | ||
| 143 | LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||
| 144 | cmd_buff[1] = result.raw; | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | |||
| 148 | memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); | ||
| 149 | } else { | ||
| 150 | // Do nothing if buffer_ptr == address | ||
| 151 | // TODO(wwylele): verify this behaviour. This is only seen in the web browser app, | ||
| 152 | // and the actual behaviour is unclear. "Do nothing" is probably an incorrect implement. | ||
| 153 | // There is also a chance that another issue causes the app passing wrong arguments. | ||
| 154 | LOG_WARNING(Service_LDR, "crs_buffer_ptr == crs_address (0x%08X)", crs_address); | ||
| 155 | } | ||
| 156 | |||
| 157 | CROHelper crs(crs_address); | ||
| 158 | crs.InitCRS(); | ||
| 159 | |||
| 160 | result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true); | ||
| 161 | if (result.IsError()) { | ||
| 162 | LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw); | ||
| 163 | cmd_buff[1] = result.raw; | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 167 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 168 | |||
| 169 | loaded_crs = crs_address; | ||
| 170 | |||
| 171 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * LDR_RO::LoadCRR service function | ||
| 176 | * Inputs: | ||
| 177 | * 0 : 0x00020082 | ||
| 178 | * 1 : CRR buffer pointer | ||
| 179 | * 2 : CRR Size | ||
| 180 | * 3 : handle translation descriptor (zero) | ||
| 181 | * 4 : KProcess handle | ||
| 182 | * Outputs: | ||
| 183 | * 0 : Return header | ||
| 184 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 185 | */ | ||
| 186 | static void LoadCRR(Service::Interface* self) { | ||
| 187 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 188 | u32 crr_buffer_ptr = cmd_buff[1]; | ||
| 189 | u32 crr_size = cmd_buff[2]; | ||
| 190 | u32 descriptor = cmd_buff[3]; | ||
| 191 | u32 process = cmd_buff[4]; | ||
| 192 | |||
| 193 | if (descriptor != 0) { | ||
| 194 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 195 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 196 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | |||
| 200 | cmd_buff[0] = IPC::MakeHeader(2, 1, 0); | ||
| 201 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 202 | |||
| 203 | LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 204 | crr_buffer_ptr, crr_size, descriptor, process); | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * LDR_RO::UnloadCRR service function | ||
| 209 | * Inputs: | ||
| 210 | * 0 : 0x00030042 | ||
| 211 | * 1 : CRR buffer pointer | ||
| 212 | * 2 : handle translation descriptor (zero) | ||
| 213 | * 3 : KProcess handle | ||
| 214 | * Outputs: | ||
| 215 | * 0 : Return header | ||
| 216 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 217 | */ | ||
| 218 | static void UnloadCRR(Service::Interface* self) { | ||
| 219 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 220 | u32 crr_buffer_ptr = cmd_buff[1]; | ||
| 221 | u32 descriptor = cmd_buff[2]; | ||
| 222 | u32 process = cmd_buff[3]; | ||
| 223 | |||
| 224 | if (descriptor != 0) { | ||
| 225 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 226 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 227 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | cmd_buff[0] = IPC::MakeHeader(3, 1, 0); | ||
| 232 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 233 | |||
| 234 | LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 235 | crr_buffer_ptr, descriptor, process); | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * LDR_RO::LoadCRO service function | ||
| 240 | * Inputs: | ||
| 241 | * 0 : 0x000402C2 (old) / 0x000902C2 (new) | ||
| 242 | * 1 : CRO buffer pointer | ||
| 243 | * 2 : memory address where the CRO will be mapped | ||
| 244 | * 3 : CRO Size | ||
| 245 | * 4 : .data segment buffer pointer | ||
| 246 | * 5 : must be zero | ||
| 247 | * 6 : .data segment buffer size | ||
| 248 | * 7 : .bss segment buffer pointer | ||
| 249 | * 8 : .bss segment buffer size | ||
| 250 | * 9 : (bool) register CRO as auto-link module | ||
| 251 | * 10 : fix level | ||
| 252 | * 11 : CRR address (zero if use loaded CRR) | ||
| 253 | * 12 : handle translation descriptor (zero) | ||
| 254 | * 13 : KProcess handle | ||
| 255 | * Outputs: | ||
| 256 | * 0 : Return header | ||
| 257 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 258 | * 2 : CRO fixed size | ||
| 259 | * Note: | ||
| 260 | * This service function has two versions. The function defined here is a | ||
| 261 | * unified one of two, with an additional parameter link_on_load_bug_fix. | ||
| 262 | * There is a dispatcher template below. | ||
| 263 | */ | ||
| 264 | static void LoadCRO(Service::Interface* self, bool link_on_load_bug_fix) { | ||
| 265 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 266 | VAddr cro_buffer_ptr = cmd_buff[1]; | ||
| 267 | VAddr cro_address = cmd_buff[2]; | ||
| 268 | u32 cro_size = cmd_buff[3]; | ||
| 269 | VAddr data_segment_address = cmd_buff[4]; | ||
| 270 | u32 zero = cmd_buff[5]; | ||
| 271 | u32 data_segment_size = cmd_buff[6]; | ||
| 272 | u32 bss_segment_address = cmd_buff[7]; | ||
| 273 | u32 bss_segment_size = cmd_buff[8]; | ||
| 274 | bool auto_link = (cmd_buff[9] & 0xFF) != 0; | ||
| 275 | u32 fix_level = cmd_buff[10]; | ||
| 276 | VAddr crr_address = cmd_buff[11]; | ||
| 277 | u32 descriptor = cmd_buff[12]; | ||
| 278 | u32 process = cmd_buff[13]; | ||
| 279 | |||
| 280 | LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, " | ||
| 281 | "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, bss_segment_address=0x%08X, bss_segment_size=0x%X, " | ||
| 282 | "auto_link=%s, fix_level=%d, crr_address=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 283 | link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size, | ||
| 284 | data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size, | ||
| 285 | auto_link ? "true" : "false", fix_level, crr_address, descriptor, process | ||
| 286 | ); | ||
| 287 | |||
| 288 | if (descriptor != 0) { | ||
| 289 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 290 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 291 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | |||
| 295 | cmd_buff[0] = IPC::MakeHeader(link_on_load_bug_fix ? 9 : 4, 2, 0); | ||
| 296 | |||
| 297 | if (loaded_crs == 0) { | ||
| 298 | LOG_ERROR(Service_LDR, "Not initialized"); | ||
| 299 | cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (cro_size < CRO_HEADER_SIZE) { | ||
| 304 | LOG_ERROR(Service_LDR, "CRO too small"); | ||
| 305 | cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; | ||
| 306 | return; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (cro_buffer_ptr & Memory::PAGE_MASK) { | ||
| 310 | LOG_ERROR(Service_LDR, "CRO original address is not aligned"); | ||
| 311 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | |||
| 315 | if (cro_address & Memory::PAGE_MASK) { | ||
| 316 | LOG_ERROR(Service_LDR, "CRO mapping address is not aligned"); | ||
| 317 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (cro_size & Memory::PAGE_MASK) { | ||
| 322 | LOG_ERROR(Service_LDR, "CRO size is not aligned"); | ||
| 323 | cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; | ||
| 324 | return; | ||
| 325 | } | ||
| 326 | |||
| 327 | if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { | ||
| 328 | LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); | ||
| 329 | cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; | ||
| 330 | return; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (cro_address < Memory::PROCESS_IMAGE_VADDR | ||
| 334 | || cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) { | ||
| 335 | LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region"); | ||
| 336 | cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | if (zero) { | ||
| 341 | LOG_ERROR(Service_LDR, "Zero is not zero %d", zero); | ||
| 342 | cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage).raw; | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | |||
| 346 | ResultCode result = RESULT_SUCCESS; | ||
| 347 | |||
| 348 | if (cro_buffer_ptr != cro_address) { | ||
| 349 | // TODO(wwylele): should be memory aliasing | ||
| 350 | std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size); | ||
| 351 | Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size); | ||
| 352 | result = Kernel::g_current_process->vm_manager.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code).Code(); | ||
| 353 | if (result.IsError()) { | ||
| 354 | LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); | ||
| 355 | cmd_buff[1] = result.raw; | ||
| 356 | return; | ||
| 357 | } | ||
| 358 | |||
| 359 | result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read); | ||
| 360 | if (result.IsError()) { | ||
| 361 | LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||
| 362 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||
| 363 | cmd_buff[1] = result.raw; | ||
| 364 | return; | ||
| 365 | } | ||
| 366 | |||
| 367 | memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); | ||
| 368 | } else { | ||
| 369 | // Do nothing if buffer_ptr == address | ||
| 370 | // TODO(wwylele): verify this behaviour. | ||
| 371 | // This is derived from the case of LoadCRS with buffer_ptr==address, | ||
| 372 | // and is never seen in any game. "Do nothing" is probably an incorrect implement. | ||
| 373 | // There is also a chance that this case is just prohibited. | ||
| 374 | LOG_WARNING(Service_LDR, "cro_buffer_ptr == cro_address (0x%08X)", cro_address); | ||
| 375 | } | ||
| 376 | |||
| 377 | CROHelper cro(cro_address); | ||
| 378 | |||
| 379 | result = cro.VerifyHash(cro_size, crr_address); | ||
| 380 | if (result.IsError()) { | ||
| 381 | LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw); | ||
| 382 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||
| 383 | cmd_buff[1] = result.raw; | ||
| 384 | return; | ||
| 385 | } | ||
| 386 | |||
| 387 | result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size, bss_segment_address, bss_segment_size, false); | ||
| 388 | if (result.IsError()) { | ||
| 389 | LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw); | ||
| 390 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||
| 391 | cmd_buff[1] = result.raw; | ||
| 392 | return; | ||
| 393 | } | ||
| 394 | |||
| 395 | result = cro.Link(loaded_crs, link_on_load_bug_fix); | ||
| 396 | if (result.IsError()) { | ||
| 397 | LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); | ||
| 398 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||
| 399 | cmd_buff[1] = result.raw; | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | |||
| 403 | cro.Register(loaded_crs, auto_link); | ||
| 404 | |||
| 405 | u32 fix_size = cro.Fix(fix_level); | ||
| 406 | |||
| 407 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 408 | |||
| 409 | // TODO(wwylele): verify the behaviour when buffer_ptr == address | ||
| 410 | if (cro_buffer_ptr != cro_address) { | ||
| 411 | if (fix_size != cro_size) { | ||
| 412 | result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size); | ||
| 413 | if (result.IsError()) { | ||
| 414 | LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw); | ||
| 415 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); | ||
| 416 | cmd_buff[1] = result.raw; | ||
| 417 | return; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | // Changes the block size | ||
| 422 | memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); | ||
| 423 | } | ||
| 424 | |||
| 425 | VAddr exe_begin; | ||
| 426 | u32 exe_size; | ||
| 427 | std::tie(exe_begin, exe_size) = cro.GetExecutablePages(); | ||
| 428 | if (exe_begin) { | ||
| 429 | result = Kernel::g_current_process->vm_manager.ReprotectRange(exe_begin, exe_size, Kernel::VMAPermission::ReadExecute); | ||
| 430 | if (result.IsError()) { | ||
| 431 | LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); | ||
| 432 | Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size); | ||
| 433 | cmd_buff[1] = result.raw; | ||
| 434 | return; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | Core::g_app_core->ClearInstructionCache(); | ||
| 439 | |||
| 440 | LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", | ||
| 441 | cro.ModuleName().data(), cro_address, cro_address+fix_size); | ||
| 442 | |||
| 443 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 444 | cmd_buff[2] = fix_size; | ||
| 445 | } | ||
| 446 | |||
| 447 | template <bool link_on_load_bug_fix> | ||
| 448 | static void LoadCRO(Service::Interface* self) { | ||
| 449 | LoadCRO(self, link_on_load_bug_fix); | ||
| 450 | } | ||
| 451 | |||
| 452 | /** | ||
| 453 | * LDR_RO::UnloadCRO service function | ||
| 454 | * Inputs: | ||
| 455 | * 0 : 0x000500C2 | ||
| 456 | * 1 : mapped CRO pointer | ||
| 457 | * 2 : zero? (RO service doesn't care) | ||
| 458 | * 3 : original CRO pointer | ||
| 459 | * 4 : handle translation descriptor (zero) | ||
| 460 | * 5 : KProcess handle | ||
| 461 | * Outputs: | ||
| 462 | * 0 : Return header | ||
| 463 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 464 | */ | ||
| 465 | static void UnloadCRO(Service::Interface* self) { | ||
| 466 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 467 | VAddr cro_address = cmd_buff[1]; | ||
| 468 | u32 zero = cmd_buff[2]; | ||
| 469 | VAddr cro_buffer_ptr = cmd_buff[3]; | ||
| 470 | u32 descriptor = cmd_buff[4]; | ||
| 471 | u32 process = cmd_buff[5]; | ||
| 472 | |||
| 473 | LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 474 | cro_address, zero, cro_buffer_ptr, descriptor, process); | ||
| 475 | |||
| 476 | if (descriptor != 0) { | ||
| 477 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 478 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 479 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 480 | return; | ||
| 481 | } | ||
| 482 | |||
| 483 | CROHelper cro(cro_address); | ||
| 484 | |||
| 485 | cmd_buff[0] = IPC::MakeHeader(5, 1, 0); | ||
| 486 | |||
| 487 | if (loaded_crs == 0) { | ||
| 488 | LOG_ERROR(Service_LDR, "Not initialized"); | ||
| 489 | cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||
| 490 | return; | ||
| 491 | } | ||
| 492 | |||
| 493 | if (cro_address & Memory::PAGE_MASK) { | ||
| 494 | LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||
| 495 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 496 | return; | ||
| 497 | } | ||
| 498 | |||
| 499 | if (!cro.IsLoaded()) { | ||
| 500 | LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||
| 501 | cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||
| 502 | return; | ||
| 503 | } | ||
| 504 | |||
| 505 | LOG_INFO(Service_LDR, "Unloading CRO \"%s\"", cro.ModuleName().data()); | ||
| 506 | |||
| 507 | u32 fixed_size = cro.GetFixedSize(); | ||
| 508 | |||
| 509 | cro.Unregister(loaded_crs); | ||
| 510 | |||
| 511 | ResultCode result = cro.Unlink(loaded_crs); | ||
| 512 | if (result.IsError()) { | ||
| 513 | LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||
| 514 | cmd_buff[1] = result.raw; | ||
| 515 | return; | ||
| 516 | } | ||
| 517 | |||
| 518 | // If the module is not fixed, clears all external/internal relocations | ||
| 519 | // to restore the state before loading, so that it can be loaded again(?) | ||
| 520 | if (!cro.IsFixed()) { | ||
| 521 | result = cro.ClearRelocations(); | ||
| 522 | if (result.IsError()) { | ||
| 523 | LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw); | ||
| 524 | cmd_buff[1] = result.raw; | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 529 | cro.Unrebase(false); | ||
| 530 | |||
| 531 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 532 | |||
| 533 | // TODO(wwylele): verify the behaviour when buffer_ptr == address | ||
| 534 | if (cro_address != cro_buffer_ptr) { | ||
| 535 | result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size); | ||
| 536 | if (result.IsError()) { | ||
| 537 | LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw); | ||
| 538 | } | ||
| 539 | memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); | ||
| 540 | } | ||
| 541 | |||
| 542 | Core::g_app_core->ClearInstructionCache(); | ||
| 543 | |||
| 544 | cmd_buff[1] = result.raw; | ||
| 545 | } | ||
| 546 | |||
| 547 | /** | ||
| 548 | * LDR_RO::LinkCRO service function | ||
| 549 | * Inputs: | ||
| 550 | * 0 : 0x00060042 | ||
| 551 | * 1 : mapped CRO pointer | ||
| 552 | * 2 : handle translation descriptor (zero) | ||
| 553 | * 3 : KProcess handle | ||
| 554 | * Outputs: | ||
| 555 | * 0 : Return header | ||
| 556 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 557 | */ | ||
| 558 | static void LinkCRO(Service::Interface* self) { | ||
| 559 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 560 | VAddr cro_address = cmd_buff[1]; | ||
| 561 | u32 descriptor = cmd_buff[2]; | ||
| 562 | u32 process = cmd_buff[3]; | ||
| 563 | |||
| 564 | LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 565 | cro_address, descriptor, process); | ||
| 566 | |||
| 567 | if (descriptor != 0) { | ||
| 568 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 569 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 570 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 571 | return; | ||
| 572 | } | ||
| 573 | |||
| 574 | CROHelper cro(cro_address); | ||
| 575 | |||
| 576 | cmd_buff[0] = IPC::MakeHeader(6, 1, 0); | ||
| 577 | |||
| 578 | if (loaded_crs == 0) { | ||
| 579 | LOG_ERROR(Service_LDR, "Not initialized"); | ||
| 580 | cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||
| 581 | return; | ||
| 582 | } | ||
| 583 | |||
| 584 | if (cro_address & Memory::PAGE_MASK) { | ||
| 585 | LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||
| 586 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 587 | return; | ||
| 588 | } | ||
| 589 | |||
| 590 | if (!cro.IsLoaded()) { | ||
| 591 | LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||
| 592 | cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||
| 593 | return; | ||
| 594 | } | ||
| 595 | |||
| 596 | LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data()); | ||
| 597 | |||
| 598 | ResultCode result = cro.Link(loaded_crs, false); | ||
| 599 | if (result.IsError()) { | ||
| 600 | LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); | ||
| 601 | } | ||
| 602 | |||
| 603 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 604 | Core::g_app_core->ClearInstructionCache(); | ||
| 605 | |||
| 606 | cmd_buff[1] = result.raw; | ||
| 607 | } | ||
| 608 | |||
| 609 | /** | ||
| 610 | * LDR_RO::UnlinkCRO service function | ||
| 611 | * Inputs: | ||
| 612 | * 0 : 0x00070042 | ||
| 613 | * 1 : mapped CRO pointer | ||
| 614 | * 2 : handle translation descriptor (zero) | ||
| 615 | * 3 : KProcess handle | ||
| 616 | * Outputs: | ||
| 617 | * 0 : Return header | ||
| 618 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 619 | */ | ||
| 620 | static void UnlinkCRO(Service::Interface* self) { | ||
| 621 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 622 | VAddr cro_address = cmd_buff[1]; | ||
| 623 | u32 descriptor = cmd_buff[2]; | ||
| 624 | u32 process = cmd_buff[3]; | ||
| 625 | |||
| 626 | LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 627 | cro_address, descriptor, process); | ||
| 628 | |||
| 629 | if (descriptor != 0) { | ||
| 630 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 631 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 632 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 633 | return; | ||
| 634 | } | ||
| 635 | |||
| 636 | CROHelper cro(cro_address); | ||
| 637 | |||
| 638 | cmd_buff[0] = IPC::MakeHeader(7, 1, 0); | ||
| 639 | |||
| 640 | if (loaded_crs == 0) { | ||
| 641 | LOG_ERROR(Service_LDR, "Not initialized"); | ||
| 642 | cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||
| 643 | return; | ||
| 644 | } | ||
| 645 | |||
| 646 | if (cro_address & Memory::PAGE_MASK) { | ||
| 647 | LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||
| 648 | cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||
| 649 | return; | ||
| 650 | } | ||
| 651 | |||
| 652 | if (!cro.IsLoaded()) { | ||
| 653 | LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||
| 654 | cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||
| 655 | return; | ||
| 656 | } | ||
| 657 | |||
| 658 | LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data()); | ||
| 659 | |||
| 660 | ResultCode result = cro.Unlink(loaded_crs); | ||
| 661 | if (result.IsError()) { | ||
| 662 | LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||
| 663 | } | ||
| 664 | |||
| 665 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 666 | Core::g_app_core->ClearInstructionCache(); | ||
| 667 | |||
| 668 | cmd_buff[1] = result.raw; | ||
| 669 | } | ||
| 670 | |||
| 671 | /** | ||
| 672 | * LDR_RO::Shutdown service function | ||
| 673 | * Inputs: | ||
| 674 | * 0 : 0x00080042 | ||
| 675 | * 1 : original CRS buffer pointer | ||
| 676 | * 2 : handle translation descriptor (zero) | ||
| 677 | * 3 : KProcess handle | ||
| 678 | * Outputs: | ||
| 679 | * 0 : Return header | ||
| 680 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 681 | */ | ||
| 682 | static void Shutdown(Service::Interface* self) { | ||
| 683 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 684 | VAddr crs_buffer_ptr = cmd_buff[1]; | ||
| 685 | u32 descriptor = cmd_buff[2]; | ||
| 686 | u32 process = cmd_buff[3]; | ||
| 687 | |||
| 688 | LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||
| 689 | crs_buffer_ptr, descriptor, process); | ||
| 690 | |||
| 691 | if (descriptor != 0) { | ||
| 692 | LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||
| 693 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 694 | cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||
| 695 | return; | ||
| 696 | } | ||
| 697 | |||
| 698 | if (loaded_crs == 0) { | ||
| 699 | LOG_ERROR(Service_LDR, "Not initialized"); | ||
| 700 | cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||
| 701 | return; | ||
| 702 | } | ||
| 703 | |||
| 704 | cmd_buff[0] = IPC::MakeHeader(8, 1, 0); | ||
| 705 | |||
| 706 | CROHelper crs(loaded_crs); | ||
| 707 | crs.Unrebase(true); | ||
| 708 | |||
| 709 | memory_synchronizer.SynchronizeOriginalMemory(); | ||
| 710 | |||
| 711 | ResultCode result = RESULT_SUCCESS; | ||
| 712 | |||
| 713 | // TODO(wwylele): verify the behaviour when buffer_ptr == address | ||
| 714 | if (loaded_crs != crs_buffer_ptr) { | ||
| 715 | result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize()); | ||
| 716 | if (result.IsError()) { | ||
| 717 | LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw); | ||
| 718 | } | ||
| 719 | memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr); | ||
| 720 | } | ||
| 721 | |||
| 722 | loaded_crs = 0; | ||
| 723 | cmd_buff[1] = result.raw; | ||
| 724 | } | ||
| 725 | |||
| 726 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 727 | {0x000100C2, Initialize, "Initialize"}, | ||
| 728 | {0x00020082, LoadCRR, "LoadCRR"}, | ||
| 729 | {0x00030042, UnloadCRR, "UnloadCRR"}, | ||
| 730 | {0x000402C2, LoadCRO<false>, "LoadCRO"}, | ||
| 731 | {0x000500C2, UnloadCRO, "UnloadCRO"}, | ||
| 732 | {0x00060042, LinkCRO, "LinkCRO"}, | ||
| 733 | {0x00070042, UnlinkCRO, "UnlinkCRO"}, | ||
| 734 | {0x00080042, Shutdown, "Shutdown"}, | ||
| 735 | {0x000902C2, LoadCRO<true>, "LoadCRO_New"}, | ||
| 736 | }; | ||
| 737 | |||
| 738 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 739 | // Interface class | ||
| 740 | |||
| 741 | Interface::Interface() { | ||
| 742 | Register(FunctionTable); | ||
| 743 | |||
| 744 | loaded_crs = 0; | ||
| 745 | memory_synchronizer.Clear(); | ||
| 746 | } | ||
| 747 | |||
| 748 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h index 331637cde..331637cde 100644 --- a/src/core/hle/service/ldr_ro.h +++ b/src/core/hle/service/ldr_ro/ldr_ro.h | |||
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp new file mode 100644 index 000000000..4402876e6 --- /dev/null +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | |||
| 9 | #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // Namespace LDR_RO | ||
| 13 | |||
| 14 | namespace LDR_RO { | ||
| 15 | |||
| 16 | auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) { | ||
| 17 | auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(), [=](MemoryBlock& b){ | ||
| 18 | return b.original == original; | ||
| 19 | }); | ||
| 20 | ASSERT(block->mapping == mapping); | ||
| 21 | return block; | ||
| 22 | } | ||
| 23 | |||
| 24 | void MemorySynchronizer::Clear() { | ||
| 25 | memory_blocks.clear(); | ||
| 26 | } | ||
| 27 | |||
| 28 | void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) { | ||
| 29 | memory_blocks.push_back(MemoryBlock{mapping, original, size}); | ||
| 30 | } | ||
| 31 | |||
| 32 | void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) { | ||
| 33 | FindMemoryBlock(mapping, original)->size = size; | ||
| 34 | } | ||
| 35 | |||
| 36 | void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) { | ||
| 37 | memory_blocks.erase(FindMemoryBlock(mapping, original)); | ||
| 38 | } | ||
| 39 | |||
| 40 | void MemorySynchronizer::SynchronizeOriginalMemory() { | ||
| 41 | for (auto& block : memory_blocks) { | ||
| 42 | Memory::CopyBlock(block.original, block.mapping, block.size); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | } // namespace | ||
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h new file mode 100644 index 000000000..92f267912 --- /dev/null +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "core/memory.h" | ||
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // Namespace LDR_RO | ||
| 13 | |||
| 14 | namespace LDR_RO { | ||
| 15 | |||
| 16 | /** | ||
| 17 | * This is a work-around before we implement memory aliasing. | ||
| 18 | * CRS and CRO are mapped (aliased) to another memory when loading. Games can read | ||
| 19 | * from both the original buffer and the mapping memory. So we use this to synchronize | ||
| 20 | * all original buffers with mapping memory after modifying the content. | ||
| 21 | */ | ||
| 22 | class MemorySynchronizer { | ||
| 23 | public: | ||
| 24 | void Clear(); | ||
| 25 | |||
| 26 | void AddMemoryBlock(VAddr mapping, VAddr original, u32 size); | ||
| 27 | void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size); | ||
| 28 | void RemoveMemoryBlock(VAddr mapping, VAddr original); | ||
| 29 | |||
| 30 | void SynchronizeOriginalMemory(); | ||
| 31 | |||
| 32 | private: | ||
| 33 | struct MemoryBlock { | ||
| 34 | VAddr mapping; | ||
| 35 | VAddr original; | ||
| 36 | u32 size; | ||
| 37 | }; | ||
| 38 | |||
| 39 | std::vector<MemoryBlock> memory_blocks; | ||
| 40 | |||
| 41 | auto FindMemoryBlock(VAddr mapping, VAddr original); | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // namespace | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 395880843..5b8440b77 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -15,7 +15,6 @@ | |||
| 15 | #include "core/hle/service/gsp_gpu.h" | 15 | #include "core/hle/service/gsp_gpu.h" |
| 16 | #include "core/hle/service/gsp_lcd.h" | 16 | #include "core/hle/service/gsp_lcd.h" |
| 17 | #include "core/hle/service/http_c.h" | 17 | #include "core/hle/service/http_c.h" |
| 18 | #include "core/hle/service/ldr_ro.h" | ||
| 19 | #include "core/hle/service/mic_u.h" | 18 | #include "core/hle/service/mic_u.h" |
| 20 | #include "core/hle/service/ns_s.h" | 19 | #include "core/hle/service/ns_s.h" |
| 21 | #include "core/hle/service/nwm_uds.h" | 20 | #include "core/hle/service/nwm_uds.h" |
| @@ -36,6 +35,7 @@ | |||
| 36 | #include "core/hle/service/cfg/cfg.h" | 35 | #include "core/hle/service/cfg/cfg.h" |
| 37 | #include "core/hle/service/hid/hid.h" | 36 | #include "core/hle/service/hid/hid.h" |
| 38 | #include "core/hle/service/ir/ir.h" | 37 | #include "core/hle/service/ir/ir.h" |
| 38 | #include "core/hle/service/ldr_ro/ldr_ro.h" | ||
| 39 | #include "core/hle/service/ndm/ndm.h" | 39 | #include "core/hle/service/ndm/ndm.h" |
| 40 | #include "core/hle/service/news/news.h" | 40 | #include "core/hle/service/news/news.h" |
| 41 | #include "core/hle/service/nim/nim.h" | 41 | #include "core/hle/service/nim/nim.h" |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8c9e5d46d..9aa8c4e5a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -280,6 +280,20 @@ u8* GetPointer(const VAddr vaddr) { | |||
| 280 | return nullptr; | 280 | return nullptr; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { | ||
| 284 | std::string string; | ||
| 285 | string.reserve(max_length); | ||
| 286 | for (std::size_t i = 0; i < max_length; ++i) { | ||
| 287 | char c = Read8(vaddr); | ||
| 288 | if (c == '\0') | ||
| 289 | break; | ||
| 290 | string.push_back(c); | ||
| 291 | ++vaddr; | ||
| 292 | } | ||
| 293 | string.shrink_to_fit(); | ||
| 294 | return string; | ||
| 295 | } | ||
| 296 | |||
| 283 | u8* GetPhysicalPointer(PAddr address) { | 297 | u8* GetPhysicalPointer(PAddr address) { |
| 284 | // TODO(Subv): This call should not go through the application's memory mapping. | 298 | // TODO(Subv): This call should not go through the application's memory mapping. |
| 285 | return GetPointer(PhysicalToVirtualAddress(address)); | 299 | return GetPointer(PhysicalToVirtualAddress(address)); |
diff --git a/src/core/memory.h b/src/core/memory.h index ae5588dee..cad845385 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| @@ -130,6 +131,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); | |||
| 130 | 131 | ||
| 131 | u8* GetPointer(VAddr virtual_address); | 132 | u8* GetPointer(VAddr virtual_address); |
| 132 | 133 | ||
| 134 | std::string ReadCString(VAddr virtual_address, std::size_t max_length); | ||
| 135 | |||
| 133 | /** | 136 | /** |
| 134 | * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical | 137 | * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical |
| 135 | * address. This should be used by services to translate addresses for use by the hardware. | 138 | * address. This should be used by services to translate addresses for use by the hardware. |