IndexCoreasmjit::CodeHolder

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:

#include <asmjit/x86.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
using namespace asmjit;
CodeHolder code;
code.init(rt.environment());
x86::Compiler cc(&code);
// Generate first function.
FuncNode* func1_node = cc.add_func(FuncSignature::build<uint32_t>());
Label func1_label = func1_node->label();
{
x86::Gp r = cc.new_gp32("r0");
cc.mov(r, 0);
cc.ret(r);
cc.end_func();
}
// Generate second function.
FuncNode* func2_node = cc.add_func(FuncSignature::build<uint32_t>());
Label func2_label = func2_node->label();
{
x86::Gp r = cc.new_gp32("r1");
cc.mov(r, 1);
cc.ret(r);
cc.end_func();
}
// Finalize the generated code - this would also call `serialize_to()`.
Error err = cc.finalize();
if (err != Error::kOk) {
printf("ERROR during finalization: %s\n", DebugUtils::error_as_string(err));
return 1;
}
// We have deliberately used void* as a pointer type as it's start of an assembled module.
void* module;
err = rt.add(&module, &code);
if (err != Error::kOk) {
printf("ERROR during allocation/relocation: %s\n", DebugUtils::error_as_string(err));
return 1;
}
// Normally both CodeHolder and Compiler are not needed after the code has been finalized
// and allocated/relocated into an executable memory. However, in order to get the required
// offsets it's necessary to query CodeHolder for positions in code, and to get these it's
// required to either have `FuncNode` or `Label`.
size_t func1_offset = code.label_offset_from_base(func1_label);
size_t func2_offset = code.label_offset_from_base(func2_label);
using Fn = uint32_t(*)(void);
Fn fn1 = ptr_as_func<Fn>(module, func1_offset);
Fn fn2 = ptr_as_func<Fn>(module, func2_offset);
printf("fn1()=%u fn2()=%u\n", fn1(), fn2());
// The module has to be released at once - individual functions cannot be released.
rt.release(module);
return 0;
}

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:

// All of them will be reused for code generation by using an 'init()/reset()' strategy.
Environment env = ...; // Environment to use, for example from JitRuntime.
CodeHolder code; // CodeHolder to reuse (all allocated memory will be held by it until it's destroyed).
x86::Compiler cc; // Emitter to reuse (for example x86::Compiler).
for (size_t i = 0; i < ...; i++) {
// Initialize the CodeHolder first.
code.init(env);
code.attach(&emitter);
[[code generation as usual]]
code.reset();
}

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:

// All of them will be reused for code generation by using a 'reinit()' strategy.
Environment env = ...; // Environment to use, for example from JitRuntime.
CodeHolder code; // CodeHolder to reuse (all allocated memory will be held by it until it's destroyed).
x86::Compiler cc; // Emitter to reuse (for example x86::Compiler).
// Initialize the CodeHolder and attach emitters to it (attaching ErrorHandler is advised!)
code.init(env);
code.attach(&emitter);
for (size_t i = 0; i < ...; i++) {
[[code generation as usual]]
// Optionally you can start the loop with 'code.reinit()', but this is cleaner as it wipes out all intermediate
// states of CodeHolder and the attached emitters. It won't detach Logger, ErrorHandler, nor attached emitters.
code.reinit();
}

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.

Classes

Member Functions

Construction & Destruction

Attach & Detach

Memory Allocators

Code & Architecture

Attached Emitters

Logging

Error Handling

Code Buffer

Sections

Labels & Symbols

Relocations

Utilities

CodeHolder::CodeHolder(
Span<uint8_t> static_arena_memory = Span<uint8_t>{}
)explicitnoexcept[¶]

Creates an uninitialized CodeHolder (you must init() it before it can be used).

An optional temporary argument can be used to initialize the first block of Arena that CodeHolder uses into a temporary memory provided by the user.

CodeHolder::~CodeHolder()noexcept[¶]

Destroys the CodeHolder and frees all resources it has allocated.

bool CodeHolder::is_initialized() constnodiscardnoexcept[¶]

Tests whether the CodeHolder has been initialized.

Emitters can be only attached to initialized CodeHolder instances.

Error CodeHolder::init(
const Environment& environment,
uint64_t base_address = Globals::kNoBaseAddress
)noexcept[1/2][¶]

