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. ElfOpenFromMemory — L: 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. ElfOpenFromMemoryCopy — R: 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);- In
Dwarf.c:4:
#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>- In
Dwarf.c:20:
DefaultAllocator alloc = DefaultAllocatorInit();
Elf elf;
if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
DefaultAllocatorDeinit(&alloc);- In
Dwarf.c:59:
u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
Elf elf;
if (!ElfOpen(&elf, r.module_path, base)) {
SymbolResolverDeinit(&res);- In
Dwarf.c:105:
u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
Elf elf;
if (!ElfOpen(&elf, r.module_path, base)) {
SymbolResolverDeinit(&res);- In
Dwarf.c:161:
u64 file_relative = (u64)&dwarf_marker_helper - r.module_base;
Elf elf;
if (!ElfOpen(&elf, r.module_path, base)) {
SymbolResolverDeinit(&res);- In
Elf.c:2:
#include <Misra.h>
#include <Misra/Parsers/Elf.h>
#include <Misra/Std/Allocator/Default.h>- In
Elf.c:12:
bool test_elf_self_exe_parse(void) {
DefaultAllocator alloc = DefaultAllocatorInit();
Elf elf;
bool opened = ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc));- In
Elf.c:35:
bool test_elf_find_text_section(void) {
DefaultAllocator alloc = DefaultAllocatorInit();
Elf elf;
if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {- In
Elf.c:59:
bool test_elf_build_id_present(void) {
DefaultAllocator alloc = DefaultAllocatorInit();
Elf elf;
if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {
DefaultAllocatorDeinit(&alloc);- In
Elf.c:72:
bool test_elf_some_function_symbol(void) {
DefaultAllocator alloc = DefaultAllocatorInit();
Elf elf;
if (!ElfOpen(&elf, "/proc/self/exe", ALLOCATOR_OF(&alloc))) {- In
Elf.c:94:
int main(void) {
WriteFmt("[INFO] Starting Elf tests\n\n");
TestFunction tests[] = {- In
Elf.c:103:
};
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);- In
Dwarf.c:532:
// ---------------------------------------------------------------------------
bool dwarf_lines_build_from_elf(DwarfLines *out, const Elf *elf, Allocator *alloc) {
if (!out || !elf || !alloc) {
LOG_FATAL("DwarfLinesBuildFromElf: NULL argument");- In
Elf.c:14:
/// or ELF32 later is mostly a matter of swapping the format strings.
#include <Misra/Parsers/Elf.h>
#include <Misra/Std.h>- In
Elf.c:92:
// ---------------------------------------------------------------------------
static const char *elf_str_at(const Elf *self, u64 strtab_offset, u64 strtab_size, u32 idx) {
if ((u64)idx >= strtab_size) {
return "";- In
Elf.c:109:
}
static bool elf_range_ok(const Elf *self, u64 offset, u64 size) {
if (offset > BufLength(&self->data))
return false;- In
Elf.c:125:
// ---------------------------------------------------------------------------
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));- In
Elf.c:127:
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;
}- In
Elf.c:133:
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;
}- In
Elf.c:138:
if (id[EI_CLASS] != (u8)ELF_CLASS_64) {
LOG_ERROR("Elf: only ELF64 supported in v1 (got class {})", (u32)id[EI_CLASS]);
return false;
}- In
Elf.c:142:
}
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;
}- In
Elf.c:184:
if (shentsize != SHDR64_SIZE && shnum > 0) {
LOG_ERROR("Elf: unexpected e_shentsize ({} vs {})", (u32)shentsize, (u32)SHDR64_SIZE);
return false;
}- In
Elf.c:191:
}
static bool elf_decode_sections(Elf *self) {
u16 n = self->header.shnum;
u64 shoff = self->header.shoff;- In
Elf.c:196:
u64 needed = (u64)n * SHDR64_SIZE;
if (!elf_range_ok(self, shoff, needed)) {
LOG_ERROR("Elf: section header table out of range");
return false;
}- In
Elf.c:201:
if (self->header.shstrndx >= n) {
LOG_ERROR("Elf: shstrndx {} out of range (shnum={})", (u32)self->header.shstrndx, (u32)n);
return false;
}- In
Elf.c:218:
}
if (!elf_range_ok(self, shstr_off, shstr_size)) {
LOG_ERROR("Elf: shstrtab out of range");
return false;
}- In
Elf.c:251:
// ---------------------------------------------------------------------------
static bool elf_decode_symbol_table(Elf *self, const ElfSection *symtab, ElfSymbols *out) {
if (!symtab || symtab->size == 0) {
return true;- In
Elf.c:256:
}
if (symtab->entry_size != SYM64_SIZE) {
LOG_ERROR("Elf: unexpected symbol entry size {}", (u64)symtab->entry_size);
return false;
}- In
Elf.c:260:
}
if (!elf_range_ok(self, symtab->offset, symtab->size)) {
LOG_ERROR("Elf: symbol table out of range");
return false;
}- In
Elf.c:268:
u32 strtab_idx = symtab->link;
if (strtab_idx >= self->sections.length) {
LOG_ERROR("Elf: symtab link {} out of range", (u32)strtab_idx);
return false;
}- In
Elf.c:273:
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;
}- In
Elf.c:286:
};
if (count > ELF_MAX_SYMBOLS) {
LOG_ERROR("Elf: symbol count {} exceeds sanity cap; refusing", count);
return false;
}- In
Elf.c:314:
}
static bool elf_decode_symbols(Elf *self) {
const ElfSection *symtab = NULL;
const ElfSection *dynsymtab = NULL;- In
Elf.c:355:
};
static void elf_decode_build_id(Elf *self, const ElfSection *note) {
if (!elf_range_ok(self, note->offset, note->size) || note->size < 16) {
return;- In
Elf.c:384:
}
static void elf_decode_debug_link(Elf *self, const ElfSection *dl) {
if (!elf_range_ok(self, dl->offset, dl->size) || dl->size < 5) {
return;- In
Elf.c:407:
}
static void elf_decode_debug_metadata(Elf *self) {
const ElfSection *note = ElfFindSection(self, ".note.gnu.build-id");
if (note) {- In
Elf.c:427:
// 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)");- In
Elf.c:455:
// 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)");- In
Elf.c:471:
}
bool elf_open(Elf *out, const char *path, Allocator *alloc) {
if (!out || !path || !alloc) {
LOG_FATAL("ElfOpen: NULL argument (contract violation)");- In
Elf.c:484:
}
void ElfDeinit(Elf *self) {
if (!self)
return;- In
Elf.c:521:
}
const ElfSymbol *ElfResolveAddress(const Elf *self, u64 vaddr) {
if (!self)
return NULL;- In
Elf.c:530:
}
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");- In
DwarfInfo.c:638:
}
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- In
Dwarf.h:24:
#define MISRA_PARSERS_DWARF_H
#include <Misra/Parsers/Elf.h>
#include <Misra/Std/Allocator.h>
#include <Misra/Std/Container/Str.h>- In
Dwarf.h:96:
/// 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)- In
Dwarf.h:194:
/// 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)- In
Dwarf.h:336:
/// 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)- In
Elf.h:187:
const char *debuglink_name;
u32 debuglink_crc;
} Elf;
///
- In
Elf.h:204:
/// 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) \- In
Elf.h:250:
/// TAGS: Parser, ELF, Memory, Ownership
///
bool elf_open_from_memory(Elf *out, Buf *in);
#define ElfOpenFromMemory(out, in) elf_open_from_memory((out), (in))- In
Elf.h:274:
/// 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)- In
Elf.h:287:
/// a zeroed struct.
///
void ElfDeinit(Elf *self);
///
- In
Elf.h:307:
/// TAGS: Parser, ELF, Symbol
///
const ElfSymbol *ElfResolveAddress(const Elf *self, u64 vaddr);
///
- In
Elf.h:312:
/// 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