Skip to content

Elf

Description

Parsed ELF file. Holds the raw bytes plus decoded indices into them. Three construction paths, all of which leave the Elf in the same lifecycle state: parser owns the bytes, parser frees them on ElfDeinit. There is no “borrowed buffer” mode – the L / R split below mirrors VecInsertL / VecInsertR:

ElfOpen — reads a file from disk; parser owns the resulting buffer end-to-end. ElfOpenFromMemoryL: takes ownership of the caller’s (data, data_size). Caller must not free or touch data afterwards. alloc MUST be the allocator that produced data. ElfOpenFromMemoryCopyR: allocates internally through alloc, copies the caller’s bytes in, and never retains the caller’s pointer. Caller’s buffer is untouched and remains theirs.

Fields

Name Description
allocator Allocator used for data and for the section / symbol vectors.
data Pointer to the raw file bytes (owned).
data_size Length of data in bytes.
header Decoded ELF header.
sections All section headers, in original order.
symbols Entries from .symtab (may be empty if stripped).
dynamic_symbols Entries from .dynsym (always present for dynamic objects).
build_id Bytes of the GNU build-ID (from .note.gnu.build-id); used to find a stripped binary’s sidecar .debug file under /usr/lib/debug/.build-id/.... NULL when the binary has no build-ID note.
build_id_size Length of build_id in bytes (typically 20).
debuglink_name Filename portion stored in .gnu_debuglink, identifying a sidecar debug file. NULL when the binary lacks the section.
debuglink_crc CRC32 of the expected sidecar contents (validated by the resolver before use).

Usage example (Cross-references)