Initializes CodeHolder to hold code described by the given environment and base_address.

Error CodeHolder::init(
const Environment& environment,
const CpuFeatures& cpu_features,
uint64_t base_address = Globals::kNoBaseAddress
)noexcept[2/2][¶]

Initializes CodeHolder to hold code described by the given environment, cpu_features, and base_address.

Error CodeHolder::reinit()noexcept[¶]

Reinitializes CodeHolder with the same environment, cpu features, and base address as it had, and notifies all attached emitters of reinitialization. If the CodeHolder was not initialized, Error::kNotInitialized is returned.

Reinitialization is designed to be a faster alternative compared to reset() followed by init() chain. The purpose of reinitialization is a very quick reuse of CodeHolder and all attached emitters (most likely Assembler or Compiler) without paying the cost of complete initialization and then assignment of all the loggers, error handlers, and emitters.

Note

Semantically reinit() is the same as using reset() with ResetPolicy::kSoft parameter followed by init(), and then by attaching loggers, error handlers, and emitters that were attached previously. This means that after reinitialization you will get a clean and ready for use CodeHolder, which was initialized the same way as before.

void CodeHolder::reset()noexcept[¶]

Detaches all code-generators attached and resets the CodeHolder.

Error CodeHolder::attach(
BaseEmitter* emitter
)noexcept[¶]

Attaches an emitter to this CodeHolder.

Error CodeHolder::detach(
BaseEmitter* emitter
)noexcept[¶]

Detaches an emitter from this CodeHolder.

Arena& CodeHolder::arena() constnodiscardnoexcept[¶]

Returns the allocator that the CodeHolder uses.

Note

This should be only used for AsmJit's purposes. Code holder uses arena allocator to allocate everything, so anything allocated through this allocator will be invalidated by CodeHolder::reset() or by CodeHolder's destructor.

const Environment& CodeHolder::environment() constnodiscardnoexcept[¶]

Returns the target environment information.

Arch CodeHolder::arch() constnodiscardnoexcept[¶]

Returns the target architecture.

SubArch CodeHolder::sub_arch() constnodiscardnoexcept[¶]

Returns the target sub-architecture.

const CpuFeatures& CodeHolder::cpu_features() constnodiscardnoexcept[¶]

Returns the minimum CPU features of the target architecture.

bool CodeHolder::has_base_address() constnodiscardnoexcept[¶]

Tests whether a static base-address is set.

uint64_t CodeHolder::base_address() constnodiscardnoexcept[¶]

Returns a static base-address or Globals::kNoBaseAddress, if not set.

BaseEmitter* CodeHolder::attached_first()nodiscardnoexcept[¶]

Returns a vector of attached emitters.

Logger* CodeHolder::logger() constnodiscardnoexcept[¶]

Returns the attached logger.

void CodeHolder::set_logger(
Logger* logger
)noexcept[¶]

Attaches a logger to CodeHolder and propagates it to all attached emitters.

void CodeHolder::reset_logger()noexcept[¶]

Resets the logger to none.

bool CodeHolder::has_error_handler() constnodiscardnoexcept[¶]

Tests whether the CodeHolder has an attached error handler, see ErrorHandler.

ErrorHandler* CodeHolder::error_handler() constnodiscardnoexcept[¶]

Returns the attached error handler.

void CodeHolder::set_error_handler(
ErrorHandler* error_handler
)noexcept[¶]

Attach an error handler to this CodeHolder.

void CodeHolder::reset_error_handler()noexcept[¶]

Resets the error handler to none.

Error CodeHolder::grow_buffer(
size_t n
)noexcept[¶]

Makes sure that at least n bytes can be added to CodeHolder's buffer cb.

Note

The buffer cb must be managed by CodeHolder - otherwise the behavior of the function is undefined.

Error CodeHolder::reserve_buffer(
size_t n
)noexcept[¶]

Reserves the size of cb to at least n bytes.

Note

The buffer cb must be managed by CodeHolder - otherwise the behavior of the function is undefined.

Span<Section*>CodeHolder::sections() constnodiscardnoexcept[¶]

Returns an array of Section* records.

Span<Section*>CodeHolder::sections_by_order() constnodiscardnoexcept[¶]

