├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── asmjit ├── asmjit.h ├── core.h ├── core │ ├── arch.h │ ├── assembler.h │ ├── build.h │ ├── builder.h │ ├── callconv.h │ ├── codebufferwriter_p.h │ ├── codeholder.h │ ├── compiler.h │ ├── constpool.h │ ├── cpuinfo.h │ ├── datatypes.h │ ├── emitter.h │ ├── features.h │ ├── func.h │ ├── globals.h │ ├── inst.h │ ├── jitallocator.h │ ├── jitruntime.h │ ├── logging.h │ ├── misc_p.h │ ├── operand.h │ ├── osutils.h │ ├── raassignment_p.h │ ├── rabuilders_p.h │ ├── radefs_p.h │ ├── ralocal_p.h │ ├── rapass_p.h │ ├── rastack_p.h │ ├── string.h │ ├── support.h │ ├── target.h │ ├── type.h │ ├── virtmem.h │ ├── zone.h │ ├── zonehash.h │ ├── zonelist.h │ ├── zonestack.h │ ├── zonestring.h │ ├── zonetree.h │ └── zonevector.h ├── x86.h └── x86 │ ├── x86assembler.h │ ├── x86builder.h │ ├── x86callconv_p.h │ ├── x86compiler.h │ ├── x86emitter.h │ ├── x86features.h │ ├── x86globals.h │ ├── x86instapi_p.h │ ├── x86instdb.h │ ├── x86instdb_p.h │ ├── x86internal_p.h │ ├── x86logging_p.h │ ├── x86opcode_p.h │ ├── x86operand.h │ └── x86rapass_p.h ├── lib └── asmjit.lib └── src ├── AssemblyAnalyzer.cpp ├── AssemblyAnalyzer.h ├── DestructorGenerator.cpp ├── DestructorGenerator.h ├── DllLoader.cpp ├── DllLoader.h ├── SymbolResolver.cpp ├── SymbolResolver.h ├── VTableFixHelper.cpp ├── VTableFixHelper.h ├── controller.cpp ├── controller.h ├── exports.h ├── logging.cpp ├── logging.h ├── provided_symbols.cpp ├── provided_symbols.h ├── util.cpp ├── util.h ├── xinput1_3.cpp ├── xinput1_3.def └── xinput1_3_asm.asm /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | .idea 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(xinput1_3) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | enable_language(ASM_MASM) 6 | 7 | include_directories("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.16.27023\\atlmfc\\include") 8 | include_directories("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\DIA SDK\\include") 9 | include_directories("${PROJECT_SOURCE_DIR}/asmjit") 10 | add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/zydis "zydis" EXCLUDE_FROM_ALL) 11 | 12 | file(GLOB source_list "src/*") 13 | add_library(xinput1_3 SHARED ${source_list}) 14 | target_link_libraries(xinput1_3 "Zydis") 15 | target_link_libraries(xinput1_3 "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\DIA SDK\\lib\\amd64\\diaguids.lib") 16 | target_link_libraries(xinput1_3 "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.16.27023\\atlmfc\\lib\\x64\\atls.lib") 17 | target_link_libraries(xinput1_3 "${PROJECT_SOURCE_DIR}/lib/asmjit.lib") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SatisfactoryModBootstrapper 2 | xinput1_3.dll hook for Satisfactory to load other modules and resolve their symbols in runtime via PDB. 3 | 4 | It will load all modules in the directory "loaders" located in the game's root directory, resolve all 5 | of their UnrealEngine & Satisfactory function import dependencies, and call BootstrapModule 6 | on each of the modules. It provides a low-level API to load additional DLLs which will have their 7 | exports resolved too, which can be used for creating fully functional mod loaders or core mods 8 | dependent only on the game's and Unreal Engine's code. 9 | Module loading order is defined by the alphabetical ordering of their names. 10 | 11 | BootstrapModule signature as follows: 12 | ``` 13 | #include "bootstrapper_exports.h" 14 | 15 | extern "C" DLLEXPORT void BootstrapModule(BootstrapAccessors& accessors); 16 | ``` 17 | Note that you should copy `src/exports.h` to your project and rename it to something 18 | like `bootstrapper_exports.h` to use functions provided by the bootstrapper. 19 | 20 | BootstrapAccessors provide access to various functions of the bootstrapper, 21 | including checking if module is loaded, loading another module, resolving it's 22 | addresses, retrieving root game directory path. 23 | List of the exposed properties can be modified in future releases, 24 | but changes are guaranteed to be backwards compatible. 25 | 26 | You can specify persistent module name independent from file name 27 | which will be used in linking by bootstrapper additionaly to file's normal name. 28 | Function signature as follows 29 | ``` 30 | #include "bootstrapper_exports.h" 31 | 32 | extern "C" DLLEXPORT const char* GetLinkageModuleName(); 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /asmjit/asmjit.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_ASMJIT_H 8 | #define _ASMJIT_ASMJIT_H 9 | 10 | //! \mainpage API Reference 11 | //! 12 | //! AsmJit C++ API reference documentation generated by Doxygen. 13 | //! 14 | //! Introduction provided by the project page at https://github.com/asmjit/asmjit. 15 | //! 16 | //! \section main_groups Groups 17 | //! 18 | //! The documentation is split into the following groups: 19 | //! 20 | //! $$DOCS_GROUP_OVERVIEW$$ 21 | //! 22 | //! \section main_other Other Pages 23 | //! 24 | //! - Class List - List of classes sorted alphabetically 25 | //! - AsmJit Namespace - List of symbols provided by `asmjit` namespace 26 | 27 | //! \namespace asmjit 28 | //! 29 | //! Root namespace used by AsmJit. 30 | 31 | #include "./core.h" 32 | 33 | #ifdef ASMJIT_BUILD_X86 34 | #include "./x86.h" 35 | #endif 36 | 37 | #ifdef ASMJIT_BUILD_ARM 38 | #include "./arm.h" 39 | #endif 40 | 41 | #endif // _ASMJIT_ASMJIT_H 42 | -------------------------------------------------------------------------------- /asmjit/core.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_H 8 | #define _ASMJIT_CORE_H 9 | 10 | //! \defgroup asmjit_core Core 11 | //! \brief Core API. 12 | //! 13 | //! API that provides classes and functions not specific to any architecture. 14 | 15 | //! \defgroup asmjit_builder Builder 16 | //! \brief Builder API. 17 | //! 18 | //! Both Builder and Compiler are emitters that emit everything to a representation 19 | //! that allows further processing. The code stored in such representation is 20 | //! completely safe to be patched, simplified, reordered, obfuscated, removed, 21 | //! injected, analyzed, or processed some other way. Each instruction, label, 22 | //! directive, or other building block is stored as \ref BaseNode (or derived 23 | //! class like \ref InstNode or \ref LabelNode) and contains all the information 24 | //! necessary to pass that node later to the Assembler. 25 | 26 | //! \defgroup asmjit_compiler Compiler 27 | //! \brief Compiler API. 28 | //! 29 | //! Compiler tool is built on top of a \ref asmjit_builder API and adds register 30 | //! allocation and support for defining and calling functions into it. At the 31 | //! moment it's the easiest way to generate some code as most architecture and 32 | //! OS specific stuff is properly abstracted, however, abstractions also mean 33 | //! that not everything is possible with the Compiler. 34 | 35 | //! \defgroup asmjit_func Function 36 | //! \brief Function API. 37 | 38 | //! \defgroup asmjit_jit JIT 39 | //! \brief JIT API and Virtual Memory Management. 40 | 41 | //! \defgroup asmjit_zone Zone 42 | //! \brief Zone allocator and zone allocated containers. 43 | 44 | //! \defgroup asmjit_support Support 45 | //! \brief Support API. 46 | 47 | //! \cond INTERNAL 48 | //! \defgroup asmjit_ra RA 49 | //! \brief Register allocator internals. 50 | //! \endcond 51 | 52 | #include "./core/globals.h" 53 | 54 | #include "./core/arch.h" 55 | #include "./core/assembler.h" 56 | #include "./core/builder.h" 57 | #include "./core/callconv.h" 58 | #include "./core/codeholder.h" 59 | #include "./core/compiler.h" 60 | #include "./core/constpool.h" 61 | #include "./core/cpuinfo.h" 62 | #include "./core/datatypes.h" 63 | #include "./core/emitter.h" 64 | #include "./core/features.h" 65 | #include "./core/func.h" 66 | #include "./core/inst.h" 67 | #include "./core/jitallocator.h" 68 | #include "./core/jitruntime.h" 69 | #include "./core/logging.h" 70 | #include "./core/operand.h" 71 | #include "./core/osutils.h" 72 | #include "./core/string.h" 73 | #include "./core/support.h" 74 | #include "./core/target.h" 75 | #include "./core/type.h" 76 | #include "./core/virtmem.h" 77 | #include "./core/zone.h" 78 | #include "./core/zonehash.h" 79 | #include "./core/zonelist.h" 80 | #include "./core/zonetree.h" 81 | #include "./core/zonestack.h" 82 | #include "./core/zonestring.h" 83 | #include "./core/zonevector.h" 84 | 85 | #endif // _ASMJIT_CORE_H 86 | -------------------------------------------------------------------------------- /asmjit/core/arch.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_ARCH_H 8 | #define _ASMJIT_CORE_ARCH_H 9 | 10 | #include "../core/globals.h" 11 | #include "../core/operand.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_core 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::ArchInfo] 20 | // ============================================================================ 21 | 22 | class ArchInfo { 23 | public: 24 | union { 25 | struct { 26 | //! Architecture id. 27 | uint8_t _id; 28 | //! Architecture sub-id. 29 | uint8_t _subId; 30 | //! Default size of a general purpose register. 31 | uint8_t _gpSize; 32 | //! Count of all general purpose registers. 33 | uint8_t _gpCount; 34 | }; 35 | //! Architecture signature (32-bit int). 36 | uint32_t _signature; 37 | }; 38 | 39 | //! Architecture id. 40 | enum Id : uint32_t { 41 | kIdNone = 0, //!< No/Unknown architecture. 42 | 43 | // X86 architectures. 44 | kIdX86 = 1, //!< X86 architecture (32-bit). 45 | kIdX64 = 2, //!< X64 architecture (64-bit) (AMD64). 46 | 47 | // ARM architectures. 48 | kIdA32 = 3, //!< ARM 32-bit architecture (AArch32/ARM/THUMB). 49 | kIdA64 = 4, //!< ARM 64-bit architecture (AArch64). 50 | 51 | //! Architecture detected at compile-time (architecture of the host). 52 | kIdHost = ASMJIT_ARCH_X86 == 32 ? kIdX86 : 53 | ASMJIT_ARCH_X86 == 64 ? kIdX64 : 54 | ASMJIT_ARCH_ARM == 32 ? kIdA32 : 55 | ASMJIT_ARCH_ARM == 64 ? kIdA64 : kIdNone 56 | }; 57 | 58 | //! Architecture sub-type or execution mode. 59 | enum SubType : uint32_t { 60 | kSubIdNone = 0, //!< Default mode (or no specific mode). 61 | 62 | // X86 sub-types. 63 | kSubIdX86_AVX = 1, //!< Code generation uses AVX by default (VEC instructions). 64 | kSubIdX86_AVX2 = 2, //!< Code generation uses AVX2 by default (VEC instructions). 65 | kSubIdX86_AVX512 = 3, //!< Code generation uses AVX-512F by default (+32 vector regs). 66 | kSubIdX86_AVX512VL = 4, //!< Code generation uses AVX-512F-VL by default (+VL extensions). 67 | 68 | // ARM sub-types. 69 | kSubIdA32_Thumb = 8, //!< THUMB|THUMBv2 sub-type (only ARM in 32-bit mode). 70 | 71 | #if (ASMJIT_ARCH_X86) && defined(__AVX512VL__) 72 | kSubIdHost = kSubIdX86_AVX512VL 73 | #elif (ASMJIT_ARCH_X86) && defined(__AVX512F__) 74 | kSubIdHost = kSubIdX86_AVX512 75 | #elif (ASMJIT_ARCH_X86) && defined(__AVX2__) 76 | kSubIdHost = kSubIdX86_AVX2 77 | #elif (ASMJIT_ARCH_X86) && defined(__AVX__) 78 | kSubIdHost = kSubIdX86_AVX 79 | #elif (ASMJIT_ARCH_ARM == 32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) 80 | kSubIdHost = kSubIdA32_Thumb 81 | #else 82 | kSubIdHost = 0 83 | #endif 84 | }; 85 | 86 | //! \name Construction & Destruction 87 | //! \{ 88 | 89 | inline ArchInfo() noexcept : _signature(0) {} 90 | inline ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {} 91 | inline explicit ArchInfo(uint32_t type, uint32_t subType = kSubIdNone) noexcept { init(type, subType); } 92 | inline explicit ArchInfo(Globals::NoInit_) noexcept {} 93 | 94 | inline static ArchInfo host() noexcept { return ArchInfo(kIdHost, kSubIdHost); } 95 | 96 | inline bool isInitialized() const noexcept { return _id != kIdNone; } 97 | 98 | ASMJIT_API void init(uint32_t type, uint32_t subType = kSubIdNone) noexcept; 99 | inline void reset() noexcept { _signature = 0; } 100 | 101 | //! \} 102 | 103 | //! \name Overloaded Operators 104 | //! \{ 105 | 106 | inline ArchInfo& operator=(const ArchInfo& other) noexcept = default; 107 | 108 | inline bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; } 109 | inline bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; } 110 | 111 | //! \} 112 | 113 | //! \name Accessors 114 | //! \{ 115 | 116 | //! Returns the architecture id, see `Id`. 117 | inline uint32_t archId() const noexcept { return _id; } 118 | 119 | //! Returns the architecture sub-id, see `SubType`. 120 | //! 121 | //! X86 & X64 122 | //! --------- 123 | //! 124 | //! Architecture subtype describe the highest instruction-set level that can 125 | //! be used. 126 | //! 127 | //! A32 & A64 128 | //! --------- 129 | //! 130 | //! Architecture mode means the instruction encoding to be used when generating 131 | //! machine code, thus mode can be used to force generation of THUMB and THUMBv2 132 | //! encoding or regular ARM encoding. 133 | inline uint32_t archSubId() const noexcept { return _subId; } 134 | 135 | //! Tests whether this architecture is 32-bit. 136 | inline bool is32Bit() const noexcept { return _gpSize == 4; } 137 | //! Tests whether this architecture is 64-bit. 138 | inline bool is64Bit() const noexcept { return _gpSize == 8; } 139 | 140 | //! Tests whether this architecture is X86, X64. 141 | inline bool isX86Family() const noexcept { return isX86Family(_id); } 142 | //! Tests whether this architecture is ARM32 or ARM64. 143 | inline bool isArmFamily() const noexcept { return isArmFamily(_id); } 144 | 145 | //! Returns the native size of a general-purpose register. 146 | inline uint32_t gpSize() const noexcept { return _gpSize; } 147 | //! Returns number of general-purpose registers. 148 | inline uint32_t gpCount() const noexcept { return _gpCount; } 149 | 150 | //! \} 151 | 152 | //! \name Static Functions 153 | //! \{ 154 | 155 | static inline bool isX86Family(uint32_t archId) noexcept { return archId >= kIdX86 && archId <= kIdX64; } 156 | static inline bool isArmFamily(uint32_t archId) noexcept { return archId >= kIdA32 && archId <= kIdA64; } 157 | 158 | //! \} 159 | }; 160 | 161 | // ============================================================================ 162 | // [asmjit::ArchRegs] 163 | // ============================================================================ 164 | 165 | //! Information about all architecture registers. 166 | struct ArchRegs { 167 | //! Register information and signatures indexed by `BaseReg::RegType`. 168 | RegInfo regInfo[BaseReg::kTypeMax + 1]; 169 | //! Count (maximum) of registers per `BaseReg::RegType`. 170 | uint8_t regCount[BaseReg::kTypeMax + 1]; 171 | //! Converts RegType to TypeId, see `Type::Id`. 172 | uint8_t regTypeToTypeId[BaseReg::kTypeMax + 1]; 173 | }; 174 | 175 | // ============================================================================ 176 | // [asmjit::ArchUtils] 177 | // ============================================================================ 178 | 179 | struct ArchUtils { 180 | ASMJIT_API static Error typeIdToRegInfo(uint32_t archId, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept; 181 | }; 182 | 183 | //! \} 184 | 185 | ASMJIT_END_NAMESPACE 186 | 187 | #endif // _ASMJIT_CORE_ARCH_H 188 | -------------------------------------------------------------------------------- /asmjit/core/assembler.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_ASSEMBLER_H 8 | #define _ASMJIT_CORE_ASSEMBLER_H 9 | 10 | #include "../core/codeholder.h" 11 | #include "../core/datatypes.h" 12 | #include "../core/emitter.h" 13 | #include "../core/operand.h" 14 | 15 | ASMJIT_BEGIN_NAMESPACE 16 | 17 | //! \addtogroup asmjit_core 18 | //! \{ 19 | 20 | // ============================================================================ 21 | // [asmjit::BaseAssembler] 22 | // ============================================================================ 23 | 24 | //! Base encoder (assembler). 25 | class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter { 26 | public: 27 | ASMJIT_NONCOPYABLE(BaseAssembler) 28 | typedef BaseEmitter Base; 29 | 30 | //! Current section where the assembling happens. 31 | Section* _section; 32 | //! Start of the CodeBuffer of the current section. 33 | uint8_t* _bufferData; 34 | //! End (first invalid byte) of the current section. 35 | uint8_t* _bufferEnd; 36 | //! Pointer in the CodeBuffer of the current section. 37 | uint8_t* _bufferPtr; 38 | //! 5th operand data, used only temporarily. 39 | Operand_ _op4; 40 | //! 6th operand data, used only temporarily. 41 | Operand_ _op5; 42 | 43 | //! \name Construction & Destruction 44 | //! \{ 45 | 46 | //! Creates a new `BaseAssembler` instance. 47 | ASMJIT_API BaseAssembler() noexcept; 48 | //! Destroys the `BaseAssembler` instance. 49 | ASMJIT_API virtual ~BaseAssembler() noexcept; 50 | 51 | //! \} 52 | 53 | //! \name Code-Buffer Management 54 | //! \{ 55 | 56 | //! Returns the capacity of the current CodeBuffer. 57 | inline size_t bufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); } 58 | //! Returns the number of remaining bytes in the current CodeBuffer. 59 | inline size_t remainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); } 60 | 61 | //! Returns the current position in the CodeBuffer. 62 | inline size_t offset() const noexcept { return (size_t)(_bufferPtr - _bufferData); } 63 | //! Sets the current position in the CodeBuffer to `offset`. 64 | //! 65 | //! \note The `offset` cannot be outside of the buffer size (even if it's 66 | //! within buffer's capacity). 67 | ASMJIT_API Error setOffset(size_t offset); 68 | 69 | //! Returns the start of the CodeBuffer in the current section. 70 | inline uint8_t* bufferData() const noexcept { return _bufferData; } 71 | //! Returns the end (first invalid byte) in the current section. 72 | inline uint8_t* bufferEnd() const noexcept { return _bufferEnd; } 73 | //! Returns the current pointer in the CodeBuffer in the current section. 74 | inline uint8_t* bufferPtr() const noexcept { return _bufferPtr; } 75 | 76 | //! \} 77 | 78 | //! \name Section Management 79 | //! \{ 80 | 81 | inline Section* currentSection() const noexcept { return _section; } 82 | 83 | ASMJIT_API Error section(Section* section) override; 84 | 85 | //! \} 86 | 87 | //! \name Label Management 88 | //! \{ 89 | 90 | ASMJIT_API Label newLabel() override; 91 | ASMJIT_API Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) override; 92 | ASMJIT_API Error bind(const Label& label) override; 93 | 94 | //! \} 95 | 96 | //! \cond INTERNAL 97 | //! \name Emit 98 | //! \{ 99 | 100 | using BaseEmitter::_emit; 101 | 102 | ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) override; 103 | ASMJIT_API Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count) override; 104 | 105 | protected: 106 | #ifndef ASMJIT_NO_LOGGING 107 | void _emitLog( 108 | uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, 109 | uint32_t relSize, uint32_t immSize, uint8_t* afterCursor); 110 | 111 | Error _emitFailed( 112 | Error err, 113 | uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); 114 | #else 115 | inline Error _emitFailed( 116 | uint32_t err, 117 | uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { 118 | 119 | ASMJIT_UNUSED(instId); 120 | ASMJIT_UNUSED(options); 121 | ASMJIT_UNUSED(o0); 122 | ASMJIT_UNUSED(o1); 123 | ASMJIT_UNUSED(o2); 124 | ASMJIT_UNUSED(o3); 125 | 126 | resetInstOptions(); 127 | resetInlineComment(); 128 | return reportError(err); 129 | } 130 | #endif 131 | public: 132 | //! \} 133 | //! \endcond 134 | 135 | //! \name Embed 136 | //! \{ 137 | 138 | ASMJIT_API Error embed(const void* data, uint32_t dataSize) override; 139 | ASMJIT_API Error embedLabel(const Label& label) override; 140 | ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) override; 141 | ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override; 142 | 143 | //! \} 144 | 145 | //! \name Comment 146 | //! \{ 147 | 148 | ASMJIT_API Error comment(const char* data, size_t size = SIZE_MAX) override; 149 | 150 | //! \} 151 | 152 | //! \name Events 153 | //! \{ 154 | 155 | ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; 156 | ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; 157 | 158 | //! \} 159 | }; 160 | 161 | //! \} 162 | 163 | ASMJIT_END_NAMESPACE 164 | 165 | #endif // _ASMJIT_CORE_ASSEMBLER_H 166 | -------------------------------------------------------------------------------- /asmjit/core/codebufferwriter_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_CODEBUFFERWRITER_P_H 8 | #define _ASMJIT_CORE_CODEBUFFERWRITER_P_H 9 | 10 | #include "../core/assembler.h" 11 | #include "../core/support.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \cond INTERNAL 16 | //! \addtogroup asmjit_core 17 | //! \{ 18 | 19 | // ============================================================================ 20 | // [asmjit::CodeBufferWriter] 21 | // ============================================================================ 22 | 23 | //! Helper that is used to write into a `CodeBuffer` held by `BaseAssembler`. 24 | class CodeBufferWriter { 25 | public: 26 | uint8_t* _cursor; 27 | 28 | ASMJIT_INLINE explicit CodeBufferWriter(BaseAssembler* a) noexcept 29 | : _cursor(a->_bufferPtr) {} 30 | 31 | ASMJIT_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept { 32 | size_t remainingSpace = (size_t)(a->_bufferEnd - _cursor); 33 | if (ASMJIT_UNLIKELY(remainingSpace < n)) { 34 | CodeBuffer& buffer = a->_section->_buffer; 35 | Error err = a->_code->growBuffer(&buffer, n); 36 | if (ASMJIT_UNLIKELY(err)) 37 | return a->reportError(err); 38 | _cursor = a->_bufferPtr; 39 | } 40 | return kErrorOk; 41 | } 42 | 43 | ASMJIT_INLINE uint8_t* cursor() const noexcept { return _cursor; } 44 | ASMJIT_INLINE void setCursor(uint8_t* cursor) noexcept { _cursor = cursor; } 45 | ASMJIT_INLINE void advance(size_t n) noexcept { _cursor += n; } 46 | 47 | ASMJIT_INLINE size_t offsetFrom(uint8_t* from) const noexcept { 48 | ASMJIT_ASSERT(_cursor >= from); 49 | return (size_t)(_cursor - from); 50 | } 51 | 52 | template 53 | ASMJIT_INLINE void emit8(T val) noexcept { 54 | typedef typename std::make_unsigned::type U; 55 | _cursor[0] = uint8_t(U(val) & U(0xFF)); 56 | _cursor++; 57 | } 58 | 59 | template 60 | ASMJIT_INLINE void emit8If(T val, Y cond) noexcept { 61 | typedef typename std::make_unsigned::type U; 62 | ASMJIT_ASSERT(size_t(cond) <= 1u); 63 | 64 | _cursor[0] = uint8_t(U(val) & U(0xFF)); 65 | _cursor += size_t(cond); 66 | } 67 | 68 | template 69 | ASMJIT_INLINE void emit16uLE(T val) noexcept { 70 | typedef typename std::make_unsigned::type U; 71 | Support::writeU16uLE(_cursor, uint32_t(U(val) & 0xFFFFu)); 72 | _cursor += 2; 73 | } 74 | 75 | template 76 | ASMJIT_INLINE void emit16uBE(T val) noexcept { 77 | typedef typename std::make_unsigned::type U; 78 | Support::writeU16uBE(_cursor, uint32_t(U(val) & 0xFFFFu)); 79 | _cursor += 2; 80 | } 81 | 82 | template 83 | ASMJIT_INLINE void emit32uLE(T val) noexcept { 84 | typedef typename std::make_unsigned::type U; 85 | Support::writeU32uLE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu)); 86 | _cursor += 4; 87 | } 88 | 89 | template 90 | ASMJIT_INLINE void emit32uBE(T val) noexcept { 91 | typedef typename std::make_unsigned::type U; 92 | Support::writeU32uBE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu)); 93 | _cursor += 4; 94 | } 95 | 96 | ASMJIT_INLINE void emitData(const void* data, size_t size) noexcept { 97 | ASMJIT_ASSERT(size != 0); 98 | memcpy(_cursor, data, size); 99 | _cursor += size; 100 | } 101 | 102 | template 103 | ASMJIT_INLINE void emitValueLE(const T& value, size_t size) noexcept { 104 | typedef typename std::make_unsigned::type U; 105 | ASMJIT_ASSERT(size <= sizeof(T)); 106 | 107 | U v = U(value); 108 | for (uint32_t i = 0; i < size; i++) { 109 | _cursor[i] = uint8_t(v & 0xFFu); 110 | v >>= 8; 111 | } 112 | _cursor += size; 113 | } 114 | 115 | template 116 | ASMJIT_INLINE void emitValueBE(const T& value, size_t size) noexcept { 117 | typedef typename std::make_unsigned::type U; 118 | ASMJIT_ASSERT(size <= sizeof(T)); 119 | 120 | U v = U(value); 121 | for (uint32_t i = 0; i < size; i++) { 122 | _cursor[i] = uint8_t(v >> (sizeof(T) - 8)); 123 | v <<= 8; 124 | } 125 | _cursor += size; 126 | } 127 | 128 | ASMJIT_INLINE void emitZeros(size_t size) noexcept { 129 | ASMJIT_ASSERT(size != 0); 130 | memset(_cursor, 0, size); 131 | _cursor += size; 132 | } 133 | 134 | ASMJIT_INLINE void remove8(uint8_t* where) noexcept { 135 | ASMJIT_ASSERT(where < _cursor); 136 | 137 | uint8_t* p = where; 138 | while (++p != _cursor) 139 | p[-1] = p[0]; 140 | _cursor--; 141 | } 142 | 143 | template 144 | ASMJIT_INLINE void insert8(uint8_t* where, T val) noexcept { 145 | uint8_t* p = _cursor; 146 | 147 | while (p != where) { 148 | p[0] = p[-1]; 149 | p--; 150 | } 151 | 152 | *p = uint8_t(val & 0xFF); 153 | _cursor++; 154 | } 155 | 156 | ASMJIT_INLINE void done(BaseAssembler* a) noexcept { 157 | CodeBuffer& buffer = a->_section->_buffer; 158 | size_t newSize = (size_t)(_cursor - a->_bufferData); 159 | ASMJIT_ASSERT(newSize <= buffer.capacity()); 160 | 161 | a->_bufferPtr = _cursor; 162 | buffer._size = Support::max(buffer._size, newSize); 163 | } 164 | }; 165 | 166 | //! \} 167 | //! \endcond 168 | 169 | ASMJIT_END_NAMESPACE 170 | 171 | #endif // _ASMJIT_CORE_CODEBUFFERWRITER_P_H 172 | -------------------------------------------------------------------------------- /asmjit/core/constpool.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_CONSTPOOL_H 8 | #define _ASMJIT_CORE_CONSTPOOL_H 9 | 10 | #include "../core/support.h" 11 | #include "../core/zone.h" 12 | #include "../core/zonetree.h" 13 | 14 | ASMJIT_BEGIN_NAMESPACE 15 | 16 | //! \addtogroup asmjit_core 17 | //! \{ 18 | 19 | // ============================================================================ 20 | // [asmjit::ConstPool] 21 | // ============================================================================ 22 | 23 | //! Constant pool. 24 | class ConstPool { 25 | public: 26 | ASMJIT_NONCOPYABLE(ConstPool) 27 | 28 | //! Constant pool scope. 29 | enum Scope : uint32_t { 30 | //! Local constant, always embedded right after the current function. 31 | kScopeLocal = 0, 32 | //! Global constant, embedded at the end of the currently compiled code. 33 | kScopeGlobal = 1 34 | }; 35 | 36 | //! \cond INTERNAL 37 | 38 | //! Index of a given size in const-pool table. 39 | enum Index : uint32_t { 40 | kIndex1 = 0, 41 | kIndex2 = 1, 42 | kIndex4 = 2, 43 | kIndex8 = 3, 44 | kIndex16 = 4, 45 | kIndex32 = 5, 46 | kIndexCount = 6 47 | }; 48 | 49 | //! Zone-allocated const-pool gap created by two differently aligned constants. 50 | struct Gap { 51 | Gap* _next; //!< Pointer to the next gap 52 | size_t _offset; //!< Offset of the gap. 53 | size_t _size; //!< Remaining bytes of the gap (basically a gap size). 54 | }; 55 | 56 | //! Zone-allocated const-pool node. 57 | class Node : public ZoneTreeNodeT { 58 | public: 59 | ASMJIT_NONCOPYABLE(Node) 60 | 61 | inline Node(size_t offset, bool shared) noexcept 62 | : ZoneTreeNodeT(), 63 | _shared(shared), 64 | _offset(uint32_t(offset)) {} 65 | 66 | inline void* data() const noexcept { 67 | return static_cast(const_cast(this) + 1); 68 | } 69 | 70 | uint32_t _shared : 1; //!< If this constant is shared with another. 71 | uint32_t _offset; //!< Data offset from the beginning of the pool. 72 | }; 73 | 74 | //! Data comparer used internally. 75 | class Compare { 76 | public: 77 | inline Compare(size_t dataSize) noexcept 78 | : _dataSize(dataSize) {} 79 | 80 | inline int operator()(const Node& a, const Node& b) const noexcept { 81 | return ::memcmp(a.data(), b.data(), _dataSize); 82 | } 83 | 84 | inline int operator()(const Node& a, const void* data) const noexcept { 85 | return ::memcmp(a.data(), data, _dataSize); 86 | } 87 | 88 | size_t _dataSize; 89 | }; 90 | 91 | //! Zone-allocated const-pool tree. 92 | struct Tree { 93 | inline explicit Tree(size_t dataSize = 0) noexcept 94 | : _tree(), 95 | _size(0), 96 | _dataSize(dataSize) {} 97 | 98 | inline void reset() noexcept { 99 | _tree.reset(); 100 | _size = 0; 101 | } 102 | 103 | inline bool empty() const noexcept { return _size == 0; } 104 | inline size_t size() const noexcept { return _size; } 105 | 106 | inline void setDataSize(size_t dataSize) noexcept { 107 | ASMJIT_ASSERT(empty()); 108 | _dataSize = dataSize; 109 | } 110 | 111 | inline Node* get(const void* data) noexcept { 112 | Compare cmp(_dataSize); 113 | return _tree.get(data, cmp); 114 | } 115 | 116 | inline void insert(Node* node) noexcept { 117 | Compare cmp(_dataSize); 118 | _tree.insert(node, cmp); 119 | _size++; 120 | } 121 | 122 | template 123 | inline void forEach(Visitor& visitor) const noexcept { 124 | Node* node = _tree.root(); 125 | if (!node) return; 126 | 127 | Node* stack[Globals::kMaxTreeHeight]; 128 | size_t top = 0; 129 | 130 | for (;;) { 131 | Node* left = node->left(); 132 | if (left != nullptr) { 133 | ASMJIT_ASSERT(top != Globals::kMaxTreeHeight); 134 | stack[top++] = node; 135 | 136 | node = left; 137 | continue; 138 | } 139 | 140 | for (;;) { 141 | visitor(node); 142 | node = node->right(); 143 | 144 | if (node != nullptr) 145 | break; 146 | 147 | if (top == 0) 148 | return; 149 | 150 | node = stack[--top]; 151 | } 152 | } 153 | } 154 | 155 | static inline Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept { 156 | Node* node = zone->allocT(sizeof(Node) + size); 157 | if (ASMJIT_UNLIKELY(!node)) return nullptr; 158 | 159 | node = new(node) Node(offset, shared); 160 | memcpy(node->data(), data, size); 161 | return node; 162 | } 163 | 164 | //! RB tree. 165 | ZoneTree _tree; 166 | //! Size of the tree (number of nodes). 167 | size_t _size; 168 | //! Size of the data. 169 | size_t _dataSize; 170 | }; 171 | 172 | //! \endcond 173 | 174 | //! Zone allocator. 175 | Zone* _zone; 176 | //! Tree per size. 177 | Tree _tree[kIndexCount]; 178 | //! Gaps per size. 179 | Gap* _gaps[kIndexCount]; 180 | //! Gaps pool 181 | Gap* _gapPool; 182 | 183 | //! Size of the pool (in bytes). 184 | size_t _size; 185 | //! Required pool alignment. 186 | size_t _alignment; 187 | 188 | //! \name Construction & Destruction 189 | //! \{ 190 | 191 | ASMJIT_API ConstPool(Zone* zone) noexcept; 192 | ASMJIT_API ~ConstPool() noexcept; 193 | 194 | ASMJIT_API void reset(Zone* zone) noexcept; 195 | 196 | //! \} 197 | 198 | //! \name Accessors 199 | //! \{ 200 | 201 | //! Tests whether the constant-pool is empty. 202 | inline bool empty() const noexcept { return _size == 0; } 203 | //! Returns the size of the constant-pool in bytes. 204 | inline size_t size() const noexcept { return _size; } 205 | //! Returns minimum alignment. 206 | inline size_t alignment() const noexcept { return _alignment; } 207 | 208 | //! \} 209 | 210 | //! \name Utilities 211 | //! \{ 212 | 213 | //! Adds a constant to the constant pool. 214 | //! 215 | //! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes. 216 | //! The constant is added to the pool only if it doesn't not exist, otherwise 217 | //! cached value is returned. 218 | //! 219 | //! AsmJit is able to subdivide added constants, so for example if you add 220 | //! 8-byte constant 0x1122334455667788 it will create the following slots: 221 | //! 222 | //! 8-byte: 0x1122334455667788 223 | //! 4-byte: 0x11223344, 0x55667788 224 | //! 225 | //! The reason is that when combining MMX/SSE/AVX code some patterns are used 226 | //! frequently. However, AsmJit is not able to reallocate a constant that has 227 | //! been already added. For example if you try to add 4-byte constant and then 228 | //! 8-byte constant having the same 4-byte pattern as the previous one, two 229 | //! independent slots will be generated by the pool. 230 | ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept; 231 | 232 | //! Fills the destination with the content of this constant pool. 233 | ASMJIT_API void fill(void* dst) const noexcept; 234 | }; 235 | 236 | //! \} 237 | 238 | ASMJIT_END_NAMESPACE 239 | 240 | #endif // _ASMJIT_CORE_CONSTPOOL_H 241 | -------------------------------------------------------------------------------- /asmjit/core/cpuinfo.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_CPUINFO_H 8 | #define _ASMJIT_CORE_CPUINFO_H 9 | 10 | #include "../core/arch.h" 11 | #include "../core/features.h" 12 | #include "../core/globals.h" 13 | #include "../core/string.h" 14 | 15 | ASMJIT_BEGIN_NAMESPACE 16 | 17 | //! \addtogroup asmjit_support 18 | //! \{ 19 | 20 | // ============================================================================ 21 | // [asmjit::CpuInfo] 22 | // ============================================================================ 23 | 24 | //! CPU information. 25 | class CpuInfo { 26 | public: 27 | //! CPU architecture information. 28 | ArchInfo _archInfo; 29 | //! CPU family ID. 30 | uint32_t _familyId; 31 | //! CPU model ID. 32 | uint32_t _modelId; 33 | //! CPU brand ID. 34 | uint32_t _brandId; 35 | //! CPU stepping. 36 | uint32_t _stepping; 37 | //! Processor type. 38 | uint32_t _processorType; 39 | //! Maximum number of addressable IDs for logical processors. 40 | uint32_t _maxLogicalProcessors; 41 | //! Cache line size (in bytes). 42 | uint32_t _cacheLineSize; 43 | //! Number of hardware threads. 44 | uint32_t _hwThreadCount; 45 | 46 | //! CPU vendor string. 47 | FixedString<16> _vendor; 48 | //! CPU brand string. 49 | FixedString<64> _brand; 50 | //! CPU features. 51 | BaseFeatures _features; 52 | 53 | //! \name Construction & Destruction 54 | //! \{ 55 | 56 | inline CpuInfo() noexcept { reset(); } 57 | inline CpuInfo(const CpuInfo& other) noexcept = default; 58 | 59 | inline explicit CpuInfo(Globals::NoInit_) noexcept 60 | : _archInfo(Globals::NoInit), 61 | _features(Globals::NoInit) {}; 62 | 63 | //! Returns the host CPU information. 64 | ASMJIT_API static const CpuInfo& host() noexcept; 65 | 66 | //! Initializes CpuInfo to the given architecture, see `ArchInfo`. 67 | inline void initArch(uint32_t archId, uint32_t archMode = 0) noexcept { 68 | _archInfo.init(archId, archMode); 69 | } 70 | 71 | inline void reset() noexcept { memset(this, 0, sizeof(*this)); } 72 | 73 | //! \} 74 | 75 | //! \name Overloaded Operators 76 | //! \{ 77 | 78 | inline CpuInfo& operator=(const CpuInfo& other) noexcept = default; 79 | 80 | //! \} 81 | 82 | //! \name Accessors 83 | //! \{ 84 | 85 | //! Returns the CPU architecture information. 86 | inline const ArchInfo& archInfo() const noexcept { return _archInfo; } 87 | //! Returns the CPU architecture id, see `ArchInfo::Id`. 88 | inline uint32_t archId() const noexcept { return _archInfo.archId(); } 89 | //! Returns the CPU architecture sub-id, see `ArchInfo::SubId`. 90 | inline uint32_t archSubId() const noexcept { return _archInfo.archSubId(); } 91 | 92 | //! Returns the CPU family ID. 93 | inline uint32_t familyId() const noexcept { return _familyId; } 94 | //! Returns the CPU model ID. 95 | inline uint32_t modelId() const noexcept { return _modelId; } 96 | //! Returns the CPU brand id. 97 | inline uint32_t brandId() const noexcept { return _brandId; } 98 | //! Returns the CPU stepping. 99 | inline uint32_t stepping() const noexcept { return _stepping; } 100 | //! Returns the processor type. 101 | inline uint32_t processorType() const noexcept { return _processorType; } 102 | //! Returns the number of maximum logical processors. 103 | inline uint32_t maxLogicalProcessors() const noexcept { return _maxLogicalProcessors; } 104 | 105 | //! Returns the size of a cache line flush. 106 | inline uint32_t cacheLineSize() const noexcept { return _cacheLineSize; } 107 | //! Returns number of hardware threads available. 108 | inline uint32_t hwThreadCount() const noexcept { return _hwThreadCount; } 109 | 110 | //! Returns the CPU vendor. 111 | inline const char* vendor() const noexcept { return _vendor.str; } 112 | //! Tests whether the CPU vendor is equal to `s`. 113 | inline bool isVendor(const char* s) const noexcept { return _vendor.eq(s); } 114 | 115 | //! Returns the CPU brand string. 116 | inline const char* brand() const noexcept { return _brand.str; } 117 | 118 | //! Returns all CPU features as `BaseFeatures`, cast to your arch-specific class 119 | //! if needed. 120 | template 121 | inline const T& features() const noexcept { return _features.as(); } 122 | 123 | //! Tests whether the CPU has the given `feature`. 124 | inline bool hasFeature(uint32_t featureId) const noexcept { return _features.has(featureId); } 125 | //! Adds the given CPU `feature` to the list of this CpuInfo features. 126 | inline CpuInfo& addFeature(uint32_t featureId) noexcept { _features.add(featureId); return *this; } 127 | 128 | //! \} 129 | }; 130 | 131 | //! \} 132 | 133 | ASMJIT_END_NAMESPACE 134 | 135 | #endif // _ASMJIT_CORE_CPUINFO_H 136 | -------------------------------------------------------------------------------- /asmjit/core/features.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_FEATURES_H 8 | #define _ASMJIT_CORE_FEATURES_H 9 | 10 | #include "../core/globals.h" 11 | #include "../core/support.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_core 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::BaseFeatures] 20 | // ============================================================================ 21 | 22 | class BaseFeatures { 23 | public: 24 | typedef Support::BitWord BitWord; 25 | 26 | enum : uint32_t { 27 | kMaxFeatures = 128, 28 | kNumBitWords = kMaxFeatures / Support::kBitWordSizeInBits 29 | }; 30 | 31 | BitWord _bits[kNumBitWords]; 32 | 33 | //! \name Construction & Destruction 34 | //! \{ 35 | 36 | inline BaseFeatures() noexcept { reset(); } 37 | inline BaseFeatures(const BaseFeatures& other) noexcept = default; 38 | inline explicit BaseFeatures(Globals::NoInit_) noexcept {} 39 | 40 | inline void reset() noexcept { 41 | for (size_t i = 0; i < kNumBitWords; i++) 42 | _bits[i] = 0; 43 | } 44 | 45 | //! \} 46 | 47 | //! \name Overloaded Operators 48 | //! \{ 49 | 50 | inline BaseFeatures& operator=(const BaseFeatures& other) noexcept = default; 51 | 52 | inline bool operator==(const BaseFeatures& other) noexcept { return eq(other); } 53 | inline bool operator!=(const BaseFeatures& other) noexcept { return !eq(other); } 54 | 55 | //! \} 56 | 57 | //! \name Cast 58 | //! \{ 59 | 60 | template 61 | inline T& as() noexcept { return static_cast(*this); } 62 | 63 | template 64 | inline const T& as() const noexcept { return static_cast(*this); } 65 | 66 | //! \} 67 | 68 | //! \name Accessors 69 | //! \{ 70 | 71 | //! Returns all features as `BitWord` array. 72 | inline BitWord* bits() noexcept { return _bits; } 73 | //! Returns all features as `BitWord` array (const). 74 | inline const BitWord* bits() const noexcept { return _bits; } 75 | 76 | //! Tests whether the feature `featureId` is present. 77 | inline bool has(uint32_t featureId) const noexcept { 78 | ASMJIT_ASSERT(featureId < kMaxFeatures); 79 | 80 | uint32_t idx = featureId / Support::kBitWordSizeInBits; 81 | uint32_t bit = featureId % Support::kBitWordSizeInBits; 82 | 83 | return bool((_bits[idx] >> bit) & 0x1); 84 | } 85 | 86 | //! Tests whether all features as defined by `other` are present. 87 | inline bool hasAll(const BaseFeatures& other) const noexcept { 88 | for (uint32_t i = 0; i < kNumBitWords; i++) 89 | if ((_bits[i] & other._bits[i]) != other._bits[i]) 90 | return false; 91 | return true; 92 | } 93 | 94 | //! \} 95 | 96 | //! \name Utilities 97 | //! \{ 98 | 99 | //! Adds the given CPU `featureId` to the list of features. 100 | inline void add(uint32_t featureId) noexcept { 101 | ASMJIT_ASSERT(featureId < kMaxFeatures); 102 | 103 | uint32_t idx = featureId / Support::kBitWordSizeInBits; 104 | uint32_t bit = featureId % Support::kBitWordSizeInBits; 105 | 106 | _bits[idx] |= BitWord(1) << bit; 107 | } 108 | 109 | template 110 | inline void add(uint32_t featureId, Args... otherIds) noexcept { 111 | add(featureId); 112 | add(otherIds...); 113 | } 114 | 115 | //! Removes the given CPU `featureId` from the list of features. 116 | inline void remove(uint32_t featureId) noexcept { 117 | ASMJIT_ASSERT(featureId < kMaxFeatures); 118 | 119 | uint32_t idx = featureId / Support::kBitWordSizeInBits; 120 | uint32_t bit = featureId % Support::kBitWordSizeInBits; 121 | 122 | _bits[idx] &= ~(BitWord(1) << bit); 123 | } 124 | 125 | template 126 | inline void remove(uint32_t featureId, Args... otherIds) noexcept { 127 | remove(featureId); 128 | remove(otherIds...); 129 | } 130 | 131 | inline bool eq(const BaseFeatures& other) const noexcept { 132 | for (size_t i = 0; i < kNumBitWords; i++) 133 | if (_bits[i] != other._bits[i]) 134 | return false; 135 | return true; 136 | } 137 | 138 | //! \} 139 | }; 140 | 141 | //! \} 142 | 143 | ASMJIT_END_NAMESPACE 144 | 145 | #endif // _ASMJIT_CORE_FEATURES_H 146 | -------------------------------------------------------------------------------- /asmjit/core/jitallocator.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_JITALLOCATOR_H 8 | #define _ASMJIT_CORE_JITALLOCATOR_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_JIT 12 | 13 | #include "../core/globals.h" 14 | #include "../core/virtmem.h" 15 | 16 | ASMJIT_BEGIN_NAMESPACE 17 | 18 | //! \addtogroup asmjit_jit 19 | //! \{ 20 | 21 | // ============================================================================ 22 | // [asmjit::JitAllocator] 23 | // ============================================================================ 24 | 25 | //! A simple implementation of memory manager that uses `asmjit::VirtMem` 26 | //! functions to manage virtual memory for JIT compiled code. 27 | //! 28 | //! Implementation notes: 29 | //! 30 | //! - Granularity of allocated blocks is different than granularity for a typical 31 | //! C malloc. In addition, the allocator can use several memory pools having a 32 | //! different granularity to minimize the maintenance overhead. Multiple pools 33 | //! feature requires `kFlagUseMultiplePools` flag to be set. 34 | //! 35 | //! - The allocator doesn't store any information in executable memory, instead, 36 | //! the implementation uses two bit-vectors to manage allocated memory of each 37 | //! allocator-block. The first bit-vector called 'used' is used to track used 38 | //! memory (where each bit represents memory size defined by granularity) and 39 | //! the second bit vector called 'stop' is used as a sentinel to mark where 40 | //! the allocated area ends. 41 | //! 42 | //! - Internally, the allocator also uses RB tree to keep track of all blocks 43 | //! across all pools. Each inserted block is added to the tree so it can be 44 | //! matched fast during `release()` and `shrink()`. 45 | class JitAllocator { 46 | public: 47 | ASMJIT_NONCOPYABLE(JitAllocator) 48 | 49 | struct Impl { 50 | //! Allocator options, see \ref JitAllocator::Options. 51 | uint32_t options; 52 | //! Base block size (0 if the allocator is not initialized). 53 | uint32_t blockSize; 54 | //! Base granularity (0 if the allocator is not initialized). 55 | uint32_t granularity; 56 | //! A pattern that is used to fill unused memory if secure mode is enabled. 57 | uint32_t fillPattern; 58 | }; 59 | 60 | //! Allocator implementation (private). 61 | Impl* _impl; 62 | 63 | enum Options : uint32_t { 64 | //! Enables the use of an anonymous memory-mapped memory that is mapped into 65 | //! two buffers having a different pointer. The first buffer has read and 66 | //! execute permissions and the second buffer has read+write permissions. 67 | //! 68 | //! See \ref VirtMem::allocDualMapping() for more details about this feature. 69 | kOptionUseDualMapping = 0x00000001u, 70 | 71 | //! Enables the use of multiple pools with increasing granularity instead of 72 | //! a single pool. This flag would enable 3 internal pools in total having 73 | //! 64, 128, and 256 bytes granularity. 74 | //! 75 | //! This feature is only recommended for users that generate a lot of code 76 | //! and would like to minimize the overhead of `JitAllocator` itself by 77 | //! having blocks of different allocation granularities. Using this feature 78 | //! only for few allocations won't pay off as the allocator may need to 79 | //! create more blocks initially before it can take the advantage of 80 | //! variable block granularity. 81 | kOptionUseMultiplePools = 0x00000002u, 82 | 83 | //! Always fill reserved memory by a fill-pattern. 84 | //! 85 | //! Causes a new block to be cleared by the fill pattern and freshly 86 | //! released memory to be cleared before making it ready for another use. 87 | kOptionFillUnusedMemory = 0x00000004u, 88 | 89 | //! When this flag is set the allocator would immediately release unused 90 | //! blocks during `release()` or `reset()`. When this flag is not set the 91 | //! allocator would keep one empty block in each pool to prevent excessive 92 | //! virtual memory allocations and deallocations in border cases, which 93 | //! involve constantly allocating and deallocating a single block caused 94 | //! by repetitive calling `alloc()` and `release()` when the allocator has 95 | //! either no blocks or have all blocks fully occupied. 96 | kOptionImmediateRelease = 0x00000008u, 97 | 98 | //! Use a custom fill pattern, must be combined with `kFlagFillUnusedMemory`. 99 | kOptionCustomFillPattern = 0x10000000u 100 | }; 101 | 102 | //! \name Construction & Destruction 103 | //! \{ 104 | 105 | //! Parameters that can be passed to `JitAllocator` constructor. 106 | //! 107 | //! Use it like this: 108 | //! 109 | //! ``` 110 | //! // Zero initialize (zero means the default value) and change what you need. 111 | //! JitAllocator::CreateParams params {}; 112 | //! params.blockSize = 1024 * 1024; 113 | //! 114 | //! // Create the allocator. 115 | //! JitAllocator allocator(¶ms); 116 | //! ``` 117 | struct CreateParams { 118 | // Reset the content of `CreateParams`. 119 | inline void reset() noexcept { memset(this, 0, sizeof(*this)); } 120 | 121 | //! Allocator options, see \ref JitAllocator::Options. 122 | //! 123 | //! No options are used by default. 124 | uint32_t options; 125 | 126 | //! Base size of a single block in bytes (default 64kB). 127 | //! 128 | //! \remarks Block size must be equal or greater to page size and must be 129 | //! power of 2. If the input is not valid then the default block size will 130 | //! be used instead. 131 | uint32_t blockSize; 132 | 133 | //! Base granularity (and also natural alignment) of allocations in bytes 134 | //! (default 64). 135 | //! 136 | //! Since the `JitAllocator` uses bit-arrays to mark used memory the 137 | //! granularity also specifies how many bytes correspond to a single bit in 138 | //! such bit-array. Higher granularity means more waste of virtual memory 139 | //! (as it increases the natural alignment), but smaller bit-arrays as less 140 | //! bits would be required per a single block. 141 | uint32_t granularity; 142 | 143 | //! Patter to use to fill unused memory. 144 | //! 145 | //! Only used if \ref kOptionCustomFillPattern is set. 146 | uint32_t fillPattern; 147 | }; 148 | 149 | //! Creates a `JitAllocator` instance. 150 | explicit ASMJIT_API JitAllocator(const CreateParams* params = nullptr) noexcept; 151 | //! Destroys the `JitAllocator` instance and release all blocks held. 152 | ASMJIT_API ~JitAllocator() noexcept; 153 | 154 | inline bool isInitialized() const noexcept { return _impl->blockSize == 0; } 155 | 156 | //! Free all allocated memory - makes all pointers returned by `alloc()` invalid. 157 | //! 158 | //! \remarks This function is not thread-safe as it's designed to be used when 159 | //! nobody else is using allocator. The reason is that there is no point of 160 | //1 calling `reset()` when the allocator is still in use. 161 | ASMJIT_API void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept; 162 | 163 | //! \} 164 | 165 | //! \name Accessors 166 | //! \{ 167 | 168 | //! Returns allocator options, see `Flags`. 169 | inline uint32_t options() const noexcept { return _impl->options; } 170 | //! Tests whether the allocator has the given `option` set. 171 | inline bool hasOption(uint32_t option) const noexcept { return (_impl->options & option) != 0; } 172 | 173 | //! Returns a base block size (a minimum size of block that the allocator would allocate). 174 | inline uint32_t blockSize() const noexcept { return _impl->blockSize; } 175 | //! Returns granularity of the allocator. 176 | inline uint32_t granularity() const noexcept { return _impl->granularity; } 177 | //! Returns pattern that is used to fill unused memory if `kFlagUseFillPattern` is set. 178 | inline uint32_t fillPattern() const noexcept { return _impl->fillPattern; } 179 | 180 | //! \} 181 | 182 | //! \name Alloc & Release 183 | //! \{ 184 | 185 | //! Allocate `size` bytes of virtual memory. 186 | //! 187 | //! \remarks This function is thread-safe. 188 | ASMJIT_API Error alloc(void** roPtrOut, void** rwPtrOut, size_t size) noexcept; 189 | 190 | //! Release a memory returned by `alloc()`. 191 | //! 192 | //! \remarks This function is thread-safe. 193 | ASMJIT_API Error release(void* ro) noexcept; 194 | 195 | //! Free extra memory allocated with `p` by restricting it to `newSize` size. 196 | //! 197 | //! \remarks This function is thread-safe. 198 | ASMJIT_API Error shrink(void* ro, size_t newSize) noexcept; 199 | 200 | //! \} 201 | 202 | //! \name Statistics 203 | //! \{ 204 | 205 | //! Statistics about `JitAllocator`. 206 | struct Statistics { 207 | inline void reset() noexcept { 208 | _blockCount = 0; 209 | _usedSize = 0; 210 | _reservedSize = 0; 211 | _overheadSize = 0; 212 | } 213 | 214 | //! Returns count of blocks managed by `JitAllocator` at the moment. 215 | inline size_t blockCount() const noexcept { return _blockCount; } 216 | 217 | //! Returns how many bytes are currently used. 218 | inline size_t usedSize() const noexcept { return _usedSize; } 219 | //! Returns the number of bytes unused by the allocator at the moment. 220 | inline size_t unusedSize() const noexcept { return _reservedSize - _usedSize; } 221 | //! Returns the total number of bytes bytes reserved by the allocator (sum of sizes of all blocks). 222 | inline size_t reservedSize() const noexcept { return _reservedSize; } 223 | //! Returns the number of bytes the allocator needs to manage the allocated memory. 224 | inline size_t overheadSize() const noexcept { return _overheadSize; } 225 | 226 | inline double usedSizeAsPercent() const noexcept { 227 | return (double(usedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 228 | } 229 | 230 | inline double unusedSizeAsPercent() const noexcept { 231 | return (double(unusedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 232 | } 233 | 234 | inline double overheadSizeAsPercent() const noexcept { 235 | return (double(overheadSize()) / (double(reservedSize()) + 1e-16)) * 100.0; 236 | } 237 | 238 | //! Number of blocks `JitAllocator` maintains. 239 | size_t _blockCount; 240 | //! How many bytes are currently used / allocated. 241 | size_t _usedSize; 242 | //! How many bytes are currently reserved by the allocator. 243 | size_t _reservedSize; 244 | //! Allocation overhead (in bytes) required to maintain all blocks. 245 | size_t _overheadSize; 246 | }; 247 | 248 | //! Returns JIT allocator statistics. 249 | //! 250 | //! \remarks This function is thread-safe. 251 | ASMJIT_API Statistics statistics() const noexcept; 252 | 253 | //! \} 254 | }; 255 | 256 | //! \} 257 | 258 | ASMJIT_END_NAMESPACE 259 | 260 | #endif 261 | #endif 262 | -------------------------------------------------------------------------------- /asmjit/core/jitruntime.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_JITRUNTIME_H 8 | #define _ASMJIT_CORE_JITRUNTIME_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_JIT 12 | 13 | #include "../core/codeholder.h" 14 | #include "../core/jitallocator.h" 15 | #include "../core/target.h" 16 | 17 | ASMJIT_BEGIN_NAMESPACE 18 | 19 | class CodeHolder; 20 | 21 | //! \addtogroup asmjit_jit 22 | //! \{ 23 | 24 | // ============================================================================ 25 | // [asmjit::JitRuntime] 26 | // ============================================================================ 27 | 28 | //! JIT execution runtime is a special `Target` that is designed to store and 29 | //! execute the generated code. 30 | class ASMJIT_VIRTAPI JitRuntime : public Target { 31 | public: 32 | ASMJIT_NONCOPYABLE(JitRuntime) 33 | 34 | //! Virtual memory allocator. 35 | JitAllocator _allocator; 36 | 37 | //! \name Construction & Destruction 38 | //! \{ 39 | 40 | //! Creates a `JitRuntime` instance. 41 | explicit ASMJIT_API JitRuntime(const JitAllocator::CreateParams* params = nullptr) noexcept; 42 | //! Destroys the `JitRuntime` instance. 43 | ASMJIT_API virtual ~JitRuntime() noexcept; 44 | 45 | inline void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept { 46 | _allocator.reset(resetPolicy); 47 | } 48 | 49 | //! \} 50 | 51 | //! \name Accessors 52 | //! \{ 53 | 54 | //! Returns the associated `JitAllocator`. 55 | inline JitAllocator* allocator() const noexcept { return const_cast(&_allocator); } 56 | 57 | //! \} 58 | 59 | //! \name Utilities 60 | //! \{ 61 | 62 | // NOTE: To allow passing function pointers to `add()` and `release()` the 63 | // virtual methods are prefixed with `_` and called from templates instead. 64 | 65 | //! Allocates memory needed for a code stored in the `CodeHolder` and relocates 66 | //! the code to the pointer allocated. 67 | //! 68 | //! The beginning of the memory allocated for the function is returned in `dst`. 69 | //! If failed `Error` code is returned and `dst` is explicitly set to `nullptr` 70 | //! (this means that you don't have to set it to null before calling `add()`). 71 | template 72 | inline Error add(Func* dst, CodeHolder* code) noexcept { 73 | return _add(Support::ptr_cast_impl(dst), code); 74 | } 75 | 76 | //! Releases `p` which was obtained by calling `add()`. 77 | template 78 | inline Error release(Func p) noexcept { 79 | return _release(Support::ptr_cast_impl(p)); 80 | } 81 | 82 | //! Type-unsafe version of `add()`. 83 | ASMJIT_API virtual Error _add(void** dst, CodeHolder* code) noexcept; 84 | 85 | //! Type-unsafe version of `release()`. 86 | ASMJIT_API virtual Error _release(void* p) noexcept; 87 | 88 | //! Flushes an instruction cache. 89 | //! 90 | //! This member function is called after the code has been copied to the 91 | //! destination buffer. It is only useful for JIT code generation as it 92 | //! causes a flush of the processor's cache. 93 | //! 94 | //! Flushing is basically a NOP under X86, but is needed by architectures 95 | //! that do not have a transparent instruction cache like ARM. 96 | //! 97 | //! This function can also be overridden to improve compatibility with tools 98 | //! such as Valgrind, however, it's not an official part of AsmJit. 99 | ASMJIT_API virtual void flush(const void* p, size_t size) noexcept; 100 | 101 | //! \} 102 | }; 103 | 104 | //! \} 105 | 106 | ASMJIT_END_NAMESPACE 107 | 108 | #endif 109 | #endif 110 | -------------------------------------------------------------------------------- /asmjit/core/logging.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_LOGGING_H 8 | #define _ASMJIT_CORE_LOGGING_H 9 | 10 | #include "../core/inst.h" 11 | #include "../core/string.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_core 16 | //! \{ 17 | 18 | #ifndef ASMJIT_NO_LOGGING 19 | 20 | // ============================================================================ 21 | // [Forward Declarations] 22 | // ============================================================================ 23 | 24 | class BaseEmitter; 25 | class BaseReg; 26 | class Logger; 27 | struct Operand_; 28 | 29 | #ifndef ASMJIT_NO_BUILDER 30 | class BaseBuilder; 31 | class BaseNode; 32 | #endif 33 | 34 | // ============================================================================ 35 | // [asmjit::FormatOptions] 36 | // ============================================================================ 37 | 38 | class FormatOptions { 39 | public: 40 | uint32_t _flags; 41 | uint8_t _indentation[4]; 42 | 43 | enum Flags : uint32_t { 44 | //!< Show also binary form of each logged instruction (assembler). 45 | kFlagMachineCode = 0x00000001u, 46 | //!< Show a text explanation of some immediate values. 47 | kFlagExplainImms = 0x00000002u, 48 | //!< Use hexadecimal notation of immediate values. 49 | kFlagHexImms = 0x00000004u, 50 | //!< Use hexadecimal notation of address offsets. 51 | kFlagHexOffsets = 0x00000008u, 52 | //!< Show casts between virtual register types (compiler). 53 | kFlagRegCasts = 0x00000010u, 54 | //!< Show positions associated with nodes (compiler). 55 | kFlagPositions = 0x00000020u, 56 | //!< Annotate nodes that are lowered by passes. 57 | kFlagAnnotations = 0x00000040u, 58 | 59 | // TODO: These must go, keep this only for formatting. 60 | //!< Show an additional output from passes. 61 | kFlagDebugPasses = 0x00000080u, 62 | //!< Show an additional output from RA. 63 | kFlagDebugRA = 0x00000100u 64 | }; 65 | 66 | enum IndentationType : uint32_t { 67 | //! Indentation used for instructions and directives. 68 | kIndentationCode = 0u, 69 | //! Indentation used for labels and function nodes. 70 | kIndentationLabel = 1u, 71 | //! Indentation used for comments (not inline comments). 72 | kIndentationComment = 2u, 73 | kIndentationReserved = 3u 74 | }; 75 | 76 | //! \name Construction & Destruction 77 | //! \{ 78 | 79 | constexpr FormatOptions() noexcept 80 | : _flags(0), 81 | _indentation { 0, 0, 0, 0 } {} 82 | 83 | constexpr FormatOptions(const FormatOptions& other) noexcept = default; 84 | inline FormatOptions& operator=(const FormatOptions& other) noexcept = default; 85 | 86 | inline void reset() noexcept { 87 | _flags = 0; 88 | _indentation[0] = 0; 89 | _indentation[1] = 0; 90 | _indentation[2] = 0; 91 | _indentation[3] = 0; 92 | } 93 | 94 | //! \} 95 | 96 | //! \name Accessors 97 | //! \{ 98 | 99 | constexpr uint32_t flags() const noexcept { return _flags; } 100 | constexpr bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } 101 | inline void setFlags(uint32_t flags) noexcept { _flags = flags; } 102 | inline void addFlags(uint32_t flags) noexcept { _flags |= flags; } 103 | inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } 104 | 105 | constexpr uint8_t indentation(uint32_t type) const noexcept { return _indentation[type]; } 106 | inline void setIndentation(uint32_t type, uint32_t n) noexcept { _indentation[type] = uint8_t(n); } 107 | inline void resetIndentation(uint32_t type) noexcept { _indentation[type] = uint8_t(0); } 108 | 109 | //! \} 110 | }; 111 | 112 | // ============================================================================ 113 | // [asmjit::Logger] 114 | // ============================================================================ 115 | 116 | //! Abstract logging interface and helpers. 117 | //! 118 | //! This class can be inherited and reimplemented to fit into your logging 119 | //! subsystem. When reimplementing use `Logger::_log()` method to log into 120 | //! a custom stream. 121 | //! 122 | //! There are two `Logger` implementations offered by AsmJit: 123 | //! - `FileLogger` - allows to log into `FILE*`. 124 | //! - `StringLogger` - logs into a `String`. 125 | class ASMJIT_VIRTAPI Logger { 126 | public: 127 | ASMJIT_BASE_CLASS(Logger) 128 | ASMJIT_NONCOPYABLE(Logger) 129 | 130 | //! Format options. 131 | FormatOptions _options; 132 | 133 | //! \name Construction & Destruction 134 | //! \{ 135 | 136 | //! Creates a `Logger` instance. 137 | ASMJIT_API Logger() noexcept; 138 | //! Destroys the `Logger` instance. 139 | ASMJIT_API virtual ~Logger() noexcept; 140 | 141 | //! \} 142 | 143 | //! \name Format Options 144 | //! \{ 145 | 146 | inline FormatOptions& options() noexcept { return _options; } 147 | inline const FormatOptions& options() const noexcept { return _options; } 148 | 149 | inline uint32_t flags() const noexcept { return _options.flags(); } 150 | inline bool hasFlag(uint32_t flag) const noexcept { return _options.hasFlag(flag); } 151 | inline void setFlags(uint32_t flags) noexcept { _options.setFlags(flags); } 152 | inline void addFlags(uint32_t flags) noexcept { _options.addFlags(flags); } 153 | inline void clearFlags(uint32_t flags) noexcept { _options.clearFlags(flags); } 154 | 155 | inline uint32_t indentation(uint32_t type) const noexcept { return _options.indentation(type); } 156 | inline void setIndentation(uint32_t type, uint32_t n) noexcept { _options.setIndentation(type, n); } 157 | inline void resetIndentation(uint32_t type) noexcept { _options.resetIndentation(type); } 158 | 159 | //! \} 160 | 161 | //! \name Logging Interface 162 | //! \{ 163 | 164 | //! Logs `str` - must be reimplemented. 165 | virtual Error _log(const char* data, size_t size) noexcept = 0; 166 | 167 | //! Logs string `str`, which is either null terminated or having size `size`. 168 | inline Error log(const char* data, size_t size = SIZE_MAX) noexcept { return _log(data, size); } 169 | //! Logs content of a string `str`. 170 | inline Error log(const String& str) noexcept { return _log(str.data(), str.size()); } 171 | 172 | //! Formats the message by using `snprintf()` and then sends the result 173 | //! to `log()`. 174 | ASMJIT_API Error logf(const char* fmt, ...) noexcept; 175 | 176 | //! Formats the message by using `vsnprintf()` and then sends the result 177 | //! to `log()`. 178 | ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept; 179 | 180 | //! Logs binary data. 181 | ASMJIT_API Error logBinary(const void* data, size_t size) noexcept; 182 | 183 | //! \} 184 | }; 185 | 186 | // ============================================================================ 187 | // [asmjit::FileLogger] 188 | // ============================================================================ 189 | 190 | //! Logger that can log to a `FILE*`. 191 | class ASMJIT_VIRTAPI FileLogger : public Logger { 192 | public: 193 | ASMJIT_NONCOPYABLE(FileLogger) 194 | 195 | FILE* _file; 196 | 197 | //! \name Construction & Destruction 198 | //! \{ 199 | 200 | //! Creates a new `FileLogger` that logs to `FILE*`. 201 | ASMJIT_API FileLogger(FILE* file = nullptr) noexcept; 202 | //! Destroys the `FileLogger`. 203 | ASMJIT_API virtual ~FileLogger() noexcept; 204 | 205 | //! \} 206 | 207 | //! \name Accessors 208 | //! \{ 209 | 210 | //! Returns the logging output stream or null if the logger has no output 211 | //! stream. 212 | inline FILE* file() const noexcept { return _file; } 213 | 214 | //! Sets the logging output stream to `stream` or null. 215 | //! 216 | //! \note If the `file` is null the logging will be disabled. When a logger 217 | //! is attached to `CodeHolder` or any emitter the logging API will always 218 | //! be called regardless of the output file. This means that if you really 219 | //! want to disable logging at emitter level you must not attach a logger 220 | //! to it. 221 | inline void setFile(FILE* file) noexcept { _file = file; } 222 | 223 | //! \} 224 | 225 | ASMJIT_API Error _log(const char* data, size_t size = SIZE_MAX) noexcept override; 226 | }; 227 | 228 | // ============================================================================ 229 | // [asmjit::StringLogger] 230 | // ============================================================================ 231 | 232 | //! Logger that stores everything in an internal string buffer. 233 | class ASMJIT_VIRTAPI StringLogger : public Logger { 234 | public: 235 | ASMJIT_NONCOPYABLE(StringLogger) 236 | 237 | //! Logger data as string. 238 | String _content; 239 | 240 | //! \name Construction & Destruction 241 | //! \{ 242 | 243 | //! Create new `StringLogger`. 244 | ASMJIT_API StringLogger() noexcept; 245 | //! Destroys the `StringLogger`. 246 | ASMJIT_API virtual ~StringLogger() noexcept; 247 | 248 | //! \} 249 | 250 | //! \name Logger Data Accessors 251 | //! \{ 252 | 253 | //! Returns aggregated logger data as `char*` pointer. 254 | //! 255 | //! The pointer is owned by `StringLogger`, it can't be modified or freed. 256 | inline const char* data() const noexcept { return _content.data(); } 257 | //! Returns size of the data returned by `data()`. 258 | inline size_t dataSize() const noexcept { return _content.size(); } 259 | 260 | //! \} 261 | 262 | //! \name Logger Data Manipulation 263 | //! \{ 264 | 265 | //! Clears the accumulated logger data. 266 | inline void clear() noexcept { _content.clear(); } 267 | 268 | //! \} 269 | 270 | ASMJIT_API Error _log(const char* data, size_t size = SIZE_MAX) noexcept override; 271 | }; 272 | 273 | // ============================================================================ 274 | // [asmjit::Logging] 275 | // ============================================================================ 276 | 277 | struct Logging { 278 | ASMJIT_API static Error formatRegister( 279 | String& sb, 280 | uint32_t flags, 281 | const BaseEmitter* emitter, 282 | uint32_t archId, 283 | uint32_t regType, 284 | uint32_t regId) noexcept; 285 | 286 | ASMJIT_API static Error formatLabel( 287 | String& sb, 288 | uint32_t flags, 289 | const BaseEmitter* emitter, 290 | uint32_t labelId) noexcept; 291 | 292 | ASMJIT_API static Error formatOperand( 293 | String& sb, 294 | uint32_t flags, 295 | const BaseEmitter* emitter, 296 | uint32_t archId, 297 | const Operand_& op) noexcept; 298 | 299 | ASMJIT_API static Error formatInstruction( 300 | String& sb, 301 | uint32_t flags, 302 | const BaseEmitter* emitter, 303 | uint32_t archId, 304 | const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept; 305 | 306 | ASMJIT_API static Error formatTypeId( 307 | String& sb, 308 | uint32_t typeId) noexcept; 309 | 310 | #ifndef ASMJIT_NO_BUILDER 311 | ASMJIT_API static Error formatNode( 312 | String& sb, 313 | uint32_t flags, 314 | const BaseBuilder* cb, 315 | const BaseNode* node_) noexcept; 316 | #endif 317 | 318 | // Only used by AsmJit internals, not available to users. 319 | #if defined(ASMJIT_EXPORTS) 320 | enum { 321 | // Has to be big to be able to hold all metadata compiler can assign to a 322 | // single instruction. 323 | kMaxInstLineSize = 44, 324 | kMaxBinarySize = 26 325 | }; 326 | 327 | static Error formatLine( 328 | String& sb, 329 | const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept; 330 | #endif 331 | }; 332 | #endif 333 | 334 | //! \} 335 | 336 | ASMJIT_END_NAMESPACE 337 | 338 | #endif // _ASMJIT_CORE_LOGGER_H 339 | -------------------------------------------------------------------------------- /asmjit/core/misc_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_MISC_P_H 8 | #define _ASMJIT_CORE_MISC_P_H 9 | 10 | #include "../core/build.h" 11 | 12 | ASMJIT_BEGIN_NAMESPACE 13 | 14 | //! \cond INTERNAL 15 | //! \addtogroup asmjit_support 16 | //! \{ 17 | 18 | #define ASMJIT_LOOKUP_TABLE_8(T, I) T((I)), T((I+1)), T((I+2)), T((I+3)), T((I+4)), T((I+5)), T((I+6)), T((I+7)) 19 | #define ASMJIT_LOOKUP_TABLE_16(T, I) ASMJIT_LOOKUP_TABLE_8(T, I), ASMJIT_LOOKUP_TABLE_8(T, I + 8) 20 | #define ASMJIT_LOOKUP_TABLE_32(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16) 21 | #define ASMJIT_LOOKUP_TABLE_64(T, I) ASMJIT_LOOKUP_TABLE_32(T, I), ASMJIT_LOOKUP_TABLE_32(T, I + 32) 22 | #define ASMJIT_LOOKUP_TABLE_128(T, I) ASMJIT_LOOKUP_TABLE_64(T, I), ASMJIT_LOOKUP_TABLE_64(T, I + 64) 23 | #define ASMJIT_LOOKUP_TABLE_256(T, I) ASMJIT_LOOKUP_TABLE_128(T, I), ASMJIT_LOOKUP_TABLE_128(T, I + 128) 24 | #define ASMJIT_LOOKUP_TABLE_512(T, I) ASMJIT_LOOKUP_TABLE_256(T, I), ASMJIT_LOOKUP_TABLE_256(T, I + 256) 25 | #define ASMJIT_LOOKUP_TABLE_1024(T, I) ASMJIT_LOOKUP_TABLE_512(T, I), ASMJIT_LOOKUP_TABLE_512(T, I + 512) 26 | 27 | //! \} 28 | //! \endcond 29 | 30 | ASMJIT_END_NAMESPACE 31 | 32 | #endif // _ASMJIT_CORE_MISC_P_H 33 | -------------------------------------------------------------------------------- /asmjit/core/osutils.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_OSUTILS_H 8 | #define _ASMJIT_CORE_OSUTILS_H 9 | 10 | #include "../core/globals.h" 11 | 12 | ASMJIT_BEGIN_NAMESPACE 13 | 14 | //! \addtogroup asmjit_support 15 | //! \{ 16 | 17 | // ============================================================================ 18 | // [asmjit::OSUtils] 19 | // ============================================================================ 20 | 21 | //! Operating system utilities. 22 | namespace OSUtils { 23 | //! Gets the current CPU tick count, used for benchmarking (1ms resolution). 24 | ASMJIT_API uint32_t getTickCount() noexcept; 25 | }; 26 | 27 | // ============================================================================ 28 | // [asmjit::Lock] 29 | // ============================================================================ 30 | 31 | //! \cond INTERNAL 32 | 33 | //! Lock. 34 | class Lock { 35 | public: 36 | ASMJIT_NONCOPYABLE(Lock) 37 | 38 | #if defined(_WIN32) 39 | 40 | typedef CRITICAL_SECTION Handle; 41 | Handle _handle; 42 | 43 | inline Lock() noexcept { InitializeCriticalSection(&_handle); } 44 | inline ~Lock() noexcept { DeleteCriticalSection(&_handle); } 45 | 46 | inline void lock() noexcept { EnterCriticalSection(&_handle); } 47 | inline void unlock() noexcept { LeaveCriticalSection(&_handle); } 48 | 49 | #elif !defined(__EMSCRIPTEN__) 50 | 51 | typedef pthread_mutex_t Handle; 52 | Handle _handle; 53 | 54 | inline Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } 55 | inline ~Lock() noexcept { pthread_mutex_destroy(&_handle); } 56 | 57 | inline void lock() noexcept { pthread_mutex_lock(&_handle); } 58 | inline void unlock() noexcept { pthread_mutex_unlock(&_handle); } 59 | 60 | #else 61 | 62 | // Browser or other unsupported OS. 63 | inline Lock() noexcept {} 64 | inline ~Lock() noexcept {} 65 | 66 | inline void lock() noexcept {} 67 | inline void unlock() noexcept {} 68 | 69 | #endif 70 | }; 71 | 72 | //! \endcond 73 | 74 | // ============================================================================ 75 | // [asmjit::ScopedLock] 76 | // ============================================================================ 77 | 78 | //! \cond INTERNAL 79 | 80 | //! Scoped lock. 81 | struct ScopedLock { 82 | ASMJIT_NONCOPYABLE(ScopedLock) 83 | 84 | Lock& _target; 85 | 86 | inline ScopedLock(Lock& target) noexcept : _target(target) { _target.lock(); } 87 | inline ~ScopedLock() noexcept { _target.unlock(); } 88 | }; 89 | 90 | //! \endcond 91 | 92 | //! \} 93 | 94 | ASMJIT_END_NAMESPACE 95 | 96 | #endif // _ASMJIT_CORE_OSUTILS_H 97 | -------------------------------------------------------------------------------- /asmjit/core/ralocal_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_RALOCAL_P_H 8 | #define _ASMJIT_CORE_RALOCAL_P_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_COMPILER 12 | 13 | #include "../core/raassignment_p.h" 14 | #include "../core/radefs_p.h" 15 | #include "../core/rapass_p.h" 16 | #include "../core/support.h" 17 | 18 | ASMJIT_BEGIN_NAMESPACE 19 | 20 | //! \cond INTERNAL 21 | //! \addtogroup asmjit_ra 22 | //! \{ 23 | 24 | // ============================================================================ 25 | // [asmjit::RALocalAllocator] 26 | // ============================================================================ 27 | 28 | //! Local register allocator. 29 | class RALocalAllocator { 30 | public: 31 | ASMJIT_NONCOPYABLE(RALocalAllocator) 32 | 33 | typedef RAAssignment::PhysToWorkMap PhysToWorkMap; 34 | typedef RAAssignment::WorkToPhysMap WorkToPhysMap; 35 | 36 | //! Link to `RAPass`. 37 | RAPass* _pass; 38 | //! Link to `BaseCompiler`. 39 | BaseCompiler* _cc; 40 | 41 | //! Architecture traits. 42 | RAArchTraits _archTraits; 43 | //! Registers available to the allocator. 44 | RARegMask _availableRegs; 45 | //! Registers clobbered by the allocator. 46 | RARegMask _clobberedRegs; 47 | 48 | //! Register assignment (current). 49 | RAAssignment _curAssignment; 50 | //! Register assignment used temporarily during assignment switches. 51 | RAAssignment _tmpAssignment; 52 | 53 | //! Link to the current `RABlock`. 54 | RABlock* _block; 55 | //! InstNode. 56 | InstNode* _node; 57 | //! RA instruction. 58 | RAInst* _raInst; 59 | 60 | //! Count of all TiedReg's. 61 | uint32_t _tiedTotal; 62 | //! TiedReg's total counter. 63 | RARegCount _tiedCount; 64 | 65 | //! \name Construction & Destruction 66 | //! \{ 67 | 68 | inline RALocalAllocator(RAPass* pass) noexcept 69 | : _pass(pass), 70 | _cc(pass->cc()), 71 | _archTraits(pass->_archTraits), 72 | _availableRegs(pass->_availableRegs), 73 | _clobberedRegs(), 74 | _curAssignment(), 75 | _block(nullptr), 76 | _node(nullptr), 77 | _raInst(nullptr), 78 | _tiedTotal(), 79 | _tiedCount() {} 80 | 81 | Error init() noexcept; 82 | 83 | //! \} 84 | 85 | //! \name Accessors 86 | //! \{ 87 | 88 | inline RAWorkReg* workRegById(uint32_t workId) const noexcept { return _pass->workRegById(workId); } 89 | inline PhysToWorkMap* physToWorkMap() const noexcept { return _curAssignment.physToWorkMap(); } 90 | inline WorkToPhysMap* workToPhysMap() const noexcept { return _curAssignment.workToPhysMap(); } 91 | 92 | //! Returns the currently processed block. 93 | inline RABlock* block() const noexcept { return _block; } 94 | //! Sets the currently processed block. 95 | inline void setBlock(RABlock* block) noexcept { _block = block; } 96 | 97 | //! Returns the currently processed `InstNode`. 98 | inline InstNode* node() const noexcept { return _node; } 99 | //! Returns the currently processed `RAInst`. 100 | inline RAInst* raInst() const noexcept { return _raInst; } 101 | 102 | //! Returns all tied regs as `RATiedReg` array. 103 | inline RATiedReg* tiedRegs() const noexcept { return _raInst->tiedRegs(); } 104 | //! Returns tied registers grouped by the given `group`. 105 | inline RATiedReg* tiedRegs(uint32_t group) const noexcept { return _raInst->tiedRegs(group); } 106 | 107 | //! Returns count of all TiedRegs used by the instruction. 108 | inline uint32_t tiedCount() const noexcept { return _tiedTotal; } 109 | //! Returns count of TiedRegs used by the given register `group`. 110 | inline uint32_t tiedCount(uint32_t group) const noexcept { return _tiedCount.get(group); } 111 | 112 | inline bool isGroupUsed(uint32_t group) const noexcept { return _tiedCount[group] != 0; } 113 | 114 | //! \} 115 | 116 | //! \name Assignment 117 | //! \{ 118 | 119 | Error makeInitialAssignment() noexcept; 120 | 121 | Error replaceAssignment( 122 | const PhysToWorkMap* physToWorkMap, 123 | const WorkToPhysMap* workToPhysMap) noexcept; 124 | 125 | //! Switch to the given assignment by reassigning all register and emitting 126 | //! code that reassigns them. This is always used to switch to a previously 127 | //! stored assignment. 128 | //! 129 | //! If `tryMode` is true then the final assignment doesn't have to be exactly 130 | //! same as specified by `dstPhysToWorkMap` and `dstWorkToPhysMap`. This mode 131 | //! is only used before conditional jumps that already have assignment to 132 | //! generate a code sequence that is always executed regardless of the flow. 133 | Error switchToAssignment( 134 | PhysToWorkMap* dstPhysToWorkMap, 135 | WorkToPhysMap* dstWorkToPhysMap, 136 | const ZoneBitVector& liveIn, 137 | bool dstReadOnly, 138 | bool tryMode) noexcept; 139 | 140 | //! \} 141 | 142 | //! \name Allocation 143 | //! \{ 144 | 145 | Error allocInst(InstNode* node) noexcept; 146 | Error spillAfterAllocation(InstNode* node) noexcept; 147 | 148 | Error allocBranch(InstNode* node, RABlock* target, RABlock* cont) noexcept; 149 | 150 | //! \} 151 | 152 | //! \name Decision Making 153 | //! \{ 154 | 155 | enum CostModel : uint32_t { 156 | kCostOfFrequency = 1048576, 157 | kCostOfDirtyFlag = kCostOfFrequency / 4 158 | }; 159 | 160 | inline uint32_t costByFrequency(float freq) const noexcept { 161 | return uint32_t(int32_t(freq * float(kCostOfFrequency))); 162 | } 163 | 164 | inline uint32_t calculateSpillCost(uint32_t group, uint32_t workId, uint32_t assignedId) const noexcept { 165 | RAWorkReg* workReg = workRegById(workId); 166 | uint32_t cost = costByFrequency(workReg->liveStats().freq()); 167 | 168 | if (_curAssignment.isPhysDirty(group, assignedId)) 169 | cost += kCostOfDirtyFlag; 170 | 171 | return cost; 172 | } 173 | 174 | //! Decides on register assignment. 175 | uint32_t decideOnAssignment(uint32_t group, uint32_t workId, uint32_t assignedId, uint32_t allocableRegs) const noexcept; 176 | 177 | //! Decides on whether to MOVE or SPILL the given WorkReg. 178 | //! 179 | //! The function must return either `RAAssignment::kPhysNone`, which means that 180 | //! the WorkReg should be spilled, or a valid physical register ID, which means 181 | //! that the register should be moved to that physical register instead. 182 | uint32_t decideOnUnassignment(uint32_t group, uint32_t workId, uint32_t assignedId, uint32_t allocableRegs) const noexcept; 183 | 184 | //! Decides on best spill given a register mask `spillableRegs` 185 | uint32_t decideOnSpillFor(uint32_t group, uint32_t workId, uint32_t spillableRegs, uint32_t* spillWorkId) const noexcept; 186 | 187 | //! \} 188 | 189 | //! \name Emit 190 | //! \{ 191 | 192 | //! Emits a move between a destination and source register, and fixes the 193 | //! register assignment. 194 | inline Error onMoveReg(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { 195 | if (dstPhysId == srcPhysId) return kErrorOk; 196 | _curAssignment.reassign(group, workId, dstPhysId, srcPhysId); 197 | return _pass->onEmitMove(workId, dstPhysId, srcPhysId); 198 | } 199 | 200 | //! Emits a swap between two physical registers and fixes their assignment. 201 | //! 202 | //! \note Target must support this operation otherwise this would ASSERT. 203 | inline Error onSwapReg(uint32_t group, uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept { 204 | _curAssignment.swap(group, aWorkId, aPhysId, bWorkId, bPhysId); 205 | return _pass->onEmitSwap(aWorkId, aPhysId, bWorkId, bPhysId); 206 | } 207 | 208 | //! Emits a load from [VirtReg/WorkReg]'s spill slot to a physical register 209 | //! and makes it assigned and clean. 210 | inline Error onLoadReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { 211 | _curAssignment.assign(group, workId, physId, RAAssignment::kClean); 212 | return _pass->onEmitLoad(workId, physId); 213 | } 214 | 215 | //! Emits a save a physical register to a [VirtReg/WorkReg]'s spill slot, 216 | //! keeps it assigned, and makes it clean. 217 | inline Error onSaveReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { 218 | ASMJIT_ASSERT(_curAssignment.workToPhysId(group, workId) == physId); 219 | ASMJIT_ASSERT(_curAssignment.physToWorkId(group, physId) == workId); 220 | 221 | _curAssignment.makeClean(group, workId, physId); 222 | return _pass->onEmitSave(workId, physId); 223 | } 224 | 225 | //! Assigns a register, the content of it is undefined at this point. 226 | inline Error onAssignReg(uint32_t group, uint32_t workId, uint32_t physId, uint32_t dirty) noexcept { 227 | _curAssignment.assign(group, workId, physId, dirty); 228 | return kErrorOk; 229 | } 230 | 231 | //! Spills a variable/register, saves the content to the memory-home if modified. 232 | inline Error onSpillReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { 233 | if (_curAssignment.isPhysDirty(group, physId)) 234 | ASMJIT_PROPAGATE(onSaveReg(group, workId, physId)); 235 | return onKillReg(group, workId, physId); 236 | } 237 | 238 | inline Error onDirtyReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { 239 | _curAssignment.makeDirty(group, workId, physId); 240 | return kErrorOk; 241 | } 242 | 243 | inline Error onKillReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { 244 | _curAssignment.unassign(group, workId, physId); 245 | return kErrorOk; 246 | } 247 | 248 | //! \} 249 | }; 250 | 251 | //! \} 252 | //! \endcond 253 | 254 | ASMJIT_END_NAMESPACE 255 | 256 | #endif // !ASMJIT_NO_COMPILER 257 | #endif // _ASMJIT_CORE_RALOCAL_P_H 258 | -------------------------------------------------------------------------------- /asmjit/core/rastack_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_RASTACK_P_H 8 | #define _ASMJIT_CORE_RASTACK_P_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_COMPILER 12 | 13 | #include "../core/radefs_p.h" 14 | 15 | ASMJIT_BEGIN_NAMESPACE 16 | 17 | //! \cond INTERNAL 18 | //! \addtogroup asmjit_ra 19 | //! \{ 20 | 21 | // ============================================================================ 22 | // [asmjit::RAStackSlot] 23 | // ============================================================================ 24 | 25 | //! Stack slot. 26 | struct RAStackSlot { 27 | enum Flags : uint32_t { 28 | // TODO: kFlagRegHome is apparently not used, but isRegHome() is. 29 | kFlagRegHome = 0x00000001u, //!< Stack slot is register home slot. 30 | kFlagStackArg = 0x00000002u //!< Stack slot position matches argument passed via stack. 31 | }; 32 | 33 | enum ArgIndex : uint32_t { 34 | kNoArgIndex = 0xFF 35 | }; 36 | 37 | //! Base register used to address the stack. 38 | uint8_t _baseRegId; 39 | //! Minimum alignment required by the slot. 40 | uint8_t _alignment; 41 | //! Reserved for future use. 42 | uint8_t _reserved[2]; 43 | //! Size of memory required by the slot. 44 | uint32_t _size; 45 | //! Slot flags. 46 | uint32_t _flags; 47 | 48 | //! Usage counter (one unit equals one memory access). 49 | uint32_t _useCount; 50 | //! Weight of the slot (calculated by `calculateStackFrame()`). 51 | uint32_t _weight; 52 | //! Stack offset (calculated by `calculateStackFrame()`). 53 | int32_t _offset; 54 | 55 | //! \name Accessors 56 | //! \{ 57 | 58 | inline uint32_t baseRegId() const noexcept { return _baseRegId; } 59 | inline void setBaseRegId(uint32_t id) noexcept { _baseRegId = uint8_t(id); } 60 | 61 | inline uint32_t size() const noexcept { return _size; } 62 | inline uint32_t alignment() const noexcept { return _alignment; } 63 | 64 | inline uint32_t flags() const noexcept { return _flags; } 65 | inline void addFlags(uint32_t flags) noexcept { _flags |= flags; } 66 | inline bool isRegHome() const noexcept { return (_flags & kFlagRegHome) != 0; } 67 | inline bool isStackArg() const noexcept { return (_flags & kFlagStackArg) != 0; } 68 | 69 | inline uint32_t useCount() const noexcept { return _useCount; } 70 | inline void addUseCount(uint32_t n = 1) noexcept { _useCount += n; } 71 | 72 | inline uint32_t weight() const noexcept { return _weight; } 73 | inline void setWeight(uint32_t weight) noexcept { _weight = weight; } 74 | 75 | inline int32_t offset() const noexcept { return _offset; } 76 | inline void setOffset(int32_t offset) noexcept { _offset = offset; } 77 | 78 | //! \} 79 | }; 80 | 81 | typedef ZoneVector RAStackSlots; 82 | 83 | // ============================================================================ 84 | // [asmjit::RAStackAllocator] 85 | // ============================================================================ 86 | 87 | //! Stack allocator. 88 | class RAStackAllocator { 89 | public: 90 | ASMJIT_NONCOPYABLE(RAStackAllocator) 91 | 92 | enum Size : uint32_t { 93 | kSize1 = 0, 94 | kSize2 = 1, 95 | kSize4 = 2, 96 | kSize8 = 3, 97 | kSize16 = 4, 98 | kSize32 = 5, 99 | kSize64 = 6, 100 | kSizeCount = 7 101 | }; 102 | 103 | //! Allocator used to allocate internal data. 104 | ZoneAllocator* _allocator; 105 | //! Count of bytes used by all slots. 106 | uint32_t _bytesUsed; 107 | //! Calculated stack size (can be a bit greater than `_bytesUsed`). 108 | uint32_t _stackSize; 109 | //! Minimum stack alignment. 110 | uint32_t _alignment; 111 | //! Stack slots vector. 112 | RAStackSlots _slots; 113 | 114 | //! \name Construction / Destruction 115 | //! \{ 116 | 117 | inline RAStackAllocator() noexcept 118 | : _allocator(nullptr), 119 | _bytesUsed(0), 120 | _stackSize(0), 121 | _alignment(1), 122 | _slots() {} 123 | 124 | inline void reset(ZoneAllocator* allocator) noexcept { 125 | _allocator = allocator; 126 | _bytesUsed = 0; 127 | _stackSize = 0; 128 | _alignment = 1; 129 | _slots.reset(); 130 | } 131 | 132 | //! \} 133 | 134 | //! \name Accessors 135 | //! \{ 136 | 137 | inline ZoneAllocator* allocator() const noexcept { return _allocator; } 138 | 139 | inline uint32_t bytesUsed() const noexcept { return _bytesUsed; } 140 | inline uint32_t stackSize() const noexcept { return _stackSize; } 141 | inline uint32_t alignment() const noexcept { return _alignment; } 142 | 143 | inline RAStackSlots& slots() noexcept { return _slots; } 144 | inline const RAStackSlots& slots() const noexcept { return _slots; } 145 | inline uint32_t slotCount() const noexcept { return _slots.size(); } 146 | 147 | //! \} 148 | 149 | //! \name Utilities 150 | //! \{ 151 | 152 | RAStackSlot* newSlot(uint32_t baseRegId, uint32_t size, uint32_t alignment, uint32_t flags = 0) noexcept; 153 | 154 | Error calculateStackFrame() noexcept; 155 | Error adjustSlotOffsets(int32_t offset) noexcept; 156 | 157 | //! \} 158 | }; 159 | 160 | //! \} 161 | //! \endcond 162 | 163 | ASMJIT_END_NAMESPACE 164 | 165 | #endif // !ASMJIT_NO_COMPILER 166 | #endif // _ASMJIT_CORE_RASTACK_P_H 167 | -------------------------------------------------------------------------------- /asmjit/core/string.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_STRING_H 8 | #define _ASMJIT_CORE_STRING_H 9 | 10 | #include "../core/support.h" 11 | #include "../core/zone.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_support 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::String] 20 | // ============================================================================ 21 | 22 | //! A simple non-reference counted string that uses small string optimization (SSO). 23 | //! 24 | //! This string has 3 allocation possibilities: 25 | //! 26 | //! 1. Small - embedded buffer is used for up to `kSSOCapacity` characters. 27 | //! This should handle most small strings and thus avoid dynamic 28 | //! memory allocation for most use-cases. 29 | //! 30 | //! 2. Large - string that doesn't fit into an embedded buffer (or string 31 | //! that was truncated from a larger buffer) and is owned by 32 | //! AsmJit. When you destroy the string AsmJit would automatically 33 | //! release the large buffer. 34 | //! 35 | //! 3. External - like Large (2), however, the large buffer is not owned by 36 | //! AsmJit and won't be released when the string is destroyed 37 | //! or reallocated. This is mostly useful for working with 38 | //! larger temporary strings allocated on stack or with immutable 39 | //! strings. 40 | class String { 41 | public: 42 | ASMJIT_NONCOPYABLE(String) 43 | 44 | //! String operation. 45 | enum Op : uint32_t { 46 | kOpAssign = 0, 47 | kOpAppend = 1 48 | }; 49 | 50 | //! String format flags. 51 | enum FormatFlags : uint32_t { 52 | kFormatShowSign = 0x00000001u, 53 | kFormatShowSpace = 0x00000002u, 54 | kFormatAlternate = 0x00000004u, 55 | kFormatSigned = 0x80000000u 56 | }; 57 | 58 | //! \cond INTERNAL 59 | enum : uint32_t { 60 | kLayoutSize = 32, 61 | kSSOCapacity = kLayoutSize - 2 62 | }; 63 | 64 | //! String type. 65 | enum Type : uint8_t { 66 | kTypeLarge = 0x1Fu, //!< Large string (owned by String). 67 | kTypeExternal = 0x20u //!< External string (zone allocated or not owned by String). 68 | }; 69 | 70 | union Raw { 71 | uint8_t u8[kLayoutSize]; 72 | uint64_t u64[kLayoutSize / sizeof(uint64_t)]; 73 | uintptr_t uptr[kLayoutSize / sizeof(uintptr_t)]; 74 | }; 75 | 76 | struct Small { 77 | uint8_t type; 78 | char data[kSSOCapacity + 1u]; 79 | }; 80 | 81 | struct Large { 82 | uint8_t type; 83 | uint8_t reserved[sizeof(uintptr_t) - 1]; 84 | size_t size; 85 | size_t capacity; 86 | char* data; 87 | }; 88 | 89 | union { 90 | uint8_t _type; 91 | Raw _raw; 92 | Small _small; 93 | Large _large; 94 | }; 95 | //! \endcond 96 | 97 | //! \name Construction & Destruction 98 | //! \{ 99 | 100 | inline String() noexcept 101 | : _small {} {} 102 | 103 | inline String(String&& other) noexcept { 104 | for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) 105 | _raw.uptr[i] = other._raw.uptr[i]; 106 | other._resetInternal(); 107 | } 108 | 109 | inline ~String() noexcept { 110 | reset(); 111 | } 112 | 113 | //! Reset the string into a construction state. 114 | ASMJIT_API Error reset() noexcept; 115 | 116 | //! \} 117 | 118 | //! \name Overloaded Operators 119 | //! \{ 120 | 121 | inline bool operator==(const char* other) const noexcept { return eq(other); } 122 | inline bool operator!=(const char* other) const noexcept { return !eq(other); } 123 | 124 | inline bool operator==(const String& other) const noexcept { return eq(other); } 125 | inline bool operator!=(const String& other) const noexcept { return !eq(other); } 126 | 127 | //! \} 128 | 129 | //! \name Accessors 130 | //! \{ 131 | 132 | inline bool isLarge() const noexcept { return _type >= kTypeLarge; } 133 | inline bool isExternal() const noexcept { return _type == kTypeExternal; } 134 | 135 | inline bool empty() const noexcept { return size() == 0; } 136 | inline size_t size() const noexcept { return isLarge() ? size_t(_large.size) : size_t(_type); } 137 | inline size_t capacity() const noexcept { return isLarge() ? _large.capacity : size_t(kSSOCapacity); } 138 | 139 | inline char* data() noexcept { return isLarge() ? _large.data : _small.data; } 140 | inline const char* data() const noexcept { return isLarge() ? _large.data : _small.data; } 141 | 142 | inline char* end() noexcept { return data() + size(); } 143 | inline const char* end() const noexcept { return data() + size(); } 144 | 145 | //! \} 146 | 147 | //! \name String Operations 148 | //! \{ 149 | 150 | //! Clear the content of the string. 151 | ASMJIT_API Error clear() noexcept; 152 | 153 | ASMJIT_API char* prepare(uint32_t op, size_t size) noexcept; 154 | 155 | ASMJIT_API Error _opString(uint32_t op, const char* str, size_t size = SIZE_MAX) noexcept; 156 | ASMJIT_API Error _opFormat(uint32_t op, const char* fmt, ...) noexcept; 157 | ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; 158 | ASMJIT_API Error _opChar(uint32_t op, char c) noexcept; 159 | ASMJIT_API Error _opChars(uint32_t op, char c, size_t n) noexcept; 160 | ASMJIT_API Error _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; 161 | ASMJIT_API Error _opHex(uint32_t op, const void* data, size_t size, char separator = '\0') noexcept; 162 | 163 | //! Replace the string content to a string specified by `data` and `size`. If 164 | //! `size` is `SIZE_MAX` then it's considered null-terminated and its length 165 | //! will be obtained through `strlen()`. 166 | ASMJIT_API Error assignString(const char* data, size_t size = SIZE_MAX) noexcept; 167 | 168 | //! Replace the current content by a formatted string `fmt`. 169 | template 170 | inline Error assignFormat(const char* fmt, Args&&... args) noexcept { 171 | return _opFormat(kOpAssign, fmt, std::forward(args)...); 172 | } 173 | 174 | //! Replace the current content by a formatted string `fmt` (va_list version). 175 | inline Error assignVFormat(const char* fmt, va_list ap) noexcept { 176 | return _opVFormat(kOpAssign, fmt, ap); 177 | } 178 | 179 | //! Replace the current content by a single `c` character. 180 | inline Error assignChar(char c) noexcept { 181 | return _opChar(kOpAssign, c); 182 | } 183 | 184 | //! Replace the current content by `c` character `n` times. 185 | inline Error assignChars(char c, size_t n) noexcept { 186 | return _opChars(kOpAssign, c, n); 187 | } 188 | 189 | //! Replace the current content by a formatted integer `i` (signed). 190 | inline Error assignInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 191 | return _opNumber(kOpAssign, uint64_t(i), base, width, flags | kFormatSigned); 192 | } 193 | 194 | //! Replace the current content by a formatted integer `i` (unsigned). 195 | inline Error assignUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 196 | return _opNumber(kOpAssign, i, base, width, flags); 197 | } 198 | 199 | //! Replace the current content by the given `data` converted to a HEX string. 200 | inline Error assignHex(const void* data, size_t size, char separator = '\0') noexcept { 201 | return _opHex(kOpAssign, data, size, separator); 202 | } 203 | 204 | //! Append string `str` of size `size` (or possibly null terminated). 205 | inline Error appendString(const char* str, size_t size = SIZE_MAX) noexcept { 206 | return _opString(kOpAppend, str, size); 207 | } 208 | 209 | template 210 | inline Error appendFormat(const char* fmt, Args&&... args) noexcept { 211 | return _opFormat(kOpAppend, fmt, std::forward(args)...); 212 | } 213 | 214 | //! Append a formatted string `fmt` (va_list version). 215 | inline Error appendVFormat(const char* fmt, va_list ap) noexcept { 216 | return _opVFormat(kOpAppend, fmt, ap); 217 | } 218 | 219 | //! Append a single `c` character. 220 | inline Error appendChar(char c) noexcept { 221 | return _opChar(kOpAppend, c); 222 | } 223 | 224 | //! Append `c` character `n` times. 225 | inline Error appendChars(char c, size_t n) noexcept { 226 | return _opChars(kOpAppend, c, n); 227 | } 228 | 229 | ASMJIT_API Error padEnd(size_t n, char c = ' ') noexcept; 230 | 231 | //! Append `i`. 232 | inline Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 233 | return _opNumber(kOpAppend, uint64_t(i), base, width, flags | kFormatSigned); 234 | } 235 | 236 | //! Append `i`. 237 | inline Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 238 | return _opNumber(kOpAppend, i, base, width, flags); 239 | } 240 | 241 | //! Append the given `data` converted to a HEX string. 242 | inline Error appendHex(const void* data, size_t size, char separator = '\0') noexcept { 243 | return _opHex(kOpAppend, data, size, separator); 244 | } 245 | 246 | //! Truncate the string length into `newSize`. 247 | ASMJIT_API Error truncate(size_t newSize) noexcept; 248 | 249 | ASMJIT_API bool eq(const char* other, size_t size = SIZE_MAX) const noexcept; 250 | inline bool eq(const String& other) const noexcept { return eq(other.data(), other.size()); } 251 | 252 | //! \} 253 | 254 | //! \name Internal Functions 255 | //! \{ 256 | 257 | //! Resets string to embedded and makes it empty (zero length, zero first char) 258 | //! 259 | //! \note This is always called internally after an external buffer was released 260 | //! as it zeroes all bytes used by String's embedded storage. 261 | inline void _resetInternal() noexcept { 262 | for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) 263 | _raw.uptr[i] = 0; 264 | } 265 | 266 | inline void _setSize(size_t newSize) noexcept { 267 | if (isLarge()) 268 | _large.size = newSize; 269 | else 270 | _small.type = uint8_t(newSize); 271 | } 272 | 273 | //! \} 274 | }; 275 | 276 | // ============================================================================ 277 | // [asmjit::StringTmp] 278 | // ============================================================================ 279 | 280 | //! Temporary string builder, has statically allocated `N` bytes. 281 | template 282 | class StringTmp : public String { 283 | public: 284 | ASMJIT_NONCOPYABLE(StringTmp) 285 | 286 | //! Embedded data. 287 | char _embeddedData[Support::alignUp(N + 1, sizeof(size_t))]; 288 | 289 | //! \name Construction & Destruction 290 | //! \{ 291 | 292 | inline StringTmp() noexcept { 293 | _resetToTemporary(); 294 | } 295 | 296 | inline void _resetToTemporary() noexcept { 297 | _large.type = kTypeExternal; 298 | _large.capacity = ASMJIT_ARRAY_SIZE(_embeddedData) - 1; 299 | _large.data = _embeddedData; 300 | _embeddedData[0] = '\0'; 301 | } 302 | 303 | //! \} 304 | }; 305 | 306 | // ============================================================================ 307 | // [asmjit::FixedString] 308 | // ============================================================================ 309 | 310 | //! A fixed string - only useful for strings that would never exceed `N - 1` 311 | //! characters; always null-terminated. 312 | template 313 | union FixedString { 314 | enum : uint32_t { 315 | kNumU32 = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t)) 316 | }; 317 | 318 | char str[kNumU32 * sizeof(uint32_t)]; 319 | uint32_t u32[kNumU32]; 320 | 321 | //! \name Utilities 322 | //! \{ 323 | 324 | inline bool eq(const char* other) const noexcept { 325 | return strcmp(str, other) == 0; 326 | } 327 | 328 | //! \} 329 | }; 330 | 331 | //! \} 332 | 333 | ASMJIT_END_NAMESPACE 334 | 335 | #endif // _ASMJIT_CORE_STRING_H 336 | -------------------------------------------------------------------------------- /asmjit/core/target.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_TARGET_H 8 | #define _ASMJIT_CORE_TARGET_H 9 | 10 | #include "../core/arch.h" 11 | #include "../core/func.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_core 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::CodeInfo] 20 | // ============================================================================ 21 | 22 | //! Basic information about a code (or target). It describes its architecture, 23 | //! code generation mode (or optimization level), and base address. 24 | class CodeInfo { 25 | public: 26 | //!< Architecture information. 27 | ArchInfo _archInfo; 28 | //! Natural stack alignment (ARCH+OS). 29 | uint8_t _stackAlignment; 30 | //! Default CDECL calling convention. 31 | uint8_t _cdeclCallConv; 32 | //! Default STDCALL calling convention. 33 | uint8_t _stdCallConv; 34 | //! Default FASTCALL calling convention. 35 | uint8_t _fastCallConv; 36 | //! Base address. 37 | uint64_t _baseAddress; 38 | 39 | //! \name Construction & Destruction 40 | //! \{ 41 | 42 | inline CodeInfo() noexcept 43 | : _archInfo(), 44 | _stackAlignment(0), 45 | _cdeclCallConv(CallConv::kIdNone), 46 | _stdCallConv(CallConv::kIdNone), 47 | _fastCallConv(CallConv::kIdNone), 48 | _baseAddress(Globals::kNoBaseAddress) {} 49 | 50 | inline explicit CodeInfo(uint32_t archId, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept 51 | : _archInfo(archId, archMode), 52 | _stackAlignment(0), 53 | _cdeclCallConv(CallConv::kIdNone), 54 | _stdCallConv(CallConv::kIdNone), 55 | _fastCallConv(CallConv::kIdNone), 56 | _baseAddress(baseAddress) {} 57 | 58 | inline CodeInfo(const CodeInfo& other) noexcept { init(other); } 59 | 60 | inline bool isInitialized() const noexcept { 61 | return _archInfo.archId() != ArchInfo::kIdNone; 62 | } 63 | 64 | inline void init(const CodeInfo& other) noexcept { 65 | *this = other; 66 | } 67 | 68 | inline void init(uint32_t archId, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept { 69 | _archInfo.init(archId, archMode); 70 | _stackAlignment = 0; 71 | _cdeclCallConv = CallConv::kIdNone; 72 | _stdCallConv = CallConv::kIdNone; 73 | _fastCallConv = CallConv::kIdNone; 74 | _baseAddress = baseAddress; 75 | } 76 | 77 | inline void reset() noexcept { 78 | _archInfo.reset(); 79 | _stackAlignment = 0; 80 | _cdeclCallConv = CallConv::kIdNone; 81 | _stdCallConv = CallConv::kIdNone; 82 | _fastCallConv = CallConv::kIdNone; 83 | _baseAddress = Globals::kNoBaseAddress; 84 | } 85 | 86 | //! \} 87 | 88 | //! \name Overloaded Operators 89 | //! \{ 90 | 91 | inline CodeInfo& operator=(const CodeInfo& other) noexcept = default; 92 | 93 | inline bool operator==(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) == 0; } 94 | inline bool operator!=(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) != 0; } 95 | 96 | //! \} 97 | 98 | //! \name Accessors 99 | //! \{ 100 | 101 | //! Returns the target architecture information, see `ArchInfo`. 102 | inline const ArchInfo& archInfo() const noexcept { return _archInfo; } 103 | 104 | //! Returns the target architecture id, see `ArchInfo::Id`. 105 | inline uint32_t archId() const noexcept { return _archInfo.archId(); } 106 | //! Returns the target architecture sub-type, see `ArchInfo::SubId`. 107 | inline uint32_t archSubId() const noexcept { return _archInfo.archSubId(); } 108 | //! Returns the native size of the target's architecture GP register. 109 | inline uint32_t gpSize() const noexcept { return _archInfo.gpSize(); } 110 | //! Returns the number of GP registers of the target's architecture. 111 | inline uint32_t gpCount() const noexcept { return _archInfo.gpCount(); } 112 | 113 | //! Returns a natural stack alignment that must be honored (or 0 if not known). 114 | inline uint32_t stackAlignment() const noexcept { return _stackAlignment; } 115 | //! Sets a natural stack alignment that must be honored. 116 | inline void setStackAlignment(uint32_t sa) noexcept { _stackAlignment = uint8_t(sa); } 117 | 118 | inline uint32_t cdeclCallConv() const noexcept { return _cdeclCallConv; } 119 | inline void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = uint8_t(cc); } 120 | 121 | inline uint32_t stdCallConv() const noexcept { return _stdCallConv; } 122 | inline void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = uint8_t(cc); } 123 | 124 | inline uint32_t fastCallConv() const noexcept { return _fastCallConv; } 125 | inline void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = uint8_t(cc); } 126 | 127 | inline bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; } 128 | inline uint64_t baseAddress() const noexcept { return _baseAddress; } 129 | inline void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; } 130 | inline void resetBaseAddress() noexcept { _baseAddress = Globals::kNoBaseAddress; } 131 | 132 | //! \} 133 | }; 134 | 135 | // ============================================================================ 136 | // [asmjit::Target] 137 | // ============================================================================ 138 | 139 | //! Target is an abstract class that describes a machine code target. 140 | class ASMJIT_VIRTAPI Target { 141 | public: 142 | ASMJIT_BASE_CLASS(Target) 143 | ASMJIT_NONCOPYABLE(Target) 144 | 145 | //! Tartget type, see `TargetType`. 146 | uint8_t _targetType; 147 | //! Reserved for future use. 148 | uint8_t _reserved[7]; 149 | //! Basic information about the Runtime's code. 150 | CodeInfo _codeInfo; 151 | 152 | enum TargetType : uint32_t { 153 | //! Uninitialized target or unknown target type. 154 | kTargetNone = 0, 155 | //! JIT target type, see `JitRuntime`. 156 | kTargetJit = 1 157 | }; 158 | 159 | //! \name Construction & Destruction 160 | //! \{ 161 | 162 | //! Creates a `Target` instance. 163 | ASMJIT_API Target() noexcept; 164 | //! Destroys the `Target` instance. 165 | ASMJIT_API virtual ~Target() noexcept; 166 | 167 | //! \} 168 | 169 | //! \name Accessors 170 | //! \{ 171 | 172 | //! Returns CodeInfo of this target. 173 | //! 174 | //! CodeInfo can be used to setup a CodeHolder in case you plan to generate a 175 | //! code compatible and executable by this Runtime. 176 | inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; } 177 | 178 | //! Returns the target architecture id, see `ArchInfo::Id`. 179 | inline uint32_t archId() const noexcept { return _codeInfo.archId(); } 180 | //! Returns the target architecture sub-id, see `ArchInfo::SubId`. 181 | inline uint32_t archSubId() const noexcept { return _codeInfo.archSubId(); } 182 | 183 | //! Returns the target type, see `TargetType`. 184 | inline uint32_t targetType() const noexcept { return _targetType; } 185 | 186 | //! \} 187 | }; 188 | 189 | //! \} 190 | 191 | ASMJIT_END_NAMESPACE 192 | 193 | #endif // _ASMJIT_CORE_TARGET_H 194 | -------------------------------------------------------------------------------- /asmjit/core/virtmem.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_VIRTMEM_H 8 | #define _ASMJIT_CORE_VIRTMEM_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_JIT 12 | 13 | #include "../core/globals.h" 14 | 15 | ASMJIT_BEGIN_NAMESPACE 16 | 17 | //! \addtogroup asmjit_jit 18 | //! \{ 19 | 20 | // ============================================================================ 21 | // [asmjit::VirtMem] 22 | // ============================================================================ 23 | 24 | //! Virtual memory management. 25 | namespace VirtMem { 26 | 27 | //! Virtual memory and memory mapping flags. 28 | enum Flags : uint32_t { 29 | //! No access flags. 30 | kAccessNone = 0x00000000u, 31 | //! Memory is readable. 32 | kAccessRead = 0x00000001u, 33 | //! Memory is writable (implies read access). 34 | kAccessWrite = 0x00000002u, 35 | //! Memory is executable (implies read access). 36 | kAccessExecute = 0x00000004u, 37 | 38 | //! A combination of `kAccessRead | kAccessWrite` 39 | kAccessReadWrite = 0x00000003u, 40 | 41 | //! Use a `MAP_JIT` flag available on Apple platforms (OSX Mojave+), which 42 | //! allows JIT code to be executed in OSX bundles. This flag is not turned 43 | //! on by default, because when a process uses `fork()` the child process 44 | //! has no access to the pages mapped with `MAP_JIT`, which could break code 45 | //! that doesn't expect this behavior. 46 | kMMapEnableMapJit = 0x00000010u, 47 | 48 | //! Not an access flag, only used by `allocDualMapping()` to override the 49 | //! default allocation strategy to always use a 'tmp' directory instead of 50 | //! "/dev/shm" (on POSIX platforms). Please note that this flag will be 51 | //! ignored if the operating system allows to allocate an executable memory 52 | //! by a different API than `open()` or `shm_open()`. For example on Linux 53 | //! `memfd_create()` is preferred and on BSDs `shm_open(SHM_ANON, ...)` is 54 | //! used if SHM_ANON is defined. 55 | kMappingPreferTmp = 0x80000000u 56 | }; 57 | 58 | //! Virtual memory information. 59 | struct Info { 60 | //! Virtual memory page size. 61 | uint32_t pageSize; 62 | //! Virtual memory page granularity. 63 | uint32_t pageGranularity; 64 | }; 65 | 66 | //! Dual memory mapping used to map an anonymous memory into two memory regions 67 | //! where one region is read-only, but executable, and the second region is 68 | //! read+write, but not executable. Please see \ref VirtMem::allocDualMapping() 69 | //! for more details. 70 | struct DualMapping { 71 | //! Pointer to data with 'Read' or 'Read+Execute' access. 72 | void* ro; 73 | //! Pointer to data with 'Read-Write' access, but never 'Write+Execute'. 74 | void* rw; 75 | }; 76 | 77 | //! Returns virtual memory information, see `VirtMem::Info` for more details. 78 | ASMJIT_API Info info() noexcept; 79 | 80 | //! Allocates virtual memory by either using `VirtualAlloc()` (Windows) 81 | //! or `mmap()` (POSIX). 82 | //! 83 | //! \note `size` should be aligned to a page size, use \ref VirtMem::info() 84 | //! to obtain it. Invalid size will not be corrected by the implementation 85 | //! and the allocation would not succeed in such case. 86 | ASMJIT_API Error alloc(void** p, size_t size, uint32_t flags) noexcept; 87 | 88 | //! Releases virtual memory previously allocated by \ref VirtMem::alloc() or 89 | //! \ref VirtMem::allocDualMapping(). 90 | //! 91 | //! \note The size must be the same as used by \ref VirtMem::alloc(). If the 92 | //! size is not the same value the call will fail on any POSIX system, but 93 | //! pass on Windows, because of the difference of the implementation. 94 | ASMJIT_API Error release(void* p, size_t size) noexcept; 95 | 96 | //! A cross-platform wrapper around `mprotect()` (POSIX) and `VirtualProtect` 97 | //! (Windows). 98 | ASMJIT_API Error protect(void* p, size_t size, uint32_t flags) noexcept; 99 | 100 | //! Allocates virtual memory and creates two views of it where the first view 101 | //! has no write access. This is an addition to the API that should be used 102 | //! in cases in which the operating system either enforces W^X security policy 103 | //! or the application wants to use this policy by default to improve security 104 | //! and prevent an accidental (or purposed) self-modifying code. 105 | //! 106 | //! The memory returned in the `dm` are two independent mappings of the same 107 | //! shared memory region. You must use \ref VirtMem::releaseDualMapping() to 108 | //! release it when it's no longer needed. Never use `VirtMem::release()` to 109 | //! release the memory returned by `allocDualMapping()` as that would fail on 110 | //! Windows. 111 | //! 112 | //! \remarks Both pointers in `dm` would be set to `nullptr` if the function fails. 113 | ASMJIT_API Error allocDualMapping(DualMapping* dm, size_t size, uint32_t flags) noexcept; 114 | 115 | //! Releases the virtual memory mapping previously allocated by 116 | //! \ref VirtMem::allocDualMapping(). 117 | //! 118 | //! \remarks Both pointers in `dm` would be set to `nullptr` if the function succeeds. 119 | ASMJIT_API Error releaseDualMapping(DualMapping* dm, size_t size) noexcept; 120 | 121 | } // VirtMem 122 | 123 | //! \} 124 | 125 | ASMJIT_END_NAMESPACE 126 | 127 | #endif 128 | #endif // _ASMJIT_CORE_VIRTMEM_H 129 | -------------------------------------------------------------------------------- /asmjit/core/zonehash.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_ZONEHASH_H 8 | #define _ASMJIT_CORE_ZONEHASH_H 9 | 10 | #include "../core/zone.h" 11 | 12 | ASMJIT_BEGIN_NAMESPACE 13 | 14 | //! \addtogroup asmjit_zone 15 | //! \{ 16 | 17 | // ============================================================================ 18 | // [asmjit::ZoneHashNode] 19 | // ============================================================================ 20 | 21 | //! Node used by `ZoneHash<>` template. 22 | //! 23 | //! You must provide function `bool eq(const Key& key)` in order to make 24 | //! `ZoneHash::get()` working. 25 | class ZoneHashNode { 26 | public: 27 | ASMJIT_NONCOPYABLE(ZoneHashNode) 28 | 29 | inline ZoneHashNode(uint32_t hashCode = 0) noexcept 30 | : _hashNext(nullptr), 31 | _hashCode(hashCode), 32 | _customData(0) {} 33 | 34 | //! Next node in the chain, null if it terminates the chain. 35 | ZoneHashNode* _hashNext; 36 | //! Precalculated hash-code of key. 37 | uint32_t _hashCode; 38 | //! Padding, can be reused by any Node that inherits `ZoneHashNode`. 39 | uint32_t _customData; 40 | }; 41 | 42 | // ============================================================================ 43 | // [asmjit::ZoneHashBase] 44 | // ============================================================================ 45 | 46 | class ZoneHashBase { 47 | public: 48 | ASMJIT_NONCOPYABLE(ZoneHashBase) 49 | 50 | //! Buckets data. 51 | ZoneHashNode** _data; 52 | //! Count of records inserted into the hash table. 53 | size_t _size; 54 | //! Count of hash buckets. 55 | uint32_t _bucketsCount; 56 | //! When buckets array should grow (only checked after insertion). 57 | uint32_t _bucketsGrow; 58 | //! Reciprocal value of `_bucketsCount`. 59 | uint32_t _rcpValue; 60 | //! How many bits to shift right when hash is multiplied with `_rcpValue`. 61 | uint8_t _rcpShift; 62 | //! Prime value index in internal prime array. 63 | uint8_t _primeIndex; 64 | 65 | //! Embedded data, used by empty hash tables. 66 | ZoneHashNode* _embedded[1]; 67 | 68 | //! \name Construction & Destruction 69 | //! \{ 70 | 71 | inline ZoneHashBase() noexcept { 72 | reset(); 73 | } 74 | 75 | inline ZoneHashBase(ZoneHashBase&& other) noexcept { 76 | _data = other._data; 77 | _size = other._size; 78 | _bucketsCount = other._bucketsCount; 79 | _bucketsGrow = other._bucketsGrow; 80 | _rcpValue = other._rcpValue; 81 | _rcpShift = other._rcpShift; 82 | _primeIndex = other._primeIndex; 83 | _embedded[0] = other._embedded[0]; 84 | 85 | if (_data == other._embedded) _data = _embedded; 86 | } 87 | 88 | inline void reset() noexcept { 89 | _data = _embedded; 90 | _size = 0; 91 | _bucketsCount = 1; 92 | _bucketsGrow = 1; 93 | _rcpValue = 1; 94 | _rcpShift = 0; 95 | _primeIndex = 0; 96 | _embedded[0] = nullptr; 97 | } 98 | 99 | inline void release(ZoneAllocator* allocator) noexcept { 100 | ZoneHashNode** oldData = _data; 101 | if (oldData != _embedded) 102 | allocator->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); 103 | reset(); 104 | } 105 | 106 | //! \} 107 | 108 | //! \name Accessors 109 | //! \{ 110 | 111 | inline bool empty() const noexcept { return _size == 0; } 112 | inline size_t size() const noexcept { return _size; } 113 | 114 | //! \} 115 | 116 | //! \name Utilities 117 | //! \{ 118 | 119 | inline void _swap(ZoneHashBase& other) noexcept { 120 | std::swap(_data, other._data); 121 | std::swap(_size, other._size); 122 | std::swap(_bucketsCount, other._bucketsCount); 123 | std::swap(_bucketsGrow, other._bucketsGrow); 124 | std::swap(_rcpValue, other._rcpValue); 125 | std::swap(_rcpShift, other._rcpShift); 126 | std::swap(_primeIndex, other._primeIndex); 127 | std::swap(_embedded[0], other._embedded[0]); 128 | 129 | if (_data == other._embedded) _data = _embedded; 130 | if (other._data == _embedded) other._data = other._embedded; 131 | } 132 | 133 | //! \cond INTERNAL 134 | inline uint32_t _calcMod(uint32_t hash) const noexcept { 135 | uint32_t x = uint32_t((uint64_t(hash) * _rcpValue) >> _rcpShift); 136 | return hash - x * _bucketsCount; 137 | } 138 | 139 | ASMJIT_API void _rehash(ZoneAllocator* allocator, uint32_t newCount) noexcept; 140 | ASMJIT_API ZoneHashNode* _insert(ZoneAllocator* allocator, ZoneHashNode* node) noexcept; 141 | ASMJIT_API ZoneHashNode* _remove(ZoneAllocator* allocator, ZoneHashNode* node) noexcept; 142 | //! \endcond 143 | 144 | //! \} 145 | }; 146 | 147 | // ============================================================================ 148 | // [asmjit::ZoneHash] 149 | // ============================================================================ 150 | 151 | //! Low-level hash table specialized for storing string keys and POD values. 152 | //! 153 | //! This hash table allows duplicates to be inserted (the API is so low 154 | //! level that it's up to you if you allow it or not, as you should first 155 | //! `get()` the node and then modify it or insert a new node by using `insert()`, 156 | //! depending on the intention). 157 | template 158 | class ZoneHash : public ZoneHashBase { 159 | public: 160 | ASMJIT_NONCOPYABLE(ZoneHash) 161 | 162 | typedef NodeT Node; 163 | 164 | //! \name Construction & Destruction 165 | //! \{ 166 | 167 | inline ZoneHash() noexcept 168 | : ZoneHashBase() {} 169 | 170 | inline ZoneHash(ZoneHash&& other) noexcept 171 | : ZoneHash(other) {} 172 | 173 | //! \} 174 | 175 | //! \name Utilities 176 | //! \{ 177 | 178 | inline void swap(ZoneHash& other) noexcept { ZoneHashBase::_swap(other); } 179 | 180 | template 181 | inline NodeT* get(const KeyT& key) const noexcept { 182 | uint32_t hashMod = _calcMod(key.hashCode()); 183 | NodeT* node = static_cast(_data[hashMod]); 184 | 185 | while (node && !key.matches(node)) 186 | node = static_cast(node->_hashNext); 187 | return node; 188 | } 189 | 190 | inline NodeT* insert(ZoneAllocator* allocator, NodeT* node) noexcept { return static_cast(_insert(allocator, node)); } 191 | inline NodeT* remove(ZoneAllocator* allocator, NodeT* node) noexcept { return static_cast(_remove(allocator, node)); } 192 | 193 | //! \} 194 | }; 195 | 196 | //! \} 197 | 198 | ASMJIT_END_NAMESPACE 199 | 200 | #endif // _ASMJIT_CORE_ZONEHASH_H 201 | -------------------------------------------------------------------------------- /asmjit/core/zonelist.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_ZONELIST_H 8 | #define _ASMJIT_CORE_ZONELIST_H 9 | 10 | #include "../core/support.h" 11 | 12 | ASMJIT_BEGIN_NAMESPACE 13 | 14 | //! \addtogroup asmjit_zone 15 | //! \{ 16 | 17 | // ============================================================================ 18 | // [asmjit::ZoneListNode] 19 | // ============================================================================ 20 | 21 | template 22 | class ZoneListNode { 23 | public: 24 | ASMJIT_NONCOPYABLE(ZoneListNode) 25 | 26 | NodeT* _listNodes[Globals::kLinkCount]; 27 | 28 | //! \name Construction & Destruction 29 | //! \{ 30 | 31 | inline ZoneListNode() noexcept 32 | : _listNodes { nullptr, nullptr } {} 33 | 34 | inline ZoneListNode(ZoneListNode&& other) noexcept 35 | : _listNodes { other._listNodes[0], other._listNodes[1] } {} 36 | 37 | //! \} 38 | 39 | //! \name Accessors 40 | //! \{ 41 | 42 | inline bool hasPrev() const noexcept { return _listNodes[Globals::kLinkPrev] != nullptr; } 43 | inline bool hasNext() const noexcept { return _listNodes[Globals::kLinkNext] != nullptr; } 44 | 45 | inline NodeT* prev() const noexcept { return _listNodes[Globals::kLinkPrev]; } 46 | inline NodeT* next() const noexcept { return _listNodes[Globals::kLinkNext]; } 47 | 48 | //! \} 49 | }; 50 | 51 | // ============================================================================ 52 | // [asmjit::ZoneList] 53 | // ============================================================================ 54 | 55 | template 56 | class ZoneList { 57 | public: 58 | ASMJIT_NONCOPYABLE(ZoneList) 59 | 60 | NodeT* _bounds[Globals::kLinkCount]; 61 | 62 | //! \name Construction & Destruction 63 | //! \{ 64 | 65 | inline ZoneList() noexcept 66 | : _bounds { nullptr, nullptr } {} 67 | 68 | inline ZoneList(ZoneList&& other) noexcept 69 | : _bounds { other._bounds[0], other._bounds[1] } {} 70 | 71 | inline void reset() noexcept { 72 | _bounds[0] = nullptr; 73 | _bounds[1] = nullptr; 74 | } 75 | 76 | //! \} 77 | 78 | //! \name Accessors 79 | //! \{ 80 | 81 | inline bool empty() const noexcept { return _bounds[0] == nullptr; } 82 | inline NodeT* first() const noexcept { return _bounds[Globals::kLinkFirst]; } 83 | inline NodeT* last() const noexcept { return _bounds[Globals::kLinkLast]; } 84 | 85 | //! \} 86 | 87 | //! \name Utilities 88 | //! \{ 89 | 90 | inline void swap(ZoneList& other) noexcept { 91 | std::swap(_bounds[0], other._bounds[0]); 92 | std::swap(_bounds[1], other._bounds[1]); 93 | } 94 | 95 | // Can be used to both prepend and append. 96 | inline void _addNode(NodeT* node, size_t dir) noexcept { 97 | NodeT* prev = _bounds[dir]; 98 | 99 | node->_listNodes[!dir] = prev; 100 | _bounds[dir] = node; 101 | if (prev) 102 | prev->_listNodes[dir] = node; 103 | else 104 | _bounds[!dir] = node; 105 | } 106 | 107 | // Can be used to both prepend and append. 108 | inline void _insertNode(NodeT* ref, NodeT* node, size_t dir) noexcept { 109 | ASMJIT_ASSERT(ref != nullptr); 110 | 111 | NodeT* prev = ref; 112 | NodeT* next = ref->_listNodes[dir]; 113 | 114 | prev->_listNodes[dir] = node; 115 | if (next) 116 | next->_listNodes[!dir] = node; 117 | else 118 | _bounds[dir] = node; 119 | 120 | node->_listNodes[!dir] = prev; 121 | node->_listNodes[ dir] = next; 122 | } 123 | 124 | inline void append(NodeT* node) noexcept { _addNode(node, Globals::kLinkLast); } 125 | inline void prepend(NodeT* node) noexcept { _addNode(node, Globals::kLinkFirst); } 126 | 127 | inline void insertAfter(NodeT* ref, NodeT* node) noexcept { _insertNode(ref, node, Globals::kLinkNext); } 128 | inline void insertBefore(NodeT* ref, NodeT* node) noexcept { _insertNode(ref, node, Globals::kLinkPrev); } 129 | 130 | inline NodeT* unlink(NodeT* node) noexcept { 131 | NodeT* prev = node->prev(); 132 | NodeT* next = node->next(); 133 | 134 | if (prev) { prev->_listNodes[1] = next; node->_listNodes[0] = nullptr; } else { _bounds[0] = next; } 135 | if (next) { next->_listNodes[0] = prev; node->_listNodes[1] = nullptr; } else { _bounds[1] = prev; } 136 | 137 | node->_listNodes[0] = nullptr; 138 | node->_listNodes[1] = nullptr; 139 | 140 | return node; 141 | } 142 | 143 | inline NodeT* popFirst() noexcept { 144 | NodeT* node = _bounds[0]; 145 | ASMJIT_ASSERT(node != nullptr); 146 | 147 | NodeT* next = node->next(); 148 | _bounds[0] = next; 149 | 150 | if (next) { 151 | next->_listNodes[0] = nullptr; 152 | node->_listNodes[1] = nullptr; 153 | } 154 | else { 155 | _bounds[1] = nullptr; 156 | } 157 | 158 | return node; 159 | } 160 | 161 | inline NodeT* pop() noexcept { 162 | NodeT* node = _bounds[1]; 163 | ASMJIT_ASSERT(node != nullptr); 164 | 165 | NodeT* prev = node->prev(); 166 | _bounds[1] = prev; 167 | 168 | if (prev) { 169 | prev->_listNodes[1] = nullptr; 170 | node->_listNodes[0] = nullptr; 171 | } 172 | else { 173 | _bounds[0] = nullptr; 174 | } 175 | 176 | return node; 177 | } 178 | 179 | //! \} 180 | }; 181 | 182 | //! \} 183 | 184 | ASMJIT_END_NAMESPACE 185 | 186 | #endif // _ASMJIT_CORE_ZONELIST_H 187 | -------------------------------------------------------------------------------- /asmjit/core/zonestack.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_ZONESTACK_H 8 | #define _ASMJIT_CORE_ZONESTACK_H 9 | 10 | #include "../core/zone.h" 11 | 12 | ASMJIT_BEGIN_NAMESPACE 13 | 14 | //! \addtogroup asmjit_zone 15 | //! \{ 16 | 17 | // ============================================================================ 18 | // [asmjit::ZoneStackBase] 19 | // ============================================================================ 20 | 21 | //! Base class used by `ZoneStack`. 22 | class ZoneStackBase { 23 | public: 24 | ASMJIT_NONCOPYABLE(ZoneStackBase) 25 | 26 | static constexpr uint32_t kBlockSize = ZoneAllocator::kHiMaxSize; 27 | 28 | struct Block { 29 | inline bool empty() const noexcept { return _start == _end; } 30 | inline Block* prev() const noexcept { return _link[Globals::kLinkLeft]; } 31 | inline Block* next() const noexcept { return _link[Globals::kLinkRight]; } 32 | 33 | inline void setPrev(Block* block) noexcept { _link[Globals::kLinkLeft] = block; } 34 | inline void setNext(Block* block) noexcept { _link[Globals::kLinkRight] = block; } 35 | 36 | template 37 | inline T* start() const noexcept { return static_cast(_start); } 38 | template 39 | inline void setStart(T* start) noexcept { _start = static_cast(start); } 40 | 41 | template 42 | inline T* end() const noexcept { return (T*)_end; } 43 | template 44 | inline void setEnd(T* end) noexcept { _end = (void*)end; } 45 | 46 | template 47 | inline T* data() const noexcept { return (T*)((uint8_t*)(this) + sizeof(Block)); } 48 | 49 | template 50 | inline bool canPrepend() const noexcept { return _start > data(); } 51 | 52 | template 53 | inline bool canAppend() const noexcept { 54 | size_t kNumBlockItems = (kBlockSize - sizeof(Block)) / sizeof(T); 55 | size_t kStartBlockIndex = sizeof(Block); 56 | size_t kEndBlockIndex = kStartBlockIndex + kNumBlockItems * sizeof(T); 57 | 58 | return (uintptr_t)_end <= ((uintptr_t)this + kEndBlockIndex - sizeof(T)); 59 | } 60 | 61 | Block* _link[Globals::kLinkCount]; //!< Next and previous blocks. 62 | void* _start; //!< Pointer to the start of the array. 63 | void* _end; //!< Pointer to the end of the array. 64 | }; 65 | 66 | //! Allocator used to allocate data. 67 | ZoneAllocator* _allocator; 68 | //! First and last blocks. 69 | Block* _block[Globals::kLinkCount]; 70 | 71 | //! \name Construction / Destruction 72 | //! \{ 73 | 74 | inline ZoneStackBase() noexcept { 75 | _allocator = nullptr; 76 | _block[0] = nullptr; 77 | _block[1] = nullptr; 78 | } 79 | inline ~ZoneStackBase() noexcept { reset(); } 80 | 81 | inline bool isInitialized() const noexcept { return _allocator != nullptr; } 82 | ASMJIT_API Error _init(ZoneAllocator* allocator, size_t middleIndex) noexcept; 83 | inline Error reset() noexcept { return _init(nullptr, 0); } 84 | 85 | //! \} 86 | 87 | //! \name Accessors 88 | //! \{ 89 | 90 | //! Returns `ZoneAllocator` attached to this container. 91 | inline ZoneAllocator* allocator() const noexcept { return _allocator; } 92 | 93 | inline bool empty() const noexcept { 94 | ASMJIT_ASSERT(isInitialized()); 95 | return _block[0]->start() == _block[1]->end(); 96 | } 97 | 98 | //! \} 99 | 100 | //! \cond INTERNAL 101 | //! \name Internal 102 | //! \{ 103 | 104 | ASMJIT_API Error _prepareBlock(uint32_t side, size_t initialIndex) noexcept; 105 | ASMJIT_API void _cleanupBlock(uint32_t side, size_t middleIndex) noexcept; 106 | 107 | //! \} 108 | //! \endcond 109 | }; 110 | 111 | // ============================================================================ 112 | // [asmjit::ZoneStack] 113 | // ============================================================================ 114 | 115 | //! Zone allocated stack container. 116 | template 117 | class ZoneStack : public ZoneStackBase { 118 | public: 119 | ASMJIT_NONCOPYABLE(ZoneStack) 120 | 121 | enum : uint32_t { 122 | kNumBlockItems = uint32_t((kBlockSize - sizeof(Block)) / sizeof(T)), 123 | kStartBlockIndex = uint32_t(sizeof(Block)), 124 | kMidBlockIndex = uint32_t(kStartBlockIndex + (kNumBlockItems / 2) * sizeof(T)), 125 | kEndBlockIndex = uint32_t(kStartBlockIndex + (kNumBlockItems ) * sizeof(T)) 126 | }; 127 | 128 | //! \name Construction / Destruction 129 | //! \{ 130 | 131 | inline ZoneStack() noexcept {} 132 | inline ~ZoneStack() noexcept {} 133 | 134 | inline Error init(ZoneAllocator* allocator) noexcept { return _init(allocator, kMidBlockIndex); } 135 | 136 | //! \} 137 | 138 | //! \name Utilities 139 | //! \{ 140 | 141 | ASMJIT_INLINE Error prepend(T item) noexcept { 142 | ASMJIT_ASSERT(isInitialized()); 143 | Block* block = _block[Globals::kLinkFirst]; 144 | 145 | if (!block->canPrepend()) { 146 | ASMJIT_PROPAGATE(_prepareBlock(Globals::kLinkFirst, kEndBlockIndex)); 147 | block = _block[Globals::kLinkFirst]; 148 | } 149 | 150 | T* ptr = block->start() - 1; 151 | ASMJIT_ASSERT(ptr >= block->data() && ptr <= block->data() + (kNumBlockItems - 1)); 152 | *ptr = item; 153 | block->setStart(ptr); 154 | return kErrorOk; 155 | } 156 | 157 | ASMJIT_INLINE Error append(T item) noexcept { 158 | ASMJIT_ASSERT(isInitialized()); 159 | Block* block = _block[Globals::kLinkLast]; 160 | 161 | if (!block->canAppend()) { 162 | ASMJIT_PROPAGATE(_prepareBlock(Globals::kLinkLast, kStartBlockIndex)); 163 | block = _block[Globals::kLinkLast]; 164 | } 165 | 166 | T* ptr = block->end(); 167 | ASMJIT_ASSERT(ptr >= block->data() && ptr <= block->data() + (kNumBlockItems - 1)); 168 | 169 | *ptr++ = item; 170 | block->setEnd(ptr); 171 | return kErrorOk; 172 | } 173 | 174 | ASMJIT_INLINE T popFirst() noexcept { 175 | ASMJIT_ASSERT(isInitialized()); 176 | ASMJIT_ASSERT(!empty()); 177 | 178 | Block* block = _block[Globals::kLinkFirst]; 179 | ASMJIT_ASSERT(!block->empty()); 180 | 181 | T* ptr = block->start(); 182 | T item = *ptr++; 183 | 184 | block->setStart(ptr); 185 | if (block->empty()) 186 | _cleanupBlock(Globals::kLinkFirst, kMidBlockIndex); 187 | 188 | return item; 189 | } 190 | 191 | ASMJIT_INLINE T pop() noexcept { 192 | ASMJIT_ASSERT(isInitialized()); 193 | ASMJIT_ASSERT(!empty()); 194 | 195 | Block* block = _block[Globals::kLinkLast]; 196 | ASMJIT_ASSERT(!block->empty()); 197 | 198 | T* ptr = block->end(); 199 | T item = *--ptr; 200 | ASMJIT_ASSERT(ptr >= block->data()); 201 | ASMJIT_ASSERT(ptr >= block->start()); 202 | 203 | block->setEnd(ptr); 204 | if (block->empty()) 205 | _cleanupBlock(Globals::kLinkLast, kMidBlockIndex); 206 | 207 | return item; 208 | } 209 | 210 | //! \} 211 | }; 212 | 213 | //! \} 214 | 215 | ASMJIT_END_NAMESPACE 216 | 217 | #endif // _ASMJIT_CORE_ZONESTACK_H 218 | -------------------------------------------------------------------------------- /asmjit/core/zonestring.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_CORE_SMALLSTRING_H 8 | #define _ASMJIT_CORE_SMALLSTRING_H 9 | 10 | #include "../core/globals.h" 11 | #include "../core/zone.h" 12 | 13 | ASMJIT_BEGIN_NAMESPACE 14 | 15 | //! \addtogroup asmjit_zone 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::ZoneStringBase] 20 | // ============================================================================ 21 | 22 | struct ZoneStringBase { 23 | union { 24 | struct { 25 | uint32_t _size; 26 | char _embedded[sizeof(void*) * 2 - 4]; 27 | }; 28 | struct { 29 | void* _dummy; 30 | char* _external; 31 | }; 32 | }; 33 | 34 | inline void reset() noexcept { 35 | _dummy = nullptr; 36 | _external = nullptr; 37 | } 38 | 39 | Error setData(Zone* zone, uint32_t maxEmbeddedSize, const char* str, size_t size) noexcept { 40 | if (size == SIZE_MAX) 41 | size = strlen(str); 42 | 43 | if (size <= maxEmbeddedSize) { 44 | memcpy(_embedded, str, size); 45 | _embedded[size] = '\0'; 46 | } 47 | else { 48 | char* external = static_cast(zone->dup(str, size, true)); 49 | if (ASMJIT_UNLIKELY(!external)) 50 | return DebugUtils::errored(kErrorOutOfMemory); 51 | _external = external; 52 | } 53 | 54 | _size = uint32_t(size); 55 | return kErrorOk; 56 | } 57 | }; 58 | 59 | // ============================================================================ 60 | // [asmjit::ZoneString] 61 | // ============================================================================ 62 | 63 | //! Small string is a template that helps to create strings that can be either 64 | //! statically allocated if they are small, or externally allocated in case 65 | //! their size exceeds the limit. The `N` represents the size of the whole 66 | //! `ZoneString` structure, based on that size the maximum size of the internal 67 | //! buffer is determined. 68 | template 69 | class ZoneString { 70 | public: 71 | static constexpr uint32_t kWholeSize = 72 | (N > sizeof(ZoneStringBase)) ? uint32_t(N) : uint32_t(sizeof(ZoneStringBase)); 73 | static constexpr uint32_t kMaxEmbeddedSize = kWholeSize - 5; 74 | 75 | union { 76 | ZoneStringBase _base; 77 | char _wholeData[kWholeSize]; 78 | }; 79 | 80 | //! \name Construction & Destruction 81 | //! \{ 82 | 83 | inline ZoneString() noexcept { reset(); } 84 | inline void reset() noexcept { _base.reset(); } 85 | 86 | //! \} 87 | 88 | //! \name Accessors 89 | //! \{ 90 | 91 | inline const char* data() const noexcept { return _base._size <= kMaxEmbeddedSize ? _base._embedded : _base._external; } 92 | inline bool empty() const noexcept { return _base._size == 0; } 93 | inline uint32_t size() const noexcept { return _base._size; } 94 | 95 | inline bool isEmbedded() const noexcept { return _base._size <= kMaxEmbeddedSize; } 96 | 97 | inline Error setData(Zone* zone, const char* data, size_t size) noexcept { 98 | return _base.setData(zone, kMaxEmbeddedSize, data, size); 99 | } 100 | 101 | //! \} 102 | }; 103 | 104 | //! \} 105 | 106 | ASMJIT_END_NAMESPACE 107 | 108 | #endif // _ASMJIT_CORE_SMALLSTRING_H 109 | -------------------------------------------------------------------------------- /asmjit/x86.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_H 8 | #define _ASMJIT_X86_H 9 | 10 | //! \defgroup asmjit_x86 X86 11 | //! 12 | //! \brief X86/X64 Backend. 13 | 14 | #include "./core.h" 15 | 16 | #include "./x86/x86assembler.h" 17 | #include "./x86/x86builder.h" 18 | #include "./x86/x86compiler.h" 19 | #include "./x86/x86emitter.h" 20 | #include "./x86/x86features.h" 21 | #include "./x86/x86globals.h" 22 | #include "./x86/x86instdb.h" 23 | #include "./x86/x86operand.h" 24 | 25 | #endif // _ASMJIT_X86_H 26 | -------------------------------------------------------------------------------- /asmjit/x86/x86assembler.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86ASSEMBLER_H 8 | #define _ASMJIT_X86_X86ASSEMBLER_H 9 | 10 | #include "../core/assembler.h" 11 | #include "../x86/x86emitter.h" 12 | #include "../x86/x86operand.h" 13 | 14 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 15 | 16 | //! \addtogroup asmjit_x86 17 | //! \{ 18 | 19 | // ============================================================================ 20 | // [asmjit::Assembler] 21 | // ============================================================================ 22 | 23 | //! Assembler (X86). 24 | //! 25 | //! Emits X86 machine-code into buffers managed by `CodeHolder`. 26 | class ASMJIT_VIRTAPI Assembler 27 | : public BaseAssembler, 28 | public EmitterImplicitT { 29 | public: 30 | ASMJIT_NONCOPYABLE(Assembler) 31 | typedef BaseAssembler Base; 32 | 33 | //! \name Construction & Destruction 34 | //! \{ 35 | 36 | ASMJIT_API explicit Assembler(CodeHolder* code = nullptr) noexcept; 37 | ASMJIT_API virtual ~Assembler() noexcept; 38 | 39 | //! \} 40 | 41 | //! \cond INTERNAL 42 | //! \name Internal 43 | //! \{ 44 | 45 | // NOTE: x86::Assembler uses _privateData to store 'address-override' bit that 46 | // is used to decide whether to emit address-override (67H) prefix based on 47 | // the memory BASE+INDEX registers. It's either `kX86MemInfo_67H_X86` or 48 | // `kX86MemInfo_67H_X64`. 49 | inline uint32_t _addressOverrideMask() const noexcept { return _privateData; } 50 | inline void _setAddressOverrideMask(uint32_t m) noexcept { _privateData = m; } 51 | 52 | //! \} 53 | //! \endcond 54 | 55 | //! \cond INTERNAL 56 | //! \name Emit 57 | //! \{ 58 | 59 | using BaseEmitter::_emit; 60 | ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) override; 61 | 62 | //! \} 63 | //! \endcond 64 | 65 | //! \name Align 66 | //! \{ 67 | 68 | ASMJIT_API Error align(uint32_t alignMode, uint32_t alignment) override; 69 | 70 | //! \} 71 | 72 | //! \name Events 73 | //! \{ 74 | 75 | ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; 76 | ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; 77 | 78 | //! \} 79 | }; 80 | 81 | //! \} 82 | 83 | ASMJIT_END_SUB_NAMESPACE 84 | 85 | #endif // _ASMJIT_X86_X86ASSEMBLER_H 86 | -------------------------------------------------------------------------------- /asmjit/x86/x86builder.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86BUILDER_H 8 | #define _ASMJIT_X86_X86BUILDER_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_BUILDER 12 | 13 | #include "../core/builder.h" 14 | #include "../core/datatypes.h" 15 | #include "../x86/x86emitter.h" 16 | 17 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 18 | 19 | //! \addtogroup asmjit_x86 20 | //! \{ 21 | 22 | // ============================================================================ 23 | // [asmjit::x86::Builder] 24 | // ============================================================================ 25 | 26 | //! Architecture-dependent asm-builder (X86). 27 | class ASMJIT_VIRTAPI Builder 28 | : public BaseBuilder, 29 | public EmitterImplicitT { 30 | public: 31 | ASMJIT_NONCOPYABLE(Builder) 32 | typedef BaseBuilder Base; 33 | 34 | //! \name Construction & Destruction 35 | //! \{ 36 | 37 | ASMJIT_API explicit Builder(CodeHolder* code = nullptr) noexcept; 38 | ASMJIT_API virtual ~Builder() noexcept; 39 | 40 | //! \} 41 | 42 | //! \name Finalize 43 | //! \{ 44 | 45 | ASMJIT_API Error finalize() override; 46 | 47 | //! \} 48 | 49 | //! \name Events 50 | //! \{ 51 | 52 | ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; 53 | 54 | //! \} 55 | }; 56 | 57 | //! \} 58 | 59 | ASMJIT_END_SUB_NAMESPACE 60 | 61 | #endif // !ASMJIT_NO_BUILDER 62 | #endif // _ASMJIT_X86_X86BUILDER_H 63 | -------------------------------------------------------------------------------- /asmjit/x86/x86callconv_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86CALLCONV_P_H 8 | #define _ASMJIT_X86_X86CALLCONV_P_H 9 | 10 | #include "../core/callconv.h" 11 | 12 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 13 | 14 | //! \cond INTERNAL 15 | //! \addtogroup asmjit_x86 16 | //! \{ 17 | 18 | // ============================================================================ 19 | // [asmjit::x86::CallConvInternal] 20 | // ============================================================================ 21 | 22 | //! X86-specific function API (calling conventions and other utilities). 23 | namespace CallConvInternal { 24 | //! Initialize `CallConv` structure (X86 specific). 25 | Error init(CallConv& cc, uint32_t ccId) noexcept; 26 | } 27 | 28 | //! \} 29 | //! \endcond 30 | 31 | ASMJIT_END_SUB_NAMESPACE 32 | 33 | #endif // _ASMJIT_X86_X86CALLCONV_P_H 34 | -------------------------------------------------------------------------------- /asmjit/x86/x86compiler.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86COMPILER_H 8 | #define _ASMJIT_X86_X86COMPILER_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_COMPILER 12 | 13 | #include "../core/compiler.h" 14 | #include "../core/datatypes.h" 15 | #include "../core/type.h" 16 | #include "../x86/x86emitter.h" 17 | 18 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 19 | 20 | //! \addtogroup asmjit_x86 21 | //! \{ 22 | 23 | // ============================================================================ 24 | // [asmjit::x86::Compiler] 25 | // ============================================================================ 26 | 27 | //! Architecture-dependent asm-compiler (X86). 28 | class ASMJIT_VIRTAPI Compiler 29 | : public BaseCompiler, 30 | public EmitterExplicitT { 31 | public: 32 | ASMJIT_NONCOPYABLE(Compiler) 33 | typedef BaseCompiler Base; 34 | 35 | //! \name Construction & Destruction 36 | //! \{ 37 | 38 | ASMJIT_API explicit Compiler(CodeHolder* code = nullptr) noexcept; 39 | ASMJIT_API virtual ~Compiler() noexcept; 40 | 41 | //! \} 42 | 43 | //! \name Virtual Registers 44 | //! \{ 45 | 46 | #ifndef ASMJIT_NO_LOGGING 47 | # define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \ 48 | _newRegFmt(OUT, PARAM, FORMAT, ARGS) 49 | #else 50 | # define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \ 51 | ASMJIT_UNUSED(FORMAT); \ 52 | _newReg(OUT, PARAM) 53 | #endif 54 | 55 | #define ASMJIT_NEW_REG_CUSTOM(FUNC, REG) \ 56 | inline REG FUNC(uint32_t typeId) { \ 57 | REG reg(Globals::NoInit); \ 58 | _newReg(reg, typeId); \ 59 | return reg; \ 60 | } \ 61 | \ 62 | template \ 63 | inline REG FUNC(uint32_t typeId, const char* fmt, Args&&... args) { \ 64 | REG reg(Globals::NoInit); \ 65 | ASMJIT_NEW_REG_FMT(reg, typeId, fmt, std::forward(args)...); \ 66 | return reg; \ 67 | } 68 | 69 | #define ASMJIT_NEW_REG_TYPED(FUNC, REG, TYPE_ID) \ 70 | inline REG FUNC() { \ 71 | REG reg(Globals::NoInit); \ 72 | _newReg(reg, TYPE_ID); \ 73 | return reg; \ 74 | } \ 75 | \ 76 | template \ 77 | inline REG FUNC(const char* fmt, Args&&... args) { \ 78 | REG reg(Globals::NoInit); \ 79 | ASMJIT_NEW_REG_FMT(reg, TYPE_ID, fmt, std::forward(args)...); \ 80 | return reg; \ 81 | } 82 | 83 | template 84 | inline RegT newSimilarReg(const RegT& ref) { 85 | RegT reg(Globals::NoInit); 86 | _newReg(reg, ref); 87 | return reg; 88 | } 89 | 90 | template 91 | inline RegT newSimilarReg(const RegT& ref, const char* fmt, Args&&... args) { 92 | RegT reg(Globals::NoInit); 93 | ASMJIT_NEW_REG_FMT(reg, ref, fmt, std::forward(args)...); 94 | return reg; 95 | } 96 | 97 | ASMJIT_NEW_REG_CUSTOM(newReg , Reg ) 98 | ASMJIT_NEW_REG_CUSTOM(newGp , Gp ) 99 | ASMJIT_NEW_REG_CUSTOM(newVec , Vec ) 100 | ASMJIT_NEW_REG_CUSTOM(newK , KReg) 101 | 102 | ASMJIT_NEW_REG_TYPED(newI8 , Gp , Type::kIdI8 ) 103 | ASMJIT_NEW_REG_TYPED(newU8 , Gp , Type::kIdU8 ) 104 | ASMJIT_NEW_REG_TYPED(newI16 , Gp , Type::kIdI16 ) 105 | ASMJIT_NEW_REG_TYPED(newU16 , Gp , Type::kIdU16 ) 106 | ASMJIT_NEW_REG_TYPED(newI32 , Gp , Type::kIdI32 ) 107 | ASMJIT_NEW_REG_TYPED(newU32 , Gp , Type::kIdU32 ) 108 | ASMJIT_NEW_REG_TYPED(newI64 , Gp , Type::kIdI64 ) 109 | ASMJIT_NEW_REG_TYPED(newU64 , Gp , Type::kIdU64 ) 110 | ASMJIT_NEW_REG_TYPED(newInt8 , Gp , Type::kIdI8 ) 111 | ASMJIT_NEW_REG_TYPED(newUInt8 , Gp , Type::kIdU8 ) 112 | ASMJIT_NEW_REG_TYPED(newInt16 , Gp , Type::kIdI16 ) 113 | ASMJIT_NEW_REG_TYPED(newUInt16 , Gp , Type::kIdU16 ) 114 | ASMJIT_NEW_REG_TYPED(newInt32 , Gp , Type::kIdI32 ) 115 | ASMJIT_NEW_REG_TYPED(newUInt32 , Gp , Type::kIdU32 ) 116 | ASMJIT_NEW_REG_TYPED(newInt64 , Gp , Type::kIdI64 ) 117 | ASMJIT_NEW_REG_TYPED(newUInt64 , Gp , Type::kIdU64 ) 118 | ASMJIT_NEW_REG_TYPED(newIntPtr , Gp , Type::kIdIntPtr ) 119 | ASMJIT_NEW_REG_TYPED(newUIntPtr, Gp , Type::kIdUIntPtr) 120 | 121 | ASMJIT_NEW_REG_TYPED(newGpb , Gp , Type::kIdU8 ) 122 | ASMJIT_NEW_REG_TYPED(newGpw , Gp , Type::kIdU16 ) 123 | ASMJIT_NEW_REG_TYPED(newGpd , Gp , Type::kIdU32 ) 124 | ASMJIT_NEW_REG_TYPED(newGpq , Gp , Type::kIdU64 ) 125 | ASMJIT_NEW_REG_TYPED(newGpz , Gp , Type::kIdUIntPtr) 126 | ASMJIT_NEW_REG_TYPED(newXmm , Xmm , Type::kIdI32x4 ) 127 | ASMJIT_NEW_REG_TYPED(newXmmSs , Xmm , Type::kIdF32x1 ) 128 | ASMJIT_NEW_REG_TYPED(newXmmSd , Xmm , Type::kIdF64x1 ) 129 | ASMJIT_NEW_REG_TYPED(newXmmPs , Xmm , Type::kIdF32x4 ) 130 | ASMJIT_NEW_REG_TYPED(newXmmPd , Xmm , Type::kIdF64x2 ) 131 | ASMJIT_NEW_REG_TYPED(newYmm , Ymm , Type::kIdI32x8 ) 132 | ASMJIT_NEW_REG_TYPED(newYmmPs , Ymm , Type::kIdF32x8 ) 133 | ASMJIT_NEW_REG_TYPED(newYmmPd , Ymm , Type::kIdF64x4 ) 134 | ASMJIT_NEW_REG_TYPED(newZmm , Zmm , Type::kIdI32x16 ) 135 | ASMJIT_NEW_REG_TYPED(newZmmPs , Zmm , Type::kIdF32x16 ) 136 | ASMJIT_NEW_REG_TYPED(newZmmPd , Zmm , Type::kIdF64x8 ) 137 | ASMJIT_NEW_REG_TYPED(newMm , Mm , Type::kIdMmx64 ) 138 | ASMJIT_NEW_REG_TYPED(newKb , KReg, Type::kIdMask8 ) 139 | ASMJIT_NEW_REG_TYPED(newKw , KReg, Type::kIdMask16 ) 140 | ASMJIT_NEW_REG_TYPED(newKd , KReg, Type::kIdMask32 ) 141 | ASMJIT_NEW_REG_TYPED(newKq , KReg, Type::kIdMask64 ) 142 | 143 | #undef ASMJIT_NEW_REG_TYPED 144 | #undef ASMJIT_NEW_REG_CUSTOM 145 | #undef ASMJIT_NEW_REG_FMT 146 | 147 | //! \} 148 | 149 | //! \name Stack 150 | //! \{ 151 | 152 | //! Creates a new memory chunk allocated on the current function's stack. 153 | inline Mem newStack(uint32_t size, uint32_t alignment, const char* name = nullptr) { 154 | Mem m(Globals::NoInit); 155 | _newStack(m, size, alignment, name); 156 | return m; 157 | } 158 | 159 | //! \} 160 | 161 | //! \name Constants 162 | //! \{ 163 | 164 | //! Put data to a constant-pool and get a memory reference to it. 165 | inline Mem newConst(uint32_t scope, const void* data, size_t size) { 166 | Mem m(Globals::NoInit); 167 | _newConst(m, scope, data, size); 168 | return m; 169 | } 170 | 171 | //! Put a BYTE `val` to a constant-pool. 172 | inline Mem newByteConst(uint32_t scope, uint8_t val) noexcept { return newConst(scope, &val, 1); } 173 | //! Put a WORD `val` to a constant-pool. 174 | inline Mem newWordConst(uint32_t scope, uint16_t val) noexcept { return newConst(scope, &val, 2); } 175 | //! Put a DWORD `val` to a constant-pool. 176 | inline Mem newDWordConst(uint32_t scope, uint32_t val) noexcept { return newConst(scope, &val, 4); } 177 | //! Put a QWORD `val` to a constant-pool. 178 | inline Mem newQWordConst(uint32_t scope, uint64_t val) noexcept { return newConst(scope, &val, 8); } 179 | 180 | //! Put a WORD `val` to a constant-pool. 181 | inline Mem newInt16Const(uint32_t scope, int16_t val) noexcept { return newConst(scope, &val, 2); } 182 | //! Put a WORD `val` to a constant-pool. 183 | inline Mem newUInt16Const(uint32_t scope, uint16_t val) noexcept { return newConst(scope, &val, 2); } 184 | //! Put a DWORD `val` to a constant-pool. 185 | inline Mem newInt32Const(uint32_t scope, int32_t val) noexcept { return newConst(scope, &val, 4); } 186 | //! Put a DWORD `val` to a constant-pool. 187 | inline Mem newUInt32Const(uint32_t scope, uint32_t val) noexcept { return newConst(scope, &val, 4); } 188 | //! Put a QWORD `val` to a constant-pool. 189 | inline Mem newInt64Const(uint32_t scope, int64_t val) noexcept { return newConst(scope, &val, 8); } 190 | //! Put a QWORD `val` to a constant-pool. 191 | inline Mem newUInt64Const(uint32_t scope, uint64_t val) noexcept { return newConst(scope, &val, 8); } 192 | 193 | //! Put a SP-FP `val` to a constant-pool. 194 | inline Mem newFloatConst(uint32_t scope, float val) noexcept { return newConst(scope, &val, 4); } 195 | //! Put a DP-FP `val` to a constant-pool. 196 | inline Mem newDoubleConst(uint32_t scope, double val) noexcept { return newConst(scope, &val, 8); } 197 | 198 | //! Put a MMX `val` to a constant-pool. 199 | inline Mem newMmConst(uint32_t scope, const Data64& val) noexcept { return newConst(scope, &val, 8); } 200 | //! Put a XMM `val` to a constant-pool. 201 | inline Mem newXmmConst(uint32_t scope, const Data128& val) noexcept { return newConst(scope, &val, 16); } 202 | //! Put a YMM `val` to a constant-pool. 203 | inline Mem newYmmConst(uint32_t scope, const Data256& val) noexcept { return newConst(scope, &val, 32); } 204 | 205 | //! \} 206 | 207 | //! \name Instruction Options 208 | //! \{ 209 | 210 | //! Force the compiler to not follow the conditional or unconditional jump. 211 | inline Compiler& unfollow() noexcept { _instOptions |= Inst::kOptionUnfollow; return *this; } 212 | //! Tell the compiler that the destination variable will be overwritten. 213 | inline Compiler& overwrite() noexcept { _instOptions |= Inst::kOptionOverwrite; return *this; } 214 | 215 | //! \} 216 | 217 | //! \name Function Call & Ret Intrinsics 218 | //! \{ 219 | 220 | //! Call a function. 221 | inline FuncCallNode* call(const Gp& dst, const FuncSignature& sign) { return addCall(Inst::kIdCall, dst, sign); } 222 | //! \overload 223 | inline FuncCallNode* call(const Mem& dst, const FuncSignature& sign) { return addCall(Inst::kIdCall, dst, sign); } 224 | //! \overload 225 | inline FuncCallNode* call(const Label& label, const FuncSignature& sign) { return addCall(Inst::kIdCall, label, sign); } 226 | //! \overload 227 | inline FuncCallNode* call(const Imm& dst, const FuncSignature& sign) { return addCall(Inst::kIdCall, dst, sign); } 228 | //! \overload 229 | inline FuncCallNode* call(uint64_t dst, const FuncSignature& sign) { return addCall(Inst::kIdCall, Imm(int64_t(dst)), sign); } 230 | 231 | //! Return. 232 | inline FuncRetNode* ret() { return addRet(Operand(), Operand()); } 233 | //! \overload 234 | inline FuncRetNode* ret(const BaseReg& o0) { return addRet(o0, Operand()); } 235 | //! \overload 236 | inline FuncRetNode* ret(const BaseReg& o0, const BaseReg& o1) { return addRet(o0, o1); } 237 | 238 | //! \} 239 | 240 | //! \name Finalize 241 | //! \{ 242 | 243 | ASMJIT_API Error finalize() override; 244 | 245 | //! \} 246 | 247 | //! \name Events 248 | //! \{ 249 | 250 | ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; 251 | 252 | //! \} 253 | }; 254 | 255 | //! \} 256 | 257 | ASMJIT_END_SUB_NAMESPACE 258 | 259 | #endif // !ASMJIT_NO_COMPILER 260 | #endif // _ASMJIT_X86_X86COMPILER_H 261 | -------------------------------------------------------------------------------- /asmjit/x86/x86instapi_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86INSTAPI_P_H 8 | #define _ASMJIT_X86_X86INSTAPI_P_H 9 | 10 | #include "../core/inst.h" 11 | #include "../core/operand.h" 12 | 13 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 14 | 15 | //! \cond INTERNAL 16 | //! \addtogroup asmjit_x86 17 | //! \{ 18 | 19 | namespace InstInternal { 20 | 21 | #ifndef ASMJIT_NO_TEXT 22 | Error instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept; 23 | uint32_t stringToInstId(uint32_t archId, const char* s, size_t len) noexcept; 24 | #endif // !ASMJIT_NO_TEXT 25 | 26 | #ifndef ASMJIT_NO_VALIDATION 27 | Error validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept; 28 | #endif // !ASMJIT_NO_VALIDATION 29 | 30 | #ifndef ASMJIT_NO_INTROSPECTION 31 | Error queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept; 32 | Error queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept; 33 | #endif // !ASMJIT_NO_INTROSPECTION 34 | 35 | } // {InstInternal} 36 | 37 | //! \} 38 | //! \endcond 39 | 40 | ASMJIT_END_SUB_NAMESPACE 41 | 42 | #endif // _ASMJIT_X86_X86INSTAPI_P_H 43 | -------------------------------------------------------------------------------- /asmjit/x86/x86internal_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86INTERNAL_P_H 8 | #define _ASMJIT_X86_X86INTERNAL_P_H 9 | 10 | #include "../core/build.h" 11 | 12 | #include "../core/func.h" 13 | #include "../x86/x86emitter.h" 14 | #include "../x86/x86operand.h" 15 | 16 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 17 | 18 | //! \cond INTERNAL 19 | //! \addtogroup asmjit_x86 20 | //! \{ 21 | 22 | // ============================================================================ 23 | // [asmjit::X86Internal] 24 | // ============================================================================ 25 | 26 | //! X86 utilities used at multiple places, not part of public API, not exported. 27 | struct X86Internal { 28 | //! Initialize `FuncDetail` (X86 specific). 29 | static Error initFuncDetail(FuncDetail& func, const FuncSignature& sign, uint32_t gpSize) noexcept; 30 | 31 | //! Initialize `FuncFrame` (X86 specific). 32 | static Error initFuncFrame(FuncFrame& frame, const FuncDetail& func) noexcept; 33 | 34 | //! Finalize `FuncFrame` (X86 specific). 35 | static Error finalizeFuncFrame(FuncFrame& frame) noexcept; 36 | 37 | static Error argsToFuncFrame(const FuncArgsAssignment& args, FuncFrame& frame) noexcept; 38 | 39 | //! Emit function prolog. 40 | static Error emitProlog(Emitter* emitter, const FuncFrame& frame); 41 | 42 | //! Emit function epilog. 43 | static Error emitEpilog(Emitter* emitter, const FuncFrame& frame); 44 | 45 | //! Emit a pure move operation between two registers or the same type or 46 | //! between a register and its home slot. This function does not handle 47 | //! register conversion. 48 | static Error emitRegMove(Emitter* emitter, 49 | const Operand_& dst_, 50 | const Operand_& src_, uint32_t typeId, bool avxEnabled, const char* comment = nullptr); 51 | 52 | //! Emit move from a function argument (either register or stack) to a register. 53 | //! 54 | //! This function can handle the necessary conversion from one argument to 55 | //! another, and from one register type to another, if it's possible. Any 56 | //! attempt of conversion that requires third register of a different group 57 | //! (for example conversion from K to MMX) will fail. 58 | static Error emitArgMove(Emitter* emitter, 59 | const Reg& dst_, uint32_t dstTypeId, 60 | const Operand_& src_, uint32_t srcTypeId, bool avxEnabled, const char* comment = nullptr); 61 | 62 | static Error emitArgsAssignment(Emitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args); 63 | }; 64 | 65 | //! \} 66 | //! \endcond 67 | 68 | ASMJIT_END_SUB_NAMESPACE 69 | 70 | #endif // _ASMJIT_X86_X86INTERNAL_P_H 71 | -------------------------------------------------------------------------------- /asmjit/x86/x86logging_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86LOGGING_P_H 8 | #define _ASMJIT_X86_X86LOGGING_P_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_LOGGING 12 | 13 | #include "../core/logging.h" 14 | #include "../core/string.h" 15 | #include "../x86/x86globals.h" 16 | 17 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 18 | 19 | //! \addtogroup asmjit_x86 20 | //! \{ 21 | 22 | // ============================================================================ 23 | // [asmjit::x86::LoggingInternal] 24 | // ============================================================================ 25 | 26 | namespace LoggingInternal { 27 | Error formatRegister( 28 | String& sb, 29 | uint32_t flags, 30 | const BaseEmitter* emitter, 31 | uint32_t archId, 32 | uint32_t regType, 33 | uint32_t regId) noexcept; 34 | 35 | Error formatOperand( 36 | String& sb, 37 | uint32_t flags, 38 | const BaseEmitter* emitter, 39 | uint32_t archId, 40 | const Operand_& op) noexcept; 41 | 42 | Error formatInstruction( 43 | String& sb, 44 | uint32_t flags, 45 | const BaseEmitter* emitter, 46 | uint32_t archId, 47 | const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept; 48 | }; 49 | 50 | //! \} 51 | 52 | ASMJIT_END_SUB_NAMESPACE 53 | 54 | #endif // !ASMJIT_NO_LOGGING 55 | #endif // _ASMJIT_X86_X86LOGGING_P_H 56 | -------------------------------------------------------------------------------- /asmjit/x86/x86rapass_p.h: -------------------------------------------------------------------------------- 1 | // [AsmJit] 2 | // Machine Code Generation for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _ASMJIT_X86_X86RAPASS_P_H 8 | #define _ASMJIT_X86_X86RAPASS_P_H 9 | 10 | #include "../core/build.h" 11 | #ifndef ASMJIT_NO_COMPILER 12 | 13 | #include "../core/compiler.h" 14 | #include "../core/rabuilders_p.h" 15 | #include "../core/rapass_p.h" 16 | #include "../x86/x86assembler.h" 17 | #include "../x86/x86compiler.h" 18 | 19 | ASMJIT_BEGIN_SUB_NAMESPACE(x86) 20 | 21 | //! \cond INTERNAL 22 | 23 | //! \defgroup asmjit_x86_ra X86 RA 24 | //! \ingroup asmjit_x86 25 | //! 26 | //! \brief X86/X64 register allocation. 27 | 28 | //! \addtogroup asmjit_x86_ra 29 | //! \{ 30 | 31 | // ============================================================================ 32 | // [asmjit::X86RAPass] 33 | // ============================================================================ 34 | 35 | //! X86 register allocation pass. 36 | //! 37 | //! Takes care of generating function prologs and epilogs, and also performs 38 | //! register allocation. 39 | class X86RAPass : public RAPass { 40 | public: 41 | ASMJIT_NONCOPYABLE(X86RAPass) 42 | typedef RAPass Base; 43 | 44 | bool _avxEnabled; 45 | 46 | // -------------------------------------------------------------------------- 47 | // [Construction / Destruction] 48 | // -------------------------------------------------------------------------- 49 | 50 | X86RAPass() noexcept; 51 | virtual ~X86RAPass() noexcept; 52 | 53 | // -------------------------------------------------------------------------- 54 | // [Accessors] 55 | // -------------------------------------------------------------------------- 56 | 57 | //! Returns the compiler casted to `x86::Compiler`. 58 | inline Compiler* cc() const noexcept { return static_cast(_cb); } 59 | 60 | // -------------------------------------------------------------------------- 61 | // [Utilities] 62 | // -------------------------------------------------------------------------- 63 | 64 | inline uint32_t choose(uint32_t sseInstId, uint32_t avxInstId) noexcept { 65 | return _avxEnabled ? avxInstId : sseInstId; 66 | } 67 | 68 | // -------------------------------------------------------------------------- 69 | // [OnInit / OnDone] 70 | // -------------------------------------------------------------------------- 71 | 72 | void onInit() noexcept override; 73 | void onDone() noexcept override; 74 | 75 | // -------------------------------------------------------------------------- 76 | // [CFG] 77 | // -------------------------------------------------------------------------- 78 | 79 | Error buildCFG() noexcept override; 80 | 81 | // -------------------------------------------------------------------------- 82 | // [Emit] 83 | // -------------------------------------------------------------------------- 84 | 85 | Error onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override; 86 | Error onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override; 87 | 88 | Error onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override; 89 | Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept override; 90 | 91 | Error onEmitJump(const Label& label) noexcept override; 92 | Error onEmitPreCall(FuncCallNode* node) noexcept override; 93 | }; 94 | 95 | //! \} 96 | //! \endcond 97 | 98 | ASMJIT_END_SUB_NAMESPACE 99 | 100 | #endif // !ASMJIT_NO_COMPILER 101 | #endif // _ASMJIT_X86_X86RAPASS_P_H 102 | -------------------------------------------------------------------------------- /lib/asmjit.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satisfactorymodding/SatisfactoryModBootstrapper/efbb7285c70b7569b212d4ea6fd1b03749b8f439/lib/asmjit.lib -------------------------------------------------------------------------------- /src/AssemblyAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "AssemblyAnalyzer.h" 2 | #include 3 | #include 4 | 5 | struct FunctionPointerContainerImpl { 6 | void* FunctionAddress; 7 | uint32_t ThisAdjustment; 8 | }; 9 | 10 | bool IsJumpThunkInstruction(const ZydisDecodedInstruction& Instruction) { 11 | return Instruction.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_JMP && Instruction.operands[0].type == ZydisOperandType::ZYDIS_OPERAND_TYPE_IMMEDIATE; 12 | } 13 | 14 | bool IsVirtualTableJumpThunkInstruction(const ZydisDecodedInstruction& Instruction) { 15 | const ZydisDecodedOperand& FirstOp = Instruction.operands[0]; 16 | return Instruction.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_JMP && 17 | FirstOp.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_MEMORY && 18 | FirstOp.mem.base == ZydisRegister::ZYDIS_REGISTER_RAX && 19 | FirstOp.mem.index == ZydisRegister::ZYDIS_REGISTER_NONE; 20 | } 21 | 22 | //basically tests for assembly sequence: mov rax, [rcx] 23 | bool IsFirstVirtualTableCallThunkInstruction(const ZydisDecodedInstruction& Instruction) { 24 | const ZydisDecodedOperand& FirstOp = Instruction.operands[0]; 25 | const ZydisDecodedOperand& SecondOp = Instruction.operands[1]; 26 | return Instruction.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_MOV && 27 | FirstOp.type == ZydisOperandType::ZYDIS_OPERAND_TYPE_REGISTER && 28 | FirstOp.reg.value == ZydisRegister::ZYDIS_REGISTER_RAX && 29 | SecondOp.type == ZydisOperandType ::ZYDIS_OPERAND_TYPE_MEMORY && 30 | SecondOp.mem.type == ZydisMemoryOperandType::ZYDIS_MEMOP_TYPE_MEM && 31 | SecondOp.mem.base == ZydisRegister::ZYDIS_REGISTER_RCX && 32 | !SecondOp.mem.disp.has_displacement && 33 | SecondOp.mem.index == ZydisRegister::ZYDIS_REGISTER_NONE; 34 | } 35 | 36 | void* DiscoverRealFunctionAddress(uint8_t* FunctionPtr, bool& bIsVirtualFunction, uint32_t& VirtualTableOffset) { 37 | ZydisDecoder decoder; 38 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); 39 | 40 | // Initialize formatter. Only required when you actually plan to do instruction 41 | // formatting ("disassembling"), like we do here 42 | ZydisFormatter formatter; 43 | ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); 44 | 45 | // Loop over the instructions in our buffer. 46 | // The runtime-address (instruction pointer) is chosen arbitrary here in order to better 47 | // visualize relative addressing 48 | ZydisDecodedInstruction Instruction; 49 | bool bFirstInstruction = ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, FunctionPtr, 4096, &Instruction)); 50 | if (!bFirstInstruction) { 51 | return nullptr; //Invalid sequence - not an instruction 52 | } 53 | //test for simple in-module jump thunk 54 | if (IsJumpThunkInstruction(Instruction)) { 55 | ZyanU64 ResultJumpAddress; 56 | ZydisCalcAbsoluteAddress(&Instruction, &Instruction.operands[0], (ZyanU64) FunctionPtr, &ResultJumpAddress); 57 | return DiscoverRealFunctionAddress((uint8_t*) ResultJumpAddress, bIsVirtualFunction, VirtualTableOffset); 58 | } 59 | //test for virtual table call thunk 60 | if (IsFirstVirtualTableCallThunkInstruction(Instruction)) { 61 | //second instruction should be jump by pointer with displacement, 62 | //which will be virtual table offset then 63 | FunctionPtr += Instruction.length; 64 | bool bSecondInstruction = ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, FunctionPtr, 4096, &Instruction)); 65 | if (!bSecondInstruction) { 66 | return nullptr; //Invalid sequence - not an instruction 67 | } 68 | //Next instruction should be: jmp qword ptr [rax+Displacement] 69 | if (IsVirtualTableJumpThunkInstruction(Instruction)) { 70 | const auto& Displacement = Instruction.operands[0].mem.disp; 71 | bIsVirtualFunction = true; 72 | VirtualTableOffset = Displacement.has_displacement ? (uint32_t) Displacement.value : 0; 73 | return nullptr; //Doesn't have an actual address because it is virtual 74 | } 75 | } 76 | //We can assume this is correct function pointer now 77 | return FunctionPtr; 78 | } 79 | 80 | MemberFunctionInfo DigestMemberFunctionPointer(void* FunctionPointerContainer, size_t ContainerSize) { 81 | auto* ptr = reinterpret_cast(FunctionPointerContainer); 82 | uint32_t ThisAdjustment; 83 | void* CodePointer; 84 | if (ContainerSize == 8) { 85 | //Member function of class with single inheritance, no this adjustment required 86 | CodePointer = ptr->FunctionAddress; 87 | ThisAdjustment = 0; 88 | } else if (ContainerSize == 16) { 89 | //Class has multiple inheritance, so this adjustment is needed 90 | CodePointer = ptr->FunctionAddress; 91 | ThisAdjustment = ptr->ThisAdjustment; 92 | } else { 93 | //unsupported case - virtual inheritance probably 94 | std::cerr << "[WARN] Unsupported member function pointer size: " << ContainerSize << std::endl; 95 | return MemberFunctionInfo{nullptr}; 96 | }; 97 | bool bIsVirtualFunction = false; 98 | uint32_t VirtualTableOffset = 0; 99 | void* RealFunctionAddress = DiscoverRealFunctionAddress((uint8_t*) CodePointer, bIsVirtualFunction, VirtualTableOffset); 100 | return MemberFunctionInfo{RealFunctionAddress, ThisAdjustment, bIsVirtualFunction, VirtualTableOffset}; 101 | } 102 | 103 | /*HRESULT CoCreateDiaDataSource(HMODULE diaDllHandle, IDiaDataSource** data_source) { 104 | auto DllGetClassObject = (BOOL (WINAPI*)(REFCLSID, REFIID, LPVOID *)) GetProcAddress(diaDllHandle, "DllGetClassObject"); 105 | if (!DllGetClassObject) { 106 | return HRESULT_FROM_WIN32(GetLastError()); 107 | } 108 | IClassFactory* pClassFactory; 109 | HRESULT hr = DllGetClassObject(CLSID_DiaSource, IID_IClassFactory, reinterpret_cast(&pClassFactory)); 110 | if (FAILED(hr)) { 111 | return hr; 112 | } 113 | hr = pClassFactory->CreateInstance(nullptr, IID_IDiaDataSource, (void **) data_source); 114 | if (FAILED(hr)) { 115 | return hr; 116 | } 117 | return S_OK; 118 | }*/ 119 | -------------------------------------------------------------------------------- /src/AssemblyAnalyzer.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_ASSEMBLY_ANALYZER_H 2 | #define XINPUT1_3_ASSEMBLY_ANALYZER_H 3 | #include 4 | 5 | template 6 | struct PointerContainer { 7 | FunctionPtrType MyPointer; 8 | }; 9 | 10 | struct MemberFunctionInfo { 11 | //Code to the real implementation of the function 12 | //Will be null pointer if function is virtual! 13 | void* OriginalCodePointer; 14 | uint32_t ThisAdjustment; 15 | bool bIsVirtualFunctionThunk; 16 | uint32_t VirtualTableOffset; 17 | }; 18 | 19 | MemberFunctionInfo DigestMemberFunctionPointer(void* FunctionPointerContainer, size_t ContainerSize); 20 | 21 | #endif //XINPUT1_3_ASSEMBLY_ANALYZER_H 22 | -------------------------------------------------------------------------------- /src/DestructorGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_DESTRUCTORGENERATOR_H 2 | #define XINPUT1_3_DESTRUCTORGENERATOR_H 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #define ASMJIT_STATIC 1 6 | #include "asmjit.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef void (*DestructorFunctionPtr)(void*); 13 | typedef void (*DummyFunctionPtr)(); 14 | typedef void (*DummyFunctionCallHandler)(const char* FunctionName); 15 | 16 | typedef void (*OpaqueFunctionPtr)(); 17 | 18 | //--- DO NOT CHANGE LAYOUT OF THIS STRUCT - ASM CODE USES IT DIRECTLY --- 19 | struct ConstructorCallbackEntry { 20 | //Generated thunk will jump to this address after calling 21 | void* TrampolineFunctionAddress; 22 | //Function that will be called with this and user data pointer 23 | void* CallProcessor; 24 | //User data passed to processor function. can be anything 25 | void* UserData; 26 | }; 27 | 28 | class DestructorGenerator { 29 | private: 30 | CComPtr globalSymbol; 31 | LPVOID dllBaseAddress; 32 | std::unordered_map GeneratedDestructorsMap; 33 | std::unordered_map GeneratedDummyFunctionsMap; 34 | asmjit::JitRuntime runtime; 35 | std::vector ConstantPoolEntries; 36 | public: 37 | DestructorGenerator(LPVOID gameDllBase, CComPtr globalSymbol) : 38 | globalSymbol(std::move(globalSymbol)), 39 | dllBaseAddress(gameDllBase) {} 40 | ~DestructorGenerator(); 41 | 42 | DestructorFunctionPtr GenerateDestructor(const std::string& ClassName); 43 | DummyFunctionPtr GenerateDummyFunction(const std::string& FunctionName, DummyFunctionCallHandler CallHandler); 44 | /** Generates constructor patch entry. CallBackEntry should be valid as long as returned function is used */ 45 | OpaqueFunctionPtr GenerateConstructorPatchEntry(ConstructorCallbackEntry* CallBackEntry); 46 | private: 47 | /** 48 | * Generates destructor call for the given symbol 49 | * RCX should be set to the this pointer of the object being destructor 50 | * @param BasePtr pointer to the location in memory where executable is loaded 51 | * @param StackOffset offset on the rsp to the beginning of the first variable 52 | */ 53 | void GenerateDestructorCall(const CComPtr& Symbol, asmjit::x86::Builder& a, uint64_t StackOffset); 54 | DestructorFunctionPtr FindOrGenerateDestructorFunction(const CComPtr& ClassSymbol); 55 | DestructorFunctionPtr GenerateDestructorFunction(const CComPtr& ClassSymbol); 56 | DummyFunctionPtr DoGenerateDummyFunction(const std::string& FunctionName, DummyFunctionCallHandler CallHandler); 57 | }; 58 | 59 | 60 | #endif //XINPUT1_3_DESTRUCTORGENERATOR_H 61 | -------------------------------------------------------------------------------- /src/DllLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "DllLoader.h" 2 | #include "logging.h" 3 | #include 4 | #include "util.h" 5 | #define MAX_NAME_LENGTH 2048 6 | 7 | DllLoader::DllLoader(SymbolResolver* importResolver) : resolver(importResolver), dbgHelpModule(nullptr) {} 8 | 9 | typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 10 | 11 | typedef DWORD64 (__stdcall *SymLoadModuleExW)(HANDLE hProcess, HANDLE hFile, PCWSTR ImageName, PCWSTR ModuleName, DWORD64 BaseOfDll, DWORD DllSize, void* Data, DWORD Flags); 12 | typedef bool (__stdcall *SymSetSearchPathW)(HANDLE hProcess, PCWSTR SearchPathStr); 13 | typedef bool (__stdcall *SymUnloadModule64)(HANDLE hProcess, DWORD64 BaseOfDll); 14 | 15 | void UnprotectPageIfNeeded(void* pagePointer, DWORD pageRangeSize); 16 | 17 | bool ResolveDllImportsInternal(std::unordered_set& alreadyLoadedLibraries, SymbolResolver* resolver, unsigned char* codeBase, PIMAGE_IMPORT_DESCRIPTOR importDesc); 18 | 19 | HMODULE DllLoader::LoadModule(const path& filePath) { 20 | HMODULE preloadedModule = GetModuleHandleW(filePath.filename().c_str()); 21 | if (preloadedModule != nullptr) { 22 | //don't try to load the same module twice. 23 | return preloadedModule; 24 | } 25 | auto* baseDll = reinterpret_cast(LoadLibraryExW(filePath.c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES)); 26 | Logging::logFile << "Loaded Raw DLL module: " << baseDll << std::endl; 27 | auto dosHeader = (PIMAGE_DOS_HEADER) baseDll; 28 | Logging::logFile << "Casted module to dos header " << dosHeader << std::endl; 29 | Logging::logFile << "Magic: " << dosHeader->e_magic << " Expected Magic: " << IMAGE_DOS_SIGNATURE << std::endl; 30 | auto pNTHeader = (PIMAGE_NT_HEADERS) ((LONGLONG) dosHeader + dosHeader->e_lfanew); 31 | auto importsDir = (PIMAGE_DATA_DIRECTORY) &(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]); 32 | if (importsDir->Size) { 33 | Logging::logFile << "Import Directory Size: " << importsDir->Size << std::endl; 34 | auto baseImp = (PIMAGE_IMPORT_DESCRIPTOR) (baseDll + importsDir->VirtualAddress); 35 | UnprotectPageIfNeeded(reinterpret_cast(baseImp), importsDir->Size); 36 | if (!ResolveDllImportsInternal(alreadyLoadedLibraries, resolver, baseDll, baseImp)) { 37 | Logging::logFile << "LoadModule failed: Cannot resolve imports of the library" << std::endl; 38 | return nullptr; 39 | } 40 | } 41 | Logging::logFile << "Resolved imports successfully; Calling DllMain" << std::endl; 42 | if (pNTHeader->OptionalHeader.AddressOfEntryPoint != 0) { 43 | auto DllEntry = (DllEntryProc)(LPVOID)(baseDll + pNTHeader->OptionalHeader.AddressOfEntryPoint); 44 | // notify library about attaching to process 45 | BOOL successful = (*DllEntry)((HINSTANCE) baseDll, DLL_PROCESS_ATTACH, nullptr); 46 | if (!successful) { 47 | Logging::logFile << "LoadModule failed: DllEntry returned false for library" << std::endl; 48 | return nullptr; 49 | } 50 | } 51 | Logging::logFile << "Called DllMain successfully on DLL. Loading finished." << std::endl; 52 | TryToLoadModulePDB((HMODULE) baseDll); 53 | return (HINSTANCE) baseDll; 54 | } 55 | 56 | /** @return path to directory containing DLL and PDB files */ 57 | std::wstring loadModuleDbgInfo(HMODULE dbgHelpModule, HMODULE dllModule) { 58 | HANDLE currentProcess = GetCurrentProcess(); 59 | auto loadFunc = reinterpret_cast(GetProcAddress(dbgHelpModule, "SymLoadModuleExW")); 60 | auto unloadFunc = reinterpret_cast(GetProcAddress(dbgHelpModule, "SymUnloadModule64")); 61 | auto setSearchPathFunc = reinterpret_cast(GetProcAddress(dbgHelpModule, "SymSetSearchPathW")); 62 | MODULEINFO moduleInfo{}; 63 | wchar_t moduleName[MAX_NAME_LENGTH] = {0}; 64 | wchar_t imageName[MAX_NAME_LENGTH] = {0}; 65 | GetModuleInformation(currentProcess, dllModule, &moduleInfo, sizeof(moduleInfo)); 66 | GetModuleFileNameExW(currentProcess, dllModule, imageName, MAX_NAME_LENGTH); 67 | GetModuleBaseNameW(currentProcess, dllModule, moduleName, MAX_NAME_LENGTH); 68 | const wchar_t* symbolSearchPath = path(imageName).parent_path().wstring().c_str(); 69 | setSearchPathFunc(currentProcess, symbolSearchPath); 70 | 71 | //unload old module symbols if they were loaded via UE4's invasive load with invalid search path 72 | unloadFunc(currentProcess, (DWORD64) moduleInfo.lpBaseOfDll); 73 | DWORD64 resultAddr = loadFunc(currentProcess, dllModule, imageName, moduleName, (DWORD64) moduleInfo.lpBaseOfDll, (UINT32) moduleInfo.SizeOfImage, nullptr, 0); 74 | if (!resultAddr) { 75 | Logging::logFile << "Failed to load debug information for module " << path(imageName).string().c_str() << std::endl; 76 | Logging::logFile << "Failure Reason: " << GetLastErrorAsString() << std::endl; 77 | } 78 | return symbolSearchPath; 79 | } 80 | 81 | void DllLoader::FlushDebugSymbols() { 82 | //only perform flushing if we don't have dbghelp initialized 83 | if (dbgHelpModule == nullptr) { 84 | dbgHelpModule = GetModuleHandleA("dbghelp.dll"); 85 | if (dbgHelpModule == nullptr) { 86 | Logging::logFile << "[WARNING] FlushDebugSymbols called too early dbghelp.dll is not loaded yet." << std::endl; 87 | return; 88 | } 89 | Logging::logFile << "Flushing debug symbols" << std::endl; 90 | //DbgHelp is loaded now, so perform registering of all earlier entries 91 | for (const auto& modulePath : delayedModulePDBs) { 92 | LoadModulePDBInternal(modulePath); 93 | } 94 | delayedModulePDBs.clear(); 95 | } 96 | } 97 | 98 | void DllLoader::LoadModulePDBInternal(HMODULE dllModule) { 99 | const std::wstring SymbolDirectory = loadModuleDbgInfo(dbgHelpModule, dllModule); 100 | pdbRootDirectories.insert(SymbolDirectory); 101 | } 102 | 103 | void DllLoader::TryToLoadModulePDB(HMODULE module) { 104 | if (dbgHelpModule == nullptr) { 105 | //DbgHelp module is not initialized yet, delay loading 106 | delayedModulePDBs.push_back(module); 107 | } else { 108 | //DbgHelp is ready to accept symbol initializations, so perform it 109 | LoadModulePDBInternal(module); 110 | } 111 | } 112 | 113 | bool ResolveDllImportsInternal(std::unordered_set& alreadyLoadedLibraries, SymbolResolver* resolver, unsigned char* codeBase, PIMAGE_IMPORT_DESCRIPTOR importDesc) { 114 | for (; importDesc->Name; importDesc++) { 115 | uintptr_t *thunkRef; 116 | FARPROC *funcRef; 117 | if (importDesc->OriginalFirstThunk) { 118 | thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk); 119 | funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 120 | } else { 121 | // no hint table 122 | thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk); 123 | funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 124 | } 125 | const char* libraryName = (LPCSTR) (codeBase + importDesc->Name); 126 | 127 | HMODULE libraryHandle = GetModuleHandleA(libraryName); 128 | if (libraryHandle == nullptr) { 129 | std::string libraryNameString = libraryName; 130 | //try to load library only once 131 | if (alreadyLoadedLibraries.count(libraryNameString) == 0) { 132 | //load library if it exists, fallback to symbol resolver 133 | libraryHandle = LoadLibraryA(libraryName); 134 | alreadyLoadedLibraries.insert(std::move(libraryNameString)); 135 | } 136 | } 137 | //iterate all thunk import entries and resolve them 138 | for (; *thunkRef; thunkRef++, funcRef++) { 139 | const char* importDescriptor; 140 | if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { 141 | importDescriptor = (LPCSTR)IMAGE_ORDINAL(*thunkRef); 142 | } else { 143 | auto thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); 144 | importDescriptor = (LPCSTR)&thunkData->Name; 145 | } 146 | UnprotectPageIfNeeded(reinterpret_cast(funcRef), 1); 147 | if (libraryHandle == nullptr) { 148 | //library handle is empty, attempt symbol resolution 149 | *funcRef = reinterpret_cast(resolver->ResolveSymbol(importDescriptor)); 150 | } else { 151 | //we have a library reference for a given import, so use GetProcAddress 152 | *funcRef = GetProcAddress(libraryHandle, importDescriptor); 153 | } 154 | if (*funcRef == nullptr) { 155 | Logging::logFile << "Failed to resolve import of symbol " << importDescriptor << " from " << libraryName << std::endl; 156 | return false; 157 | } 158 | } 159 | } 160 | return true; 161 | } 162 | 163 | //checks if memory page is protected and changes access to PAGE_READWRITE if it is protected 164 | void UnprotectPageIfNeeded(void* pagePointer, DWORD pageRangeSize) { 165 | MEMORY_BASIC_INFORMATION memoryInfo; 166 | auto result = VirtualQuery(pagePointer, &memoryInfo, sizeof(memoryInfo)); 167 | if ((memoryInfo.Protect & PAGE_READONLY) > 0) { 168 | Logging::logFile << "Removing protection from page " << pagePointer << std::endl; 169 | VirtualProtect(pagePointer, 1, PAGE_READWRITE, &memoryInfo.Protect); 170 | } 171 | } -------------------------------------------------------------------------------- /src/DllLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_DLLLOADER_H 2 | #define XINPUT1_3_DLLLOADER_H 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #include 7 | #include 8 | #include "SymbolResolver.h" 9 | #include 10 | using path = std::filesystem::path; 11 | 12 | class DllLoader { 13 | public: 14 | SymbolResolver* resolver; 15 | std::unordered_set pdbRootDirectories; 16 | private: 17 | std::unordered_set alreadyLoadedLibraries; 18 | std::vector delayedModulePDBs; 19 | HMODULE dbgHelpModule; 20 | public: 21 | explicit DllLoader(SymbolResolver* importResolver); 22 | 23 | HMODULE LoadModule(const path& filePath); 24 | 25 | /** 26 | * Flushes all delay loaded PDBs into the 27 | * symbol storage for the loaded DbgHelp.dll instance 28 | * Note that DbgHelp should be initialized at this point, 29 | * otherwise it will cause undefined behavior 30 | */ 31 | void FlushDebugSymbols(); 32 | private: 33 | void LoadModulePDBInternal(HMODULE module); 34 | void TryToLoadModulePDB(HMODULE module); 35 | }; 36 | 37 | 38 | #endif //XINPUT1_3_DLLLOADER_H 39 | -------------------------------------------------------------------------------- /src/SymbolResolver.cpp: -------------------------------------------------------------------------------- 1 | #include "DestructorGenerator.h" 2 | #include "SymbolResolver.h" 3 | #include "logging.h" 4 | #include "atlbase.h" 5 | #include "comdef.h" 6 | #include "util.h" 7 | #include 8 | #include "provided_symbols.h" 9 | 10 | // Implemented in VC CRT (msvcVERSION.dll or vcruntimeVERSION.dll or UCRT (Windows 10 only)) 11 | extern "C" char * __unDName(char* outputString, const char* name, int maxStringLength, void* (*pAlloc)(size_t), void(*pFree)(void*), unsigned short disableFlags); 12 | 13 | #define CHECK_FAILED(hr, message) \ 14 | if (FAILED(hr)) { \ 15 | _com_error err(hr); \ 16 | LPCTSTR errMsg = err.ErrorMessage(); \ 17 | Logging::logFile << message << errMsg << std::endl; \ 18 | exit(1); \ 19 | } 20 | 21 | HRESULT CoCreateDiaDataSource(HMODULE diaDllHandle, CComPtr& data_source); 22 | 23 | SymbolResolver::SymbolResolver(HMODULE gameModuleHandle, HMODULE diaDllHandle, bool exitOnUnresolvedSymbol) { 24 | this->exitOnUnresolvedSymbol = exitOnUnresolvedSymbol; 25 | CComPtr dataSource; 26 | HRESULT hr = CoCreateDiaDataSource(diaDllHandle, dataSource); 27 | CHECK_FAILED(hr, "Failed to create IDiaDatSource: "); 28 | std::wstring executablePath = getModuleFileName(gameModuleHandle); 29 | (*dataSource).loadDataForExe(executablePath.c_str(), nullptr, nullptr); 30 | CHECK_FAILED(hr, "Failed to load DIA data from executable file: "); 31 | hr = (*dataSource).openSession(&diaSession); 32 | CHECK_FAILED(hr, "Failed to open DIA session: "); 33 | //because HMODULE value is the same as the DLL load base address, according to the MS documentation 34 | hr = (*diaSession).put_loadAddress(reinterpret_cast(gameModuleHandle)); 35 | CHECK_FAILED(hr, "Failed to update DLL load address on IDiaSession: "); 36 | hr = (*diaSession).get_globalScope(&globalSymbol); 37 | CHECK_FAILED(hr, "Failed to retrieve global DLL scope"); 38 | dllBaseAddress = (LPVOID) gameModuleHandle; 39 | destructorGenerator = new DestructorGenerator(dllBaseAddress, globalSymbol); 40 | hookRequiredSymbols(*this); 41 | } 42 | 43 | void DummyUnresolvedSymbolHandler(const char* symbolName) { 44 | Logging::logFile << "Attempt to call unresolved symbol from a loaded module code!!!" << std::endl; 45 | Logging::logFile << "This is a dummy symbol and it cannot be called. Aborting." << std::endl; 46 | Logging::logFile << "Symbol Name: " << symbolName << std::endl; 47 | exit(1); 48 | } 49 | 50 | void* SymbolResolver::ResolveSymbol(const char* mangledSymbolName) { 51 | USES_CONVERSION; 52 | const wchar_t* resultName = A2CW(mangledSymbolName); 53 | CComPtr enumSymbols; 54 | HRESULT hr = (*globalSymbol).findChildren(SymTagNull, resultName, nsfCaseSensitive, &enumSymbols); 55 | CHECK_FAILED(hr, "Failed find symbol in executable: "); 56 | LONG symbolCount = 0L; 57 | enumSymbols->get_Count(&symbolCount); 58 | if (symbolCount == 0L) { 59 | void* providedSymbolPointer = provideSymbolImplementation(mangledSymbolName); 60 | if (providedSymbolPointer != nullptr) { 61 | return providedSymbolPointer; //fallback to provided symbol 62 | } 63 | Logging::logFile << "[FATAL] Executable missing symbol with mangled name: " << mangledSymbolName << std::endl; 64 | char* demangledName = __unDName(nullptr, mangledSymbolName, 0, malloc, free, 0); 65 | Logging::logFile << "[FATAL] De-mangled symbol name (for reference): " << demangledName << std::endl; 66 | free(demangledName); 67 | if (exitOnUnresolvedSymbol) { 68 | Logging::logFile << "[FATAL] Strict mode enabled. Aborting on missing symbol." << std::endl; 69 | exit(1); 70 | } 71 | Logging::logFile << "[FATAL] Overriding it with dummy symbol. Bad things will happen if it is going to be actually called!" << std::endl; 72 | return generateDummySymbol(demangledName, &DummyUnresolvedSymbolHandler); 73 | } 74 | CComPtr resolvedSymbol; 75 | hr = enumSymbols->Item(0, &resolvedSymbol); 76 | CHECK_FAILED(hr, "Failed to retrieve first matching symbol: "); 77 | DWORD resultAddress; 78 | hr = resolvedSymbol->get_relativeVirtualAddress(&resultAddress); 79 | CHECK_FAILED(hr, "Failed to retrieve symbol relative virtual address: "); 80 | return reinterpret_cast((unsigned long long)dllBaseAddress + resultAddress); 81 | } 82 | 83 | SymbolDigestInfo SymbolResolver::DigestGameSymbol(const wchar_t* SymbolName) { 84 | CComPtr enumSymbols; 85 | HRESULT hr = (*globalSymbol).findChildren(SymTagNull, SymbolName, nsfCaseSensitive, &enumSymbols); 86 | CHECK_FAILED(hr, "findChildren failed in executable"); 87 | LONG SymbolCount = 0L; 88 | enumSymbols->get_Count(&SymbolCount); 89 | SymbolDigestInfo ResultDigestInfo{}; 90 | if (SymbolCount == 0) { 91 | ResultDigestInfo.bSymbolNotFound = true; 92 | return ResultDigestInfo; 93 | } 94 | if (SymbolCount > 1) { 95 | ResultDigestInfo.bMultipleSymbolsMatch = true; 96 | return ResultDigestInfo; 97 | } 98 | CComPtr ResultSymbol; 99 | enumSymbols->Item(0, &ResultSymbol); 100 | ResultSymbol->get_undecoratedName(&ResultDigestInfo.SymbolName.String); 101 | ResultDigestInfo.SymbolName.StringFree = &SysFreeString; 102 | DWORD LocationType = 0; 103 | ResultSymbol->get_locationType(&LocationType); 104 | if (LocationType == LocIsNull) { 105 | ResultDigestInfo.bSymbolOptimizedAway = LocationType == LocIsNull; 106 | return ResultDigestInfo; 107 | } 108 | BOOL bIsVirtual = false; 109 | ResultSymbol->get_virtual(&bIsVirtual); 110 | ResultDigestInfo.bSymbolVirtual = bIsVirtual; 111 | 112 | if (LocationType == LocIsStatic) { 113 | DWORD RelativeVirtualAddress; 114 | ResultSymbol->get_relativeVirtualAddress(&RelativeVirtualAddress); 115 | void* SymbolPointer = reinterpret_cast((uint64_t)dllBaseAddress + RelativeVirtualAddress); 116 | ResultDigestInfo.SymbolImplementationPointer = SymbolPointer; 117 | } 118 | return ResultDigestInfo; 119 | } 120 | 121 | SymbolResolver::~SymbolResolver() = default; 122 | 123 | 124 | HRESULT CoCreateDiaDataSource(HMODULE diaDllHandle, CComPtr& data_source) { 125 | auto DllGetClassObject = (BOOL (WINAPI*)(REFCLSID, REFIID, LPVOID *)) GetProcAddress(diaDllHandle, "DllGetClassObject"); 126 | if (!DllGetClassObject) { 127 | return HRESULT_FROM_WIN32(GetLastError()); 128 | } 129 | CComPtr pClassFactory; 130 | HRESULT hr = DllGetClassObject(CLSID_DiaSource, IID_IClassFactory, reinterpret_cast(&pClassFactory)); 131 | if (FAILED(hr)) { 132 | return hr; 133 | } 134 | hr = pClassFactory->CreateInstance(nullptr, IID_IDiaDataSource, (void **) &data_source); 135 | if (FAILED(hr)) { 136 | return hr; 137 | } 138 | return S_OK; 139 | } 140 | -------------------------------------------------------------------------------- /src/SymbolResolver.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_SYMBOLRESOLVER_H 2 | #define XINPUT1_3_SYMBOLRESOLVER_H 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "exports.h" 12 | 13 | class SymbolResolver { 14 | public: 15 | CComPtr diaSession; 16 | CComPtr globalSymbol; 17 | bool exitOnUnresolvedSymbol; 18 | LPVOID dllBaseAddress; 19 | class DestructorGenerator* destructorGenerator; 20 | public: 21 | explicit SymbolResolver(HMODULE gameModuleHandle, HMODULE diaDllHandle, bool exitOnUnresolvedSymbol); 22 | ~SymbolResolver(); 23 | 24 | SymbolDigestInfo DigestGameSymbol(const wchar_t* SymbolName); 25 | void* ResolveSymbol(const char* mangledSymbolName); 26 | }; 27 | 28 | #endif //XINPUT1_3_SYMBOLRESOLVER_H 29 | -------------------------------------------------------------------------------- /src/VTableFixHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "VTableFixHelper.h" 2 | #include 3 | 4 | static std::unordered_map CachedVirtualTables; 5 | 6 | uint32_t DetermineVirtualTableSize(void* VirtualTablePtr) { 7 | //DIA seems to be unable to give exact virtual table size 8 | //tried get_length and get_staticSize - both return 0 at SymTagPublicSymbol 9 | //Fallback to 4kB virtual table size for now - it's big enough for most classes 10 | //without bloating memory too much with hooked virtual tables 11 | return 4096; 12 | } 13 | 14 | uint8_t* CreateAndCacheVirtualTable(uint8_t* OriginalTable, VTableDefinition& Definition) { 15 | auto RemoveIterator = CachedVirtualTables.find(OriginalTable); 16 | if (Definition.bFlushCaches && RemoveIterator != CachedVirtualTables.end()) { 17 | //Remove old value and free memory it occupied 18 | CachedVirtualTables.erase(RemoveIterator); 19 | free(RemoveIterator->second); 20 | } 21 | Definition.bFlushCaches = false; 22 | //Allocate enough memory, copy values, apply overrides 23 | size_t TableSize = DetermineVirtualTableSize(OriginalTable); 24 | auto* NewVirtualTable = (uint8_t*) malloc(TableSize); 25 | memcpy(NewVirtualTable, OriginalTable, TableSize); 26 | //Apply overrides now 27 | for (VTableFixEntry& FixEntry : Definition.FixEntries) { 28 | void** FunctionPointer = (void**) (NewVirtualTable + FixEntry.FunctionEntryOffset); 29 | *FixEntry.OutOriginalFunctionPtr = *FunctionPointer; 30 | *FunctionPointer = FixEntry.FunctionToCallInstead; 31 | } 32 | //Add our entry to the caches map and return it 33 | CachedVirtualTables.insert({OriginalTable, NewVirtualTable}); 34 | return NewVirtualTable; 35 | } 36 | 37 | void ApplyConstructorFixes(void* ThisPointer, ConstructorFixInfo* FixInfo) { 38 | uint8_t* OffsetPointer = (uint8_t*) ThisPointer; 39 | for (VTableDefinition& Definition : FixInfo->Fixes) { 40 | uint32_t OriginOffset = Definition.VirtualTableOriginOffset; 41 | uint8_t** VirtualTableField = (uint8_t**) (OffsetPointer + OriginOffset); 42 | uint8_t* VirtualTablePointer = *VirtualTableField; 43 | uint8_t* NewVirtualTablePointer = CreateAndCacheVirtualTable(VirtualTablePointer, Definition); 44 | *VirtualTableField = NewVirtualTablePointer; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/VTableFixHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT_1_3_VTABLEFIXHELPER_H 2 | #define XINPUT_1_3_VTABLEFIXHELPER_H 3 | #include "SymbolResolver.h" 4 | #include 5 | 6 | struct VTableFixEntry { 7 | unsigned int FunctionEntryOffset; 8 | void* FunctionToCallInstead; 9 | void** OutOriginalFunctionPtr; 10 | }; 11 | 12 | struct VTableDefinition { 13 | unsigned int VirtualTableOriginOffset; 14 | bool bFlushCaches; 15 | std::vector FixEntries; 16 | }; 17 | 18 | struct ConstructorFixInfo { 19 | std::vector Fixes; 20 | }; 21 | 22 | void ApplyConstructorFixes(void* ThisPointer, ConstructorFixInfo* FixInfo); 23 | 24 | #endif //XINPUT_1_3_VTABLEFIXHELPER_H 25 | -------------------------------------------------------------------------------- /src/controller.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include "logging.h" 4 | #include "DllLoader.h" 5 | #include 6 | #include 7 | #include 8 | #include "exports.h" 9 | #include "util.h" 10 | #include "DestructorGenerator.h" 11 | #include "VTableFixHelper.h" 12 | #include "AssemblyAnalyzer.h" 13 | 14 | using namespace std::filesystem; 15 | 16 | #define GAME_MODULE_NAME "FactoryGame-Win64-Shipping.exe" 17 | 18 | static DllLoader* dllLoader; 19 | 20 | extern "C" __declspec(dllexport) const wchar_t* bootstrapperVersion = L"2.0.11"; 21 | 22 | bool EXPORTS_IsLoaderModuleLoaded(const char* moduleName) { 23 | return GetModuleHandleA(moduleName) != nullptr; 24 | } 25 | 26 | void* EXPORTS_LoadModule(const char*, const wchar_t* filePath) { 27 | return dllLoader->LoadModule(filePath); 28 | } 29 | 30 | FUNCTION_PTR EXPORTS_GetModuleProcAddress(void* module, const char* symbolName) { 31 | return GetProcAddress(reinterpret_cast(module), symbolName); 32 | } 33 | 34 | FUNCTION_PTR EXPORTS_ResolveModuleSymbol(const char* symbolName) { 35 | return reinterpret_cast(dllLoader->resolver->ResolveSymbol(symbolName)); 36 | } 37 | 38 | SymbolDigestInfo EXPORTS_DigestGameSymbol(const wchar_t* SymbolName) { 39 | return dllLoader->resolver->DigestGameSymbol(SymbolName); 40 | } 41 | 42 | ConstructorHookThunk EXPORTS_CreateConstructorHookThunkFunc() { 43 | auto* CallbackEntry = new ConstructorCallbackEntry; 44 | CallbackEntry->CallProcessor = (void*) &ApplyConstructorFixes; 45 | CallbackEntry->UserData = new ConstructorFixInfo{}; 46 | void* GeneratedThunk = (void*) dllLoader->resolver->destructorGenerator->GenerateConstructorPatchEntry(CallbackEntry); 47 | ConstructorHookThunk HookThunk{}; 48 | HookThunk.OpaquePointer = CallbackEntry; 49 | HookThunk.OutTrampolineAddress = &CallbackEntry->TrampolineFunctionAddress; 50 | HookThunk.GeneratedThunkAddress = GeneratedThunk; 51 | return HookThunk; 52 | } 53 | 54 | void FreeString(wchar_t* Pointer) { free(Pointer); } 55 | 56 | MemberFunctionPointerDigestInfo EXPORTS_DigestMemberFunctionPointer(MemberFunctionPointerInfo Info) { 57 | MemberFunctionInfo FunctionInfo = DigestMemberFunctionPointer(Info.MemberFunctionPointer, Info.MemberFunctionPointerSize); 58 | std::wstring ResultUniqueName{}; 59 | ResultUniqueName.append(std::to_wstring(FunctionInfo.ThisAdjustment)); 60 | ResultUniqueName.append(L"_"); 61 | ResultUniqueName.append(std::to_wstring(FunctionInfo.VirtualTableOffset)); 62 | 63 | size_t StringMemorySize = sizeof(wchar_t) * (ResultUniqueName.size() + 1); 64 | wchar_t* ResultMemory = (wchar_t*) malloc(StringMemorySize); 65 | memcpy(ResultMemory, ResultUniqueName.data(), StringMemorySize); 66 | 67 | MemberFunctionPointerDigestInfo DigestInfo{}; 68 | DigestInfo.bIsVirtualFunctionPointer = FunctionInfo.bIsVirtualFunctionThunk; 69 | DigestInfo.UniqueName.String = ResultMemory; 70 | DigestInfo.UniqueName.StringFree = &FreeString; 71 | return DigestInfo; 72 | } 73 | 74 | bool EXPORTS_AddConstructorHook(ConstructorHookThunk ConstructorThunk, VirtualFunctionHookInfo HookInfo) { 75 | auto* CallbackEntry = reinterpret_cast(ConstructorThunk.OpaquePointer); 76 | auto* FixInfo = reinterpret_cast(CallbackEntry->UserData); 77 | MemberFunctionInfo FunctionInfo = DigestMemberFunctionPointer(HookInfo.PointerInfo.MemberFunctionPointer, HookInfo.PointerInfo.MemberFunctionPointerSize); 78 | if (FunctionInfo.bIsVirtualFunctionThunk) { 79 | VTableDefinition* TableDefinition = nullptr; 80 | for (VTableDefinition& FixEntry : FixInfo->Fixes) { 81 | if (FixEntry.VirtualTableOriginOffset == FunctionInfo.ThisAdjustment) { 82 | TableDefinition = &FixEntry; break; 83 | } 84 | } 85 | if (TableDefinition == nullptr) { 86 | FixInfo->Fixes.push_back(VTableDefinition{FunctionInfo.ThisAdjustment}); 87 | TableDefinition = &FixInfo->Fixes[FixInfo->Fixes.size() - 1]; 88 | } 89 | TableDefinition->bFlushCaches = true; 90 | VTableFixEntry FixEntry{}; 91 | FixEntry.FunctionEntryOffset = FunctionInfo.VirtualTableOffset; 92 | FixEntry.FunctionToCallInstead = HookInfo.FunctionToCallInstead; 93 | FixEntry.OutOriginalFunctionPtr = HookInfo.OutOriginalFunctionPtr; 94 | TableDefinition->FixEntries.push_back(FixEntry); 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | void EXPORTS_FlushDebugSymbols() { 101 | dllLoader->FlushDebugSymbols(); 102 | } 103 | 104 | wchar_t* EXPORTS_GetSymbolFileRoots(void*(*Malloc)(uint64_t)) { 105 | std::wstring Result; 106 | for (const std::wstring& RootDirectory : dllLoader->pdbRootDirectories) { 107 | Result.append(RootDirectory); 108 | Result.append(SYMBOL_ROOT_SEPARATOR); 109 | } 110 | //remove last ';' appended if we appended anything at all 111 | if (Result.length() > 0) 112 | Result.erase(Result.length() - 1); 113 | //string length + \0 terminator * size of wchar_t 114 | const size_t AllocationSize = (Result.length() + 1) * sizeof(wchar_t); 115 | void* AllocatedMemory = Malloc(AllocationSize); 116 | memcpy(AllocatedMemory, Result.data(), AllocationSize); 117 | return (wchar_t*) AllocatedMemory; 118 | } 119 | 120 | std::string GetLastErrorAsString(); 121 | 122 | void discoverLoaderMods(std::map& discoveredModules, const std::filesystem::path& rootGameDirectory) { 123 | std::filesystem::path directoryPath = rootGameDirectory / "loaders"; 124 | std::filesystem::create_directories(directoryPath); 125 | for (auto& file : std::filesystem::directory_iterator(directoryPath)) { 126 | if (file.is_regular_file() && file.path().extension() == ".dll") { 127 | Logging::logFile << "Discovering loader module candidate tetest " << file.path().filename() << std::endl; 128 | HMODULE loadedModule = dllLoader->LoadModule(file.path().wstring().c_str()); 129 | if (loadedModule != nullptr) { 130 | Logging::logFile << "Successfully loaded module " << file.path().filename() << std::endl; 131 | discoveredModules.insert({file.path().filename().string(), loadedModule}); 132 | } else { 133 | Logging::logFile << "Failed to load module " << file.path().filename() << ": " << std::endl; 134 | Logging::logFile << "Last Error Message: " << GetLastErrorAsString() << std::endl; 135 | exit(1); 136 | } 137 | } 138 | } 139 | } 140 | 141 | void bootstrapLoaderMods(const std::map& discoveredModules, const std::wstring& gameRootDirectory) { 142 | for (auto& loaderModule : discoveredModules) { 143 | FUNCTION_PTR bootstrapFunc = GetProcAddress(loaderModule.second, "BootstrapModule"); 144 | if (bootstrapFunc == nullptr) { 145 | Logging::logFile << "[WARNING]: BootstrapModule() not found in loader module " << loaderModule.first << "!" << std::endl; 146 | return; 147 | } 148 | BootstrapAccessors accessors{ 149 | gameRootDirectory.c_str(), 150 | &EXPORTS_LoadModule, 151 | &EXPORTS_GetModuleProcAddress, 152 | &EXPORTS_IsLoaderModuleLoaded, 153 | &EXPORTS_ResolveModuleSymbol, 154 | bootstrapperVersion, 155 | &EXPORTS_FlushDebugSymbols, 156 | &EXPORTS_GetSymbolFileRoots, 157 | &EXPORTS_DigestGameSymbol, 158 | &EXPORTS_CreateConstructorHookThunkFunc, 159 | &EXPORTS_AddConstructorHook, 160 | &EXPORTS_DigestMemberFunctionPointer 161 | }; 162 | Logging::logFile << "Bootstrapping module " << loaderModule.first << std::endl; 163 | ((BootstrapModuleFunc) bootstrapFunc)(accessors); 164 | } 165 | } 166 | 167 | static std::mutex setupHookMutex; 168 | static volatile bool hookAlreadySetup = false; 169 | 170 | std::filesystem::path resolveGameRootDir() { 171 | wchar_t pathBuffer[2048]; //just to be sure it will always fit 172 | GetModuleFileNameW(GetModuleHandleA(GAME_MODULE_NAME), pathBuffer, 2048); 173 | std::filesystem::path rootDirPath{pathBuffer}; 174 | std::string gameFolderName(GAME_MODULE_NAME); 175 | gameFolderName.erase(gameFolderName.find('-')); 176 | //we go up the directory tree until we find the folder called 177 | //FactoryGame, which denotes the root of the game directory 178 | while (!std::filesystem::exists(rootDirPath / gameFolderName)) { 179 | rootDirPath = rootDirPath.parent_path(); 180 | } 181 | return rootDirPath; 182 | } 183 | 184 | void setupExecutableHook(HMODULE selfModuleHandle) { 185 | //fast route to exit before locking on mutex 186 | if (hookAlreadySetup) return; 187 | std::lock_guard guard(setupHookMutex); 188 | //check if we have loaded while we loaded on mutex 189 | if (hookAlreadySetup) return; 190 | hookAlreadySetup = true; //mark as loaded 191 | 192 | //initialize systems, load symbols, call bootstrapper modules 193 | Logging::initializeLogging(); 194 | Logging::logFile << "Setting up hooking" << std::endl; 195 | 196 | path rootGameDirectory = resolveGameRootDir(); 197 | path bootstrapperDirectory = path(getModuleFileName(selfModuleHandle)).parent_path(); 198 | Logging::logFile << "Game Root Directory: " << rootGameDirectory << std::endl; 199 | Logging::logFile << "Bootstrapper Directory: " << bootstrapperDirectory << std::endl; 200 | 201 | HMODULE gameModule = GetModuleHandleA(GAME_MODULE_NAME); 202 | if (gameModule == nullptr) { 203 | Logging::logFile << "Failed to find primary game module with name: " << GAME_MODULE_NAME << std::endl; 204 | exit(1); 205 | } 206 | path diaDllPath = bootstrapperDirectory / "msdia140.dll"; 207 | HMODULE diaDllHandle = LoadLibraryW(diaDllPath.wstring().c_str()); 208 | if (diaDllHandle == nullptr) { 209 | Logging::logFile << "Failed to load DIA SDK implementation DLL." << std::endl; 210 | Logging::logFile << "Expected to find it at: " << diaDllPath.string() << std::endl; 211 | Logging::logFile << "Make sure it is here and restart. Exiting now." << std::endl; 212 | exit(1); 213 | } 214 | //TODO strict mode where missing symbols result in aborting? 215 | auto* resolver = new SymbolResolver(gameModule, diaDllHandle, false); 216 | dllLoader = new DllLoader(resolver); 217 | 218 | Logging::logFile << "Discovering loader modules..." << std::endl; 219 | std::map discoveredMods; 220 | discoverLoaderMods(discoveredMods, rootGameDirectory); 221 | 222 | Logging::logFile << "Bootstrapping loader modules..." << std::endl; 223 | bootstrapLoaderMods(discoveredMods, rootGameDirectory.wstring()); 224 | 225 | Logging::logFile << "Successfully performed bootstrapping." << std::endl; 226 | } 227 | -------------------------------------------------------------------------------- /src/controller.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_CONTROLLER_H 2 | #define XINPUT1_3_CONTROLLER_H 3 | 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | void setupExecutableHook(HMODULE selfModule); 8 | 9 | #endif //XINPUT1_3_CONTROLLER_H 10 | -------------------------------------------------------------------------------- /src/exports.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_EXPORTS_H 2 | #define XINPUT1_3_EXPORTS_H 3 | 4 | typedef __int64(__stdcall *FUNCTION_PTR)(); 5 | typedef void* HLOADEDMODULE; 6 | typedef HLOADEDMODULE(*LoadModuleFunc)(const char*, const wchar_t* filePath); 7 | typedef FUNCTION_PTR(*GetModuleProcAddress)(HLOADEDMODULE module, const char* symbolName); 8 | typedef bool(*IsLoaderModuleLoaded)(const char* moduleName); 9 | /** 10 | * @deprecated Use DigestGameSymbol instead 11 | */ 12 | typedef FUNCTION_PTR(*ResolveGameSymbolPtr)(const char* symbolName); 13 | typedef void(*FlushDebugSymbolsFunc)(); 14 | 15 | /** 16 | * @param symbolName name of the symbol to search for. Can be both decorated and undecorated name 17 | * @note When multiple symbols with same name are found, only bMultipleSymbolsMatch is set to true 18 | * @note You have to manually free SymbolName with provided free to avoid memory leaks! 19 | * @return information about symbol 20 | */ 21 | typedef struct SymbolDigestInfo(*DigestGameSymbolFunc)(const wchar_t* symbolName); 22 | 23 | /** 24 | * @return list of symbol root directories joined by ';' symbol; 25 | * @note Memory will be allocated by using provided malloc and **should be freed manually by the caller** 26 | */ 27 | #define SYMBOL_ROOT_SEPARATOR L";" 28 | typedef wchar_t* (*GetSymbolFileRootsFunc)(void*(*malloc)(unsigned long long)); 29 | 30 | /** 31 | * Creates function thunk usable for overriding virtual table of the constructor function 32 | * Before using it, set OutTrampolineAddress to the correct pointer to the original constructor 33 | * You can add hooked functions via 34 | */ 35 | typedef struct ConstructorHookThunk(*CreateConstructorHookThunkFunc)(); 36 | 37 | /** 38 | * Adds virtual function hook to the given constructor thunk 39 | * @return true when successfully hooked, false otherwise 40 | */ 41 | typedef bool(*AddConstructorHookFunc)(struct ConstructorHookThunk ConstructorThunk, struct VirtualFunctionHookInfo HookInfo); 42 | 43 | typedef struct MemberFunctionPointerDigestInfo(*DigestMemberFunctionPointerFunc)(struct MemberFunctionPointerInfo Info); 44 | 45 | typedef void(*FreeStringFunc)(wchar_t* String); 46 | 47 | struct BootstrapperString { 48 | wchar_t* String; 49 | FreeStringFunc StringFree; 50 | inline void Free() const { StringFree(String); } 51 | }; 52 | 53 | struct SymbolDigestInfo { 54 | bool bSymbolNotFound; 55 | bool bSymbolOptimizedAway; 56 | bool bSymbolVirtual; 57 | bool bMultipleSymbolsMatch; 58 | void* SymbolImplementationPointer; 59 | BootstrapperString SymbolName; 60 | }; 61 | 62 | struct ConstructorHookThunk { 63 | void* OpaquePointer; 64 | void* GeneratedThunkAddress; 65 | //set this address after hooking to point to the trampoline function 66 | void** OutTrampolineAddress; 67 | }; 68 | 69 | struct MemberFunctionPointerDigestInfo { 70 | bool bIsVirtualFunctionPointer; 71 | BootstrapperString UniqueName; 72 | }; 73 | 74 | struct MemberFunctionPointerInfo { 75 | void* MemberFunctionPointer; 76 | int MemberFunctionPointerSize; 77 | }; 78 | 79 | struct VirtualFunctionHookInfo { 80 | MemberFunctionPointerInfo PointerInfo; 81 | void* FunctionToCallInstead; 82 | void** OutOriginalFunctionPtr; 83 | }; 84 | 85 | struct BootstrapAccessors { 86 | const wchar_t* gameRootDirectory; 87 | LoadModuleFunc LoadModule; 88 | GetModuleProcAddress GetModuleProcAddress; 89 | IsLoaderModuleLoaded IsLoaderModuleLoaded; 90 | ResolveGameSymbolPtr ResolveGameSymbol; 91 | const wchar_t* version; 92 | FlushDebugSymbolsFunc FlushDebugSymbols; 93 | GetSymbolFileRootsFunc GetSymbolFileRoots; 94 | DigestGameSymbolFunc DigestGameSymbol; 95 | CreateConstructorHookThunkFunc CreateConstructorHookThunk; 96 | AddConstructorHookFunc AddConstructorHook; 97 | DigestMemberFunctionPointerFunc DigestMemberFunctionPointer; 98 | }; 99 | 100 | typedef void(*BootstrapModuleFunc)(BootstrapAccessors& accessors); 101 | 102 | #endif //XINPUT1_3_EXPORTS_H 103 | -------------------------------------------------------------------------------- /src/logging.cpp: -------------------------------------------------------------------------------- 1 | #include "logging.h" 2 | 3 | namespace Logging { 4 | std::ofstream logFile; 5 | 6 | void initializeLogging() { 7 | logFile.open("pre-launch-debug.log", std::ifstream::trunc | std::ifstream::out); 8 | logFile << "Log System Initialized!" << std::endl; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/logging.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef XINPUT1_3_LOGGING_H 3 | #define XINPUT1_3_LOGGING_H 4 | 5 | #include 6 | 7 | namespace Logging { 8 | extern std::ofstream logFile; 9 | 10 | void initializeLogging(); 11 | } 12 | 13 | #endif //XINPUT1_3_LOGGING_H 14 | -------------------------------------------------------------------------------- /src/provided_symbols.cpp: -------------------------------------------------------------------------------- 1 | #include "provided_symbols.h" 2 | #include "logging.h" 3 | 4 | static SymbolResolver* symbolResolver; 5 | 6 | void hookRequiredSymbols(SymbolResolver& provider) { 7 | //hook symbols required to implement provided symbols here and store them in static variables 8 | symbolResolver = &provider; 9 | } 10 | 11 | //??0FString@@QEAA@XZ - FString* FString::FString(void) 12 | //just zeroes out string instance and returns this 13 | void* FStringAllocateEmpty(void* thisPtr) { 14 | *(uint64_t*)thisPtr = 0; 15 | *((uint64_t*)thisPtr + 1) = 0; 16 | return thisPtr; 17 | } 18 | 19 | //??1FChatMessageStruct@@QEAA@XZ 20 | //??1UObject@@UEAA@XZ 21 | void* DummyDestructorCall(void* thisPtr) { 22 | //TODO: Determine object type, and call destructors on other fields (virtual methods still won't be supported) 23 | return thisPtr; 24 | } 25 | 26 | void* DummyFVTableConstructor() { 27 | Logging::logFile << "[FATAL] FVTableHelper& constructor called. This should never happen in Shipping!" << std::endl; 28 | exit(1); 29 | } 30 | 31 | //Static config names - macro defined for easier use 32 | #define DEFINE_STATIC_CONFIG_NAME(ClassName) \ 33 | const wchar_t* ClassName() { return L#ClassName; } 34 | #define CHECK_RETURN_STATIC_CONFIG_NAME(ClassName) \ 35 | if (strcmp(mangledName, "?StaticConfigName@"#ClassName"@@SAPEB_WXZ") == 0) { return reinterpret_cast(StaticConfigName::ClassName); } 36 | 37 | namespace StaticConfigName { 38 | //add more as necessary. Technically, all UObjects should have one 39 | DEFINE_STATIC_CONFIG_NAME(AActor) 40 | DEFINE_STATIC_CONFIG_NAME(UActorComponent) 41 | DEFINE_STATIC_CONFIG_NAME(UObject) 42 | } 43 | 44 | bool startsWith(const char *str, const char *pre) { 45 | size_t lenpre = strlen(pre), 46 | lenstr = strlen(str); 47 | return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0; 48 | } 49 | 50 | int endsWith(const char *str, const char *suffix) { 51 | size_t lenstr = strlen(str); 52 | size_t lensuffix = strlen(suffix); 53 | if (lensuffix > lenstr) 54 | return 0; 55 | return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 56 | } 57 | 58 | //??1UObject@@UEAA@XZ 59 | //??1FJsonValue@@MEAA@XZ 60 | std::string GetClassNameFromDestructorMangledName(const char* mangledName) { 61 | std::string ClassName(mangledName); 62 | ClassName.erase(0, 3); 63 | ClassName.erase(ClassName.find('@')); 64 | return ClassName; 65 | } 66 | 67 | //?Z_Construct_UClass_AFGSubsystem_NoRegister@@YAPEAVUClass@@XZ 68 | //?GetPrivateStaticClass@UComponentDelegateBinding@@CAPEAVUClass@@XZ 69 | //?StaticClass@AFGSubsystem_NoRegister@@SAPEAVUClass@@XZ 70 | void* remapZConstructUClassNoRegister(const char* mangledName) { 71 | std::string newSymbolName(mangledName); 72 | newSymbolName.erase(0, strlen("?Z_Construct_UClass_")); 73 | size_t noRegisterIdx = newSymbolName.find("_NoRegister@@"); 74 | newSymbolName.erase(noRegisterIdx); 75 | newSymbolName.insert(0, "?GetPrivateStaticClass@"); 76 | newSymbolName.append("@@CAPEAVUClass@@XZ"); 77 | return symbolResolver->ResolveSymbol(newSymbolName.c_str()); 78 | } 79 | 80 | //?GetPrivateStaticClass@UComponentDelegateBinding@@CAPEAVUClass@@XZ 81 | //?StaticClass@UWidgetAnimationDelegateBinding@@SAPEAVUClass@@XZ 82 | void* remapPrivateStaticClass(const char* mangledName) { 83 | std::string newSymbolName(mangledName); 84 | newSymbolName.erase(0, strlen("?GetPrivateStaticClass@")); 85 | newSymbolName.erase(newSymbolName.find('@')); 86 | newSymbolName.insert(0, "?StaticClass@"); 87 | newSymbolName.append("@@SAPEAVUClass@@XZ"); 88 | return symbolResolver->ResolveSymbol(newSymbolName.c_str()); 89 | } 90 | 91 | void* generateDummySymbol(const char* mangledName, DummyFunctionCallHandler CallHandler) { 92 | return reinterpret_cast(symbolResolver->destructorGenerator->GenerateDummyFunction(mangledName, CallHandler)); 93 | } 94 | 95 | void* provideSymbolImplementation(const char* mangledName) { 96 | //string constructors 97 | if (strcmp(mangledName, "??0FString@@QEAA@XZ") == 0) { 98 | return reinterpret_cast(&FStringAllocateEmpty); 99 | } 100 | //Sometimes StaticClass() exists but GetPrivateStaticClass() doesn't, in that case we fallback to it 101 | if (startsWith(mangledName, "?GetPrivateStaticClass@")) { 102 | return remapPrivateStaticClass(mangledName); 103 | } 104 | //And sometimes Z_Construct_UClass_XXX_NoRegister is missing. In that case we fallback to GetPrivateStaticClass 105 | if (startsWith(mangledName, "?Z_Construct_UClass_")) { 106 | return remapZConstructUClassNoRegister(mangledName); 107 | } 108 | //Missing destructor. Generate default implementation and print warning 109 | if (startsWith(mangledName, "??1")) { 110 | std::string ClassName = GetClassNameFromDestructorMangledName(mangledName); 111 | Logging::logFile << "Providing default implementation for destructor of class: " << ClassName << std::endl; 112 | Logging::logFile << "Warning! It can result in memory leaks if object is not freed up properly." << std::endl; 113 | DestructorFunctionPtr ResultPtr = symbolResolver->destructorGenerator->GenerateDestructor(ClassName); 114 | if (ResultPtr == nullptr) { 115 | Logging::logFile << "[ERROR] Failed to generate destructor for class: Class Not Found in PDB: " << ClassName << std::endl; 116 | return nullptr; 117 | } 118 | return reinterpret_cast(ResultPtr); 119 | } 120 | //Dummy FVTableHelper& constructors. Should never get called 121 | if (endsWith(mangledName, "@@QEAA@AEAVFVTableHelper@@@Z")) { 122 | return reinterpret_cast(&DummyFVTableConstructor); 123 | } 124 | //StaticConfigName for UObject 125 | CHECK_RETURN_STATIC_CONFIG_NAME(AActor); 126 | CHECK_RETURN_STATIC_CONFIG_NAME(UActorComponent); 127 | CHECK_RETURN_STATIC_CONFIG_NAME(UObject); 128 | //other stuff goes here 129 | return nullptr; 130 | } -------------------------------------------------------------------------------- /src/provided_symbols.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_PROVIDED_SYMBOLS_H 2 | #define XINPUT1_3_PROVIDED_SYMBOLS_H 3 | #include "DestructorGenerator.h" 4 | #include "SymbolResolver.h" 5 | 6 | void hookRequiredSymbols(SymbolResolver& provider); 7 | 8 | void* generateDummySymbol(const char* mangledName, DummyFunctionCallHandler CallHandler); 9 | void* provideSymbolImplementation(const char* mangledName); 10 | 11 | #endif //XINPUT1_3_PROVIDED_SYMBOLS_H 12 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util.h" 3 | #include "logging.h" 4 | 5 | std::wstring getModuleFileName(HMODULE moduleHandle) { 6 | std::wstring resultBuffer(MAX_PATH, '\0'); 7 | size_t resultCopied; 8 | while (true) { 9 | resultCopied = GetModuleFileNameW(moduleHandle, resultBuffer.data(), resultBuffer.length() + 1); 10 | if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 11 | resultBuffer.append(resultBuffer.length(), '\0'); 12 | } else { 13 | resultBuffer.erase(resultCopied); 14 | break; 15 | } 16 | } 17 | return resultBuffer; 18 | } 19 | 20 | std::string GetLastErrorAsString() { 21 | //Get the error message, if any. 22 | DWORD errorMessageID = GetLastError(); 23 | if (errorMessageID == 0) 24 | return std::string(); //No error message has been recorded 25 | 26 | LPSTR messageBuffer = nullptr; 27 | size_t size = FormatMessageA( 28 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 29 | nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, nullptr); 30 | 31 | std::string message(messageBuffer, size); 32 | 33 | //Free the buffer. 34 | LocalFree(messageBuffer); 35 | 36 | return message; 37 | } -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef XINPUT1_3_UTIL_H 2 | #define XINPUT1_3_UTIL_H 3 | 4 | #include 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | 8 | std::wstring getModuleFileName(HMODULE moduleHandle); 9 | 10 | std::string GetLastErrorAsString(); 11 | 12 | #endif //XINPUT1_3_UTIL_H 13 | -------------------------------------------------------------------------------- /src/xinput1_3.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include "controller.h" 5 | 6 | HMODULE mHinst = 0, mHinstDLL = 0; 7 | extern "C" UINT_PTR mProcs[12] = {0}; 8 | 9 | void load_original_dll(); 10 | 11 | static bool hooked = false; 12 | 13 | LPCSTR mImportNames[] = {"DllMain", "XInputEnable", "XInputGetBatteryInformation", "XInputGetCapabilities", "XInputGetDSoundAudioDeviceGuids", "XInputGetKeystroke", "XInputGetState", "XInputSetState", (LPCSTR)100, (LPCSTR)101, (LPCSTR)102, (LPCSTR)103}; 14 | BOOL WINAPI DllMain( HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { 15 | mHinst = hinstDLL; 16 | if (fdwReason == DLL_PROCESS_ATTACH) { 17 | load_original_dll(); 18 | for (int i = 0; i < 12; i++) { 19 | mProcs[i] = (UINT_PTR)GetProcAddress(mHinstDLL, mImportNames[i]); 20 | } 21 | } 22 | else if (fdwReason == DLL_PROCESS_DETACH) { 23 | FreeLibrary(mHinstDLL); 24 | } 25 | 26 | if (hooked) { 27 | return ( TRUE ); 28 | } 29 | if ( fdwReason == DLL_THREAD_ATTACH) { 30 | hooked = true; 31 | setupExecutableHook(hinstDLL); 32 | } 33 | return ( TRUE ); 34 | } 35 | 36 | extern "C" void DllMain_wrapper(); 37 | extern "C" void XInputEnable_wrapper(); 38 | extern "C" void XInputGetBatteryInformation_wrapper(); 39 | extern "C" void XInputGetCapabilities_wrapper(); 40 | extern "C" void XInputGetDSoundAudioDeviceGuids_wrapper(); 41 | extern "C" void XInputGetKeystroke_wrapper(); 42 | extern "C" void XInputGetState_wrapper(); 43 | extern "C" void XInputSetState_wrapper(); 44 | extern "C" void ExportByOrdinal100(); 45 | extern "C" void ExportByOrdinal101(); 46 | extern "C" void ExportByOrdinal102(); 47 | extern "C" void ExportByOrdinal103(); 48 | 49 | 50 | // Loads the original DLL from the default system directory 51 | // Function originally written by Michael Koch 52 | void load_original_dll() 53 | { 54 | char buffer[MAX_PATH]; 55 | 56 | // Get path to system dir and to xinput1_3.dll 57 | GetSystemDirectoryA(buffer, MAX_PATH); 58 | 59 | // Append DLL name 60 | strcat_s(buffer, "\\xinput1_3.dll"); 61 | 62 | // Try to load the system's xinput1_3.dll, if pointer empty 63 | if (!mHinstDLL) { 64 | mHinstDLL = LoadLibraryA(buffer); 65 | } 66 | 67 | // Debug 68 | if (!mHinstDLL) { 69 | OutputDebugStringA("PROXYDLL: Original xinput1_3.dll not loaded ERROR ****\r\n"); 70 | ExitProcess(0); // Exit the hard way 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/xinput1_3.def: -------------------------------------------------------------------------------- 1 | LIBRARY xinput1_3.dll 2 | EXPORTS 3 | DllMain=DllMain_wrapper @1 4 | XInputEnable=XInputEnable_wrapper @5 5 | XInputGetBatteryInformation=XInputGetBatteryInformation_wrapper @7 6 | XInputGetCapabilities=XInputGetCapabilities_wrapper @4 7 | XInputGetDSoundAudioDeviceGuids=XInputGetDSoundAudioDeviceGuids_wrapper @6 8 | XInputGetKeystroke=XInputGetKeystroke_wrapper @8 9 | XInputGetState=XInputGetState_wrapper @2 10 | XInputSetState=XInputSetState_wrapper @3 11 | ExportByOrdinal100 @100 NONAME 12 | ExportByOrdinal101 @101 NONAME 13 | ExportByOrdinal102 @102 NONAME 14 | ExportByOrdinal103 @103 NONAME 15 | -------------------------------------------------------------------------------- /src/xinput1_3_asm.asm: -------------------------------------------------------------------------------- 1 | .code 2 | extern mProcs:QWORD 3 | DllMain_wrapper proc 4 | jmp mProcs[0*8] 5 | DllMain_wrapper endp 6 | XInputEnable_wrapper proc 7 | jmp mProcs[1*8] 8 | XInputEnable_wrapper endp 9 | XInputGetBatteryInformation_wrapper proc 10 | jmp mProcs[2*8] 11 | XInputGetBatteryInformation_wrapper endp 12 | XInputGetCapabilities_wrapper proc 13 | jmp mProcs[3*8] 14 | XInputGetCapabilities_wrapper endp 15 | XInputGetDSoundAudioDeviceGuids_wrapper proc 16 | jmp mProcs[4*8] 17 | XInputGetDSoundAudioDeviceGuids_wrapper endp 18 | XInputGetKeystroke_wrapper proc 19 | jmp mProcs[5*8] 20 | XInputGetKeystroke_wrapper endp 21 | XInputGetState_wrapper proc 22 | jmp mProcs[6*8] 23 | XInputGetState_wrapper endp 24 | XInputSetState_wrapper proc 25 | jmp mProcs[7*8] 26 | XInputSetState_wrapper endp 27 | ExportByOrdinal100 proc 28 | jmp mProcs[8*8] 29 | ExportByOrdinal100 endp 30 | ExportByOrdinal101 proc 31 | jmp mProcs[9*8] 32 | ExportByOrdinal101 endp 33 | ExportByOrdinal102 proc 34 | jmp mProcs[10*8] 35 | ExportByOrdinal102 endp 36 | ExportByOrdinal103 proc 37 | jmp mProcs[11*8] 38 | ExportByOrdinal103 endp 39 | end 40 | --------------------------------------------------------------------------------