├── .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