asmjit::CodeHolder Class Reference [¶]
Holds assembled code and data (including sections, labels, and relocation information).
CodeHolder connects emitters with their targets. It provides them interface that can be used to query information about the target environment (architecture, etc...) and API to create labels, sections, relocations, and to write data to a CodeBuffer, which is always part of Section. More than one emitter can be attached to a single CodeHolder instance at a time, which is used in practice
CodeHolder provides interface for all emitter types. Assemblers use CodeHolder to write into CodeBuffer, and higher level emitters like Builder and Compiler use CodeHolder to manage labels and sections so higher level code can be serialized to Assembler by BaseEmitter::finalize() and BaseBuilder::serialize_to().
In order to use CodeHolder, it must be first initialized by init(). After the CodeHolder has been successfully initialized it can be used to hold assembled code, sections, labels, relocations, and to attach / detach code emitters. After the end of code generation it can be used to query physical locations of labels and to relocate the assembled code into the right address. Please not that calling init() twice doesn't work and would return an error - to reuse CodeHolder it has to be first reset() or reinitialized by calling reinit().
Multiple Functions
CodeHolder can be used to hold a single function or multiple functions - when it's holding multiple functions it's considered like a module (or library, or something that provides more than just a single function). When a code is relocated and moved into executable memory, you typically get a single pointer back. When CodeHolder holds a single function, it's the pointer to such function. However, when CodeHolder holds multiple functions, that pointer is basically start of the code, which is usually the first function.
In order to get a pointer to more functions, it's necessary to use Label for each function and then to get the offset to each such function via CodeHolder::label_offset_from_base() - which returns an offset, which is relative to the start of the assembled code. When using higher level emitters such as Compiler labels are created automatically - FuncNode inherits from LabelNode, so a function is a label at the same time.
To query and apply an offset, consider the following code, which uses x86::Compiler to create two functions:
CodeHolder Reusability
If you intend to generate a lot of code, or tiny code, it's advised to reuse CodeHolder and emitter instances. There are currently two ways of reusing CodeHolder and emitters - one is using CodeHolder::init() followed by CodeHolder::reset(), and another is initializing once by CodeHolder::init() and then reinitializing by CodeHolder::reinit(). The first strategy is shown below:
While this approach is good for many use-cases, there is even a faster strategy called reinitialization, which is provided by CodeHolder::reinit(). The idea of reinit is to reinitialize the CodeHolder into a state, which was achieved by initializing it by CodeHolder::init(), by optionally attaching Logger, ErrorHandler, and emitters of any kind. See an example below:
Note
CodeHolder has an ability to attach an ErrorHandler, however, the error handler is not triggered by CodeHolder itself, it's instead propagated to all emitters that attach to it.