Usage examples (Cross-references)
    #include <Misra/Parsers/Dwarf.h>
    #include <Misra/Std/Zstr.h>
    #include <Misra/Parsers/Elf.h>
    #include <Misra/Std/Allocator/Default.h>
    #include <Misra/Std/File.h>
    
        // (2) Open the stripped sibling.
        Elf stripped;
        if (!ElfOpen(&stripped, stripped_path_arg, base)) {
            LOG_ERROR("stripped binary not openable: {}", stripped_path_arg);
    #include <Misra/Parsers/Dwarf.h>
    #include <Misra/Std/Zstr.h>
    #include <Misra/Parsers/Elf.h>
    #include <Misra/Std/Allocator/Default.h>
    #include <Misra/Sys/SymbolResolver.h>
        DefaultAllocator alloc = DefaultAllocatorInit();
    
        Elf elf;
        if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
            DefaultAllocatorDeinit(&alloc);
        u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
    
        Elf elf;
        if (!ElfOpen(&elf, r.module_path, base)) {
            SymbolResolverDeinit(&res);
        u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
    
        Elf elf;
        if (!ElfOpen(&elf, r.module_path, base)) {
            SymbolResolverDeinit(&res);
        u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
    
        Elf elf;
        if (!ElfOpen(&elf, r.module_path, base)) {
            SymbolResolverDeinit(&res);
    #include <Misra.h>
    #include <Misra/Parsers/Elf.h>
    #include <Misra/Std/Allocator/Default.h>
    bool test_elf_self_exe_parse(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        Elf          elf;
    
        bool opened = ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc));
    bool test_elf_find_text_section(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        Elf          elf;
    
        if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
    bool test_elf_build_id_present(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        Elf          elf;
        if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
            DefaultAllocatorDeinit(&alloc);
    bool test_elf_some_function_symbol(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        Elf          elf;
    
        if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
    
    int main(void) {
        WriteFmt("[INFO] Starting Elf tests\n\n");
    
        TestFunction tests[] = {
        };
    
        return run_test_suite(tests, sizeof(tests) / sizeof(tests[0]), NULL, 0, "Elf");
    }
    // For debuglink lookups, the file's mere presence is good enough in v1
    // (CRC32 cross-check is in FUTURE-PLANS).
    static bool sidecar_matches(const Elf *main, const Elf *sidecar, bool by_build_id) {
        if (!by_build_id) {
            return true;
    //
    // Returns true on success; `out` is populated with an opened Elf.
    static bool try_open_sidecar(const char *main_path, const Elf *main, Elf *out, Allocator *alloc) {
        Str path = StrInit(alloc);
    // ---------------------------------------------------------------------------
    
    bool dwarf_lines_build_from_elf(DwarfLines *out, const Elf *elf, Allocator *alloc) {
        if (!out || !elf || !alloc) {
            LOG_FATAL("DwarfLinesBuildFromElf: NULL argument");
    /// or ELF32 later is mostly a matter of swapping the format strings.
    
    #include <Misra/Parsers/Elf.h>
    
    #include <Misra/Std.h>
    // ---------------------------------------------------------------------------
    
    static const char *elf_str_at(const Elf *self, u64 strtab_offset, u64 strtab_size, u32 idx) {
        if ((u64)idx >= strtab_size) {
            return "";
    }
    
    static bool elf_range_ok(const Elf *self, u64 offset, u64 size) {
        if (offset > BufLength(&self->data))
            return false;
    // ---------------------------------------------------------------------------
    
    static bool elf_decode_header(Elf *self) {
        if (BufLength(&self->data) < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("Elf: file too small for ELF64 header ({} bytes)", (u64)BufLength(&self->data));
    static bool elf_decode_header(Elf *self) {
        if (BufLength(&self->data) < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("Elf: file too small for ELF64 header ({} bytes)", (u64)BufLength(&self->data));
            return false;
        }
        const u8 *id = BufData(&self->data);
        if (id[EI_MAG0] != ELF_MAG0 || id[EI_MAG1] != ELF_MAG1 || id[EI_MAG2] != ELF_MAG2 || id[EI_MAG3] != ELF_MAG3) {
            LOG_ERROR("Elf: bad magic");
            return false;
        }
    
        if (id[EI_CLASS] != (u8)ELF_CLASS_64) {
            LOG_ERROR("Elf: only ELF64 supported in v1 (got class {})", (u32)id[EI_CLASS]);
            return false;
        }
        }
        if (id[EI_DATA] != (u8)ELF_DATA_LSB) {
            LOG_ERROR("Elf: only little-endian supported in v1 (got data {})", (u32)id[EI_DATA]);
            return false;
        }
    
        if (shentsize != SHDR64_SIZE && shnum > 0) {
            LOG_ERROR("Elf: unexpected e_shentsize ({} vs {})", (u32)shentsize, (u32)SHDR64_SIZE);
            return false;
        }
    }
    
    static bool elf_decode_sections(Elf *self) {
        u16 n      = self->header.shnum;
        u64 shoff  = self->header.shoff;
        u64 needed = (u64)n * SHDR64_SIZE;
        if (!elf_range_ok(self, shoff, needed)) {
            LOG_ERROR("Elf: section header table out of range");
            return false;
        }
    
        if (self->header.shstrndx >= n) {
            LOG_ERROR("Elf: shstrndx {} out of range (shnum={})", (u32)self->header.shstrndx, (u32)n);
            return false;
        }
        }
        if (!elf_range_ok(self, shstr_off, shstr_size)) {
            LOG_ERROR("Elf: shstrtab out of range");
            return false;
        }
    // ---------------------------------------------------------------------------
    
    static bool elf_decode_symbol_table(Elf *self, const ElfSection *symtab, ElfSymbols *out) {
        if (!symtab || symtab->size == 0) {
            return true;
        }
        if (symtab->entry_size != SYM64_SIZE) {
            LOG_ERROR("Elf: unexpected symbol entry size {}", (u64)symtab->entry_size);
            return false;
        }
        }
        if (!elf_range_ok(self, symtab->offset, symtab->size)) {
            LOG_ERROR("Elf: symbol table out of range");
            return false;
        }
        u32 strtab_idx = symtab->link;
        if (strtab_idx >= self->sections.length) {
            LOG_ERROR("Elf: symtab link {} out of range", (u32)strtab_idx);
            return false;
        }
        const ElfSection *strtab = &self->sections.data[strtab_idx];
        if (!elf_range_ok(self, strtab->offset, strtab->size)) {
            LOG_ERROR("Elf: strtab out of range");
            return false;
        }
        };
        if (count > ELF_MAX_SYMBOLS) {
            LOG_ERROR("Elf: symbol count {} exceeds sanity cap; refusing", count);
            return false;
        }
    }
    
    static bool elf_decode_symbols(Elf *self) {
        const ElfSection *symtab    = NULL;
        const ElfSection *dynsymtab = NULL;
    };
    
    static void elf_decode_build_id(Elf *self, const ElfSection *note) {
        if (!elf_range_ok(self, note->offset, note->size) || note->size < 16) {
            return;
    }
    
    static void elf_decode_debug_link(Elf *self, const ElfSection *dl) {
        if (!elf_range_ok(self, dl->offset, dl->size) || dl->size < 5) {
            return;
    }
    
    static void elf_decode_debug_metadata(Elf *self) {
        const ElfSection *note = ElfFindSection(self, ".note.gnu.build-id");
        if (note) {
    // Anything that fails past the snapshot cleans up via ElfDeinit,
    // so the buffer never leaks.
    bool elf_open_from_memory(Elf *out, Buf *in) {
        if (!out || !in || !in->data || !in->allocator) {
            LOG_FATAL("ElfOpenFromMemory: NULL argument (contract violation)");
    
    // R-value form (copy). Caller's `data` is never retained.
    bool elf_open_from_memory_copy(Elf *out, const u8 *data, size data_size, Allocator *alloc) {
        if (!out || !data || !alloc) {
            LOG_FATAL("ElfOpenFromMemoryCopy: NULL argument (contract violation)");
    }
    
    bool elf_open(Elf *out, const char *path, Allocator *alloc) {
        if (!out || !path || !alloc) {
            LOG_FATAL("ElfOpen: NULL argument (contract violation)");
    }
    
    void ElfDeinit(Elf *self) {
        if (!self)
            return;
    }
    
    const ElfSymbol *ElfResolveAddress(const Elf *self, u64 vaddr) {
        if (!self)
            return NULL;
    }
    
    const ElfSection *ElfFindSection(const Elf *self, const char *name) {
        if (!self || !name)
            return NULL;
    // ---------------------------------------------------------------------------
    
    bool dwarf_cfi_build_from_elf(DwarfCfi *out, const Elf *elf, Allocator *alloc) {
        if (!out || !elf || !alloc) {
            LOG_FATAL("DwarfCfiBuildFromElf: NULL argument");
    }
    
    bool dwarf_functions_build_from_elf(DwarfFunctions *out, const Elf *elf, Allocator *alloc) {
        if (!out || !elf || !alloc) {
            LOG_FATAL("DwarfFunctionsBuildFromElf: NULL argument");
    #define MISRA_SYS_SYMBOL_RESOLVER_H
    
    #include <Misra/Parsers/Elf.h>
    #if FEATURE_PARSER_DWARF
    #    include <Misra/Parsers/Dwarf.h>
        const char *path; // borrowed from ProcMaps.raw
        u64         load_base;
        Elf     elf;
        // Sidecar debug file found via .gnu_debuglink or .note.gnu.build-id.
        // Populated lazily for stripped binaries that have an installed
        // is true, the sidecar's symbol tables (and DWARF lines, below)
        // are searched after the main file's.
        Elf sidecar;
        bool    has_sidecar;
    #if FEATURE_PARSER_DWARF
    #define MISRA_PARSERS_DWARF_H
    
    #include <Misra/Parsers/Elf.h>
    #include <Misra/Std/Allocator.h>
    #include <Misra/Std/Container/Str.h>
    /// TAGS: Parser, DWARF, Lines
    ///
    bool dwarf_lines_build_from_elf(DwarfLines *out, const Elf *elf, Allocator *alloc);
    #define DwarfLinesBuildFromElf(...)               MISRA_OVERLOAD(DwarfLinesBuildFromElf, __VA_ARGS__)
    #define DwarfLinesBuildFromElf_2(out, elf)        dwarf_lines_build_from_elf((out), (elf), MisraScope)
    /// TAGS: Parser, DWARF, CFI
    ///
    bool dwarf_cfi_build_from_elf(DwarfCfi *out, const Elf *elf, Allocator *alloc);
    #define DwarfCfiBuildFromElf(...)               MISRA_OVERLOAD(DwarfCfiBuildFromElf, __VA_ARGS__)
    #define DwarfCfiBuildFromElf_2(out, elf)        dwarf_cfi_build_from_elf((out), (elf), MisraScope)
    /// TAGS: Parser, DWARF, Info
    ///
    bool dwarf_functions_build_from_elf(DwarfFunctions *out, const Elf *elf, Allocator *alloc);
    #define DwarfFunctionsBuildFromElf(...)               MISRA_OVERLOAD(DwarfFunctionsBuildFromElf, __VA_ARGS__)
    #define DwarfFunctionsBuildFromElf_2(out, elf)        dwarf_functions_build_from_elf((out), (elf), MisraScope)
        const char *debuglink_name;
        u32         debuglink_crc;
    } Elf;
    
    ///
    /// TAGS: Parser, ELF, File
    ///
    bool elf_open(Elf *out, const char *path, Allocator *alloc);
    #define ElfOpen(...) MISRA_OVERLOAD(ElfOpen, __VA_ARGS__)
    #define ElfOpen_2(out, path)                                                                                           \
    /// TAGS: Parser, ELF, Memory, Ownership
    ///
    bool elf_open_from_memory(Elf *out, Buf *in);
    #define ElfOpenFromMemory(out, in) elf_open_from_memory((out), (in))
    /// TAGS: Parser, ELF, Memory, Copy
    ///
    bool elf_open_from_memory_copy(Elf *out, const u8 *data, size data_size, Allocator *alloc);
    #define ElfOpenFromMemoryCopy(...)                    MISRA_OVERLOAD(ElfOpenFromMemoryCopy, __VA_ARGS__)
    #define ElfOpenFromMemoryCopy_3(out, data, data_size) elf_open_from_memory_copy((out), (data), (data_size), MisraScope)
    /// a zeroed struct.
    ///
    void ElfDeinit(Elf *self);
    
    ///
    /// TAGS: Parser, ELF, Symbol
    ///
    const ElfSymbol *ElfResolveAddress(const Elf *self, u64 vaddr);
    
    ///
    /// Find a section by name (first match). Returns NULL if absent.
    ///
    const ElfSection *ElfFindSection(const Elf *self, const char *name);
    
    #endif // MISRA_PARSERS_ELF_H
Last updated on