Returns an array of Section* records sorted according to section order first, then section id.

size_t CodeHolder::section_count() constnodiscardnoexcept[¶]

Returns the number of sections.

bool CodeHolder::is_section_valid(
uint32_t section_id
) constnodiscardnoexcept[¶]

Tests whether the given section_id is valid.

Error CodeHolder::new_section(
Out<Section*> section_out,
const char* name,
size_t name_size = SIZE_MAX,
uint32_t alignment = 1,
int32_t order = 0
)noexcept[¶]

Creates a new section and return its pointer in section_out.

Returns Error, does not report a possible error to ErrorHandler.

Section* CodeHolder::section_by_id(
uint32_t section_id
) constnodiscardnoexcept[¶]

Returns a section entry of the given index.

Section* CodeHolder::section_by_name(
const char* name,
size_t name_size = SIZE_MAX
) constnodiscardnoexcept[¶]

Returns section-id that matches the given name.

If there is no such section Section::kInvalidId is returned.

Section* CodeHolder::text_section() constnodiscardnoexcept[¶]

Returns '.text' section (section that commonly represents code).

Note

Text section is always the first section in CodeHolder::sections() array.

bool CodeHolder::has_address_table_section() constnodiscardnoexcept[¶]

Tests whether '.addrtab' section exists.

Section* CodeHolder::address_table_section() constnodiscardnoexcept[¶]

Returns '.addrtab' section.

This section is used exclusively by AsmJit to store absolute 64-bit addresses that cannot be encoded in instructions like 'jmp' or 'call'.

Note

This section is created on demand, the returned pointer can be null.

Section* CodeHolder::ensure_address_table_section()nodiscardnoexcept[¶]

