LIBUNWIND-DYNAMIC(3libunwind)
π libunwind-dynamic(3libunwind) π
okay so welcome to the libunwind-dynamic(3libunwind) page from the Programming Library! this is part 1 of 2, so don't go anywhere, bestie π
πΈ NAME πΈ
libunwind-dynamic β libunwind-support for runtime-generated code β¨
β¨ INTRODUCTION β¨
okay so first things first: for libunwind to do its thing, it needs to be able to reconstruct the frame state of every frame in a call-chain. and what's a frame state? it's the subset of the machine-state that's made up of the frame registers (usually the instruction-pointer and the stack-pointer) plus all the callee-saved registers (aka preserved registers). and how does it describe each register? either by giving its current value (for frame registers) or by telling you where that current value is stored (for callee-saved registers). no cap π
so for statically generated code, the compiler usually handles emitting unwind-info, which gives the minimum info needed to reconstruct the frame-state for each instruction in a procedure. but for dynamically generated code? the runtime code generator has to use the dynamic unwind-info interface that libunwind provides to supply the same kind of info. and this manual page? it's describing the format of that info in detail. iykyk β¨
for the purpose of this discussion, a procedure is just an arbitrary piece of contiguous code. normally, each procedure maps directly to a function in the source-language, but that's not strictly required. for example, a runtime code-generator could split a given function into two separate (discontiguous) procedures: one for frequently-executed (hot) code and one for rarely-executed (cold) code. similarly, simple source-language functions (usually leaf functions) might get translated into code where the default unwind-conventions apply, and for that kind of code, you don't strictly need to register dynamic unwind-info. periodt π
a procedure logically consists of a sequence of regions. and these regions are nested, meaning that the frame state at the end of one region is, by default, assumed to be the frame state for the next region. each region is thought of as being divided into a prologue, a body, and an epilogue. any of them can be empty. if it's not empty, the prologue sets up the frame state for the body. like, the prologue might need to allocate some space on the stack and save certain callee-saved registers. the body does the actual work of the procedure but doesn't change the frame state at all. and if it's not empty, the epilogue restores the previous frame state, so it undoes or cancels the effect of the prologue. in fact, a single epilogue can undo the effect of the prologues of several (nested) regions. slay π¦
we should point out that even though prologue, body, and epilogue are logically separate, optimizing code-generators will generally interleave instructions from all three. so for this reason, the dynamic unwind-info interface of libunwind makes absolutely no distinction between prologue and body. similarly, the exact set of instructions that make up an epilogue is also irrelevant. the only point in the epilogue that needs to be described explicitly by the dynamic unwind-info is the point where the stack-pointer gets restored. why? because once the stack-pointer is restored, all values saved in the deallocated portion of the stack frame become invalid, and libunwind needs to know about that. the portion of the frame state not saved on the stack is assumed to remain valid through the end of the region. so for this reason, there's usually no need to describe instructions that restore the contents of callee-saved registers. lowkey important π₯
within a region, every instruction that affects the frame state in some way needs to be described with an operation descriptor. so each instruction in the region gets assigned a unique index. how that index is derived? depends on the architecture. for example, on RISC and EPIC-style architecture, instructions have a fixed size, so you can just number the instructions. but most CISC use variable-length instruction encodings, so it's usually necessary to use a byte-offset as the index. given the instruction index, the operation descriptor specifies the effect of the instruction in an abstract manner. like, it might say that the instruction stores callee-saved register r1 at offset 16 in the stack frame. got it? π
π¦ PROCEDURES π¦
so how do you register dynamic unwind-info for a procedure? a runtime code-generator sets up a structure of type unw_dyn_info_t and calls _U_dyn_register(), passing the address of that structure as the sole argument. and here's what the members of unw_dyn_info_t look like, bestie:
void *nextβ private to libunwind. do NOT use this in your application, fr.void *prevβ also private to libunwind. same deal, hands off. πunw_word_t start_ipβ the start-address of the instructions of the procedure. (remember: procedures are defined as contiguous pieces of code, so a single code-range is enough.)unw_word_t end_ipβ the end-address of the instructions of the procedure. it's non-inclusive, soend_ip-start_ipis the size of the procedure in bytes.unw_word_t gpβ the global-pointer value in use for this procedure. what does global-pointer even mean? it's architecture-specific, and on some architectures it's not used at all.int32_t formatβ the format of the unwind-info. this member can be one ofUNW_INFO_FORMAT_DYNAMIC,UNW_INFO_FORMAT_TABLE, orUNW_INFO_FORMAT_REMOTE_TABLE.union uβ this union contains one sub-member structure for every possible unwind-info format:unw_dyn_proc_info_t piβ used for formatUNW_INFO_FORMAT_DYNAMIC.unw_dyn_table_info_t tiβ used for formatUNW_INFO_FORMAT_TABLE.unw_dyn_remote_table_info_t rtiβ used for formatUNW_INFO_FORMAT_REMOTE_TABLE.- the format of these sub-members is described in detail below. π
π PROC-INFO FORMAT π
this is the preferred dynamic unwind-info format, and it's generally the one used by full-blown runtime code generators. in this format, the details of a procedure are described by a structure of type unw_dyn_proc_info_t. here's what's inside:
unw_word_t name_ptrβ the address of a (human-readable) name of the procedure, or0if there's no such name available. if it's non-zero, the string stored at this address must be ASCII NUL terminated. for source languages that use name mangling (like C++ or Java), the string stored at this address should be the demangled version of the name. cute πunw_word_t handlerβ the address of the personality routine for this procedure. personality routines are used in conjunction with exception handling. see the C++ ABI draft (http://www.codesourcery.com/cxx-abi/) for an overview and a description of the personality routine. if the procedure has no personality routine,handlermust be set to0.uint32_t flagsβ a bitmask of flags. at the moment, no flags have been defined and this member must be set to0. periodt.unw_dyn_region_info_t *regionsβ aNULL-terminated linked list of region descriptors. see the sectionRegion descriptorsbelow for more details.
π TABLE-INFO FORMAT π
this format is generally used when the dynamically generated code was derived from static code and the unwind-info for the dynamic and static versions are identical. for example, it's useful when you're loading statically-generated code into an address-space in a non-standard fashion (i.e., through some means other than dlopen()). in this format, the details of a group of procedures are described by a structure of type unw_dyn_table_info. here are the members:
unw_word_t name_ptrβ the address of a (human-readable) name of the procedure, or0if no such name is available. if non-zero, the string stored at this address must be ASCII NUL terminated. for source languages that use name-mangling (like C++ or Java), the string stored at this address should be the demangled version of the name.unw_word_t segbaseβ the segment-base value that needs to be added to the segment-relative values stored in the unwind-info. the exact meaning of this value is architecture-specific.unw_word_t table_lenβ the length of the unwind-info (table_data) counted in units of words (unw_word_t).unw_word_t table_dataβ a pointer to the actual data encoding the unwind info. the exact format is architecture-specific (see architecture-specific sections below).
β REMOTE TABLE-INFO FORMAT β
the remote table-info format has the same basic purpose as the regular table-info format. the only difference is that when libunwind uses the unwind-info, it will keep the table data in the target address-space (which may be remote). consequently, the type of the table_data member is unw_word_t rather than a pointer. this means libunwind will have to access the table-data via the address-space's access_mem() call-back, rather than through a direct memory reference. no cap.
from the point of view of a runtime code generator, the remote table-info format offers no advantage, and it's expected that such generators will describe their procedures either with the proc-info format or the normal table-info format. the main reason the remote table-info format exists is to enable the address-space-specific find_proc_info() callback (see unw_create_addr_space(3libunwind)) to return unwind tables whose data remains in remote memory. this can speed up unwinding (e.g., for a debugger) because it reduces the amount of data that needs to be loaded from remote memory. slay π
REGIONS DESCRIPTORS πΈ
Okay bestie, so a region descriptor is literally a variable-length structure that describes how each instruction in the region affects the frame state. And like, most instructions in a region usually do not change the frame state, and for those, nothing needs to be recorded in the region descriptor. Periodt. A region descriptor is a structure of type unw_dyn_region_info_t and it has these members:
unw_dyn_region_info_t *nextβ a pointer to the next region. If this is the last region,nextisNULL. Bye bestie πint32_t insn_countβ the length of the region in instructions. Each instruction is assumed to have a fixed size (check the architecture-specific sections for details). The value ofinsn_countmay be negative in the last region of a procedure (i.e., it may be negative only ifnextisNULL). A negative value indicates that the region covers the last N instructions of the procedure, where N is the absolute value ofinsn_count. The math is mathing πuint32_t op_countβ the (allocated) length of theop_countarray.unw_dyn_op_t opβ an array of dynamic unwind directives. See Section βDynamic unwind directivesβ for a description of the directives.
A region descriptor with an insn_count of zero is an empty region and such regions are perfectly legal. In fact, empty regions can be useful to establish a particular frame state before the start of another region.
A single region list can be shared across multiple procedures provided those procedures share a common prologue and epilogue (their bodies may differ, of course). Normally, such procedures consist of a canned prologue, the body, and a canned epilogue. This could be described by two regions: one covering the prologue and one covering the epilogue. Since the body length is variable, the latter region would need to specify a negative value in insn_count such that libunwind knows that the region covers the end of the procedure (up to the address specified by end_ip).
The region descriptor is a variable length structure to make it possible to allocate all the necessary memory with a single memory-allocation request. To facilitate the allocation of a region descriptors libunwind provides a helper routine with the following synopsis:
size_t _U_dyn_region_size(int op_count);
This routine returns the number of bytes needed to hold a region descriptor with space for op_count unwind directives. Note that the length of the op array does not have to match exactly with the number of directives in a region. Instead, it is sufficient if the op array contains at least as many entries as there are directives, since the end of the directives can always be indicated with the UNW_DYN_STOP directive.
DYNAMIC UNWIND DIRECTIVES π¦
A dynamic unwind directive describes how the frame state changes at a particular point within a region. The description is in the form of a structure of type unw_dyn_op_t. This structure has the following members:
int8_t tagβ the operation tag. Must be one of theunw_dyn_operation_tvalues described below.int8_t qpβ the qualifying predicate that controls whether or not this directive is active. This is useful for predicated architectures such as IA-64 or ARM, where the contents of another (callee-saved) register determines whether or not an instruction is executed (takes effect). If the directive is always active, this member should be set to the manifest constant_U_QP_TRUE(this constant is defined for all architectures, predicated or not).int16_t regβ the number of the register affected by the instruction.int32_t whenβ the region-relative number of the instruction to which this directive applies. For example, a value of0means that the effect described by this directive has taken place once the first instruction in the region has executed.unw_word_t valβ the value to be applied by the operation tag. The exact meaning of this value varies by tag. See Section βOperation tagsβ below.
It is perfectly legitimate to specify multiple dynamic unwind directives with the same when value, if a particular instruction has a complex effect on the frame state.
Empty regions by definition contain no actual instructions and as such the directives are not tied to a particular instruction. By convention, the when member should be set to 0, however.
There is no need for the dynamic unwind directives to appear in order of increasing when values. If the directives happen to be sorted in that order, it may result in slightly faster execution, but a runtime code-generator should not go to extra lengths just to ensure that the directives are sorted.
IMPLEMENTATION NOTE: should libunwind implementations for certain architectures prefer the list of unwind directives to be sorted, it is recommended that such implementations first check whether the list happens to be sorted already and, if not, sort the directives explicitly before the first use. With this approach, the overhead of explicit sorting is only paid when there is a real benefit and if the runtime code-generator happens to generate sorted lists naturally, the performance penalty is limited to a simple O(N) check.
β¨ OPERATIONS TAGS β¨
The possible operation tags are defined by enumeration type unw_dyn_operation_t which defines the following values:
UNW_DYN_STOPβ marks the end of the dynamic unwind directive list. All remaining entries in theoparray of the region-descriptor are ignored. This tag is guaranteed to have a value of0.UNW_DYN_SAVE_REGβ marks an instruction which saves registerregto registerval.UNW_DYN_SPILL_FP_RELβ marks an instruction which spills registerregto a frame-pointer-relative location. The frame-pointer-relative offset is given by the value stored in memberval. See the architecture-specific sections for a description of the stack frame layout.UNW_DYN_SPILL_SP_RELβ marks an instruction which spills registerregto a stack-pointer-relative location. The stack-pointer-relative offset is given by the value stored in memberval. See the architecture-specific sections for a description of the stack frame layout.UNW_DYN_ADDβ marks an instruction which adds the constant valuevalto registerreg. To add subtract a constant value, store the two's-complement of the value inval. The set of registers that can be specified for this tag is described in the architecture-specific sections below.UNW_DYN_POP_FRAMESβ.PP(that's literally all the source gives here, bestie, no extra lore π)UNW_DYN_LABEL_STATEβ.PP(same vibe, nothing more in the page)UNW_DYN_COPY_STATEβ.PP(also just.PP, periodt)UNW_DYN_ALIASβ.PP unw_dyn_op_t(kept exactly as shown, no cap)
Then there's this literal list of helper routines, exactly as the page gives them:
_U_dyn_op_save_reg(); _U_dyn_op_spill_fp_rel();
_U_dyn_op_spill_sp_rel(); _U_dyn_op_add(); _U_dyn_op_pop_frames();
_U_dyn_op_label_state(); _U_dyn_op_copy_state(); _U_dyn_op_alias();
_U_dyn_op_stop();
IA-64 SPECIFICS π
- meaning of
segbasemember intable-info/table-remote-infoformat - format of
table_dataintable-info/table-remote-infoformat - instruction size: each bundle is counted as 3 instructions, regardless of template (MLX)
- describe stack-frame layout, especially with regards to sp-relative and fp-relative addressing
UNW_DYN_ADDcan only add tosp(always a negative value); use POP_FRAMES otherwise
SEE ALSO π
AUTHOR π«Ά
David Mosberger-Tang
Email: dmosberger@gmail.com
WWW: http://www.nongnu.org/libunwind/
That's the whole page, bestie. This manual page came from Programming Library, dated 29 August 2023, and it's for LIBUNWIND-DYNAMIC(3libunwind). Slay. πβ¨
π¦ where this page comes from
- alpine 3.22: libunwind-doc 1.8.1-r0 Β· MIT
- alpine 3.23: libunwind-doc 1.8.1-r0 Β· MIT
- alpine 3.24: libunwind-doc 1.8.3-r0 Β· MIT
- arch rolling: libunwind 1.8.3-1 Β· MIT
- epel 10: libunwind-devel 1.8.0-4.el10_0 Β· MIT
- epel 9: libunwind-devel 1.8.0-4.el9 Β· MIT
- fedora 43: libunwind-devel 1.8.1-3.fc43 Β· MIT
- fedora 43: libunwind-devel 1.8.1-3.fc43 Β· MIT
- fedora 43: libunwind-devel 1.8.3-4.fc43 Β· MIT
- fedora 43: libunwind-devel 1.8.3-4.fc43 Β· MIT
- fedora 44: libunwind-devel 1.8.3-1.fc44 Β· MIT
- fedora 44: libunwind-devel 1.8.3-1.fc44 Β· MIT
- fedora 44: libunwind-devel 1.8.3-4.fc44 Β· MIT
- fedora 44: libunwind-devel 1.8.3-4.fc44 Β· MIT
- nixpkgs unstable: libunwind 1.8.3-devman
- opensuse-leap 16.0: libunwind-devel 1.8.1-160000.2.2 Β· MIT
- opensuse-leap 16.0: libunwind-devel 1.8.1-160000.2.2 Β· MIT
- opensuse-leap 16.0: libunwind-devel 1.8.1-160000.2.2 Β· MIT
- opensuse-leap 16.0: libunwind-devel 1.8.1-160000.2.2 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms