summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/ldr_ro/cro_helper.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.cpp1477
1 files changed, 1477 insertions, 0 deletions
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/alignment.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+
+#include "core/hle/service/ldr_ro/cro_helper.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
+ ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+
+static ResultCode CROFormatError(u32 description) {
+ return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+}
+
+const std::array<int, 17> CROHelper::ENTRY_SIZE {{
+ 1, // code
+ 1, // data
+ 1, // module name
+ sizeof(SegmentEntry),
+ sizeof(ExportNamedSymbolEntry),
+ sizeof(ExportIndexedSymbolEntry),
+ 1, // export strings
+ sizeof(ExportTreeEntry),
+ sizeof(ImportModuleEntry),
+ sizeof(ExternalRelocationEntry),
+ sizeof(ImportNamedSymbolEntry),
+ sizeof(ImportIndexedSymbolEntry),
+ sizeof(ImportAnonymousSymbolEntry),
+ 1, // import strings
+ sizeof(StaticAnonymousSymbolEntry),
+ sizeof(InternalRelocationEntry),
+ sizeof(StaticRelocationEntry)
+}};
+
+const std::array<CROHelper::HeaderField, 4> CROHelper::FIX_BARRIERS {{
+ Fix0Barrier,
+ Fix1Barrier,
+ Fix2Barrier,
+ Fix3Barrier
+}};
+
+VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const {
+ u32 segment_num = GetField(SegmentNum);
+
+ if (segment_tag.segment_index >= segment_num)
+ return 0;
+
+ SegmentEntry entry;
+ GetEntry(segment_tag.segment_index, entry);
+
+ if (segment_tag.offset_into_segment >= entry.size)
+ return 0;
+
+ return entry.offset + segment_tag.offset_into_segment;
+}
+
+ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType relocation_type,
+ u32 addend, u32 symbol_address, u32 target_future_address) {
+
+ switch (relocation_type) {
+ case RelocationType::Nothing:
+ break;
+ case RelocationType::AbsoluteAddress:
+ case RelocationType::AbsoluteAddress2:
+ Memory::Write32(target_address, symbol_address + addend);
+ break;
+ case RelocationType::RelativeAddress:
+ Memory::Write32(target_address, symbol_address + addend - target_future_address);
+ break;
+ case RelocationType::ThumbBranch:
+ case RelocationType::ArmBranch:
+ case RelocationType::ModifyArmBranch:
+ case RelocationType::AlignedRelativeAddress:
+ // TODO(wwylele): implement other types
+ UNIMPLEMENTED();
+ break;
+ default:
+ return CROFormatError(0x22);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType relocation_type) {
+ switch (relocation_type) {
+ case RelocationType::Nothing:
+ break;
+ case RelocationType::AbsoluteAddress:
+ case RelocationType::AbsoluteAddress2:
+ case RelocationType::RelativeAddress:
+ Memory::Write32(target_address, 0);
+ break;
+ case RelocationType::ThumbBranch:
+ case RelocationType::ArmBranch:
+ case RelocationType::ModifyArmBranch:
+ case RelocationType::AlignedRelativeAddress:
+ // TODO(wwylele): implement other types
+ UNIMPLEMENTED();
+ break;
+ default:
+ return CROFormatError(0x22);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset) {
+ if (symbol_address == 0 && !reset)
+ return CROFormatError(0x10);
+
+ VAddr relocation_address = batch;
+ while (true) {
+ RelocationEntry relocation;
+ Memory::ReadBlock(relocation_address, &relocation, sizeof(RelocationEntry));
+
+ VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
+ if (relocation_target == 0) {
+ return CROFormatError(0x12);
+ }
+
+ ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, symbol_address, relocation_target);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
+ return result;
+ }
+
+ if (relocation.is_batch_end)
+ break;
+
+ relocation_address += sizeof(RelocationEntry);
+ }
+
+ RelocationEntry relocation;
+ Memory::ReadBlock(batch, &relocation, sizeof(RelocationEntry));
+ relocation.is_batch_resolved = reset ? 0 : 1;
+ Memory::WriteBlock(batch, &relocation, sizeof(RelocationEntry));
+ return RESULT_SUCCESS;
+}
+
+VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const {
+ if (!GetField(ExportTreeNum))
+ return 0;
+
+ std::size_t len = name.size();
+ ExportTreeEntry entry;
+ GetEntry(0, entry);
+ ExportTreeEntry::Child next;
+ next.raw = entry.left.raw;
+ u32 found_id;
+
+ while (true) {
+ GetEntry(next.next_index, entry);
+
+ if (next.is_end) {
+ found_id = entry.export_table_index;
+ break;
+ }
+
+ u16 test_byte = entry.test_bit >> 3;
+ u16 test_bit_in_byte = entry.test_bit & 7;
+
+ if (test_byte >= len) {
+ next.raw = entry.left.raw;
+ } else if((name[test_byte] >> test_bit_in_byte) & 1) {
+ next.raw = entry.right.raw;
+ } else {
+ next.raw = entry.left.raw;
+ }
+ }
+
+ u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
+
+ if (found_id >= export_named_symbol_num)
+ return 0;
+
+ u32 export_strings_size = GetField(ExportStringsSize);
+ ExportNamedSymbolEntry symbol_entry;
+ GetEntry(found_id, symbol_entry);
+
+ if (Memory::ReadCString(symbol_entry.name_offset, export_strings_size) != name)
+ return 0;
+
+ return SegmentTagToAddress(symbol_entry.symbol_position);
+}
+
+ResultCode CROHelper::RebaseHeader(u32 cro_size) {
+ ResultCode error = CROFormatError(0x11);
+
+ // verifies magic
+ if (GetField(Magic) != MAGIC_CRO0)
+ return error;
+
+ // verifies not registered
+ if (GetField(NextCRO) != 0 || GetField(PreviousCRO) != 0)
+ return error;
+
+ // This seems to be a hard limit set by the RO module
+ if (GetField(FileSize) > 0x10000000 || GetField(BssSize) > 0x10000000)
+ return error;
+
+ // verifies not fixed
+ if (GetField(FixedSize) != 0)
+ return error;
+
+ if (GetField(CodeOffset) < CRO_HEADER_SIZE)
+ return error;
+
+ // verifies that all offsets are in the correct order
+ constexpr std::array<HeaderField, 18> OFFSET_ORDER = {{
+ CodeOffset,
+ ModuleNameOffset,
+ SegmentTableOffset,
+ ExportNamedSymbolTableOffset,
+ ExportTreeTableOffset,
+ ExportIndexedSymbolTableOffset,
+ ExportStringsOffset,
+ ImportModuleTableOffset,
+ ExternalRelocationTableOffset,
+ ImportNamedSymbolTableOffset,
+ ImportIndexedSymbolTableOffset,
+ ImportAnonymousSymbolTableOffset,
+ ImportStringsOffset,
+ StaticAnonymousSymbolTableOffset,
+ InternalRelocationTableOffset,
+ StaticRelocationTableOffset,
+ DataOffset,
+ FileSize
+ }};
+
+ u32 prev_offset = GetField(OFFSET_ORDER[0]);
+ u32 cur_offset;
+ for (std::size_t i = 1; i < OFFSET_ORDER.size(); ++i) {
+ cur_offset = GetField(OFFSET_ORDER[i]);
+ if (cur_offset < prev_offset)
+ return error;
+ prev_offset = cur_offset;
+ }
+
+ // rebases offsets
+ u32 offset = GetField(NameOffset);
+ if (offset != 0)
+ SetField(NameOffset, offset + module_address);
+
+ for (int field = CodeOffset; field < Fix0Barrier; field += 2) {
+ HeaderField header_field = static_cast<HeaderField>(field);
+ offset = GetField(header_field);
+ if (offset != 0)
+ SetField(header_field, offset + module_address);
+ }
+
+ // verifies everything is not beyond the buffer
+ u32 file_end = module_address + cro_size;
+ for (int field = CodeOffset, i = 0; field < Fix0Barrier; field += 2, ++i) {
+ HeaderField offset_field = static_cast<HeaderField>(field);
+ HeaderField size_field = static_cast<HeaderField>(field + 1);
+ if (GetField(offset_field) + GetField(size_field) * ENTRY_SIZE[i] > file_end)
+ return error;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size,
+ VAddr data_segment_address, u32 data_segment_size,
+ VAddr bss_segment_address, u32 bss_segment_size) {
+
+ u32 prev_data_segment = 0;
+ u32 segment_num = GetField(SegmentNum);
+ for (u32 i = 0; i < segment_num; ++i) {
+ SegmentEntry segment;
+ GetEntry(i, segment);
+ if (segment.type == SegmentType::Data) {
+ if (segment.size != 0) {
+ if (segment.size > data_segment_size)
+ return ERROR_BUFFER_TOO_SMALL;
+ prev_data_segment = segment.offset;
+ segment.offset = data_segment_address;
+ }
+ } else if (segment.type == SegmentType::BSS) {
+ if (segment.size != 0) {
+ if (segment.size > bss_segment_size)
+ return ERROR_BUFFER_TOO_SMALL;
+ segment.offset = bss_segment_address;
+ }
+ } else if (segment.offset != 0) {
+ segment.offset += module_address;
+ if (segment.offset > module_address + cro_size)
+ return CROFormatError(0x19);
+ }
+ SetEntry(i, segment);
+ }
+ return MakeResult<u32>(prev_data_segment + module_address);
+}
+
+ResultCode CROHelper::RebaseExportNamedSymbolTable() {
+ VAddr export_strings_offset = GetField(ExportStringsOffset);
+ VAddr export_strings_end = export_strings_offset + GetField(ExportStringsSize);
+
+ u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
+ for (u32 i = 0; i < export_named_symbol_num; ++i) {
+ ExportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset += module_address;
+ if (entry.name_offset < export_strings_offset
+ || entry.name_offset >= export_strings_end) {
+ return CROFormatError(0x11);
+ }
+ }
+
+ SetEntry(i, entry);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::VerifyExportTreeTable() const {
+ u32 tree_num = GetField(ExportTreeNum);
+ for (u32 i = 0; i < tree_num; ++i) {
+ ExportTreeEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) {
+ return CROFormatError(0x11);
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::RebaseImportModuleTable() {
+ VAddr import_strings_offset = GetField(ImportStringsOffset);
+ VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize);
+ VAddr import_indexed_symbol_table_offset = GetField(ImportIndexedSymbolTableOffset);
+ VAddr index_import_table_end = import_indexed_symbol_table_offset + GetField(ImportIndexedSymbolNum) * sizeof(ImportIndexedSymbolEntry);
+ VAddr import_anonymous_symbol_table_offset = GetField(ImportAnonymousSymbolTableOffset);
+ VAddr offset_import_table_end = import_anonymous_symbol_table_offset + GetField(ImportAnonymousSymbolNum) * sizeof(ImportAnonymousSymbolEntry);
+
+ u32 module_num = GetField(ImportModuleNum);
+ for (u32 i = 0; i < module_num; ++i) {
+ ImportModuleEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset += module_address;
+ if (entry.name_offset < import_strings_offset
+ || entry.name_offset >= import_strings_end) {
+ return CROFormatError(0x18);
+ }
+ }
+
+ if (entry.import_indexed_symbol_table_offset != 0) {
+ entry.import_indexed_symbol_table_offset += module_address;
+ if (entry.import_indexed_symbol_table_offset < import_indexed_symbol_table_offset
+ || entry.import_indexed_symbol_table_offset > index_import_table_end) {
+ return CROFormatError(0x18);
+ }
+ }
+
+ if (entry.import_anonymous_symbol_table_offset != 0) {
+ entry.import_anonymous_symbol_table_offset += module_address;
+ if (entry.import_anonymous_symbol_table_offset < import_anonymous_symbol_table_offset
+ || entry.import_anonymous_symbol_table_offset > offset_import_table_end) {
+ return CROFormatError(0x18);
+ }
+ }
+
+ SetEntry(i, entry);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::RebaseImportNamedSymbolTable() {
+ VAddr import_strings_offset = GetField(ImportStringsOffset);
+ VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize);
+ VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
+ VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
+
+ u32 num = GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < num ; ++i) {
+ ImportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset += module_address;
+ if (entry.name_offset < import_strings_offset
+ || entry.name_offset >= import_strings_end) {
+ return CROFormatError(0x1B);
+ }
+ }
+
+ if (entry.relocation_batch_offset != 0) {
+ entry.relocation_batch_offset += module_address;
+ if (entry.relocation_batch_offset < external_relocation_table_offset
+ || entry.relocation_batch_offset > external_relocation_table_end) {
+ return CROFormatError(0x1B);
+ }
+ }
+
+ SetEntry(i, entry);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::RebaseImportIndexedSymbolTable() {
+ VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
+ VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
+
+ u32 num = GetField(ImportIndexedSymbolNum);
+ for (u32 i = 0; i < num ; ++i) {
+ ImportIndexedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.relocation_batch_offset != 0) {
+ entry.relocation_batch_offset += module_address;
+ if (entry.relocation_batch_offset < external_relocation_table_offset
+ || entry.relocation_batch_offset > external_relocation_table_end) {
+ return CROFormatError(0x14);
+ }
+ }
+
+ SetEntry(i, entry);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::RebaseImportAnonymousSymbolTable() {
+ VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
+ VAddr external_relocation_table_end = external_relocation_table_offset + GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
+
+ u32 num = GetField(ImportAnonymousSymbolNum);
+ for (u32 i = 0; i < num ; ++i) {
+ ImportAnonymousSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.relocation_batch_offset != 0) {
+ entry.relocation_batch_offset += module_address;
+ if (entry.relocation_batch_offset < external_relocation_table_offset
+ || entry.relocation_batch_offset > external_relocation_table_end) {
+ return CROFormatError(0x17);
+ }
+ }
+
+ SetEntry(i, entry);
+ }
+ return RESULT_SUCCESS;
+}
+
+VAddr CROHelper::GetOnUnresolvedAddress() {
+ return SegmentTagToAddress(SegmentTag(GetField(OnUnresolvedSegmentTag)));
+}
+
+ResultCode CROHelper::ResetExternalRelocations() {
+ u32 unresolved_symbol = GetOnUnresolvedAddress();
+ u32 external_relocation_num = GetField(ExternalRelocationNum);
+ ExternalRelocationEntry relocation;
+
+ // Verifies that the last relocation is the end of a batch
+ GetEntry(external_relocation_num - 1, relocation);
+ if (!relocation.is_batch_end) {
+ return CROFormatError(0x12);
+ }
+
+ bool batch_begin = true;
+ for (u32 i = 0; i < external_relocation_num; ++i) {
+ GetEntry(i, relocation);
+ VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
+
+ if (relocation_target == 0) {
+ return CROFormatError(0x12);
+ }
+
+ ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend, unresolved_symbol, relocation_target);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
+ return result;
+ }
+
+ if (batch_begin) {
+ // resets to unresolved state
+ relocation.is_batch_resolved = 0;
+ SetEntry(i, relocation);
+ }
+
+ // if current is an end, then the next is a beginning
+ batch_begin = relocation.is_batch_end != 0;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ClearExternalRelocations() {
+ u32 external_relocation_num = GetField(ExternalRelocationNum);
+ ExternalRelocationEntry relocation;
+
+ bool batch_begin = true;
+ for (u32 i = 0; i < external_relocation_num; ++i) {
+ GetEntry(i, relocation);
+ VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
+
+ if (relocation_target == 0) {
+ return CROFormatError(0x12);
+ }
+
+ ResultCode result = ClearRelocation(relocation_target, relocation.type);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw);
+ return result;
+ }
+
+ if (batch_begin) {
+ // resets to unresolved state
+ relocation.is_batch_resolved = 0;
+ SetEntry(i, relocation);
+ }
+
+ // if current is an end, then the next is a beginning
+ batch_begin = relocation.is_batch_end != 0;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) {
+ VAddr static_relocation_table_offset = GetField(StaticRelocationTableOffset);
+ VAddr static_relocation_table_end = static_relocation_table_offset + GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry);
+
+ CROHelper crs(crs_address);
+ u32 offset_export_num = GetField(StaticAnonymousSymbolNum);
+ LOG_INFO(Service_LDR, "CRO \"%s\" exports %d static anonymous symbols", ModuleName().data(), offset_export_num);
+ for (u32 i = 0; i < offset_export_num; ++i) {
+ StaticAnonymousSymbolEntry entry;
+ GetEntry(i, entry);
+ u32 batch_address = entry.relocation_batch_offset + module_address;
+
+ if (batch_address < static_relocation_table_offset
+ || batch_address > static_relocation_table_end) {
+ return CROFormatError(0x16);
+ }
+
+ u32 symbol_address = SegmentTagToAddress(entry.symbol_position);
+ LOG_TRACE(Service_LDR, "CRO \"%s\" exports 0x%08X to the static module", ModuleName().data(), symbol_address);
+ ResultCode result = crs.ApplyRelocationBatch(batch_address, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) {
+ u32 segment_num = GetField(SegmentNum);
+ u32 internal_relocation_num = GetField(InternalRelocationNum);
+ for (u32 i = 0; i < internal_relocation_num; ++i) {
+ InternalRelocationEntry relocation;
+ GetEntry(i, relocation);
+ VAddr target_addressB = SegmentTagToAddress(relocation.target_position);
+ if (target_addressB == 0) {
+ return CROFormatError(0x15);
+ }
+
+ VAddr target_address;
+ SegmentEntry target_segment;
+ GetEntry(relocation.target_position.segment_index, target_segment);
+
+ if (target_segment.type == SegmentType::Data) {
+ // If the relocation is to the .data segment, we need to relocate it in the old buffer
+ target_address = old_data_segment_address + relocation.target_position.offset_into_segment;
+ } else {
+ target_address = target_addressB;
+ }
+
+ if (relocation.symbol_segment >= segment_num) {
+ return CROFormatError(0x15);
+ }
+
+ SegmentEntry symbol_segment;
+ GetEntry(relocation.symbol_segment, symbol_segment);
+ LOG_TRACE(Service_LDR, "Internally relocates 0x%08X with 0x%08X", target_address, symbol_segment.offset);
+ ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend, symbol_segment.offset, target_addressB);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ClearInternalRelocations() {
+ u32 internal_relocation_num = GetField(InternalRelocationNum);
+ for (u32 i = 0; i < internal_relocation_num; ++i) {
+ InternalRelocationEntry relocation;
+ GetEntry(i, relocation);
+ VAddr target_address = SegmentTagToAddress(relocation.target_position);
+
+ if (target_address == 0) {
+ return CROFormatError(0x15);
+ }
+
+ ResultCode result = ClearRelocation(target_address, relocation.type);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw);
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+void CROHelper::UnrebaseImportAnonymousSymbolTable() {
+ u32 num = GetField(ImportAnonymousSymbolNum);
+ for (u32 i = 0; i < num; ++i) {
+ ImportAnonymousSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.relocation_batch_offset != 0) {
+ entry.relocation_batch_offset -= module_address;
+ }
+
+ SetEntry(i, entry);
+ }
+}
+
+void CROHelper::UnrebaseImportIndexedSymbolTable() {
+ u32 num = GetField(ImportIndexedSymbolNum);
+ for (u32 i = 0; i < num; ++i) {
+ ImportIndexedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.relocation_batch_offset != 0) {
+ entry.relocation_batch_offset -= module_address;
+ }
+
+ SetEntry(i, entry);
+ }
+}
+
+void CROHelper::UnrebaseImportNamedSymbolTable() {
+ u32 num = GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < num; ++i) {
+ ImportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset -= module_address;
+ }
+
+ if (entry.relocation_batch_offset) {
+ entry.relocation_batch_offset -= module_address;
+ }
+
+ SetEntry(i, entry);
+ }
+}
+
+void CROHelper::UnrebaseImportModuleTable() {
+ u32 module_num = GetField(ImportModuleNum);
+ for (u32 i = 0; i < module_num; ++i) {
+ ImportModuleEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset -= module_address;
+ }
+
+ if (entry.import_indexed_symbol_table_offset) {
+ entry.import_indexed_symbol_table_offset -= module_address;
+ }
+
+ if (entry.import_anonymous_symbol_table_offset) {
+ entry.import_anonymous_symbol_table_offset -= module_address;
+ }
+
+ SetEntry(i, entry);
+ }
+}
+
+void CROHelper::UnrebaseExportNamedSymbolTable() {
+ u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
+ for (u32 i = 0; i < export_named_symbol_num; ++i) {
+ ExportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+
+ if (entry.name_offset != 0) {
+ entry.name_offset -= module_address;
+ }
+
+ SetEntry(i, entry);
+ }
+}
+
+void CROHelper::UnrebaseSegmentTable() {
+ u32 segment_num = GetField(SegmentNum);
+ for (u32 i = 0; i < segment_num; ++i) {
+ SegmentEntry segment;
+ GetEntry(i, segment);
+
+ if (segment.type == SegmentType::BSS) {
+ segment.offset = 0;
+ } else if (segment.offset != 0) {
+ segment.offset -= module_address;
+ }
+
+ SetEntry(i, segment);
+ }
+}
+
+void CROHelper::UnrebaseHeader() {
+ u32 offset = GetField(NameOffset);
+ if (offset != 0)
+ SetField(NameOffset, offset - module_address);
+
+ for (int field = CodeOffset; field < Fix0Barrier; field += 2) {
+ HeaderField header_field = static_cast<HeaderField>(field);
+ offset = GetField(header_field);
+ if (offset != 0)
+ SetField(header_field, offset - module_address);
+ }
+}
+
+ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) {
+ u32 import_strings_size = GetField(ImportStringsSize);
+ u32 symbol_import_num = GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < symbol_import_num; ++i) {
+ ImportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ if (!relocation_entry.is_batch_resolved) {
+ ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
+ std::string symbol_name = Memory::ReadCString(entry.name_offset, import_strings_size);
+ u32 symbol_address = source.FindExportNamedSymbol(symbol_name);
+
+ if (symbol_address != 0) {
+ LOG_TRACE(Service_LDR, "CRO \"%s\" imports \"%s\" from \"%s\"",
+ ModuleName().data(), symbol_name.data(), source.ModuleName().data());
+
+ ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+
+ return MakeResult<bool>(false);
+ }
+
+ return MakeResult<bool>(true);
+ });
+ if (result.IsError()) {
+ return result;
+ }
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ResetImportNamedSymbol() {
+ u32 unresolved_symbol = GetOnUnresolvedAddress();
+
+ u32 symbol_import_num = GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < symbol_import_num; ++i) {
+ ImportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
+ return result;
+ }
+
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ResetImportIndexedSymbol() {
+ u32 unresolved_symbol = GetOnUnresolvedAddress();
+
+ u32 import_num = GetField(ImportIndexedSymbolNum);
+ for (u32 i = 0; i < import_num; ++i) {
+ ImportIndexedSymbolEntry entry;
+ GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ResetImportAnonymousSymbol() {
+ u32 unresolved_symbol = GetOnUnresolvedAddress();
+
+ u32 import_num = GetField(ImportAnonymousSymbolNum);
+ for (u32 i = 0; i < import_num; ++i) {
+ ImportAnonymousSymbolEntry entry;
+ GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) {
+ u32 import_strings_size = GetField(ImportStringsSize);
+
+ u32 import_module_num = GetField(ImportModuleNum);
+ for (u32 i = 0; i < import_module_num; ++i) {
+ ImportModuleEntry entry;
+ GetEntry(i, entry);
+ std::string want_cro_name = Memory::ReadCString(entry.name_offset, import_strings_size);
+
+ ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
+ if (want_cro_name == source.ModuleName()) {
+ LOG_INFO(Service_LDR, "CRO \"%s\" imports %d indexed symbols from \"%s\"",
+ ModuleName().data(), entry.import_indexed_symbol_num, source.ModuleName().data());
+ for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
+ ImportIndexedSymbolEntry im;
+ entry.GetImportIndexedSymbolEntry(j, im);
+ ExportIndexedSymbolEntry ex;
+ source.GetEntry(im.index, ex);
+ u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position);
+ LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address);
+ ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ LOG_INFO(Service_LDR, "CRO \"%s\" imports %d anonymous symbols from \"%s\"",
+ ModuleName().data(), entry.import_anonymous_symbol_num, source.ModuleName().data());
+ for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
+ ImportAnonymousSymbolEntry im;
+ entry.GetImportAnonymousSymbolEntry(j, im);
+ u32 symbol_address = source.SegmentTagToAddress(im.symbol_position);
+ LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address);
+ ResultCode result = ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ return MakeResult<bool>(false);
+ }
+ return MakeResult<bool>(true);
+ });
+ if (result.IsError()) {
+ return result;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) {
+ LOG_DEBUG(Service_LDR, "CRO \"%s\" exports named symbols to \"%s\"",
+ ModuleName().data(), target.ModuleName().data());
+ u32 target_import_strings_size = target.GetField(ImportStringsSize);
+ u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < target_symbol_import_num; ++i) {
+ ImportNamedSymbolEntry entry;
+ target.GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ if (!relocation_entry.is_batch_resolved) {
+ std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size);
+ u32 symbol_address = FindExportNamedSymbol(symbol_name);
+ if (symbol_address != 0) {
+ LOG_TRACE(Service_LDR, " exports symbol \"%s\"", symbol_name.data());
+ ResultCode result = target.ApplyRelocationBatch(relocation_addr, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ResetExportNamedSymbol(CROHelper target) {
+ LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports named symbols to \"%s\"",
+ ModuleName().data(), target.ModuleName().data());
+ u32 unresolved_symbol = target.GetOnUnresolvedAddress();
+ u32 target_import_strings_size = target.GetField(ImportStringsSize);
+ u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < target_symbol_import_num; ++i) {
+ ImportNamedSymbolEntry entry;
+ target.GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ if (relocation_entry.is_batch_resolved) {
+ std::string symbol_name = Memory::ReadCString(entry.name_offset, target_import_strings_size);
+ u32 symbol_address = FindExportNamedSymbol(symbol_name);
+ if (symbol_address != 0) {
+ LOG_TRACE(Service_LDR, " unexports symbol \"%s\"", symbol_name.data());
+ ResultCode result = target.ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyModuleExport(CROHelper target) {
+ std::string module_name = ModuleName();
+ u32 target_import_string_size = target.GetField(ImportStringsSize);
+ u32 target_import_module_num = target.GetField(ImportModuleNum);
+ for (u32 i = 0; i < target_import_module_num; ++i) {
+ ImportModuleEntry entry;
+ target.GetEntry(i, entry);
+
+ if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name)
+ continue;
+
+ LOG_INFO(Service_LDR, "CRO \"%s\" exports %d indexed symbols to \"%s\"",
+ module_name.data(), entry.import_indexed_symbol_num, target.ModuleName().data());
+ for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
+ ImportIndexedSymbolEntry im;
+ entry.GetImportIndexedSymbolEntry(j, im);
+ ExportIndexedSymbolEntry ex;
+ GetEntry(im.index, ex);
+ u32 symbol_address = SegmentTagToAddress(ex.symbol_position);
+ LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address);
+ ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+
+ LOG_INFO(Service_LDR, "CRO \"%s\" exports %d anonymous symbols to \"%s\"",
+ module_name.data(), entry.import_anonymous_symbol_num, target.ModuleName().data());
+ for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
+ ImportAnonymousSymbolEntry im;
+ entry.GetImportAnonymousSymbolEntry(j, im);
+ u32 symbol_address = SegmentTagToAddress(im.symbol_position);
+ LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address);
+ ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ResetModuleExport(CROHelper target) {
+ u32 unresolved_symbol = target.GetOnUnresolvedAddress();
+
+ std::string module_name = ModuleName();
+ u32 target_import_string_size = target.GetField(ImportStringsSize);
+ u32 target_import_module_num = target.GetField(ImportModuleNum);
+ for (u32 i = 0; i < target_import_module_num; ++i) {
+ ImportModuleEntry entry;
+ target.GetEntry(i, entry);
+
+ if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name)
+ continue;
+
+ LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports indexed symbols to \"%s\"",
+ module_name.data(), target.ModuleName().data());
+ for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
+ ImportIndexedSymbolEntry im;
+ entry.GetImportIndexedSymbolEntry(j, im);
+ ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+
+ LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports anonymous symbols to \"%s\"",
+ module_name.data(), target.ModuleName().data());
+ for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
+ ImportAnonymousSymbolEntry im;
+ entry.GetImportAnonymousSymbolEntry(j, im);
+ ResultCode result = target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) {
+ u32 import_strings_size = GetField(ImportStringsSize);
+ u32 symbol_import_num = GetField(ImportNamedSymbolNum);
+ for (u32 i = 0; i < symbol_import_num; ++i) {
+ ImportNamedSymbolEntry entry;
+ GetEntry(i, entry);
+ VAddr relocation_addr = entry.relocation_batch_offset;
+ ExternalRelocationEntry relocation_entry;
+ Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
+
+ if (Memory::ReadCString(entry.name_offset, import_strings_size) == "__aeabi_atexit"){
+ ResultCode result = ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
+ u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_");
+
+ if (symbol_address != 0) {
+ LOG_DEBUG(Service_LDR, "CRO \"%s\" import exit function from \"%s\"",
+ ModuleName().data(), source.ModuleName().data());
+
+ ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
+ return result;
+ }
+
+ return MakeResult<bool>(false);
+ }
+
+ return MakeResult<bool>(true);
+ });
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying exit relocation %08X", result.raw);
+ return result;
+ }
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+/**
+ * Verifies a string or a string table matching a predicted size (i.e. terminated by 0)
+ * if it is not empty. There can be many other nulls in the string table because
+ * they are composed by many sub strings. This function is to check whether the
+ * whole string (table) is terminated properly, despite that it is not actually one string.
+ * @param address the virtual address of the string (table)
+ * @param size the size of the string (table), including the terminating 0
+ * @returns ResultCode RESULT_SUCCESS if the size matches, otherwise error code.
+ */
+static ResultCode VerifyStringTableLength(VAddr address, u32 size) {
+ if (size != 0) {
+ if (Memory::Read8(address + size - 1) != 0)
+ return CROFormatError(0x0B);
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size,
+ VAddr data_segment_addresss, u32 data_segment_size,
+ VAddr bss_segment_address, u32 bss_segment_size, bool is_crs) {
+
+ ResultCode result = RebaseHeader(cro_size);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing header %08X", result.raw);
+ return result;
+ }
+
+ result = VerifyStringTableLength(GetField(ModuleNameOffset), GetField(ModuleNameSize));
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error verifying module name %08X", result.raw);
+ return result;
+ }
+
+ u32 prev_data_segment_address = 0;
+ if (!is_crs) {
+ auto result_val = RebaseSegmentTable(cro_size,
+ data_segment_addresss, data_segment_size,
+ bss_segment_address, bss_segment_size);
+ if (result_val.Failed()) {
+ LOG_ERROR(Service_LDR, "Error rebasing segment table %08X", result_val.Code().raw);
+ return result_val.Code();
+ }
+ prev_data_segment_address = *result_val;
+ }
+
+ result = RebaseExportNamedSymbolTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing symbol export table %08X", result.raw);
+ return result;
+ }
+
+ result = VerifyExportTreeTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error verifying export tree %08X", result.raw);
+ return result;
+ }
+
+ result = VerifyStringTableLength(GetField(ExportStringsOffset), GetField(ExportStringsSize));
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error verifying export strings %08X", result.raw);
+ return result;
+ }
+
+ result = RebaseImportModuleTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing object table %08X", result.raw);
+ return result;
+ }
+
+ result = ResetExternalRelocations();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error resetting all external relocations %08X", result.raw);
+ return result;
+ }
+
+ result = RebaseImportNamedSymbolTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing symbol import table %08X", result.raw);
+ return result;
+ }
+
+ result = RebaseImportIndexedSymbolTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing index import table %08X", result.raw);
+ return result;
+ }
+
+ result = RebaseImportAnonymousSymbolTable();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error rebasing offset import table %08X", result.raw);
+ return result;
+ }
+
+ result = VerifyStringTableLength(GetField(ImportStringsOffset), GetField(ImportStringsSize));
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error verifying import strings %08X", result.raw);
+ return result;
+ }
+
+ if (!is_crs) {
+ result = ApplyStaticAnonymousSymbolToCRS(crs_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying offset export to CRS %08X", result.raw);
+ return result;
+ }
+ }
+
+ result = ApplyInternalRelocations(prev_data_segment_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying internal relocations %08X", result.raw);
+ return result;
+ }
+
+ if (!is_crs) {
+ result = ApplyExitRelocations(crs_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying exit relocations %08X", result.raw);
+ return result;
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+void CROHelper::Unrebase(bool is_crs) {
+ UnrebaseImportAnonymousSymbolTable();
+ UnrebaseImportIndexedSymbolTable();
+ UnrebaseImportNamedSymbolTable();
+ UnrebaseImportModuleTable();
+ UnrebaseExportNamedSymbolTable();
+
+ if (!is_crs)
+ UnrebaseSegmentTable();
+
+ SetNextModule(0);
+ SetPreviousModule(0);
+
+ SetField(FixedSize, 0);
+
+ UnrebaseHeader();
+}
+
+ResultCode CROHelper::VerifyHash(u32 cro_size, VAddr crr) const {
+ // TODO(wwylele): actually verify the hash
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) {
+ ResultCode result = RESULT_SUCCESS;
+
+ {
+ VAddr data_segment_address;
+ if (link_on_load_bug_fix) {
+ // this is a bug fix introduced by 7.2.0-17's LoadCRO_New
+ // The bug itself is:
+ // If a relocation target is in .data segment, it will relocate to the
+ // user-specified buffer. But if this is linking during loading,
+ // the .data segment hasn't been tranfer from CRO to the buffer,
+ // thus the relocation will be overwritten by data transfer.
+ // To fix this bug, we need temporarily restore the old .data segment
+ // offset and apply imported symbols.
+
+ // RO service seems assuming segment_index == segment_type,
+ // so we do the same
+ if (GetField(SegmentNum) >= 2) { // means we have .data segment
+ SegmentEntry entry;
+ GetEntry(2, entry);
+ ASSERT(entry.type == SegmentType::Data);
+ data_segment_address = entry.offset;
+ entry.offset = GetField(DataOffset);
+ SetEntry(2, entry);
+ }
+ }
+ SCOPE_EXIT({
+ // Restore the new .data segment address after importing
+ if (link_on_load_bug_fix) {
+ if (GetField(SegmentNum) >= 2) {
+ SegmentEntry entry;
+ GetEntry(2, entry);
+ entry.offset = data_segment_address;
+ SetEntry(2, entry);
+ }
+ }
+ });
+
+ // Imports named symbols from other modules
+ result = ApplyImportNamedSymbol(crs_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying symbol import %08X", result.raw);
+ return result;
+ }
+
+ // Imports indexed and anonymous symbols from other modules
+ result = ApplyModuleImport(crs_address);
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying module import %08X", result.raw);
+ return result;
+ }
+ }
+
+ // Exports symbols to other modules
+ result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> {
+ ResultCode result = ApplyExportNamedSymbol(target);
+ if (result.IsError())
+ return result;
+
+ result = ApplyModuleExport(target);
+ if (result.IsError())
+ return result;
+
+ return MakeResult<bool>(true);
+ });
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error applying export %08X", result.raw);
+ return result;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::Unlink(VAddr crs_address) {
+
+ // Resets all imported named symbols
+ ResultCode result = ResetImportNamedSymbol();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error resetting symbol import %08X", result.raw);
+ return result;
+ }
+
+ // Resets all imported indexed symbols
+ result = ResetImportIndexedSymbol();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error resetting indexed import %08X", result.raw);
+ return result;
+ }
+
+ // Resets all imported anonymous symbols
+ result = ResetImportAnonymousSymbol();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error resetting anonymous import %08X", result.raw);
+ return result;
+ }
+
+ // Resets all symbols in other modules imported from this module
+ // Note: the RO service seems only searching in auto-link modules
+ result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> {
+ ResultCode result = ResetExportNamedSymbol(target);
+ if (result.IsError())
+ return result;
+
+ result = ResetModuleExport(target);
+ if (result.IsError())
+ return result;
+
+ return MakeResult<bool>(true);
+ });
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error resetting export %08X", result.raw);
+ return result;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode CROHelper::ClearRelocations() {
+ ResultCode result = ClearExternalRelocations();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error clearing external relocations %08X", result.raw);
+ return result;
+ }
+
+ result = ClearInternalRelocations();
+ if (result.IsError()) {
+ LOG_ERROR(Service_LDR, "Error clearing internal relocations %08X", result.raw);
+ return result;
+ }
+ return RESULT_SUCCESS;
+}
+
+void CROHelper::InitCRS() {
+ SetNextModule(0);
+ SetPreviousModule(0);
+}
+
+void CROHelper::Register(VAddr crs_address, bool auto_link) {
+ CROHelper crs(crs_address);
+ CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule());
+
+ if (head.module_address) {
+ // there are already CROs registered
+ // register as the new tail
+ CROHelper tail(head.PreviousModule());
+
+ // link with the old tail
+ ASSERT(tail.NextModule() == 0);
+ SetPreviousModule(tail.module_address);
+ tail.SetNextModule(module_address);
+
+ // set previous of the head pointing to the new tail
+ head.SetPreviousModule(module_address);
+ } else {
+ // register as the first CRO
+ // set previous to self as tail
+ SetPreviousModule(module_address);
+
+ // set self as head
+ if (auto_link)
+ crs.SetNextModule(module_address);
+ else
+ crs.SetPreviousModule(module_address);
+ }
+
+ // the new one is the tail
+ SetNextModule(0);
+}
+
+void CROHelper::Unregister(VAddr crs_address) {
+ CROHelper crs(crs_address);
+ CROHelper next_head(crs.NextModule()), previous_head(crs.PreviousModule());
+ CROHelper next(NextModule()), previous(PreviousModule());
+
+ if (module_address == next_head.module_address || module_address == previous_head.module_address) {
+ // removing head
+ if (next.module_address) {
+ // the next is new head
+ // let its previous point to the tail
+ next.SetPreviousModule(previous.module_address);
+ }
+
+ // set new head
+ if (module_address == previous_head.module_address) {
+ crs.SetPreviousModule(next.module_address);
+ } else {
+ crs.SetNextModule(next.module_address);
+ }
+ } else if (next.module_address) {
+ // link previous and next
+ previous.SetNextModule(next.module_address);
+ next.SetPreviousModule(previous.module_address);
+ } else {
+ // removing tail
+ // set previous as new tail
+ previous.SetNextModule(0);
+
+ // let head's previous point to the new tail
+ if (next_head.module_address && next_head.PreviousModule() == module_address) {
+ next_head.SetPreviousModule(previous.module_address);
+ } else if (previous_head.module_address && previous_head.PreviousModule() == module_address) {
+ previous_head.SetPreviousModule(previous.module_address);
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ // unlink self
+ SetNextModule(0);
+ SetPreviousModule(0);
+}
+
+u32 CROHelper::GetFixEnd(u32 fix_level) const {
+ u32 end = CRO_HEADER_SIZE;
+ end = std::max<u32>(end, GetField(CodeOffset) + GetField(CodeSize));
+
+ u32 entry_size_i = 2;
+ int field = ModuleNameOffset;
+ while (true) {
+ end = std::max<u32>(end,
+ GetField(static_cast<HeaderField>(field)) +
+ GetField(static_cast<HeaderField>(field + 1)) * ENTRY_SIZE[entry_size_i]);
+
+ ++entry_size_i;
+ field += 2;
+
+ if (field == FIX_BARRIERS[fix_level])
+ return end;
+ }
+}
+
+u32 CROHelper::Fix(u32 fix_level) {
+ u32 fix_end = GetFixEnd(fix_level);
+
+ if (fix_level != 0) {
+ SetField(Magic, MAGIC_FIXD);
+
+ for (int field = FIX_BARRIERS[fix_level]; field < Fix0Barrier; field += 2) {
+ SetField(static_cast<HeaderField>(field), fix_end);
+ SetField(static_cast<HeaderField>(field + 1), 0);
+ }
+ }
+
+ fix_end = Common::AlignUp(fix_end, Memory::PAGE_SIZE);
+
+ u32 fixed_size = fix_end - module_address;
+ SetField(FixedSize, fixed_size);
+ return fixed_size;
+}
+
+bool CROHelper::IsLoaded() const {
+ u32 magic = GetField(Magic);
+ if (magic != MAGIC_CRO0 && magic != MAGIC_FIXD)
+ return false;
+
+ // TODO(wwylele): verify memory state here after memory aliasing is implemented
+
+ return true;
+}
+
+std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const {
+ u32 segment_num = GetField(SegmentNum);
+ for (u32 i = 0; i < segment_num; ++i) {
+ SegmentEntry entry;
+ GetEntry(i, entry);
+ if (entry.type == SegmentType::Code && entry.size != 0) {
+ VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE);
+ VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE);
+ return std::make_tuple(begin, end - begin);
+ }
+ }
+ return std::make_tuple(0, 0);
+}
+
+} // namespace