Skip to content
SymbolResolverResolve

SymbolResolverResolve

Description

Resolve a runtime instruction pointer to a symbol.

Parameters

Name Direction Description
self in,out Resolver. The cache may grow on this call.
runtime_addr in Address to resolve, as captured at runtime.
out out Populated on success. Untouched on failure.

Success

Returns true. out->module_path is set; out->symbol_name may still be NULL if the address falls in a module range that lacks symbol coverage there.

Failure

Returns false if no loaded module contains runtime_addr, or if the resolver fails to open the backing ELF file.

Usage example (Cross-references)

Usage examples (Cross-references)
    // ---------------------------------------------------------------------------
    
    bool SymbolResolverResolve(SymbolResolver *self, void *runtime_addr, ResolvedSymbol *out) {
        if (!self || !out)
            return false;
        for (size i = 0; i < count; ++i) {
            ResolvedSymbol r;
            if (SymbolResolverResolve(resolver, frames[i].ip, &r)) {
                emit_resolved_line(out, (u32)i, &r, frames[i].ip);
            } else {
    
    static bool resolve_addr(SymbolResolver *res, void *addr, ResolvedSymbol *out) {
        return SymbolResolverResolve(res, addr, out);
    }
        ResolvedSymbol r;
        // Page-zero-ish address that no module maps.
        bool ok = !SymbolResolverResolve(&res, (void *)(u64)0x1000, &r);
    
        SymbolResolverDeinit(&res);
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&test_symres_resolve_self, &r);
        ok                = ok && r.module_path && r.module_path[0] != '\0';
        // We're a function so a symbol should resolve. Name may or may
        // .symtab. libc dladdr would fail to name this; we should not.
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&symres_marker_helper, &r);
        ok                = ok && r.symbol_name != NULL && ZstrFindSubstring(r.symbol_name, "symres_marker_helper") != NULL;
    
        ResolvedSymbol ra;
        bool           ok    = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &ra) && ra.module_path;
        size           live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol rb;
        ok         = ok && SymbolResolverResolve(&res, (void *)&sr1_marker_b, &rb) && rb.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol rc;
        bool           ok    = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &rc) && rc.module_path;
        size           live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol rd;
        ok         = ok && SymbolResolverResolve(&res, (void *)&sr_cache_data_marker, &rd) && rd.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol ra;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &ra) && ra.module_path;
    
        u64 other  = 0;
    
        ResolvedSymbol ro;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &ro) && ro.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol ra;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &ra) && ra.module_path;
    
        u64 other = 0;
    
        ResolvedSymbol r1;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &r1) && r1.module_path;
        size live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol r2;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &r2) && r2.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
        }
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &r);
    
        SymbolResolverDeinit(&res);
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_notype_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sr_notype_marker") != NULL;
        // size-0 symbol: it matched at its exact value, so the offset is zero.
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_cache_data_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sr_cache_data_marker") != NULL;
    
        ResolvedSymbol r0;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &r0);
        ok                = ok && r0.symbol_name && r0.symbol_size > 0;
        if (ok) {
            u64            base = (u64)(void *)&sr1_marker_a;
            ResolvedSymbol re;
            if (SymbolResolverResolve(&res, (void *)(base + r0.symbol_size), &re)) {
                // The one-past-end address must not land back inside marker_a:
                // either no name, or a different enclosing symbol.
    
        ResolvedSymbol ra;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &ra) && ra.module_path;
    
        u64 other = 0;
    
        ResolvedSymbol ro;
        ok = ok && SymbolResolverResolve(&res, (void *)other, &ro) && ro.module_path;
        ok = ok && ZstrCompare(ra.module_path, ro.module_path) != 0; // two distinct cache entries
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &r) && r.module_base != 0;
    
        const DwarfCfi *cfi  = NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_bind_global_sized, &r);
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_sized") != NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_bind_global_zero, &r);
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_zero") != NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&scd_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "scd_marker") != NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&scd_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "scd_marker") != NULL;
        ok                = ok && DebugAllocatorLiveCount(&alloc) > baseline;
        u64            addr = (u64)(void *)&subdir_marker;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "subdir_marker") != NULL;
        ok                = ok && r.offset == 0;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&subdir_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "subdir_marker") != NULL;
        ok                = ok && DebugAllocatorLiveCount(&alloc) > baseline;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sidecar_marker, &r);
        ok                = ok && r.module_path && r.module_path[0] != '\0';
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sidecar_marker") != NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sidecar_marker, &r);
        // The marker is named only via the opened sidecar, proving the cache
        // entry actually carries one.
        u64            addr = (u64)(void *)&df_marker_a;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "df_marker_a") != NULL;
        // Entry address: offset 0, reconstruction holds against the DWARF-sourced
        if (ok) {
            ResolvedSymbol rlast;
            ok = SymbolResolverResolve(&res, (void *)(addr + r.symbol_size - 1), &rlast);
            ok = ok && rlast.symbol_name && ZstrFindSubstring(rlast.symbol_name, "df_marker_a") != NULL;
            ok = ok && rlast.symbol_value == r.symbol_value;
        u64            base = (u64)(void *)&df_marker_a;
        ResolvedSymbol r0, rk;
        bool           ok = SymbolResolverResolve(&res, (void *)base, &r0);
        ok                = ok && r0.symbol_name && r0.symbol_size > 4;
        if (ok) {
        ok                = ok && r0.symbol_name && r0.symbol_size > 4;
        if (ok) {
            ok = SymbolResolverResolve(&res, (void *)(base + 4), &rk);
            ok = ok && rk.symbol_name && rk.symbol_value == r0.symbol_value;
            ok = ok && rk.offset == 4;
    
        ResolvedSymbol ra, rb;
        bool           ok = SymbolResolverResolve(&res, (void *)&df_marker_a, &ra);
        ok                = ok && SymbolResolverResolve(&res, (void *)&df_marker_b, &rb);
        ok                = ok && ra.source_file && ZstrFindSubstring(ra.source_file, "DwarfFallback.c") != NULL;
        ResolvedSymbol ra, rb;
        bool           ok = SymbolResolverResolve(&res, (void *)&df_marker_a, &ra);
        ok                = ok && SymbolResolverResolve(&res, (void *)&df_marker_b, &rb);
        ok                = ok && ra.source_file && ZstrFindSubstring(ra.source_file, "DwarfFallback.c") != NULL;
        ok                = ok && rb.source_file && ZstrFindSubstring(rb.source_file, "DwarfFallback.c") != NULL;
    
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&df_marker_a, &r);
        // The name came from DWARF, proving the function table was built (and the
        // line table alongside it).
        void          *addr = symres_bias_data_addr();
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "symres_bias_data") != NULL;
        ok                = ok && r.offset == 0;
        const u64      K    = 8;
        ResolvedSymbol r0, rk;
        bool           ok = SymbolResolverResolve(&res, (void *)base, &r0);
        ok                = ok && SymbolResolverResolve(&res, (void *)(base + K), &rk);
        ok                = ok && r0.symbol_name && rk.symbol_name;
        ResolvedSymbol r0, rk;
        bool           ok = SymbolResolverResolve(&res, (void *)base, &r0);
        ok                = ok && SymbolResolverResolve(&res, (void *)(base + K), &rk);
        ok                = ok && r0.symbol_name && rk.symbol_name;
        ok                = ok && ZstrFindSubstring(rk.symbol_name, "symres_bias_data") != NULL;
        void          *data = symres_bias_data_addr();
        ResolvedSymbol r0;
        bool           ok = SymbolResolverResolve(&res, data, &r0) && r0.module_path && r0.symbol_size > 0;
    
        u64 ms = 0, mfo = 0;
            u64            uncovered = (u64)data + r0.symbol_size + 8;
            ResolvedSymbol rf;
            ok = SymbolResolverResolve(&res, (void *)uncovered, &rf);
            ok = ok && rf.module_base == ms - mfo;
        }
        void          *data = symres_bias_data_addr();
        ResolvedSymbol r0;
        bool           ok = SymbolResolverResolve(&res, data, &r0) && r0.module_path && r0.symbol_size > 0;
    
        u64 ms = 0, mfo = 0;
            u64            past = (u64)data + r0.symbol_size;
            ResolvedSymbol re;
            ok = SymbolResolverResolve(&res, (void *)past, &re);
            ok = ok && re.module_base == ms - mfo;       // fallback base, segment excluded
            ok = ok && re.module_base != r0.module_base; // distinct from the in-segment base
        }
        ResolvedSymbol r;
        if (!SymbolResolverResolve(&res, (void *)func, &r)) {
            SymbolResolverDeinit(&res);
            DefaultAllocatorDeinit(&alloc);
    
        ResolvedSymbol r;
        if (!SymbolResolverResolve(&res, (void *)&dwarf_marker_helper, &r)) {
            SymbolResolverDeinit(&res);
            DefaultAllocatorDeinit(&alloc);
    
        ResolvedSymbol r;
        if (!SymbolResolverResolve(&res, (void *)&dwarf_marker_helper, &r)) {
            SymbolResolverDeinit(&res);
            DefaultAllocatorDeinit(&alloc);
    
        ResolvedSymbol r;
        if (!SymbolResolverResolve(&res, (void *)&dwarf_marker_helper, &r)) {
            SymbolResolverDeinit(&res);
            DefaultAllocatorDeinit(&alloc);
Last updated on