// 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 transfer 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