summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2016-08-28 22:21:24 -0700
committerGravatar GitHub2016-08-28 22:21:24 -0700
commit474586bc53eb6fda40fb0db23ea1d50d32af28b6 (patch)
tree49df9979f3351f3eac26d8a74e25c5181ba35e6c /src
parentMerge pull request #1987 from Lectem/ipcdescriptors (diff)
parentLDR: Implement CRO (diff)
downloadyuzu-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.txt8
-rw-r--r--src/core/arm/arm_interface.h3
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp6
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h2
-rw-r--r--src/core/hle/service/ldr_ro.cpp96
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.cpp1477
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h691
-rw-r--r--src/core/hle/service/ldr_ro/ldr_ro.cpp748
-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.cpp46
-rw-r--r--src/core/hle/service/ldr_ro/memory_synchronizer.h44
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/memory.h3
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) {
23ARM_DynCom::~ARM_DynCom() { 24ARM_DynCom::~ARM_DynCom() {
24} 25}
25 26
27void ARM_DynCom::ClearInstructionCache() {
28 state->instruction_cache.clear();
29 trans_cache_buf_top = 0;
30}
31
26void ARM_DynCom::SetPC(u32 pc) { 32void 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
13namespace 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 */
27static 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 */
58static 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
77const 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
92Interface::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
14namespace LDR_RO {
15
16static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
17 ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
18
19static ResultCode CROFormatError(u32 description) {
20 return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
21}
22
23const 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
43const std::array<CROHelper::HeaderField, 4> CROHelper::FIX_BARRIERS {{
44 Fix0Barrier,
45 Fix1Barrier,
46 Fix2Barrier,
47 Fix3Barrier
48}};
49
50VAddr 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
65ResultCode 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
91ResultCode 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
113ResultCode 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
146VAddr 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
192ResultCode 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
269ResultVal<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
301ResultCode 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
323ResultCode 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
336ResultCode 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
378ResultCode 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
410ResultCode 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
432ResultCode 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
454VAddr CROHelper::GetOnUnresolvedAddress() {
455 return SegmentTagToAddress(SegmentTag(GetField(OnUnresolvedSegmentTag)));
456}
457
458ResultCode 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
497ResultCode 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
529ResultCode 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
557ResultCode 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
595ResultCode 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
615void 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
629void 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
643void 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
661void 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
683void 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
697void 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
713void 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
726ResultCode 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
764ResultCode 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
785ResultCode 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
805ResultCode 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
825ResultCode 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
875ResultCode 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
903ResultCode 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
932ResultCode 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
977ResultCode 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
1018ResultCode 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 */
1065static 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
1073ResultCode 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
1180void 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
1198ResultCode CROHelper::VerifyHash(u32 cro_size, VAddr crr) const {
1199 // TODO(wwylele): actually verify the hash
1200 return RESULT_SUCCESS;
1201}
1202
1203ResultCode 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
1276ResultCode 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
1320ResultCode 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
1335void CROHelper::InitCRS() {
1336 SetNextModule(0);
1337 SetPreviousModule(0);
1338}
1339
1340void 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
1372void 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
1415u32 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
1434u32 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
1453bool 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
1463std::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
19namespace 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
34static constexpr u32 CRO_HEADER_SIZE = 0x138;
35static constexpr u32 CRO_HASH_SIZE = 0x80;
36
37/// Represents a loaded module (CRO) with interfaces manipulating it.
38class CROHelper final {
39public:
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
147private:
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
19namespace LDR_RO {
20
21static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9
22 ResultCode(ErrorDescription::AlreadyInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent);
23static const ResultCode ERROR_NOT_INITIALIZED = // 0xD9612FF8
24 ResultCode(ErrorDescription::NotInitialized, ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Permanent);
25static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
26 ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
27static const ResultCode ERROR_MISALIGNED_ADDRESS = // 0xD9012FF1
28 ResultCode(ErrorDescription::MisalignedAddress, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
29static const ResultCode ERROR_MISALIGNED_SIZE = // 0xD9012FF2
30 ResultCode(ErrorDescription::MisalignedSize, ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
31static const ResultCode ERROR_ILLEGAL_ADDRESS = // 0xE1612C0F
32 ResultCode(static_cast<ErrorDescription>(15), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage);
33static const ResultCode ERROR_INVALID_MEMORY_STATE = // 0xD8A12C08
34 ResultCode(static_cast<ErrorDescription>(8), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent);
35static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D
36 ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent);
37static const ResultCode ERROR_INVALID_DESCRIPTOR = // 0xD9001830
38 ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
39
40static MemorySynchronizer memory_synchronizer;
41
42// TODO(wwylele): this should be in the per-client storage when we implement multi-process
43static VAddr loaded_crs; ///< the virtual address of the static module
44
45static 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 */
66static 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 */
186static 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 */
218static 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 */
264static 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
447template <bool link_on_load_bug_fix>
448static 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 */
465static 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 */
558static 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 */
620static 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 */
682static 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
726const 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
741Interface::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
14namespace LDR_RO {
15
16auto 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
24void MemorySynchronizer::Clear() {
25 memory_blocks.clear();
26}
27
28void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) {
29 memory_blocks.push_back(MemoryBlock{mapping, original, size});
30}
31
32void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) {
33 FindMemoryBlock(mapping, original)->size = size;
34}
35
36void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) {
37 memory_blocks.erase(FindMemoryBlock(mapping, original));
38}
39
40void 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
14namespace 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 */
22class MemorySynchronizer {
23public:
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
32private:
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
283std::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
283u8* GetPhysicalPointer(PAddr address) { 297u8* 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
131u8* GetPointer(VAddr virtual_address); 132u8* GetPointer(VAddr virtual_address);
132 133
134std::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.