1 // [AsmJit] 2 // Complete JIT Assembler for C++ Language. 3 // 4 // [License] 5 // Zlib - See COPYING file in this package. 6 7 // [Guard] 8 #ifndef _ASMJIT_CORE_ASSEMBLER_H 9 #define _ASMJIT_CORE_ASSEMBLER_H 10 11 // [Dependencies - AsmJit] 12 #include "../core/buffer.h" 13 #include "../core/context.h" 14 #include "../core/defs.h" 15 #include "../core/logger.h" 16 #include "../core/podvector.h" 17 #include "../core/zonememory.h" 18 19 // [Api-Begin] 20 #include "../core/apibegin.h" 21 22 namespace AsmJit { 23 24 //! @addtogroup AsmJit_Core 25 //! @{ 26 27 // ============================================================================ 28 // [AsmJit::Assembler] 29 // ============================================================================ 30 31 //! @brief Base class for @ref Assembler. 32 //! 33 //! This class implements core setialization API only. The platform specific 34 //! methods and intrinsics is implemented by derived classes. 35 //! 36 //! @sa @c Assembler. 37 struct Assembler 38 { 39 // -------------------------------------------------------------------------- 40 // [Construction / Destruction] 41 // -------------------------------------------------------------------------- 42 43 //! @brief Creates Assembler instance. 44 ASMJIT_API Assembler(Context* context); 45 //! @brief Destroys Assembler instance 46 ASMJIT_API virtual ~Assembler(); 47 48 // -------------------------------------------------------------------------- 49 // [LabelLink] 50 // -------------------------------------------------------------------------- 51 52 //! @brief Data structure used to link linked-labels. 53 struct LabelLink 54 { 55 //! @brief Previous link. 56 LabelLink* prev; 57 //! @brief Offset. 58 sysint_t offset; 59 //! @brief Inlined displacement. 60 sysint_t displacement; 61 //! @brief RelocId if link must be absolute when relocated. 62 sysint_t relocId; 63 }; 64 65 // -------------------------------------------------------------------------- 66 // [LabelData] 67 // -------------------------------------------------------------------------- 68 69 //! @brief Label data. 70 struct LabelData 71 { 72 //! @brief Label offset. 73 sysint_t offset; 74 //! @brief Label links chain. 75 LabelLink* links; 76 }; 77 78 // -------------------------------------------------------------------------- 79 // [RelocData] 80 // -------------------------------------------------------------------------- 81 82 // X86 architecture uses 32-bit absolute addressing model by memory operands, 83 // but 64-bit mode uses relative addressing model (RIP + displacement). In 84 // code we are always using relative addressing model for referencing labels 85 // and embedded data. In 32-bit mode we must patch all references to absolute 86 // address before we can call generated function. We are patching only memory 87 // operands. 88 89 //! @brief Code relocation data (relative vs absolute addresses). 90 struct RelocData 91 { 92 //! @brief Type of relocation. 93 uint32_t type; 94 //! @brief Size of relocation (4 or 8 bytes). 95 uint32_t size; 96 //! @brief Offset from code begin address. 97 sysint_t offset; 98 99 //! @brief Relative displacement or absolute address. 100 union 101 { 102 //! @brief Relative displacement from code begin address (not to @c offset). 103 sysint_t destination; 104 //! @brief Absolute address where to jump; 105 void* address; 106 }; 107 }; 108 109 // -------------------------------------------------------------------------- 110 // [Context] 111 // -------------------------------------------------------------------------- 112 113 //! @brief Get code generator. getContextAssembler114 inline Context* getContext() const 115 { return _context; } 116 117 // -------------------------------------------------------------------------- 118 // [Memory Management] 119 // -------------------------------------------------------------------------- 120 121 //! @brief Get zone memory manager. getZoneMemoryAssembler122 inline ZoneMemory* getZoneMemory() const 123 { return const_cast<ZoneMemory*>(&_zoneMemory); } 124 125 // -------------------------------------------------------------------------- 126 // [Logging] 127 // -------------------------------------------------------------------------- 128 129 //! @brief Get logger. getLoggerAssembler130 inline Logger* getLogger() const 131 { return _logger; } 132 133 //! @brief Set logger to @a logger. 134 ASMJIT_API virtual void setLogger(Logger* logger); 135 136 // -------------------------------------------------------------------------- 137 // [Error Handling] 138 // -------------------------------------------------------------------------- 139 140 //! @brief Get error code. getErrorAssembler141 inline uint32_t getError() const 142 { return _error; } 143 144 //! @brief Set error code. 145 //! 146 //! This method is virtual, because higher classes can use it to catch all 147 //! errors. 148 ASMJIT_API virtual void setError(uint32_t error); 149 150 // -------------------------------------------------------------------------- 151 // [Properties] 152 // -------------------------------------------------------------------------- 153 154 //! @brief Get assembler property. 155 ASMJIT_API virtual uint32_t getProperty(uint32_t propertyId) const; 156 157 //! @brief Set assembler property. 158 ASMJIT_API virtual void setProperty(uint32_t propertyId, uint32_t value); 159 160 // -------------------------------------------------------------------------- 161 // [Capacity] 162 // -------------------------------------------------------------------------- 163 164 //! @brief Get capacity of internal code buffer. getCapacityAssembler165 inline size_t getCapacity() const 166 { return _buffer.getCapacity(); } 167 168 // -------------------------------------------------------------------------- 169 // [Offset] 170 // -------------------------------------------------------------------------- 171 172 //! @brief Return current offset in buffer. getOffsetAssembler173 inline size_t getOffset() const 174 { return _buffer.getOffset(); } 175 176 //! @brief Set offset to @a o and returns previous offset. 177 //! 178 //! This method can be used to truncate code (previous offset is not 179 //! recorded) or to overwrite instruction stream at position @a o. 180 //! 181 //! @return Previous offset value that can be uset to set offset back later. toOffsetAssembler182 inline size_t toOffset(size_t o) 183 { return _buffer.toOffset(o); } 184 185 // -------------------------------------------------------------------------- 186 // [GetCode / GetCodeSize] 187 // -------------------------------------------------------------------------- 188 189 //! @brief Return start of assembler code buffer. 190 //! 191 //! Note that buffer address can change if you emit instruction or something 192 //! else. Use this pointer only when you finished or make sure you do not 193 //! use returned pointer after emitting. getCodeAssembler194 inline uint8_t* getCode() const 195 { return _buffer.getData(); } 196 197 //! @brief Return current offset in buffer (same as <code>getOffset() + getTramplineSize()</code>). getCodeSizeAssembler198 inline size_t getCodeSize() const 199 { return _buffer.getOffset() + getTrampolineSize(); } 200 201 // -------------------------------------------------------------------------- 202 // [TakeCode] 203 // -------------------------------------------------------------------------- 204 205 //! @brief Take internal code buffer and NULL all pointers (you take the ownership). 206 ASMJIT_API uint8_t* takeCode(); 207 208 // -------------------------------------------------------------------------- 209 // [Clear / Reset] 210 // -------------------------------------------------------------------------- 211 212 //! @brief Clear everything, but not deallocate buffers. 213 ASMJIT_API void clear(); 214 215 //! @brief Reset everything (means also to free all buffers). 216 ASMJIT_API void reset(); 217 218 //! @brief Called by clear() and reset() to clear all data related to derived 219 //! class implementation. 220 ASMJIT_API virtual void _purge(); 221 222 // -------------------------------------------------------------------------- 223 // [EnsureSpace] 224 // -------------------------------------------------------------------------- 225 226 //! @brief Ensure space for next instruction. 227 //! 228 //! Note that this method can return false. It's rare and probably you never 229 //! get this, but in some situations it's still possible. ensureSpaceAssembler230 inline bool ensureSpace() 231 { return _buffer.ensureSpace(); } 232 233 // -------------------------------------------------------------------------- 234 // [GetTrampolineSize] 235 // -------------------------------------------------------------------------- 236 237 //! @brief Get size of all possible trampolines needed to successfuly generate 238 //! relative jumps to absolute addresses. This value is only non-zero if jmp 239 //! of call instructions were used with immediate operand (this means jump or 240 //! call absolute address directly). 241 //! 242 //! Currently only _emitJmpOrCallReloc() method can increase trampoline size 243 //! value. getTrampolineSizeAssembler244 inline size_t getTrampolineSize() const 245 { return _trampolineSize; } 246 247 // -------------------------------------------------------------------------- 248 // [Buffer - Getters] 249 // -------------------------------------------------------------------------- 250 251 //! @brief Get byte at position @a pos. getByteAtAssembler252 inline uint8_t getByteAt(size_t pos) const 253 { return _buffer.getByteAt(pos); } 254 255 //! @brief Get word at position @a pos. getWordAtAssembler256 inline uint16_t getWordAt(size_t pos) const 257 { return _buffer.getWordAt(pos); } 258 259 //! @brief Get dword at position @a pos. getDWordAtAssembler260 inline uint32_t getDWordAt(size_t pos) const 261 { return _buffer.getDWordAt(pos); } 262 263 //! @brief Get qword at position @a pos. getQWordAtAssembler264 inline uint64_t getQWordAt(size_t pos) const 265 { return _buffer.getQWordAt(pos); } 266 267 //! @brief Get int32_t at position @a pos. getInt32AtAssembler268 inline int32_t getInt32At(size_t pos) const 269 { return (int32_t)_buffer.getDWordAt(pos); } 270 271 //! @brief Get int64_t at position @a pos. getInt64AtAssembler272 inline int64_t getInt64At(size_t pos) const 273 { return (int64_t)_buffer.getQWordAt(pos); } 274 275 //! @brief Get intptr_t at position @a pos. getIntPtrTAtAssembler276 inline intptr_t getIntPtrTAt(size_t pos) const 277 { return _buffer.getIntPtrTAt(pos); } 278 279 //! @brief Get uintptr_t at position @a pos. getUIntPtrTAtAssembler280 inline uintptr_t getUIntPtrTAt(size_t pos) const 281 { return _buffer.getUIntPtrTAt(pos); } 282 283 //! @brief Get uintptr_t at position @a pos. getSizeTAtAssembler284 inline size_t getSizeTAt(size_t pos) const 285 { return _buffer.getSizeTAt(pos); } 286 287 // -------------------------------------------------------------------------- 288 // [Buffer - Setters] 289 // -------------------------------------------------------------------------- 290 291 //! @brief Set byte at position @a pos. setByteAtAssembler292 inline void setByteAt(size_t pos, uint8_t x) 293 { _buffer.setByteAt(pos, x); } 294 295 //! @brief Set word at position @a pos. setWordAtAssembler296 inline void setWordAt(size_t pos, uint16_t x) 297 { _buffer.setWordAt(pos, x); } 298 299 //! @brief Set dword at position @a pos. setDWordAtAssembler300 inline void setDWordAt(size_t pos, uint32_t x) 301 { _buffer.setDWordAt(pos, x); } 302 303 //! @brief Set qword at position @a pos. setQWordAtAssembler304 inline void setQWordAt(size_t pos, uint64_t x) 305 { _buffer.setQWordAt(pos, x); } 306 307 //! @brief Set int32_t at position @a pos. setInt32AtAssembler308 inline void setInt32At(size_t pos, int32_t x) 309 { _buffer.setDWordAt(pos, (uint32_t)x); } 310 311 //! @brief Set int64_t at position @a pos. setInt64AtAssembler312 inline void setInt64At(size_t pos, int64_t x) 313 { _buffer.setQWordAt(pos, (uint64_t)x); } 314 315 //! @brief Set intptr_t at position @a pos. setIntPtrTAtAssembler316 inline void setIntPtrTAt(size_t pos, intptr_t x) 317 { _buffer.setIntPtrTAt(pos, x); } 318 319 //! @brief Set uintptr_t at position @a pos. setUInt64AtAssembler320 inline void setUInt64At(size_t pos, uintptr_t x) 321 { _buffer.setUIntPtrTAt(pos, x); } 322 323 //! @brief Set size_t at position @a pos. setSizeTAtAssembler324 inline void setSizeTAt(size_t pos, size_t x) 325 { _buffer.setSizeTAt(pos, x); } 326 327 // -------------------------------------------------------------------------- 328 // [CanEmit] 329 // -------------------------------------------------------------------------- 330 331 //! @brief Get whether the instruction can be emitted. 332 //! 333 //! This function behaves like @c ensureSpace(), but it also checks if 334 //! assembler is in error state and in that case it returns @c false. 335 //! Assembler internally always uses this function before new instruction is 336 //! emitted. 337 //! 338 //! It's implemented like: 339 //! <code>return ensureSpace() && !getError();</code> canEmitAssembler340 inline bool canEmit() 341 { 342 // If there is an error, we can't emit another instruction until last error 343 // is cleared by calling @c setError(kErrorOk). If something caused the 344 // error while generating code it's probably fatal in all cases. You can't 345 // use generated code anymore, because you are not sure about the status. 346 if (_error) 347 return false; 348 349 // The ensureSpace() method returns true on success and false on failure. We 350 // are catching return value and setting error code here. 351 if (ensureSpace()) 352 return true; 353 354 // If we are here, there is memory allocation error. Note that this is HEAP 355 // allocation error, virtual allocation error can be caused only by 356 // AsmJit::VirtualMemory class! 357 setError(kErrorNoHeapMemory); 358 return false; 359 } 360 361 // -------------------------------------------------------------------------- 362 // [Emit] 363 // 364 // These functions are not protected against buffer overrun. Each place of 365 // code which calls these functions ensures that there is some space using 366 // canEmit() method. Emitters are internally protected in AsmJit::Buffer, 367 // but only in debug builds. 368 // -------------------------------------------------------------------------- 369 370 //! @brief Emit Byte to internal buffer. _emitByteAssembler371 inline void _emitByte(uint8_t x) 372 { _buffer.emitByte(x); } 373 374 //! @brief Emit word (2 bytes) to internal buffer. _emitWordAssembler375 inline void _emitWord(uint16_t x) 376 { _buffer.emitWord(x); } 377 378 //! @brief Emit dword (4 bytes) to internal buffer. _emitDWordAssembler379 inline void _emitDWord(uint32_t x) 380 { _buffer.emitDWord(x); } 381 382 //! @brief Emit qword (8 bytes) to internal buffer. _emitQWordAssembler383 inline void _emitQWord(uint64_t x) 384 { _buffer.emitQWord(x); } 385 386 //! @brief Emit Int32 (4 bytes) to internal buffer. _emitInt32Assembler387 inline void _emitInt32(int32_t x) 388 { _buffer.emitDWord((uint32_t)x); } 389 390 //! @brief Emit Int64 (8 bytes) to internal buffer. _emitInt64Assembler391 inline void _emitInt64(int64_t x) 392 { _buffer.emitQWord((uint64_t)x); } 393 394 //! @brief Emit intptr_t (4 or 8 bytes) to internal buffer. _emitIntPtrTAssembler395 inline void _emitIntPtrT(intptr_t x) 396 { _buffer.emitIntPtrT(x); } 397 398 //! @brief Emit uintptr_t (4 or 8 bytes) to internal buffer. _emitUIntPtrTAssembler399 inline void _emitUIntPtrT(uintptr_t x) 400 { _buffer.emitUIntPtrT(x); } 401 402 //! @brief Emit size_t (4 or 8 bytes) to internal buffer. _emitSizeTAssembler403 inline void _emitSizeT(size_t x) 404 { _buffer.emitSizeT(x); } 405 406 //! @brief Embed data into instruction stream. 407 ASMJIT_API void embed(const void* data, size_t len); 408 409 // -------------------------------------------------------------------------- 410 // [Reloc] 411 // -------------------------------------------------------------------------- 412 413 //! @brief Relocate code to a given address @a dst. 414 //! 415 //! @param dst Where the relocated code should me stored. The pointer can be 416 //! address returned by virtual memory allocator or your own address if you 417 //! want only to store the code for later reuse (or load, etc...). 418 //! @param addressBase Base address used for relocation. When using JIT code 419 //! generation, this will be the same as @a dst, only casted to system 420 //! integer type. But when generating code for remote process then the value 421 //! can be different. 422 //! 423 //! @retval The bytes used. Code-generator can create trampolines which are 424 //! used when calling other functions inside the JIT code. However, these 425 //! trampolines can be unused so the relocCode() returns the exact size needed 426 //! for the function. 427 //! 428 //! A given buffer will be overwritten, to get number of bytes required use 429 //! @c getCodeSize(). 430 virtual size_t relocCode(void* dst, sysuint_t addressBase) const = 0; 431 432 //! @brief Simplifed version of @c relocCode() method designed for JIT. 433 //! 434 //! @overload relocCodeAssembler435 inline size_t relocCode(void* dst) const 436 { return relocCode(dst, (uintptr_t)dst); } 437 438 // -------------------------------------------------------------------------- 439 // [Make] 440 // -------------------------------------------------------------------------- 441 442 //! @brief Make is convenience method to make currently serialized code and 443 //! return pointer to generated function. 444 //! 445 //! What you need is only to cast this pointer to your function type and call 446 //! it. Note that if there was an error and calling @c getError() method not 447 //! returns @c kErrorOk (zero) then this function always return @c NULL and 448 //! error value remains the same. 449 virtual void* make() = 0; 450 451 // -------------------------------------------------------------------------- 452 // [Helpers] 453 // -------------------------------------------------------------------------- 454 455 ASMJIT_API LabelLink* _newLabelLink(); 456 457 // -------------------------------------------------------------------------- 458 // [Members] 459 // -------------------------------------------------------------------------- 460 461 //! @brief ZoneMemory management. 462 ZoneMemory _zoneMemory; 463 //! @brief Binary code buffer. 464 Buffer _buffer; 465 466 //! @brief Context (for example @ref JitContext). 467 Context* _context; 468 //! @brief Logger. 469 Logger* _logger; 470 471 //! @brief Error code. 472 uint32_t _error; 473 //! @brief Properties. 474 uint32_t _properties; 475 //! @brief Emit flags for next instruction (cleared after emit). 476 uint32_t _emitOptions; 477 //! @brief Size of possible trampolines. 478 uint32_t _trampolineSize; 479 480 //! @brief Inline comment that will be logged by the next instruction and 481 //! set to NULL. 482 const char* _inlineComment; 483 //! @brief Linked list of unused links (@c LabelLink* structures) 484 LabelLink* _unusedLinks; 485 486 //! @brief Labels data. 487 PodVector<LabelData> _labels; 488 //! @brief Relocations data. 489 PodVector<RelocData> _relocData; 490 }; 491 492 //! @} 493 494 } // AsmJit namespace 495 496 // [Api-End] 497 #include "../core/apiend.h" 498 499 // [Guard] 500 #endif // _ASMJIT_CORE_ASSEMBLER_H 501