Ensures that '.addrtab' section exists (creates it if it doesn't) and returns it. Can return nullptr on out of memory condition.

Error CodeHolder::add_address_to_address_table(
uint64_t address
)noexcept[¶]

Used to add an address to an address table.

This implicitly calls ensure_address_table_section() and then creates AddressTableEntry that is inserted to _address_table_entries. If the address already exists this operation does nothing as the same addresses use the same slot.

This function should be considered internal as it's used by assemblers to insert an absolute address into the address table. Inserting address into address table without creating a particular relocation entry makes no sense.

Span<LabelEntry>CodeHolder::label_entries() constnodiscardnoexcept[¶]

Returns array of LabelEntry records.

size_t CodeHolder::label_count() constnodiscardnoexcept[¶]

Returns number of labels created.

bool CodeHolder::is_label_valid(
uint32_t label_id
) constnodiscardnoexcept[1/2][¶]

Tests whether the label having label_id is valid (i.e. created by new_label_id()).

bool CodeHolder::is_label_valid(
const Label& label
) constnodiscardnoexcept[2/2][¶]

Tests whether the label is valid (i.e. created by new_label_id()).

bool CodeHolder::is_label_bound(
uint32_t label_id
) constnodiscardnoexcept[1/2][¶]

Tests whether a label having label_id is already bound.

Returns false if the label_id is not valid.

bool CodeHolder::is_label_bound(
const Label& label
) constnodiscardnoexcept[2/2][¶]

Tests whether the label is already bound.

Returns false if the label is not valid.

LabelEntry& CodeHolder::label_entry_of(
uint32_t label_id
)nodiscardnoexcept[1/4][¶]

Returns LabelEntry of the given label identifier label_id (or label if you are using overloads).

Attention

The passed label_id must be valid as it's used as an index to _label_entries[] array. In debug builds the array access uses an assertion, but such assertion is not present in release builds. To get whether a label is valid, check out CodeHolder::is_label_valid() function.

const LabelEntry& CodeHolder::label_entry_of(
uint32_t label_id
) constnodiscardnoexcept[2/4][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

LabelEntry& CodeHolder::label_entry_of(
const Label& label
)nodiscardnoexcept[3/4][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

const LabelEntry& CodeHolder::label_entry_of(
const Label& label
) constnodiscardnoexcept[4/4][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

uint64_t CodeHolder::label_offset(
uint32_t label_id
) constnodiscardnoexcept[1/2][¶]

Returns offset of a Label by its label_id.

The offset returned is relative to the start of the section where the label is bound. Zero offset is returned for unbound labels, which is their initial offset value.

Attention

The passed label_id must be valid as it's used as an index to _label_entries[] array. In debug builds the array access uses an assertion, but such assertion is not present in release builds. To get whether a label is valid, check out CodeHolder::is_label_valid() function.

uint64_t CodeHolder::label_offset(
const Label& label
) constnodiscardnoexcept[2/2][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

uint64_t CodeHolder::label_offset_from_base(
uint32_t label_id
) constnodiscardnoexcept[1/2][¶]

Returns offset of a label by it's label_id relative to the base offset.

Attention

The passed label_id must be valid as it's used as an index to _label_entries[] array. In debug builds the array access uses an assertion, but such assertion is not present in release builds. To get whether a label is valid, check out CodeHolder::is_label_valid() function.

Note

The offset of the section where the label is bound must be valid in order to use this function, otherwise the value returned will not be reliable. Typically, sections have offsets when they are flattened, see CodeHolder::flatten() function for more details.

uint64_t CodeHolder::label_offset_from_base(
const Label& label
) constnodiscardnoexcept[2/2][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Error CodeHolder::new_label_id(
Out<uint32_t> label_id_out
)nodiscardnoexcept[¶]

Creates a new anonymous label and return its id in label_id_out.

Returns Error, does not report error to ErrorHandler.

Error CodeHolder::new_named_label_id(
Out<uint32_t> label_id_out,
const char* name,
size_t name_size,
uint32_t parent_id = Globals::kInvalidId
)nodiscardnoexcept[¶]

Creates a new named LabelEntry of the given label type.

Parameters
label_id_outWhere to store the created Label id.
nameThe name of the label.
name_sizeThe length of name argument, or SIZE_MAX if name is a null terminated string, which means that the CodeHolder will use strlen() to determine the length.
typeThe type of the label to create, see LabelType.
parent_idParent id of a local label, otherwise it must be Globals::kInvalidId.
Return values
Alwaysreturns Error, does not report a possible error to the attached ErrorHandler.

AsmJit has a support for local labels (LabelType::kLocal) which require a parent label id (parent_id). The names of local labels can conflict with names of other local labels that have a different parent. In addition, AsmJit supports named anonymous labels, which are useful only for debugging purposes as the anonymous name will have a name, which will be formatted, but the label itself cannot be queried by such name.

Label CodeHolder::label_by_name(
const char* name,
size_t name_size = SIZE_MAX,
uint32_t parent_id = Globals::kInvalidId
)nodiscardnoexcept[1/2][¶]

Returns a label by name.

Remarks

If the named label doesn't exist a default constructed Label is returned, which has its id set to Globals::kInvalidId. In other words, this function doesn't create new labels, it can only be used to query an existing Label by name.

Label CodeHolder::label_by_name(
Span<const char> name,
uint32_t parent_id = Globals::kInvalidId
)nodiscardnoexcept[2/2][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

uint32_t CodeHolder::label_id_by_name(
const char* name,
size_t name_size = SIZE_MAX,
uint32_t parent_id = Globals::kInvalidId
)nodiscardnoexcept[1/2][¶]

Returns a label id by name.

Remarks

If the named label doesn't exist Globals::kInvalidId is returned. In other words, this function doesn't create new labels, it can only be used to query an existing label identifier by name.

uint32_t CodeHolder::label_id_by_name(
Span<const char> name,
uint32_t parent_id = Globals::kInvalidId
)nodiscardnoexcept[2/2][¶]

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

bool CodeHolder::has_unresolved_fixups() constnodiscardnoexcept[¶]

Tests whether there are any unresolved fixups related to unbound labels.

size_t CodeHolder::unresolved_fixup_count() constnodiscardnoexcept[¶]

Returns the number of unresolved fixups.

Fixup* CodeHolder::new_fixup(
uint32_t section_id,
size_t offset,
intptr_t rel,
const OffsetFormat& format
)nodiscardnoexcept[¶]

Creates a new label-link used to store information about yet unbound labels.

Returns null if the allocation failed.

Error CodeHolder::resolve_cross_section_fixups()noexcept[¶]

Resolves cross-section fixups associated with each label that was used as a destination in code of a different section. It's only useful to people that use multiple sections as it will do nothing if the code only contains a single section in which cross-section fixups are not possible.

Error CodeHolder::bind_label(
const Label& label,
uint32_t section_id,
uint64_t offset
)noexcept[¶]

Binds a label to a given section_id and offset (relative to start of the section).

This function is generally used by BaseAssembler::bind() to do the heavy lifting.

bool CodeHolder::has_reloc_entries() constnodiscardnoexcept[¶]

Tests whether the code contains relocation entries.

Span<RelocEntry*>CodeHolder::reloc_entries() constnodiscardnoexcept[¶]

Returns array of RelocEntry* records.

RelocEntry* CodeHolder::reloc_entry_of(
uint32_t id
) constnodiscardnoexcept[¶]

Returns a RelocEntry of the given id.

Error CodeHolder::new_reloc_entry(
Out<RelocEntry*> dst,
RelocType reloc_type
)nodiscardnoexcept[¶]

Creates a new relocation entry of type reloc_type.

Additional fields can be set after the relocation entry was created.

Error CodeHolder::flatten()noexcept[¶]

Flattens all sections by recalculating their offsets, starting at 0.

Note

This should never be called more than once.

size_t CodeHolder::code_size() constnodiscardnoexcept[¶]

Returns computed the size of code & data of all sections.

Note

All sections will be iterated over and the code size returned would represent the minimum code size of all combined sections after applying minimum alignment. Code size may decrease after calling flatten() and relocate_to_base().

Error CodeHolder::relocate_to_base(
uint64_t base_address,
RelocationSummary* summary_out = nullptr
)noexcept[¶]

Relocates the code to the given base_address.

Parameters
base_addressAbsolute base address where the code will be relocated to. Please note that nothing is copied to such base address, it's just an absolute value used by the relocation code to resolve all stored relocations.
summary_outOptional argument that can be used to get back information about the relocation.

Note

This should never be called more than once.

Error CodeHolder::copy_section_data(
void* dst,
size_t dst_size,
uint32_t section_id,
)noexcept[¶]

Copies a single section into dst.

Error CodeHolder::copy_flattened_data(
void* dst,
size_t dst_size,
)noexcept[¶]

Copies all sections into dst.

This should only be used if the data was flattened and there are no gaps between the sections. The dst_size is always checked and the copy will never write anything outside the provided buffer.

Environment CodeHolder::_environment[¶]

Environment information.

CpuFeatures CodeHolder::_cpu_features[¶]

CPU features of the target architecture.

uint64_t CodeHolder::_base_address[¶]

Base address or Globals::kNoBaseAddress.

Logger* CodeHolder::_logger[¶]

Attached Logger, used by all consumers.

ErrorHandler* CodeHolder::_error_handler[¶]

Attached ErrorHandler.

Arena CodeHolder::_arena[¶]

Arena allocator used to allocate core structures.

BaseEmitter* CodeHolder::_attached_first[¶]

First emitter attached to this CodeHolder (double-linked list).

BaseEmitter* CodeHolder::_attached_last[¶]

Last emitter attached to this CodeHolder (double-linked list).

ArenaVector<Section*>CodeHolder::_sections[¶]

Section entries.

ArenaVector<Section*>CodeHolder::_sections_by_order[¶]

Section entries sorted by section order and then section id.

ArenaVector<LabelEntry>CodeHolder::_label_entries[¶]

Label entries.

ArenaVector<RelocEntry*>CodeHolder::_relocations[¶]

Relocation entries.

ArenaHash<NamedLabelExtraData>CodeHolder::_named_labels[¶]

Label name -> LabelEntry::ExtraData (only used by labels that have a name and are not anonymous).

Fixup* CodeHolder::_fixups[¶]

Unresolved fixups that are most likely references across sections.

ArenaPool<Fixup>CodeHolder::_fixup_data_pool[¶]

Pool containing Fixup instances for quickly recycling them.

size_t CodeHolder::_unresolved_fixup_count[¶]

Count of unresolved fixups of unbound labels (at the end of assembling this should be zero).

Section CodeHolder::_text_section[¶]

Text section - always one part of a CodeHolder itself.

Section* CodeHolder::_address_table_section[¶]

Pointer to an address table section (or null if this section doesn't exist).

ArenaTree<AddressTableEntry>CodeHolder::_address_table_entries[¶]

Address table entries.