mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	
						commit
						474586bc53
					
				@ -95,7 +95,9 @@ set(SRCS
 | 
			
		||||
            hle/service/ir/ir_rst.cpp
 | 
			
		||||
            hle/service/ir/ir_u.cpp
 | 
			
		||||
            hle/service/ir/ir_user.cpp
 | 
			
		||||
            hle/service/ldr_ro.cpp
 | 
			
		||||
            hle/service/ldr_ro/cro_helper.cpp
 | 
			
		||||
            hle/service/ldr_ro/ldr_ro.cpp
 | 
			
		||||
            hle/service/ldr_ro/memory_synchronizer.cpp
 | 
			
		||||
            hle/service/mic_u.cpp
 | 
			
		||||
            hle/service/ndm/ndm.cpp
 | 
			
		||||
            hle/service/ndm/ndm_u.cpp
 | 
			
		||||
@ -238,7 +240,9 @@ set(HEADERS
 | 
			
		||||
            hle/service/ir/ir_rst.h
 | 
			
		||||
            hle/service/ir/ir_u.h
 | 
			
		||||
            hle/service/ir/ir_user.h
 | 
			
		||||
            hle/service/ldr_ro.h
 | 
			
		||||
            hle/service/ldr_ro/cro_helper.h
 | 
			
		||||
            hle/service/ldr_ro/ldr_ro.h
 | 
			
		||||
            hle/service/ldr_ro/memory_synchronizer.h
 | 
			
		||||
            hle/service/mic_u.h
 | 
			
		||||
            hle/service/ndm/ndm.h
 | 
			
		||||
            hle/service/ndm/ndm_u.h
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,9 @@ public:
 | 
			
		||||
        Run(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Clear all instruction cache
 | 
			
		||||
    virtual void ClearInstructionCache() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the Program Counter to an address
 | 
			
		||||
     * @param addr Address to set PC to
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
#include "core/arm/dyncom/arm_dyncom.h"
 | 
			
		||||
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
 | 
			
		||||
#include "core/arm/dyncom/arm_dyncom_run.h"
 | 
			
		||||
#include "core/arm/dyncom/arm_dyncom_trans.h"
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
@ -23,6 +24,11 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
 | 
			
		||||
ARM_DynCom::~ARM_DynCom() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARM_DynCom::ClearInstructionCache() {
 | 
			
		||||
    state->instruction_cache.clear();
 | 
			
		||||
    trans_cache_buf_top = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARM_DynCom::SetPC(u32 pc) {
 | 
			
		||||
    state->Reg[15] = pc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,8 @@ public:
 | 
			
		||||
    ARM_DynCom(PrivilegeMode initial_mode);
 | 
			
		||||
    ~ARM_DynCom();
 | 
			
		||||
 | 
			
		||||
    void ClearInstructionCache() override;
 | 
			
		||||
 | 
			
		||||
    void SetPC(u32 pc) override;
 | 
			
		||||
    u32 GetPC() const override;
 | 
			
		||||
    u32 GetReg(int index) const override;
 | 
			
		||||
 | 
			
		||||
@ -1,96 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/service/ldr_ro.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Namespace LDR_RO
 | 
			
		||||
 | 
			
		||||
namespace LDR_RO {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::Initialize service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      1 : CRS buffer pointer
 | 
			
		||||
 *      2 : CRS Size
 | 
			
		||||
 *      3 : Process memory address where the CRS will be mapped
 | 
			
		||||
 *      4 : Value, must be zero
 | 
			
		||||
 *      5 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void Initialize(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    u32 crs_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 crs_size       = cmd_buff[2];
 | 
			
		||||
    u32 address        = cmd_buff[3];
 | 
			
		||||
    u32 value          = cmd_buff[4];
 | 
			
		||||
    u32 process        = cmd_buff[5];
 | 
			
		||||
 | 
			
		||||
    if (value != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(purpasmart96): Verify return header on HW
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | 
			
		||||
 | 
			
		||||
    LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X",
 | 
			
		||||
                crs_buffer_ptr, crs_size, address, value, process);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::LoadCRR service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      1 : CRS buffer pointer
 | 
			
		||||
 *      2 : CRS Size
 | 
			
		||||
 *      3 : Value, must be zero
 | 
			
		||||
 *      4 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void LoadCRR(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    u32 crs_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 crs_size       = cmd_buff[2];
 | 
			
		||||
    u32 value          = cmd_buff[3];
 | 
			
		||||
    u32 process        = cmd_buff[4];
 | 
			
		||||
 | 
			
		||||
    if (value != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(purpasmart96): Verify return header on HW
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | 
			
		||||
 | 
			
		||||
    LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, value=0x%08X, process=0x%08X",
 | 
			
		||||
                crs_buffer_ptr, crs_size, value, process);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x000100C2, Initialize,            "Initialize"},
 | 
			
		||||
    {0x00020082, LoadCRR,               "LoadCRR"},
 | 
			
		||||
    {0x00030042, nullptr,               "UnloadCCR"},
 | 
			
		||||
    {0x000402C2, nullptr,               "LoadExeCRO"},
 | 
			
		||||
    {0x000500C2, nullptr,               "LoadCROSymbols"},
 | 
			
		||||
    {0x00060042, nullptr,               "CRO_Load?"},
 | 
			
		||||
    {0x00070042, nullptr,               "LoadCROSymbols"},
 | 
			
		||||
    {0x00080042, nullptr,               "Shutdown"},
 | 
			
		||||
    {0x000902C2, nullptr,               "LoadExeCRO_New?"},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Interface class
 | 
			
		||||
 | 
			
		||||
Interface::Interface() {
 | 
			
		||||
    Register(FunctionTable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
							
								
								
									
										1477
									
								
								src/core/hle/service/ldr_ro/cro_helper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1477
									
								
								src/core/hle/service/ldr_ro/cro_helper.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										691
									
								
								src/core/hle/service/ldr_ro/cro_helper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										691
									
								
								src/core/hle/service/ldr_ro/cro_helper.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,691 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/hle/result.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Namespace LDR_RO
 | 
			
		||||
 | 
			
		||||
namespace LDR_RO {
 | 
			
		||||
 | 
			
		||||
// GCC versions < 5.0 do not implement std::is_trivially_copyable.
 | 
			
		||||
// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
 | 
			
		||||
#if (__GNUC__ >= 5) || defined(__clang__)
 | 
			
		||||
    #define ASSERT_CRO_STRUCT(name, size) \
 | 
			
		||||
        static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \
 | 
			
		||||
        static_assert(std::is_trivially_copyable<name>::value, "CRO structure " #name " isn't trivially copyable"); \
 | 
			
		||||
        static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name)
 | 
			
		||||
#else
 | 
			
		||||
    #define ASSERT_CRO_STRUCT(name, size) \
 | 
			
		||||
        static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \
 | 
			
		||||
        static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static constexpr u32 CRO_HEADER_SIZE = 0x138;
 | 
			
		||||
static constexpr u32 CRO_HASH_SIZE = 0x80;
 | 
			
		||||
 | 
			
		||||
/// Represents a loaded module (CRO) with interfaces manipulating it.
 | 
			
		||||
class CROHelper final {
 | 
			
		||||
public:
 | 
			
		||||
    explicit CROHelper(VAddr cro_address) : module_address(cro_address) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string ModuleName() const {
 | 
			
		||||
        return Memory::ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 GetFileSize() const {
 | 
			
		||||
        return GetField(FileSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases the module according to its address.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @param cro_size the size of the CRO file
 | 
			
		||||
     * @param data_segment_address buffer address for .data segment
 | 
			
		||||
     * @param data_segment_size the buffer size for .data segment
 | 
			
		||||
     * @param bss_segment_address the buffer address for .bss segment
 | 
			
		||||
     * @param bss_segment_size the buffer size for .bss segment
 | 
			
		||||
     * @param is_crs true if the module itself is the static module
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode 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);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unrebases the module.
 | 
			
		||||
     * @param is_crs true if the module itself is the static module
 | 
			
		||||
     */
 | 
			
		||||
    void Unrebase(bool is_crs);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Verifies module hash by CRR.
 | 
			
		||||
     * @param cro_size the size of the CRO
 | 
			
		||||
     * @param crr the virtual address of the CRR
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode VerifyHash(u32 cro_size, VAddr crr) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Links this module with all registered auto-link module.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @param link_on_load_bug_fix true if links when loading and fixes the bug
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode Link(VAddr crs_address, bool link_on_load_bug_fix);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unlinks this module with other modules.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode Unlink(VAddr crs_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clears all relocations to zero.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ClearRelocations();
 | 
			
		||||
 | 
			
		||||
    /// Initialize this module as the static module (CRS)
 | 
			
		||||
    void InitCRS();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Registers this module and adds it to the module list.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @auto_link whether to register as an auto link module
 | 
			
		||||
     */
 | 
			
		||||
    void Register(VAddr crs_address, bool auto_link);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unregisters this module and removes from the module list.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     */
 | 
			
		||||
    void Unregister(VAddr crs_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the end of reserved data according to the fix level.
 | 
			
		||||
     * @param fix_level fix level from 0 to 3
 | 
			
		||||
     * @returns the end of reserved data.
 | 
			
		||||
     */
 | 
			
		||||
    u32 GetFixEnd(u32 fix_level) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Zeros offsets to cropped data according to the fix level and marks as fixed.
 | 
			
		||||
     * @param fix_level fix level from 0 to 3
 | 
			
		||||
     * @returns page-aligned size of the module after fixing.
 | 
			
		||||
     */
 | 
			
		||||
    u32 Fix(u32 fix_level);
 | 
			
		||||
 | 
			
		||||
    bool IsFixed() const {
 | 
			
		||||
        return GetField(Magic) == MAGIC_FIXD;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 GetFixedSize() const {
 | 
			
		||||
        return GetField(FixedSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsLoaded() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the page address and size of the code segment.
 | 
			
		||||
     * @returns a tuple of (address, size); (0, 0) if the code segment doesn't exist.
 | 
			
		||||
     */
 | 
			
		||||
    std::tuple<VAddr, u32> GetExecutablePages() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    const VAddr module_address; ///< the virtual address of this module
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Each item in this enum represents a u32 field in the header begin from address+0x80, successively.
 | 
			
		||||
     * We don't directly use a struct here, to avoid GetPointer, reinterpret_cast, or Read/WriteBlock repeatedly.
 | 
			
		||||
     */
 | 
			
		||||
    enum HeaderField {
 | 
			
		||||
        Magic = 0,
 | 
			
		||||
        NameOffset,
 | 
			
		||||
        NextCRO,
 | 
			
		||||
        PreviousCRO,
 | 
			
		||||
        FileSize,
 | 
			
		||||
        BssSize,
 | 
			
		||||
        FixedSize,
 | 
			
		||||
        UnknownZero,
 | 
			
		||||
        UnkSegmentTag,
 | 
			
		||||
        OnLoadSegmentTag,
 | 
			
		||||
        OnExitSegmentTag,
 | 
			
		||||
        OnUnresolvedSegmentTag,
 | 
			
		||||
 | 
			
		||||
        CodeOffset,
 | 
			
		||||
        CodeSize,
 | 
			
		||||
        DataOffset,
 | 
			
		||||
        DataSize,
 | 
			
		||||
        ModuleNameOffset,
 | 
			
		||||
        ModuleNameSize,
 | 
			
		||||
        SegmentTableOffset,
 | 
			
		||||
        SegmentNum,
 | 
			
		||||
 | 
			
		||||
        ExportNamedSymbolTableOffset,
 | 
			
		||||
        ExportNamedSymbolNum,
 | 
			
		||||
        ExportIndexedSymbolTableOffset,
 | 
			
		||||
        ExportIndexedSymbolNum,
 | 
			
		||||
        ExportStringsOffset,
 | 
			
		||||
        ExportStringsSize,
 | 
			
		||||
        ExportTreeTableOffset,
 | 
			
		||||
        ExportTreeNum,
 | 
			
		||||
 | 
			
		||||
        ImportModuleTableOffset,
 | 
			
		||||
        ImportModuleNum,
 | 
			
		||||
        ExternalRelocationTableOffset,
 | 
			
		||||
        ExternalRelocationNum,
 | 
			
		||||
        ImportNamedSymbolTableOffset,
 | 
			
		||||
        ImportNamedSymbolNum,
 | 
			
		||||
        ImportIndexedSymbolTableOffset,
 | 
			
		||||
        ImportIndexedSymbolNum,
 | 
			
		||||
        ImportAnonymousSymbolTableOffset,
 | 
			
		||||
        ImportAnonymousSymbolNum,
 | 
			
		||||
        ImportStringsOffset,
 | 
			
		||||
        ImportStringsSize,
 | 
			
		||||
 | 
			
		||||
        StaticAnonymousSymbolTableOffset,
 | 
			
		||||
        StaticAnonymousSymbolNum,
 | 
			
		||||
        InternalRelocationTableOffset,
 | 
			
		||||
        InternalRelocationNum,
 | 
			
		||||
        StaticRelocationTableOffset,
 | 
			
		||||
        StaticRelocationNum,
 | 
			
		||||
        Fix0Barrier,
 | 
			
		||||
 | 
			
		||||
        Fix3Barrier = ExportNamedSymbolTableOffset,
 | 
			
		||||
        Fix2Barrier = ImportModuleTableOffset,
 | 
			
		||||
        Fix1Barrier = StaticAnonymousSymbolTableOffset,
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(Fix0Barrier == (CRO_HEADER_SIZE - CRO_HASH_SIZE) / 4, "CRO Header fields are wrong!");
 | 
			
		||||
 | 
			
		||||
    enum class SegmentType : u32 {
 | 
			
		||||
        Code   = 0,
 | 
			
		||||
        ROData = 1,
 | 
			
		||||
        Data   = 2,
 | 
			
		||||
        BSS    = 3,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Identifies a program location inside of a segment.
 | 
			
		||||
     * Required to refer to program locations because individual segments may be relocated independently of each other.
 | 
			
		||||
     */
 | 
			
		||||
    union SegmentTag {
 | 
			
		||||
        u32_le raw;
 | 
			
		||||
        BitField<0, 4, u32_le> segment_index;
 | 
			
		||||
        BitField<4, 28, u32_le> offset_into_segment;
 | 
			
		||||
 | 
			
		||||
        SegmentTag() = default;
 | 
			
		||||
        explicit SegmentTag(u32 raw_) : raw(raw_) {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /// Information of a segment in this module.
 | 
			
		||||
    struct SegmentEntry {
 | 
			
		||||
        u32_le offset;
 | 
			
		||||
        u32_le size;
 | 
			
		||||
        SegmentType type;
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(SegmentEntry, 12);
 | 
			
		||||
 | 
			
		||||
    /// Identifies a named symbol exported from this module.
 | 
			
		||||
    struct ExportNamedSymbolEntry {
 | 
			
		||||
        u32_le name_offset;         // pointing to a substring in ExportStrings
 | 
			
		||||
        SegmentTag symbol_position; // to self's segment
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ExportNamedSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ExportNamedSymbolEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /// Identifies an indexed symbol exported from this module.
 | 
			
		||||
    struct ExportIndexedSymbolEntry {
 | 
			
		||||
        SegmentTag symbol_position; // to self's segment
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ExportIndexedSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ExportIndexedSymbolEntry, 4);
 | 
			
		||||
 | 
			
		||||
    /// A tree node in the symbol lookup tree.
 | 
			
		||||
    struct ExportTreeEntry {
 | 
			
		||||
        u16_le test_bit; // bit address into the name to test
 | 
			
		||||
        union Child {
 | 
			
		||||
            u16_le raw;
 | 
			
		||||
            BitField<0, 15, u16_le> next_index;
 | 
			
		||||
            BitField<15, 1, u16_le> is_end;
 | 
			
		||||
        } left, right;
 | 
			
		||||
        u16_le export_table_index; // index of an ExportNamedSymbolEntry
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ExportTreeTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ExportTreeEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /// Identifies a named symbol imported from another module.
 | 
			
		||||
    struct ImportNamedSymbolEntry {
 | 
			
		||||
        u32_le name_offset;             // pointing to a substring in ImportStrings
 | 
			
		||||
        u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ImportNamedSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ImportNamedSymbolEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /// Identifies an indexed symbol imported from another module.
 | 
			
		||||
    struct ImportIndexedSymbolEntry {
 | 
			
		||||
        u32_le index;                   // index of an ExportIndexedSymbolEntry in the exporting module
 | 
			
		||||
        u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ImportIndexedSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ImportIndexedSymbolEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /// Identifies an anonymous symbol imported from another module.
 | 
			
		||||
    struct ImportAnonymousSymbolEntry {
 | 
			
		||||
        SegmentTag symbol_position;      // in the exporting segment
 | 
			
		||||
        u32_le relocation_batch_offset;  // pointing to a relocation batch in ExternalRelocationTable
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ImportAnonymousSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ImportAnonymousSymbolEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /// Information of a imported module and symbols imported from it.
 | 
			
		||||
    struct ImportModuleEntry {
 | 
			
		||||
        u32_le name_offset;                          // pointing to a substring in ImportStrings
 | 
			
		||||
        u32_le import_indexed_symbol_table_offset;   // pointing to a subtable in ImportIndexedSymbolTable
 | 
			
		||||
        u32_le import_indexed_symbol_num;
 | 
			
		||||
        u32_le import_anonymous_symbol_table_offset; // pointing to a subtable in ImportAnonymousSymbolTable
 | 
			
		||||
        u32_le import_anonymous_symbol_num;
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ImportModuleTableOffset;
 | 
			
		||||
 | 
			
		||||
        void GetImportIndexedSymbolEntry(u32 index, ImportIndexedSymbolEntry& entry) {
 | 
			
		||||
            Memory::ReadBlock(import_indexed_symbol_table_offset + index * sizeof(ImportIndexedSymbolEntry),
 | 
			
		||||
                &entry, sizeof(ImportIndexedSymbolEntry));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void GetImportAnonymousSymbolEntry(u32 index, ImportAnonymousSymbolEntry& entry) {
 | 
			
		||||
            Memory::ReadBlock(import_anonymous_symbol_table_offset + index * sizeof(ImportAnonymousSymbolEntry),
 | 
			
		||||
                &entry, sizeof(ImportAnonymousSymbolEntry));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ImportModuleEntry, 20);
 | 
			
		||||
 | 
			
		||||
    enum class RelocationType : u8 {
 | 
			
		||||
        Nothing                = 0,
 | 
			
		||||
        AbsoluteAddress        = 2,
 | 
			
		||||
        RelativeAddress        = 3,
 | 
			
		||||
        ThumbBranch            = 10,
 | 
			
		||||
        ArmBranch              = 28,
 | 
			
		||||
        ModifyArmBranch        = 29,
 | 
			
		||||
        AbsoluteAddress2       = 38,
 | 
			
		||||
        AlignedRelativeAddress = 42,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct RelocationEntry {
 | 
			
		||||
        SegmentTag target_position; // to self's segment as an ExternalRelocationEntry; to static module segment as a StaticRelocationEntry
 | 
			
		||||
        RelocationType type;
 | 
			
		||||
        u8 is_batch_end;
 | 
			
		||||
        u8 is_batch_resolved;       // set at a batch beginning if the batch is resolved
 | 
			
		||||
        INSERT_PADDING_BYTES(1);
 | 
			
		||||
        u32_le addend;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /// Identifies a normal cross-module relocation.
 | 
			
		||||
    struct ExternalRelocationEntry : RelocationEntry {
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = ExternalRelocationTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(ExternalRelocationEntry, 12);
 | 
			
		||||
 | 
			
		||||
    /// Identifies a special static relocation (no game is known using this).
 | 
			
		||||
    struct StaticRelocationEntry : RelocationEntry {
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = StaticRelocationTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(StaticRelocationEntry, 12);
 | 
			
		||||
 | 
			
		||||
    /// Identifies a in-module relocation.
 | 
			
		||||
    struct InternalRelocationEntry {
 | 
			
		||||
        SegmentTag target_position; // to self's segment
 | 
			
		||||
        RelocationType type;
 | 
			
		||||
        u8 symbol_segment;
 | 
			
		||||
        INSERT_PADDING_BYTES(2);
 | 
			
		||||
        u32_le addend;
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = InternalRelocationTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(InternalRelocationEntry, 12);
 | 
			
		||||
 | 
			
		||||
    /// Identifies a special static anonymous symbol (no game is known using this).
 | 
			
		||||
    struct StaticAnonymousSymbolEntry {
 | 
			
		||||
        SegmentTag symbol_position;      // to self's segment
 | 
			
		||||
        u32_le relocation_batch_offset;  // pointing to a relocation batch in StaticRelocationTable
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = StaticAnonymousSymbolTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
    ASSERT_CRO_STRUCT(StaticAnonymousSymbolEntry, 8);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Entry size of each table, from Code to StaticRelocationTable.
 | 
			
		||||
     * Byte string contents (such as Code) are treated with entries of size 1.
 | 
			
		||||
     * This is used for verifying the size of each table and calculating the fix end.
 | 
			
		||||
     */
 | 
			
		||||
    static const std::array<int, 17> ENTRY_SIZE;
 | 
			
		||||
 | 
			
		||||
    /// The offset field of the table where to crop for each fix level
 | 
			
		||||
    static const std::array<HeaderField, 4> FIX_BARRIERS;
 | 
			
		||||
 | 
			
		||||
    static constexpr u32 MAGIC_CRO0 = 0x304F5243;
 | 
			
		||||
    static constexpr u32 MAGIC_FIXD = 0x44584946;
 | 
			
		||||
 | 
			
		||||
    VAddr Field(HeaderField field) const {
 | 
			
		||||
        return module_address + CRO_HASH_SIZE + field * 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 GetField(HeaderField field) const {
 | 
			
		||||
        return Memory::Read32(Field(field));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetField(HeaderField field, u32 value) {
 | 
			
		||||
        Memory::Write32(Field(field), value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads an entry in one of module tables.
 | 
			
		||||
     * @param index index of the entry
 | 
			
		||||
     * @param data where to put the read entry
 | 
			
		||||
     * @note the entry type must have the static member TABLE_OFFSET_FIELD
 | 
			
		||||
     *       indicating which table the entry is in.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    void GetEntry(std::size_t index, T& data) const {
 | 
			
		||||
        Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Writes an entry to one of module tables.
 | 
			
		||||
     * @param index index of the entry
 | 
			
		||||
     * @param data the entry data to write
 | 
			
		||||
     * @note the entry type must have the static member TABLE_OFFSET_FIELD
 | 
			
		||||
     *       indicating which table the entry is in.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    void SetEntry(std::size_t index, const T& data) {
 | 
			
		||||
        Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts a segment tag to virtual address in this module.
 | 
			
		||||
     * @param segment_tag the segment tag to convert
 | 
			
		||||
     * @returns VAddr the virtual address the segment tag points to; 0 if invalid.
 | 
			
		||||
     */
 | 
			
		||||
    VAddr SegmentTagToAddress(SegmentTag segment_tag) const;
 | 
			
		||||
 | 
			
		||||
    VAddr NextModule() const {
 | 
			
		||||
        return GetField(NextCRO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VAddr PreviousModule() const {
 | 
			
		||||
        return GetField(PreviousCRO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetNextModule(VAddr next) {
 | 
			
		||||
        SetField(NextCRO, next);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetPreviousModule(VAddr previous) {
 | 
			
		||||
        SetField(PreviousCRO, previous);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A helper function iterating over all registered auto-link modules, including the static module.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @param func a function object to operate on a module. It accepts one parameter
 | 
			
		||||
     *        CROHelper and returns ResultVal<bool>. It should return true to continue the iteration,
 | 
			
		||||
     *        false to stop the iteration, or an error code (which will also stop the iteration).
 | 
			
		||||
     * @returns ResultCode indicating the result of the operation, RESULT_SUCCESS if all iteration success,
 | 
			
		||||
     *         otherwise error code of the last iteration.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename FunctionObject>
 | 
			
		||||
    static ResultCode ForEachAutoLinkCRO(VAddr crs_address, FunctionObject func) {
 | 
			
		||||
        VAddr current = crs_address;
 | 
			
		||||
        while (current != 0) {
 | 
			
		||||
            CROHelper cro(current);
 | 
			
		||||
            CASCADE_RESULT(bool next, func(cro));
 | 
			
		||||
            if (!next)
 | 
			
		||||
                break;
 | 
			
		||||
            current = cro.NextModule();
 | 
			
		||||
        }
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies a relocation
 | 
			
		||||
     * @param target_address where to apply the relocation
 | 
			
		||||
     * @param relocation_type the type of the relocation
 | 
			
		||||
     * @param addend address addend applied to the relocated symbol
 | 
			
		||||
     * @param symbol_address the symbol address to be relocated with
 | 
			
		||||
     * @param target_future_address the future address of the target.
 | 
			
		||||
     *        Usually equals to target_address, but will be different for a target in .data segment
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyRelocation(VAddr target_address, RelocationType relocation_type,
 | 
			
		||||
        u32 addend, u32 symbol_address, u32 target_future_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clears a relocation to zero
 | 
			
		||||
     * @param target_address where to apply the relocation
 | 
			
		||||
     * @param relocation_type the type of the relocation
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ClearRelocation(VAddr target_address, RelocationType relocation_type);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies or resets a batch of relocations
 | 
			
		||||
     * @param batch the virtual address of the first relocation in the batch
 | 
			
		||||
     * @param symbol_address the symbol address to be relocated with
 | 
			
		||||
     * @param reset false to set the batch to resolved state, true to reset the batch to unresolved state
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset = false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finds an exported named symbol in this module.
 | 
			
		||||
     * @param name the name of the symbol to find
 | 
			
		||||
     * @return VAddr the virtual address of the symbol; 0 if not found.
 | 
			
		||||
     */
 | 
			
		||||
    VAddr FindExportNamedSymbol(const std::string& name) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in module header according to module address.
 | 
			
		||||
     * @param cro_size the size of the CRO file
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseHeader(u32 cro_size);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in segment table according to module address.
 | 
			
		||||
     * @param cro_size the size of the CRO file
 | 
			
		||||
     * @param data_segment_address the buffer address for .data segment
 | 
			
		||||
     * @param data_segment_size the buffer size for .data segment
 | 
			
		||||
     * @param bss_segment_address the buffer address for .bss segment
 | 
			
		||||
     * @param bss_segment_size the buffer size for .bss segment
 | 
			
		||||
     * @returns ResultVal<VAddr> with the virtual address of .data segment in CRO.
 | 
			
		||||
     */
 | 
			
		||||
    ResultVal<VAddr> RebaseSegmentTable(u32 cro_size,
 | 
			
		||||
        VAddr data_segment_address, u32 data_segment_size,
 | 
			
		||||
        VAddr bss_segment_address, u32 bss_segment_size);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in exported named symbol table according to module address.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseExportNamedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Verifies indices in export tree table.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all indices are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode VerifyExportTreeTable() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in exported module table according to module address.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseImportModuleTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in imported named symbol table according to module address.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseImportNamedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in imported indexed symbol table according to module address.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseImportIndexedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rebases offsets in imported anonymous symbol table according to module address.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode RebaseImportAnonymousSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the address of OnUnresolved function in this module.
 | 
			
		||||
     * Used as the applied symbol for reset relocation.
 | 
			
		||||
     * @returns the virtual address of OnUnresolved. 0 if not provided.
 | 
			
		||||
     */
 | 
			
		||||
    VAddr GetOnUnresolvedAddress();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets all external relocations to unresolved state.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetExternalRelocations();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clears all external relocations to zero.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ClearExternalRelocations();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies all static anonymous symbol to the static module.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyStaticAnonymousSymbolToCRS(VAddr crs_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies all internal relocations to the module itself.
 | 
			
		||||
     * @param old_data_segment_address the virtual address of data segment in CRO buffer
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyInternalRelocations(u32 old_data_segment_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clears all internal relocations to zero.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ClearInternalRelocations();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in imported anonymous symbol table
 | 
			
		||||
    void UnrebaseImportAnonymousSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in imported indexed symbol table
 | 
			
		||||
    void UnrebaseImportIndexedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in imported named symbol table
 | 
			
		||||
    void UnrebaseImportNamedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in imported module table
 | 
			
		||||
    void UnrebaseImportModuleTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in exported named symbol table
 | 
			
		||||
    void UnrebaseExportNamedSymbolTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in segment table
 | 
			
		||||
    void UnrebaseSegmentTable();
 | 
			
		||||
 | 
			
		||||
    /// Unrebases offsets in module header
 | 
			
		||||
    void UnrebaseHeader();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Looks up all imported named symbols of this module in all registered auto-link modules, and resolves them if found.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyImportNamedSymbol(VAddr crs_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets all imported named symbols of this module to unresolved state.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetImportNamedSymbol();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets all imported indexed symbols of this module to unresolved state.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetImportIndexedSymbol();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets all imported anonymous symbols of this module to unresolved state.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetImportAnonymousSymbol();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finds registered auto-link modules that this module imports, and resolves indexed and anonymous symbols exported by them.
 | 
			
		||||
     * @param crs_address the virtual address of the static module
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyModuleImport(VAddr crs_address);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolves target module's imported named symbols that exported by this module.
 | 
			
		||||
     * @param target the module to resolve.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyExportNamedSymbol(CROHelper target);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets target's named symbols imported from this module to unresolved state.
 | 
			
		||||
     * @param target the module to reset.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetExportNamedSymbol(CROHelper target);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolves imported indexed and anonymous symbols in the target module which imports this module.
 | 
			
		||||
     * @param target the module to resolve.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyModuleExport(CROHelper target);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets target's indexed and anonymous symbol imported from this module to unresolved state.
 | 
			
		||||
     * @param target the module to reset.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ResetModuleExport(CROHelper target);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolves the exit function in this module
 | 
			
		||||
     * @param crs_address the virtual address of the static module.
 | 
			
		||||
     * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode ApplyExitRelocations(VAddr crs_address);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
							
								
								
									
										748
									
								
								src/core/hle/service/ldr_ro/ldr_ro.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										748
									
								
								src/core/hle/service/ldr_ro/ldr_ro.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,748 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "common/alignment.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "core/arm/arm_interface.h"
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/hle/kernel/vm_manager.h"
 | 
			
		||||
#include "core/hle/service/ldr_ro/cro_helper.h"
 | 
			
		||||
#include "core/hle/service/ldr_ro/ldr_ro.h"
 | 
			
		||||
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Namespace LDR_RO
 | 
			
		||||
 | 
			
		||||
namespace LDR_RO {
 | 
			
		||||
 | 
			
		||||
static const ResultCode ERROR_ALREADY_INITIALIZED =   // 0xD9612FF9
 | 
			
		||||
    ResultCode(ErrorDescription::AlreadyInitialized,         ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_NOT_INITIALIZED =       // 0xD9612FF8
 | 
			
		||||
    ResultCode(ErrorDescription::NotInitialized,             ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_BUFFER_TOO_SMALL =      // 0xE0E12C1F
 | 
			
		||||
    ResultCode(static_cast<ErrorDescription>(31),            ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
 | 
			
		||||
static const ResultCode ERROR_MISALIGNED_ADDRESS =    // 0xD9012FF1
 | 
			
		||||
    ResultCode(ErrorDescription::MisalignedAddress,          ErrorModule::RO, ErrorSummary::WrongArgument,   ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_MISALIGNED_SIZE =       // 0xD9012FF2
 | 
			
		||||
    ResultCode(ErrorDescription::MisalignedSize,             ErrorModule::RO, ErrorSummary::WrongArgument,   ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_ILLEGAL_ADDRESS =       // 0xE1612C0F
 | 
			
		||||
    ResultCode(static_cast<ErrorDescription>(15),            ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Usage);
 | 
			
		||||
static const ResultCode ERROR_INVALID_MEMORY_STATE =  // 0xD8A12C08
 | 
			
		||||
    ResultCode(static_cast<ErrorDescription>(8),             ErrorModule::RO, ErrorSummary::InvalidState,    ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_NOT_LOADED =            // 0xD8A12C0D
 | 
			
		||||
    ResultCode(static_cast<ErrorDescription>(13),            ErrorModule::RO, ErrorSummary::InvalidState,    ErrorLevel::Permanent);
 | 
			
		||||
static const ResultCode ERROR_INVALID_DESCRIPTOR =    // 0xD9001830
 | 
			
		||||
    ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument,   ErrorLevel::Permanent);
 | 
			
		||||
 | 
			
		||||
static MemorySynchronizer memory_synchronizer;
 | 
			
		||||
 | 
			
		||||
// TODO(wwylele): this should be in the per-client storage when we implement multi-process
 | 
			
		||||
static VAddr loaded_crs; ///< the virtual address of the static module
 | 
			
		||||
 | 
			
		||||
static bool VerifyBufferState(VAddr buffer_ptr, u32 size) {
 | 
			
		||||
    auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr);
 | 
			
		||||
    return vma != Kernel::g_current_process->vm_manager.vma_map.end()
 | 
			
		||||
        && vma->second.base + vma->second.size >= buffer_ptr + size
 | 
			
		||||
        && vma->second.permissions == Kernel::VMAPermission::ReadWrite
 | 
			
		||||
        && vma->second.meminfo_state == Kernel::MemoryState::Private;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::Initialize service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x000100C2
 | 
			
		||||
 *      1 : CRS buffer pointer
 | 
			
		||||
 *      2 : CRS Size
 | 
			
		||||
 *      3 : Process memory address where the CRS will be mapped
 | 
			
		||||
 *      4 : handle translation descriptor (zero)
 | 
			
		||||
 *      5 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void Initialize(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr crs_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 crs_size         = cmd_buff[2];
 | 
			
		||||
    VAddr crs_address    = cmd_buff[3];
 | 
			
		||||
    u32 descriptor       = cmd_buff[4];
 | 
			
		||||
    u32 process          = cmd_buff[5];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
                crs_buffer_ptr, crs_address, crs_size, descriptor, process);
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(1, 1, 0);
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Already initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_ALREADY_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (crs_size < CRO_HEADER_SIZE) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS is too small");
 | 
			
		||||
        cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (crs_buffer_ptr & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS original address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (crs_address & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS mapping address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (crs_size & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS size is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!VerifyBufferState(crs_buffer_ptr, crs_size)) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state");
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (crs_address < Memory::PROCESS_IMAGE_VADDR || crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region");
 | 
			
		||||
        cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ResultCode result = RESULT_SUCCESS;
 | 
			
		||||
 | 
			
		||||
    if (crs_buffer_ptr != crs_address) {
 | 
			
		||||
        // TODO(wwylele): should be memory aliasing
 | 
			
		||||
        std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size);
 | 
			
		||||
        Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size);
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code).Code();
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read);
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
 | 
			
		||||
    } else {
 | 
			
		||||
        // Do nothing if buffer_ptr == address
 | 
			
		||||
        // TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
 | 
			
		||||
        //     and the actual behaviour is unclear. "Do nothing" is probably an incorrect implement.
 | 
			
		||||
        //     There is also a chance that another issue causes the app passing wrong arguments.
 | 
			
		||||
        LOG_WARNING(Service_LDR, "crs_buffer_ptr == crs_address (0x%08X)", crs_address);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CROHelper crs(crs_address);
 | 
			
		||||
    crs.InitCRS();
 | 
			
		||||
 | 
			
		||||
    result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw);
 | 
			
		||||
        cmd_buff[1] = result.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
 | 
			
		||||
    loaded_crs = crs_address;
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::LoadCRR service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x00020082
 | 
			
		||||
 *      1 : CRR buffer pointer
 | 
			
		||||
 *      2 : CRR Size
 | 
			
		||||
 *      3 : handle translation descriptor (zero)
 | 
			
		||||
 *      4 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void LoadCRR(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    u32 crr_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 crr_size       = cmd_buff[2];
 | 
			
		||||
    u32 descriptor     = cmd_buff[3];
 | 
			
		||||
    u32 process        = cmd_buff[4];
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(2, 1, 0);
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | 
			
		||||
 | 
			
		||||
    LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
                crr_buffer_ptr, crr_size, descriptor, process);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::UnloadCRR service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x00030042
 | 
			
		||||
 *      1 : CRR buffer pointer
 | 
			
		||||
 *      2 : handle translation descriptor (zero)
 | 
			
		||||
 *      3 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void UnloadCRR(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    u32 crr_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 descriptor     = cmd_buff[2];
 | 
			
		||||
    u32 process        = cmd_buff[3];
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(3, 1, 0);
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | 
			
		||||
 | 
			
		||||
    LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
                crr_buffer_ptr, descriptor, process);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::LoadCRO service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x000402C2 (old) / 0x000902C2 (new)
 | 
			
		||||
 *      1 : CRO buffer pointer
 | 
			
		||||
 *      2 : memory address where the CRO will be mapped
 | 
			
		||||
 *      3 : CRO Size
 | 
			
		||||
 *      4 : .data segment buffer pointer
 | 
			
		||||
 *      5 : must be zero
 | 
			
		||||
 *      6 : .data segment buffer size
 | 
			
		||||
 *      7 : .bss segment buffer pointer
 | 
			
		||||
 *      8 : .bss segment buffer size
 | 
			
		||||
 *      9 : (bool) register CRO as auto-link module
 | 
			
		||||
 *     10 : fix level
 | 
			
		||||
 *     11 : CRR address (zero if use loaded CRR)
 | 
			
		||||
 *     12 : handle translation descriptor (zero)
 | 
			
		||||
 *     13 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 *      2 : CRO fixed size
 | 
			
		||||
 *  Note:
 | 
			
		||||
 *      This service function has two versions. The function defined here is a
 | 
			
		||||
 *      unified one of two, with an additional parameter link_on_load_bug_fix.
 | 
			
		||||
 *      There is a dispatcher template below.
 | 
			
		||||
 */
 | 
			
		||||
static void LoadCRO(Service::Interface* self, bool link_on_load_bug_fix) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr cro_buffer_ptr       = cmd_buff[1];
 | 
			
		||||
    VAddr cro_address          = cmd_buff[2];
 | 
			
		||||
    u32 cro_size               = cmd_buff[3];
 | 
			
		||||
    VAddr data_segment_address = cmd_buff[4];
 | 
			
		||||
    u32 zero                   = cmd_buff[5];
 | 
			
		||||
    u32 data_segment_size      = cmd_buff[6];
 | 
			
		||||
    u32 bss_segment_address    = cmd_buff[7];
 | 
			
		||||
    u32 bss_segment_size       = cmd_buff[8];
 | 
			
		||||
    bool auto_link             = (cmd_buff[9] & 0xFF) != 0;
 | 
			
		||||
    u32 fix_level              = cmd_buff[10];
 | 
			
		||||
    VAddr crr_address          = cmd_buff[11];
 | 
			
		||||
    u32 descriptor             = cmd_buff[12];
 | 
			
		||||
    u32 process                = cmd_buff[13];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, "
 | 
			
		||||
        "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, bss_segment_address=0x%08X, bss_segment_size=0x%X, "
 | 
			
		||||
        "auto_link=%s, fix_level=%d, crr_address=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
        link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size,
 | 
			
		||||
        data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size,
 | 
			
		||||
        auto_link ? "true" : "false", fix_level, crr_address, descriptor, process
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(link_on_load_bug_fix ? 9 : 4, 2, 0);
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs == 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Not initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_size < CRO_HEADER_SIZE) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO too small");
 | 
			
		||||
        cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_buffer_ptr & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO original address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_address & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO mapping address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_size & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO size is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!VerifyBufferState(cro_buffer_ptr, cro_size)) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state");
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_address < Memory::PROCESS_IMAGE_VADDR
 | 
			
		||||
        || cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region");
 | 
			
		||||
        cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (zero) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Zero is not zero %d", zero);
 | 
			
		||||
        cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage).raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ResultCode result = RESULT_SUCCESS;
 | 
			
		||||
 | 
			
		||||
    if (cro_buffer_ptr != cro_address) {
 | 
			
		||||
        // TODO(wwylele): should be memory aliasing
 | 
			
		||||
        std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size);
 | 
			
		||||
        Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size);
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code).Code();
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read);
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
 | 
			
		||||
            Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
 | 
			
		||||
    } else {
 | 
			
		||||
        // Do nothing if buffer_ptr == address
 | 
			
		||||
        // TODO(wwylele): verify this behaviour.
 | 
			
		||||
        //     This is derived from the case of LoadCRS with buffer_ptr==address,
 | 
			
		||||
        //     and is never seen in any game. "Do nothing" is probably an incorrect implement.
 | 
			
		||||
        //     There is also a chance that this case is just prohibited.
 | 
			
		||||
        LOG_WARNING(Service_LDR, "cro_buffer_ptr == cro_address (0x%08X)", cro_address);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CROHelper cro(cro_address);
 | 
			
		||||
 | 
			
		||||
    result = cro.VerifyHash(cro_size, crr_address);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
 | 
			
		||||
        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
 | 
			
		||||
        cmd_buff[1] = result.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size, bss_segment_address, bss_segment_size, false);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw);
 | 
			
		||||
        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
 | 
			
		||||
        cmd_buff[1] = result.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result = cro.Link(loaded_crs, link_on_load_bug_fix);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
 | 
			
		||||
        Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
 | 
			
		||||
        cmd_buff[1] = result.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cro.Register(loaded_crs, auto_link);
 | 
			
		||||
 | 
			
		||||
    u32 fix_size = cro.Fix(fix_level);
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
 | 
			
		||||
    // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | 
			
		||||
    if (cro_buffer_ptr != cro_address) {
 | 
			
		||||
        if (fix_size != cro_size) {
 | 
			
		||||
            result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size);
 | 
			
		||||
            if (result.IsError()) {
 | 
			
		||||
                LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
 | 
			
		||||
                Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
 | 
			
		||||
                cmd_buff[1] = result.raw;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Changes the block size
 | 
			
		||||
        memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VAddr exe_begin;
 | 
			
		||||
    u32 exe_size;
 | 
			
		||||
    std::tie(exe_begin, exe_size) = cro.GetExecutablePages();
 | 
			
		||||
    if (exe_begin) {
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.ReprotectRange(exe_begin, exe_size, Kernel::VMAPermission::ReadExecute);
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
 | 
			
		||||
            Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Core::g_app_core->ClearInstructionCache();
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X",
 | 
			
		||||
        cro.ModuleName().data(), cro_address, cro_address+fix_size);
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;
 | 
			
		||||
    cmd_buff[2] = fix_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <bool link_on_load_bug_fix>
 | 
			
		||||
static void LoadCRO(Service::Interface* self) {
 | 
			
		||||
    LoadCRO(self, link_on_load_bug_fix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::UnloadCRO service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x000500C2
 | 
			
		||||
 *      1 : mapped CRO pointer
 | 
			
		||||
 *      2 : zero? (RO service doesn't care)
 | 
			
		||||
 *      3 : original CRO pointer
 | 
			
		||||
 *      4 : handle translation descriptor (zero)
 | 
			
		||||
 *      5 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void UnloadCRO(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr cro_address      = cmd_buff[1];
 | 
			
		||||
    u32 zero               = cmd_buff[2];
 | 
			
		||||
    VAddr cro_buffer_ptr   = cmd_buff[3];
 | 
			
		||||
    u32 descriptor         = cmd_buff[4];
 | 
			
		||||
    u32 process            = cmd_buff[5];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
        cro_address, zero, cro_buffer_ptr, descriptor, process);
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CROHelper cro(cro_address);
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(5, 1, 0);
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs == 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Not initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_address & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!cro.IsLoaded()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_LOADED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Service_LDR, "Unloading CRO \"%s\"", cro.ModuleName().data());
 | 
			
		||||
 | 
			
		||||
    u32 fixed_size = cro.GetFixedSize();
 | 
			
		||||
 | 
			
		||||
    cro.Unregister(loaded_crs);
 | 
			
		||||
 | 
			
		||||
    ResultCode result = cro.Unlink(loaded_crs);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
 | 
			
		||||
        cmd_buff[1] = result.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the module is not fixed, clears all external/internal relocations
 | 
			
		||||
    // to restore the state before loading, so that it can be loaded again(?)
 | 
			
		||||
    if (!cro.IsFixed()) {
 | 
			
		||||
        result = cro.ClearRelocations();
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw);
 | 
			
		||||
            cmd_buff[1] = result.raw;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cro.Unrebase(false);
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
 | 
			
		||||
    // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | 
			
		||||
    if (cro_address != cro_buffer_ptr) {
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size);
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw);
 | 
			
		||||
        }
 | 
			
		||||
        memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Core::g_app_core->ClearInstructionCache();
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = result.raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::LinkCRO service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x00060042
 | 
			
		||||
 *      1 : mapped CRO pointer
 | 
			
		||||
 *      2 : handle translation descriptor (zero)
 | 
			
		||||
 *      3 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void LinkCRO(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr cro_address = cmd_buff[1];
 | 
			
		||||
    u32 descriptor    = cmd_buff[2];
 | 
			
		||||
    u32 process       = cmd_buff[3];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
        cro_address, descriptor, process);
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CROHelper cro(cro_address);
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(6, 1, 0);
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs == 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Not initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_address & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!cro.IsLoaded()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_LOADED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data());
 | 
			
		||||
 | 
			
		||||
    ResultCode result = cro.Link(loaded_crs, false);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
    Core::g_app_core->ClearInstructionCache();
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = result.raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::UnlinkCRO service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x00070042
 | 
			
		||||
 *      1 : mapped CRO pointer
 | 
			
		||||
 *      2 : handle translation descriptor (zero)
 | 
			
		||||
 *      3 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void UnlinkCRO(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr cro_address = cmd_buff[1];
 | 
			
		||||
    u32 descriptor    = cmd_buff[2];
 | 
			
		||||
    u32 process       = cmd_buff[3];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
        cro_address, descriptor, process);
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CROHelper cro(cro_address);
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(7, 1, 0);
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs == 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Not initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cro_address & Memory::PAGE_MASK) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "CRO address is not aligned");
 | 
			
		||||
        cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!cro.IsLoaded()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_LOADED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data());
 | 
			
		||||
 | 
			
		||||
    ResultCode result = cro.Unlink(loaded_crs);
 | 
			
		||||
    if (result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
    Core::g_app_core->ClearInstructionCache();
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = result.raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LDR_RO::Shutdown service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x00080042
 | 
			
		||||
 *      1 : original CRS buffer pointer
 | 
			
		||||
 *      2 : handle translation descriptor (zero)
 | 
			
		||||
 *      3 : KProcess handle
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      0 : Return header
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
static void Shutdown(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    VAddr crs_buffer_ptr = cmd_buff[1];
 | 
			
		||||
    u32 descriptor       = cmd_buff[2];
 | 
			
		||||
    u32 process          = cmd_buff[3];
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X",
 | 
			
		||||
        crs_buffer_ptr, descriptor, process);
 | 
			
		||||
 | 
			
		||||
    if (descriptor != 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor);
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
 | 
			
		||||
        cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (loaded_crs == 0) {
 | 
			
		||||
        LOG_ERROR(Service_LDR, "Not initialized");
 | 
			
		||||
        cmd_buff[1] = ERROR_NOT_INITIALIZED.raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(8, 1, 0);
 | 
			
		||||
 | 
			
		||||
    CROHelper crs(loaded_crs);
 | 
			
		||||
    crs.Unrebase(true);
 | 
			
		||||
 | 
			
		||||
    memory_synchronizer.SynchronizeOriginalMemory();
 | 
			
		||||
 | 
			
		||||
    ResultCode result = RESULT_SUCCESS;
 | 
			
		||||
 | 
			
		||||
    // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | 
			
		||||
    if (loaded_crs != crs_buffer_ptr) {
 | 
			
		||||
        result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize());
 | 
			
		||||
        if (result.IsError()) {
 | 
			
		||||
            LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
 | 
			
		||||
        }
 | 
			
		||||
        memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loaded_crs = 0;
 | 
			
		||||
    cmd_buff[1] = result.raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x000100C2, Initialize,            "Initialize"},
 | 
			
		||||
    {0x00020082, LoadCRR,               "LoadCRR"},
 | 
			
		||||
    {0x00030042, UnloadCRR,             "UnloadCRR"},
 | 
			
		||||
    {0x000402C2, LoadCRO<false>,        "LoadCRO"},
 | 
			
		||||
    {0x000500C2, UnloadCRO,             "UnloadCRO"},
 | 
			
		||||
    {0x00060042, LinkCRO,               "LinkCRO"},
 | 
			
		||||
    {0x00070042, UnlinkCRO,             "UnlinkCRO"},
 | 
			
		||||
    {0x00080042, Shutdown,              "Shutdown"},
 | 
			
		||||
    {0x000902C2, LoadCRO<true>,         "LoadCRO_New"},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Interface class
 | 
			
		||||
 | 
			
		||||
Interface::Interface() {
 | 
			
		||||
    Register(FunctionTable);
 | 
			
		||||
 | 
			
		||||
    loaded_crs = 0;
 | 
			
		||||
    memory_synchronizer.Clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
							
								
								
									
										46
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Namespace LDR_RO
 | 
			
		||||
 | 
			
		||||
namespace LDR_RO {
 | 
			
		||||
 | 
			
		||||
auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) {
 | 
			
		||||
    auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(), [=](MemoryBlock& b){
 | 
			
		||||
        return b.original == original;
 | 
			
		||||
    });
 | 
			
		||||
    ASSERT(block->mapping == mapping);
 | 
			
		||||
    return block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemorySynchronizer::Clear() {
 | 
			
		||||
    memory_blocks.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) {
 | 
			
		||||
    memory_blocks.push_back(MemoryBlock{mapping, original, size});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) {
 | 
			
		||||
    FindMemoryBlock(mapping, original)->size = size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) {
 | 
			
		||||
    memory_blocks.erase(FindMemoryBlock(mapping, original));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MemorySynchronizer::SynchronizeOriginalMemory() {
 | 
			
		||||
    for (auto& block : memory_blocks) {
 | 
			
		||||
        Memory::CopyBlock(block.original, block.mapping, block.size);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
							
								
								
									
										44
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Namespace LDR_RO
 | 
			
		||||
 | 
			
		||||
namespace LDR_RO {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is a work-around before we implement memory aliasing.
 | 
			
		||||
 * CRS and CRO are mapped (aliased) to another memory when loading. Games can read
 | 
			
		||||
 * from both the original buffer and the mapping memory. So we use this to synchronize
 | 
			
		||||
 * all original buffers with mapping memory after modifying the content.
 | 
			
		||||
 */
 | 
			
		||||
class MemorySynchronizer {
 | 
			
		||||
public:
 | 
			
		||||
    void Clear();
 | 
			
		||||
 | 
			
		||||
    void AddMemoryBlock(VAddr mapping, VAddr original, u32 size);
 | 
			
		||||
    void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size);
 | 
			
		||||
    void RemoveMemoryBlock(VAddr mapping, VAddr original);
 | 
			
		||||
 | 
			
		||||
    void SynchronizeOriginalMemory();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct MemoryBlock {
 | 
			
		||||
        VAddr mapping;
 | 
			
		||||
        VAddr original;
 | 
			
		||||
        u32 size;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<MemoryBlock> memory_blocks;
 | 
			
		||||
 | 
			
		||||
    auto FindMemoryBlock(VAddr mapping, VAddr original);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
@ -15,7 +15,6 @@
 | 
			
		||||
#include "core/hle/service/gsp_gpu.h"
 | 
			
		||||
#include "core/hle/service/gsp_lcd.h"
 | 
			
		||||
#include "core/hle/service/http_c.h"
 | 
			
		||||
#include "core/hle/service/ldr_ro.h"
 | 
			
		||||
#include "core/hle/service/mic_u.h"
 | 
			
		||||
#include "core/hle/service/ns_s.h"
 | 
			
		||||
#include "core/hle/service/nwm_uds.h"
 | 
			
		||||
@ -36,6 +35,7 @@
 | 
			
		||||
#include "core/hle/service/cfg/cfg.h"
 | 
			
		||||
#include "core/hle/service/hid/hid.h"
 | 
			
		||||
#include "core/hle/service/ir/ir.h"
 | 
			
		||||
#include "core/hle/service/ldr_ro/ldr_ro.h"
 | 
			
		||||
#include "core/hle/service/ndm/ndm.h"
 | 
			
		||||
#include "core/hle/service/news/news.h"
 | 
			
		||||
#include "core/hle/service/nim/nim.h"
 | 
			
		||||
 | 
			
		||||
@ -280,6 +280,20 @@ u8* GetPointer(const VAddr vaddr) {
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ReadCString(VAddr vaddr, std::size_t max_length) {
 | 
			
		||||
    std::string string;
 | 
			
		||||
    string.reserve(max_length);
 | 
			
		||||
    for (std::size_t i = 0; i < max_length; ++i) {
 | 
			
		||||
        char c = Read8(vaddr);
 | 
			
		||||
        if (c == '\0')
 | 
			
		||||
            break;
 | 
			
		||||
        string.push_back(c);
 | 
			
		||||
        ++vaddr;
 | 
			
		||||
    }
 | 
			
		||||
    string.shrink_to_fit();
 | 
			
		||||
    return string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u8* GetPhysicalPointer(PAddr address) {
 | 
			
		||||
    // TODO(Subv): This call should not go through the application's memory mapping.
 | 
			
		||||
    return GetPointer(PhysicalToVirtualAddress(address));
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
@ -130,6 +131,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
 | 
			
		||||
 | 
			
		||||
u8* GetPointer(VAddr virtual_address);
 | 
			
		||||
 | 
			
		||||
std::string ReadCString(VAddr virtual_address, std::size_t max_length);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical
 | 
			
		||||
* address. This should be used by services to translate addresses for use by the hardware.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user