Skip to content

ElfFile

Description

Parsed ELF file. Holds the raw bytes plus decoded indices into them. Two construction paths:

ElfFileOpen — reads a file from disk; the ElfFile owns the byte buffer and frees it on ElfFileDeinit. ElfFileOpenFromMemory — borrows a caller-supplied buffer; the caller is responsible for keeping the buffer alive until ElfFileDeinit.

Fields

Name Description
allocator Allocator used for the owned buffer (if any) and for the section / symbol vectors.
data Pointer to the raw file bytes.
data_size Length of data in bytes.
owns_data True if data was allocated via allocator.
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).

Usage example (Cross-references)

Usage examples (Cross-references)
        DefaultAllocator alloc = DefaultAllocatorInit();
    
        ElfFile elf;
        if (!ElfFileOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
            DefaultAllocatorDeinit(&alloc);
        u64 file_relative = (u64)(uintptr_t)&dwarf_marker_helper - r.module_base;
    
        ElfFile elf;
        if (!ElfFileOpen(&elf, r.module_path, base)) {
            SymbolResolverDeinit(&res);
    bool test_elf_self_exe_parse(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        ElfFile          elf;
    
        bool opened = ElfFileOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc));
    bool test_elf_find_text_section(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        ElfFile          elf;
    
        if (!ElfFileOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
    bool test_elf_some_function_symbol(void) {
        DefaultAllocator alloc = DefaultAllocatorInit();
        ElfFile          elf;
    
        if (!ElfFileOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
    // ---------------------------------------------------------------------------
    
    bool DwarfLinesBuildFromElf(DwarfLines *out, const ElfFile *elf, Allocator *alloc) {
        if (!out || !elf || !alloc) {
            LOG_ERROR("DwarfLinesBuildFromElf: NULL argument");
    // ---------------------------------------------------------------------------
    
    static const char *elf_str_at(const ElfFile *self, u64 strtab_offset, u64 strtab_size, u32 idx) {
        if ((u64)idx >= strtab_size) {
            return "";
    }
    
    static bool elf_range_ok(const ElfFile *self, u64 offset, u64 size) {
        if (offset > self->data_size)
            return false;
    // ---------------------------------------------------------------------------
    
    static bool elf_decode_header(ElfFile *self) {
        if (self->data_size < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("ElfFile: file too small for ELF64 header ({} bytes)", (u64)self->data_size);
    static bool elf_decode_header(ElfFile *self) {
        if (self->data_size < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("ElfFile: file too small for ELF64 header ({} bytes)", (u64)self->data_size);
            return false;
        }
        const u8 *id = 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("ElfFile: bad magic");
            return false;
        }
    
        if (id[EI_CLASS] != (u8)ELF_CLASS_64) {
            LOG_ERROR("ElfFile: only ELF64 supported in v1 (got class {})", (u32)id[EI_CLASS]);
            return false;
        }
        }
        if (id[EI_DATA] != (u8)ELF_DATA_LSB) {
            LOG_ERROR("ElfFile: only little-endian supported in v1 (got data {})", (u32)id[EI_DATA]);
            return false;
        }
    
        if (shentsize != SHDR64_SIZE && shnum > 0) {
            LOG_ERROR("ElfFile: unexpected e_shentsize ({} vs {})", (u32)shentsize, (u32)SHDR64_SIZE);
            return false;
        }
    }
    
    static bool elf_decode_sections(ElfFile *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("ElfFile: section header table out of range");
            return false;
        }
    
        if (self->header.shstrndx >= n) {
            LOG_ERROR("ElfFile: shstrndx {} out of range (shnum={})", (u32)self->header.shstrndx, (u32)n);
            return false;
        }
        }
        if (!elf_range_ok(self, shstr_off, shstr_size)) {
            LOG_ERROR("ElfFile: shstrtab out of range");
            return false;
        }
    // ---------------------------------------------------------------------------
    
    static bool elf_decode_symbol_table(ElfFile *self, const ElfSection *symtab, ElfSymbols *out) {
        if (!symtab || symtab->size == 0) {
            return true;
        }
        if (symtab->entry_size != SYM64_SIZE) {
            LOG_ERROR("ElfFile: unexpected symbol entry size {}", (u64)symtab->entry_size);
            return false;
        }
        }
        if (!elf_range_ok(self, symtab->offset, symtab->size)) {
            LOG_ERROR("ElfFile: symbol table out of range");
            return false;
        }
        u32 strtab_idx = symtab->link;
        if (strtab_idx >= self->sections.length) {
            LOG_ERROR("ElfFile: 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("ElfFile: strtab out of range");
            return false;
        }
    }
    
    static bool elf_decode_symbols(ElfFile *self) {
        const ElfSection *symtab    = NULL;
        const ElfSection *dynsymtab = NULL;
    // ---------------------------------------------------------------------------
    
    bool ElfFileOpenFromMemory(ElfFile *out, u8 *data, size data_size, Allocator *alloc) {
        if (!out || !data || !alloc) {
            LOG_ERROR("ElfFileOpenFromMemory: NULL argument");
    }
    
    bool ElfFileOpen(ElfFile *out, const char *path, Allocator *alloc) {
        if (!out || !path || !alloc) {
            LOG_ERROR("ElfFileOpen: NULL argument");
    }
    
    void ElfFileDeinit(ElfFile *self) {
        if (!self)
            return;
    }
    
    const ElfSymbol *ElfFileResolveAddress(const ElfFile *self, u64 vaddr) {
        if (!self)
            return NULL;
    }
    
    const ElfSection *ElfFileFindSection(const ElfFile *self, const char *name) {
        if (!self || !name)
            return NULL;
        const char *path; // borrowed from ProcMaps.raw
        u64         load_base;
        ElfFile     elf;
    #if MISRA_HAVE_PARSER_DWARF
        DwarfLines dwarf;
    /// TAGS: Parser, DWARF, Lines
    ///
    bool DwarfLinesBuildFromElf(DwarfLines *out, const ElfFile *elf, Allocator *alloc);
    
    ///
        ElfSymbols  symbols;
        ElfSymbols  dynamic_symbols;
    } ElfFile;
    
    ///
    /// TAGS: Parser, ELF, File
    ///
    bool ElfFileOpen(ElfFile *out, const char *path, Allocator *alloc);
    
    ///
    /// TAGS: Parser, ELF, Memory
    ///
    bool ElfFileOpenFromMemory(ElfFile *out, u8 *data, size data_size, Allocator *alloc);
    
    ///
    /// `owns_data` was true. Safe to call on a zeroed struct.
    ///
    void ElfFileDeinit(ElfFile *self);
    
    ///
    /// TAGS: Parser, ELF, Symbol
    ///
    const ElfSymbol *ElfFileResolveAddress(const ElfFile *self, u64 vaddr);
    
    ///
    /// Find a section by name (first match). Returns NULL if absent.
    ///
    const ElfSection *ElfFileFindSection(const ElfFile *self, const char *name);
    
    #endif // MISRA_PARSERS_ELF_H
Last updated on