LIBUNWIND(3libunwind)
yo bestie, welcome to libunwind(3libunwind)! it's giving (mostly) platform-independent unwind API energy, and i'm here to spill all the tea βπ
NAME π
libunwind β a (mostly) platform-independent unwind API
SYNOPSIS β¨
okay so here's the whole synopsis, bestie. don't skip it, it's important π
#include <libunwind.h>
int unw_getcontext(unw_context_t *);
int unw_init_local(unw_cursor_t *, unw_context_t *);
int unw_init_remote(unw_cursor_t *, unw_addr_space_t, void *);
int unw_step(unw_cursor_t *);
int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *);
int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);
int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t);
int unw_resume(unw_cursor_t *);
unw_addr_space_t unw_local_addr_space;
unw_addr_space_t unw_create_addr_space(unw_accessors_t, int);
void unw_destroy_addr_space(unw_addr_space_t);
unw_accessors_t unw_get_accessors(unw_addr_space_t);
void unw_flush_cache(unw_addr_space_t, unw_word_t, unw_word_t);
int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t);
int unw_set_cache_size(unw_addr_space_t, size_t, int);
const char *unw_regname(unw_regnum_t);
int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *);
int unw_get_save_loc(unw_cursor_t *, int, unw_save_loc_t *);
int unw_is_fpreg(unw_regnum_t);
int unw_is_signal_frame(unw_cursor_t *);
int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *);
void _U_dyn_register(unw_dyn_info_t *);
void _U_dyn_cancel(unw_dyn_info_t *);
LOCAL UNWINDING π¦
so local unwinding is when you unwind a stack from within a running program. say you're executing in some function F() and you wanna unwind the stack. you call unw_getcontext() to get a snapshot of the CPU registers (machine state). then you initialize an unwind cursor based on that snapshot with unw_init_local(). now the cursor points to the current frameβthat's the stack frame corresponding to the current activation of function F(). you can move the cursor ``up'' (towards earlier stack frames) by calling unw_step(). keep calling it and you'll uncover the entire call chain that led to the activation of F(). the return value from unw_step() is giving: positive = more frames in the chain, zero = end of chain, negative = some sort of error. periodt.
while you can't directly move the cursor down'' (towards newer stack frames), you can fake it by making copies. like, if you sometimes need to move down one frame, keep two cursor variables: curr'' and prev''. use curr'' as the current cursor and maintain prev'' as the previous frame'' cursor by copying curr into prev right before calling unw_step(). then you can move one step down by copying prev back into curr whenever needed. in the most extreme case, you can keep a separate cursor for each call frame and bounce up and down the call chain at will. no cap.
given a cursor, you can read and write the CPU registers preserved for the current stack frame (as identified by the cursor). libunwind has: unw_get_reg() reads an integer (general) register, unw_get_fpreg() reads a floating-point register, unw_set_reg() writes an integer register, and unw_set_fpreg() writes a floating-point register. note that by definition, only the preserved machine state can be accessed during an unwind. normally that's the callee-saved (preserved'') registers. but in some special circumstances (e.g., in a signal handler trampoline), even the caller-saved (scratch'') registers are preserved in the stack frame, and then libunwind grants access to them too. the exact set of registers depends on the platform, of course. but two registers can be read on all platforms: the instruction pointer (IP), sometimes called the ``program counter'', and the stack pointer (SP). in libunwind, these are identified by the macros UNW_REG_IP and UNW_REG_SP, respectively. slay.
besides moving the cursor and reading/writing saved registers, libunwind can also resume execution at an arbitrary stack frame. that's useful for implementing non-local gotos and the exception handling needed by some high-level languages like Java. to resume, just call unw_resume() and pass the cursor identifying the target frame as the only argument. easy.
normally, libunwind supports both local and remote unwinding (remote is next section, stay tuned). but if you tell libunwind your program only needs local unwinding, a special implementation can be selected that may run much faster than the generic one that supports both. to select this optimized version, define the macro UNW_LOCAL_ONLY before including the headerfile <libunwind.h>. it's perfectly OK for a single program to employ both local-only and generic unwinding. that is, whether or not UNW_LOCAL_ONLY is defined is a choice each source file (compilation unit) can make on its own. independent of the setting(s) of UNW_LOCAL_ONLY, you'll always link the same library (normally -lunwind). furthermore, the portion of libunwind that manages unwind info for dynamically generated code is not affected by the setting of UNW_LOCAL_ONLY. π
if we put all that together, here's how you could write a function ``show_backtrace()'' that prints a classic stack trace:
#define UNW_LOCAL_ONLY
#include <libunwind.h>
void show_backtrace (void) {
unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
while (unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
printf ("ip = %lx, sp = %lx\n", (long) ip, (long) sp);
}
}
REMOTE UNWINDING π
libunwind can also unwind a stack in a remote'' process. here, remote'' may mean another process on the same machine or even a process on a completely different machine from the one running libunwind. remote unwinding is typically used by debuggers and instruction-set simulators, for example. iconic.
before you can unwind a remote process, you need to create a new address space object for that process. that's done with unw_create_addr_space(). this routine takes two arguments: a pointer to a set of accessor routines and an integer that specifies the byte order of the target process. the accessor routines give libunwind the means to communicate with the remote process. in particular, there are callbacks to read and write the process's memory, its registers, and to access unwind information that libunwind may need. so basically, the communication hub.
with the address space created, unwinding can be initiated by a call to unw_init_remote(). this routine is very similar to unw_init_local(), except it takes an address space object and an opaque pointer as arguments. it uses these to fetch the initial machine state. libunwind never uses the opaque pointer on its ownβit just passes it on to the accessor (callback) routines. typically, this pointer is used to select, e.g., the thread within a process that is to be unwound. iykyk.
once a cursor has been initialized with unw_init_remote(), unwinding works exactly like in the local case. that is, you can use unw_step() to move ``up'' in the call chain, read and write registers, or resume execution at a particular stack frame by calling unw_resume(). same energy.
CROSS-PLATFORM AND MULTI-PLATFORM UNWINDING π
libunwind has been designed to enable unwinding across platforms (architectures). indeed, a single program can use libunwind to unwind an arbitrary number of target platforms, all at the same time! fr fr.
we call the machine running libunwind the host and the machine running the process being unwound the target. if the host and target platform are the same, it's native unwinding. if they differ, it's cross-platform unwinding.
the principle behind native, cross-platform, and multi-platform unwinding is very simple: for native unwinding, a program includes <libunwind.h> and uses the linker switch -lunwind. for cross-platform unwinding, a program includes <libunwind-PLAT.h> and uses the linker switch -lunwind-PLAT, where PLAT is the name of the target platform (e.g., ia64 for IA-64, hppa-elf for ELF-based HP PA-RISC, or x86 for 80386). multi-platform unwinding works exactly like cross-platform unwinding, the only limitation is that a single source file (compilation unit) can include at most one libunwind header file. in other words, the platform-specific support for each supported target needs to be isolated in separate source filesβa limitation that shouldn't be an issue in practice. lowkey fine.
note that, by definition, local unwinding is possible only for the native case. attempting to call, e.g., unw_local_init() when targeting a cross-platform will result in a link-time error (unresolved references). so don't do that.
THREAD- AND SIGNAL-SAFETY π§΅
all libunwind routines are thread-safe. what that means is multiple threads may use libunwind simultaneously. however, any given cursor may be accessed by only one thread at any given time. no sharing cursors, bestie.
to ensure thread safety, some libunwind routines may have to use locking. such routines must not be called from signal handlers (directly or indirectly) and are therefore not signal-safe. the manual page for each libunwind routine identifies whether or not it is signal-safe, but as a general rule, any routine that may be needed for local unwinding is signal-safe (e.g., unw_step() for local unwinding is signal-safe). for remote unwinding, none of the libunwind routines are guaranteed to be signal-safe. so be careful out there.
β¨ UNWINDING THROUGH DYNAMICALLY GENERATED CODE β¨
okay bestie, so sometimes code doesn't exist until runtime β like a just-in-time (JIT) compiler just spawning functions out of thin air π«. Libunwind provides the routines _U_dyn_register() and _U_dyn_cancel() to register/cancel the information required to unwind through code that has been generated at runtime (e.g., by a just-in-time (JIT) compiler). And it is important to register the information for all dynamically generated code, because otherwise a debugger may not be able to function properly or high-level language exception handling may not work as expected. no cap.
The interface for registering and canceling dynamic unwind info has been designed for maximum efficiency, so as to minimize the performance impact on JIT compilers. In particular, both routines are guaranteed to execute in βconstant timeβ (O(1)) and the data structure encapsulating the dynamic unwind info has been designed to facilitate sharing, such that similar procedures can share much of the underlying information. slay.
For more information on the libunwind support for dynamically generated code, see libunwind-dynamic(3libunwind).
π§ CACHING OF UNWIND INFO π§
To speed up execution, libunwind may aggressively cache the information it needs to perform unwinding. But if a process changes during its lifetime, this creates a risk of libunwind using stale data. For example, this would happen if libunwind were to cache information about a shared library which later on gets unloaded (e.g., via dlclose(3libunwind)). the way that's a whole risk.
To prevent the risk of using stale data, libunwind provides two facilities:
- First, it is possible to flush the cached information associated with a specific address range in the target process (or the entire address space, if desired). This functionality is provided by
unw_flush_cache(). - Second,
unw_set_caching_policy()lets a program select the exact caching policy in use for a given address space object. In particular, by selecting the policyUNW_CACHE_NONE, it is possible to turn off caching completely, therefore eliminating the risk of stale data altogether (at the cost of slower execution).
By default, caching is enabled for local unwinding only. The cache size can be dynamically changed with unw_set_cache_size(), which also flushes the current cache. obsessed with the control honestly π
.
π FILES π
libunwind.hβ Headerfile to include for native (same platform) unwinding.libunwind-PLAT.hβ Headerfile to include when the unwind target runs on platform PLAT. For example, to unwind an IA-64 program, the header filelibunwind-ia64.hshould be included.-lunwindβ Linker switch to add when building a program that does native (same platform) unwinding.-lunwind-PLATβ Linker switch to add when building a program that unwinds a program on platform PLAT. For example, to (cross-)unwind an IA-64 program, the linker switch-lunwind-ia64should be added. Note: multiple such switches may need to be specified for programs that can unwind programs on multiple platforms.
π SEE ALSO π
libunwind-dynamic(3libunwind)libunwind-ia64(3libunwind)libunwind-ptrace(3libunwind)libunwind-setjmp(3libunwind)unw_create_addr_space(3libunwind)unw_destroy_addr_space(3libunwind)unw_flush_cache(3libunwind)unw_get_accessors(3libunwind)unw_get_fpreg(3libunwind)unw_get_proc_info(3libunwind)unw_get_proc_name(3libunwind)unw_get_reg(3libunwind)unw_getcontext(3libunwind)unw_init_local(3libunwind)unw_init_remote(3libunwind)unw_is_fpreg(3libunwind)unw_is_signal_frame(3libunwind)unw_regname(3libunwind)unw_resume(3libunwind)unw_set_caching_policy(3libunwind)unw_set_cache_size(3libunwind)unw_set_fpreg(3libunwind)unw_set_reg(3libunwind)unw_step(3libunwind)unw_strerror(3libunwind)_U_dyn_register(3libunwind)_U_dyn_cancel(3libunwind)
π AUTHOR π
- David Mosberger-Tang
- Email:
dmosberger@gmail.com - WWW:
http://www.nongnu.org/libunwind/
and that's the whole page, bestie π«Ά this was brought to you by Programming Library, dated 29 August 2023, filed under LIBUNWIND(3libunwind). bye π β¨
π¦ where this page comes from
- debian trixie: libunwind-dev 1.8.1-0.1 Β· MIT
- ubuntu questing: libunwind-dev 1.8.1-0.1ubuntu1
- ubuntu resolute: libunwind-dev 1.8.3-0ubuntu1 Β· MIT
- ubuntu stonking: libunwind-dev 1.8.3-0ubuntu1 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms