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