Commit: ee61f06318c5c451b70af1449a5e23a412125178
Parent: 596aa077e472e89a3d8598fca14f760cc88ced1b
Author: Randy Palamar
Date: Mon, 19 May 2025 16:18:08 -0600
print program sections in a nice table format
Diffstat:
M | elfinspect.c | | | 400 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- |
M | main_posix.c | | | 6 | +++--- |
2 files changed, 310 insertions(+), 96 deletions(-)
diff --git a/elfinspect.c b/elfinspect.c
@@ -1,43 +1,68 @@
/* See LICENSE for license details. */
-/* TODO(rnp):
- * [ ]: remove all the unaligned reads so that this compiles with optimizations on
- */
#define local_persist static
#define global static
#define function static
-/* TODO(rnp) platform specific */
-#define assert(c) do { if (!(c)) asm("int3; nop"); } while (0)
+#ifdef __ARM_ARCH_ISA_A64
+#define debugbreak() asm("brk 0xf000")
+#elif __x86_64__
+#define debugbreak() asm("int3; nop")
+#else
+#error Unsupported Architecture
+#endif
+#define assert(c) do { if (!(c)) debugbreak(); } while (0)
#define TODO(c) assert(c)
+#ifdef __clang__
+#define read_only __attribute__((section(".rodata")))
+#else
+/* TODO(rnp): how to do this with gcc */
+#define read_only
+#endif
+
#define countof(a) (sizeof(a) / sizeof(*a))
#define static_assert(c, msg) _Static_assert(c, msg)
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+
#define KB(a) ((u64)a << 10ULL)
#define MB(a) ((u64)a << 20ULL)
typedef struct {u8 *beg, *end;} Arena;
#define str8(s) (str8){.len = countof(s) - 1, .data = (u8 *)s}
-typedef struct { iz len; u8 *data;} str8;
+typedef struct { sz len; u8 *data;} str8;
typedef struct {
u8 *data;
- iz index;
- iz count;
+ sz index;
+ sz count;
b32 errors;
-} str8_reader;
+} str8_stream;
typedef struct {
- u8 *argv0;
+ sz *max_column_widths;
+
+ str8 *data;
+ s16 capacity;
+ s16 count;
+ u16 columns;
+} Table;
+
+typedef struct {
+ u8 *argv0;
struct {
u8 **data;
s16 capacity;
s16 count;
} file_names;
+
+ b32 sections;
+ b32 header;
} Options;
/* NOTE: platform api; defined here so that other platform symbols are not visible to this TU */
@@ -111,32 +136,33 @@ typedef enum { ELF_KINDS } ELFKind;
typedef struct {ELF_HEADER_MEMBERS} ELFHeader;
#undef X
-typedef enum {
- ESK_NULL = 0,
- ESK_PROGBITS = 1,
- ESK_SYMBOL_TABLE = 2,
- ESK_STR_TABLE = 3,
- ESK_RELA = 4,
- ESK_HASH = 5,
- ESK_DYNAMIC = 6,
- ESK_NOTE = 7,
- ESK_NOBITS = 8,
- ESK_REL = 9,
- ESK_SHLIB = 10,
- ESK_DYNSYM = 11,
- ESK_INIT_ARRAY = 14,
- ESK_FINI_ARRAY = 15,
- ESK_PREINIT_ARR = 16,
- ESK_GROUP = 17,
- ESK_SYMTAB_SHND = 18,
- ESK_RELR = 19,
- ESK_NUM = 20,
- ESK_LOPROC = 0x70000000,
- ESK_HIPROC = 0x7fffffff,
- ESK_LOUSER = 0x80000000,
- ESK_HIUSER = 0x8fffffff,
-} ELFSectionKind;
-static_assert(sizeof(ELFSectionKind) == 4, "sizeof(ELFSectionKind) must be 4 bytes");
+/* X(name, value, pretty) */
+/* NOTE: base section kinds; extended kinds handled directly */
+#define ELFSectionKinds \
+ X(NULL, 0x00, "null") \
+ X(PROGBITS, 0x01, "progdata") \
+ X(SYMBOL_TABLE, 0x02, "symtab") \
+ X(STR_TABLE, 0x03, "strtab") \
+ X(RELA, 0x04, "rela") \
+ X(HASH, 0x05, "symhashtab") \
+ X(DYNAMIC, 0x06, "dynamic") \
+ X(NOTE, 0x07, "notes") \
+ X(NOBITS, 0x08, "nobits") \
+ X(REL, 0x09, "rel") \
+ X(SHLIB, 0x0A, "shlib") \
+ X(DYNSYM, 0x0B, "dynsym") \
+ X(INIT_ARRAY, 0x0E, "init_array") \
+ X(FINI_ARRAY, 0x0F, "fini_array") \
+ X(PREINIT_ARR, 0x10, "preinit_array") \
+ X(GROUP, 0x11, "group") \
+ X(SYMTAB_SHND, 0x12, "extended symtab") \
+ X(RELR, 0x13, "relr") \
+ X(NUM, 0x14, "number")
+
+
+#define X(name, value, pretty) ELFSectionKind_ ## name = value,
+typedef enum {ELFSectionKinds ELFSectionKind_COUNT} ELFSectionKind;
+#undef X
/* X(ctype, read32, read64, name) */
#define ELF_SECTION_HEADER_MEMBERS \
@@ -590,29 +616,29 @@ u64_host_from_le(u8 *s)
#define zero_struct(s) mem_clear(s, 0, sizeof(*s));
function void *
-mem_clear(void *restrict _s, u8 byte, iz size)
+mem_clear(void *restrict _s, u8 byte, sz size)
{
u8 *s = _s;
- for (iz i = 0; i < size; i++)
+ for (sz i = 0; i < size; i++)
s[i] = byte;
return s;
}
function void *
-mem_copy(void *restrict dest, void *restrict src, iz size)
+mem_copy(void *restrict dest, void *restrict src, sz size)
{
u8 *s = src, *d = dest;
- for (iz i = 0; i < size; i++)
+ for (sz i = 0; i < size; i++)
d[i] = s[i];
return dest;
}
-#define alloc(a, t, c) (t *)alloc_(a, _Alignof(t), sizeof(t), c)
+#define push_array(a, t, c) (t *)alloc_(a, _Alignof(t), sizeof(t), c)
function void *
-alloc_(Arena *a, iz alignment, iz size, iz count)
+alloc_(Arena *a, sz alignment, sz size, sz count)
{
- iz capacity = a->end - a->beg;
- iz padding = -(uintptr_t)a->beg & (alignment - 1);
+ sz capacity = a->end - a->beg;
+ sz padding = -(uintptr_t)a->beg & (alignment - 1);
if ((capacity - padding) / size < count) {
assert(0 && "OOM: buy more ram lol");
}
@@ -633,26 +659,47 @@ enum { DA_INITIAL_CAP = 4 };
: (s)->data + (s)->count++)
function void *
-da_reserve_(Arena *a, void *data, s16 *capacity, iz needed, iz align, iz size)
+da_reserve_(Arena *a, void *data, s16 *capacity, sz needed, sz align, sz size)
{
- iz cap = *capacity;
+ sz cap = *capacity;
/* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started
* on the stack or someone allocated something in the middle of the arena during usage) */
if (!data || a->beg != (u8 *)data + cap * size) {
- void *copy = alloc_(a, size, align, cap);
+ void *copy = alloc_(a, align, size, cap);
if (data) mem_copy(copy, data, cap * size);
data = copy;
}
if (!cap) cap = DA_INITIAL_CAP;
while (cap < needed) cap *= 2;
- alloc_(a, size, align, cap - *capacity);
+ alloc_(a, align, size, cap - *capacity);
*capacity = cap;
return data;
}
function str8
+arena_commit_str8_stream(Arena *a, str8_stream *s)
+{
+ assert(s->data == a->beg);
+ str8 result = {.data = s->data, .len = s->index};
+ a->beg += s->index;
+ s->count -= s->index;
+ s->data = a->beg;
+ s->index = 0;
+ return result;
+}
+
+function str8_stream
+str8_stream_from_arena(Arena arena)
+{
+ str8_stream result = {0};
+ result.data = arena.beg;
+ result.count = arena.end - arena.beg;
+ return result;
+}
+
+function str8
str8_from_c_str(u8 *c_str)
{
str8 result = {.data = c_str};
@@ -665,13 +712,13 @@ function b32
str8_equal(str8 a, str8 b)
{
b32 result = a.len == b.len;
- for (iz i = 0; result && i < a.len; i++)
+ for (sz i = 0; result && i < a.len; i++)
result &= a.data[i] == b.data[i];
return result;
}
function str8
-str8_chop_at(str8 a, iz length)
+str8_chop_at(str8 a, sz length)
{
str8 result = {0};
if (length < a.len) {
@@ -681,17 +728,17 @@ str8_chop_at(str8 a, iz length)
return result;
}
-function str8_reader
+function str8_stream
str8_reader_from_str8(str8 s)
{
- str8_reader result = {0};
+ str8_stream result = {0};
result.data = s.data;
result.count = s.len;
return result;
}
function u8
-str8_read_u8(str8_reader *r)
+str8_read_u8(str8_stream *r)
{
u8 result = 0;
r->errors |= r->index + 1 > r->count;
@@ -700,7 +747,7 @@ str8_read_u8(str8_reader *r)
}
function u16
-str8_read_u16(str8_reader *r, b32 big_endian)
+str8_read_u16(str8_stream *r, b32 big_endian)
{
u16 result = 0;
r->errors |= r->index + 2 > r->count;
@@ -713,7 +760,7 @@ str8_read_u16(str8_reader *r, b32 big_endian)
}
function u32
-str8_read_u32(str8_reader *r, b32 big_endian)
+str8_read_u32(str8_stream *r, b32 big_endian)
{
u32 result = 0;
r->errors |= r->index + 4 > r->count;
@@ -726,7 +773,7 @@ str8_read_u32(str8_reader *r, b32 big_endian)
}
function u64
-str8_read_u64(str8_reader *r, b32 big_endian)
+str8_read_u64(str8_stream *r, b32 big_endian)
{
u64 result = 0;
r->errors |= r->index + 8 > r->count;
@@ -739,10 +786,10 @@ str8_read_u64(str8_reader *r, b32 big_endian)
}
function u64
-str8_read_uleb128(str8_reader *r)
+str8_read_uleb128(str8_stream *r)
{
/* TODO(rnp): check for overflow ... */
- iz shift = 0;
+ sz shift = 0;
u64 result = 0;
while (r->index < r->count) {
u8 byte = r->data[r->index++];
@@ -830,14 +877,103 @@ print_elf_header(ELFHeader *eh)
#undef X
}
+function void
+str8_stream_append(str8_stream *s, void *restrict data, sz size)
+{
+ s->errors |= s->count - s->index < size;
+ if (!s->errors) {
+ mem_copy(s->data + s->index, data, size);
+ s->index += size;
+ }
+}
+
+function void
+str8_stream_print_byte(str8_stream *s, u8 byte)
+{
+ str8_stream_append(s, &byte, 1);
+}
+
+function void
+str8_stream_print_str8(str8_stream *s, str8 str)
+{
+ str8_stream_append(s, str.data, str.len);
+}
+
+function void
+str8_stream_print_u64(str8_stream *s, u64 n)
+{
+ u8 buffer[64];
+ u8 *end = buffer + sizeof(buffer);
+ u8 *beg = end;
+ do { *--beg = '0' + (n % 10); } while (n /= 10);
+ str8_stream_append(s, beg, end - beg);
+}
+
+function void
+str8_stream_print_u64_hex(str8_stream *s, u64 n)
+{
+ u8 buffer[16];
+ u8 *end = buffer + sizeof(buffer);
+ u8 *beg = end;
+ while (n) {
+ *--beg = "0123456789abcdef"[n & 0x0Fu];
+ n >>= 4;
+ }
+ while (end - beg < 2) *--beg = '0';
+ str8_stream_append(s, beg, end - beg);
+}
+
+function void
+str8_stream_print_elf_section_header_kind(str8_stream *s, ELFSectionKind kind)
+{
+ #define X(name, value, pretty) [ELFSectionKind_##name] = str8(pretty),
+ read_only local_persist str8 kind_pretty[ELFSectionKind_COUNT] = {ELFSectionKinds};
+ #undef X
+ if (kind_pretty[MIN(kind, ELFSectionKind_COUNT - 1)].len) {
+ str8_stream_print_str8(s, kind_pretty[MIN(kind, ELFSectionKind_COUNT - 1)]);
+ } else {
+ str8_stream_print_str8(s, str8("0x"));
+ str8_stream_print_u64_hex(s, kind);
+ }
+}
+
+function void
+print_table_line_marker(Table *t, str8_stream *s)
+{
+ str8_stream_print_byte(s, '+');
+ for (s16 column = 0; column < t->columns; column++) {
+ for (sz i = 0; i < t->max_column_widths[column] + 2; i++)
+ str8_stream_print_byte(s, '-');
+ str8_stream_print_byte(s, '+');
+ }
+ str8_stream_print_byte(s, '\n');
+}
+
+function void
+print_table_row(Table *t, str8 *cells, str8_stream *s)
+{
+ str8_stream_print_byte(s, '|');
+ for (s16 column = 0; column < t->columns; column++) {
+ str8_stream_print_byte(s, ' ');
+ str8_stream_print_str8(s, cells[column]);
+ for (s32 i = cells[column].len; i < t->max_column_widths[column]; i++)
+ str8_stream_print_byte(s, ' ');
+ str8_stream_print_byte(s, ' ');
+ str8_stream_print_byte(s, '|');
+ }
+ str8_stream_print_byte(s, '\n');
+}
+
function b32
is_elf(str8 file)
{
b32 result = file.len >= 16;
- result &= file.data[0] == 0x7F;
- result &= file.data[1] == 'E';
- result &= file.data[2] == 'L';
- result &= file.data[3] == 'F';
+ if (result) {
+ result &= file.data[0] == 0x7F;
+ result &= file.data[1] == 'E';
+ result &= file.data[2] == 'L';
+ result &= file.data[3] == 'F';
+ }
return result;
}
@@ -851,7 +987,7 @@ elf_header_from_file(ELFHeader *eh, str8 file)
eh->abi = file.data[7];
eh->abi_version = file.data[8];
- str8_reader reader = str8_reader_from_str8(str8_chop_at(file, 16));
+ str8_stream reader = str8_reader_from_str8(str8_chop_at(file, 16));
b32 big_endian = eh->endianness == EEK_BIG;
eh->kind = str8_read_u16(&reader, big_endian);
eh->machine = str8_read_u16(&reader, big_endian);
@@ -931,9 +1067,9 @@ elf_extract_section_headers(Arena *a, str8 file, ELFHeader *eh)
assert(file.len >= eh->section_header_offset + eh->section_header_entry_size * eh->section_header_count);
u32 sections = eh->section_header_count;
- ELFSectionHeader *result = alloc(a, ELFSectionHeader, sections);
+ ELFSectionHeader *result = push_array(a, ELFSectionHeader, sections);
for (u32 i = 0; i < sections; i++) {
- iz offset = eh->section_header_offset + eh->section_header_entry_size * i;
+ sz offset = eh->section_header_offset + eh->section_header_entry_size * i;
elf_section_header_from_str8(result + i, str8_chop_at(file, offset),
eh->endianness == EEK_BIG, eh->format == EF_32);
}
@@ -961,7 +1097,7 @@ elf_lookup_section(str8 name, str8 file, ELFSectionHeader *shs, u32 sections_cou
}
function void
-dwarf_read_unit_header(str8_reader *store, DWARFUnitHeader *duh)
+dwarf_read_unit_header(str8_stream *store, DWARFUnitHeader *duh)
{
/* TODO(rnp): context containing endianess, dwarf size */
u32 test_length = str8_read_u32(store, 0);
@@ -975,11 +1111,11 @@ dwarf_read_unit_header(str8_reader *store, DWARFUnitHeader *duh)
else duh->abbreviation_offset = str8_read_u32(store, 0);
}
-function iz
+function sz
dwarf_parse_abbrevation(Arena *a, DWARFAbbreviation *abbrv, str8 table)
{
- str8_reader table_reader = str8_reader_from_str8(table);
- iz table_start_size = table.len;
+ str8_stream table_reader = str8_reader_from_str8(table);
+ sz table_start_size = table.len;
abbrv->abbreviation_code = str8_read_uleb128(&table_reader);
abbrv->kind = str8_read_uleb128(&table_reader);
if (table_reader.count - table_reader.index < 1)
@@ -1011,27 +1147,78 @@ dwarf_lookup_abbreviation(Arena *a, str8 table, u64 key)
return result;
}
-function Options
-parse_command_line(Arena *arena, s32 argc, char *argv[])
+function Table
+table_new(Arena *arena, u16 column_count, u16 reserved_rows)
{
- Options result = {0};
-
- #define shift(c, v) ((c)--, *(v)++)
+ Table result = {0};
+ result.max_column_widths = push_array(arena, typeof(*result.max_column_widths), column_count);
+ result.columns = column_count;
+ assert(reserved_rows < 0xFFFF / column_count);
+ result.data = da_reserve(arena, &result, reserved_rows * column_count);
+ return result;
+}
- char *c_arg = shift(argc, argv);
- result.argv0 = (u8 *)c_arg;
- while (argc) {
- c_arg = shift(argc, argv);
- *da_push(arena, &result.file_names) = (u8 *)c_arg;
+#define table_push_row(t, a, ...) \
+ table_push_row_(t, a, (str8 []){__VA_ARGS__}, sizeof((str8 []){__VA_ARGS__}) / (sizeof(str8)))
+function void
+table_push_row_(Table *table, Arena *arena, str8 *cells, sz cells_count)
+{
+ assert(table->columns == cells_count);
+ for (sz i = 0; i < cells_count; i++) {
+ table->max_column_widths[i] = MAX(table->max_column_widths[i], cells[i].len);
+ str8 *out = da_push(arena, table);
+ str8 cell = cells[i];
+ *out = cell;
}
+}
- #undef shift
+function void
+print_section_table(Arena arena, ELFSectionHeader *sections, u32 section_count)
+{
+ str8 header_row[] = {str8("name"), str8("kind"), str8("size"), str8("offset"), str8("flags"), str8("align")};
+ Table table = table_new(&arena, countof(header_row), section_count);
+ for (u32 i = 0; i < countof(header_row); i++)
+ table.max_column_widths[i] = header_row[i].len;
+
+ for (u32 i = 0; i < section_count; i++) {
+ if (sections[i].size) {
+ str8_stream sb = str8_stream_from_arena(arena);
+ str8 name = sections[i].name;
- return result;
+ str8_stream_print_elf_section_header_kind(&sb, sections[i].kind);
+ str8 kind = arena_commit_str8_stream(&arena, &sb);
+
+ str8_stream_print_u64(&sb, sections[i].size);
+ str8 size = arena_commit_str8_stream(&arena, &sb);
+
+ str8_stream_print_str8(&sb, str8("0x"));
+ str8_stream_print_u64_hex(&sb, sections[i].offset);
+ str8 offset = arena_commit_str8_stream(&arena, &sb);
+
+ str8_stream_print_str8(&sb, str8("0x"));
+ str8_stream_print_u64_hex(&sb, sections[i].flags);
+ str8 flags = arena_commit_str8_stream(&arena, &sb);
+
+ str8_stream_print_u64(&sb, sections[i].addralign);
+ str8 align = arena_commit_str8_stream(&arena, &sb);
+
+ table_push_row(&table, &arena, name, kind, size, offset, flags, align);
+ }
+ }
+
+ str8_stream sb = str8_stream_from_arena(arena);
+ print_table_line_marker(&table, &sb);
+ print_table_row(&table, header_row, &sb);
+ print_table_line_marker(&table, &sb);
+ for (s16 index = 0; index < table.count; index += table.columns)
+ print_table_row(&table, table.data + index, &sb);
+ print_table_line_marker(&table, &sb);
+ fwrite(sb.data, 1, sb.index, stdout);
+ fflush(stdout);
}
function b32
-elf_inspect_file(Arena arena, str8 file)
+elf_inspect_file(Arena arena, str8 file, Options *options)
{
b32 result = is_elf(file);
if (result) {
@@ -1039,16 +1226,16 @@ elf_inspect_file(Arena arena, str8 file)
if (!elf_header_from_file(&header, file)) {
return 1;
}
+
+ if (options->header)
+ print_elf_header(&header);
+
ELFSectionHeader *sections = elf_extract_section_headers(&arena, file, &header);
- print_elf_header(&header);
- printf("\nSections:\n");
- for (u32 i = 0; i < header.section_header_count; i++) {
- printf("[%2u]:", i);
- str8 name = sections[i].name;
- if (name.len) printf(" %.*s", (s32)name.len, name.data);
- printf("\n");
- }
+ if (options->sections)
+ print_section_table(arena, sections, header.section_header_count);
+
+ #if 0 /* TODO(rnp): fix this. it broke when da_reserve was fixed (i.e. it was always busted)*/
ELFSection debug_info = elf_lookup_section(str8(".debug_info"), file,
sections, header.section_header_count);
ELFSection debug_abbrv = elf_lookup_section(str8(".debug_abbrev"), file,
@@ -1064,7 +1251,7 @@ elf_inspect_file(Arena arena, str8 file)
return 0;
}
- str8_reader d_info_reader = str8_reader_from_str8(debug_info.store);
+ str8_stream d_info_reader = str8_reader_from_str8(debug_info.store);
DWARFUnitHeader d_info_header = {0};
dwarf_read_unit_header(&d_info_reader, &d_info_header);
str8 abbreviation_table = str8_chop_at(debug_abbrv.store, d_info_header.abbreviation_offset);
@@ -1083,6 +1270,7 @@ elf_inspect_file(Arena arena, str8 file)
case DATK_ADDR_BASE: printf("addr base: "); break;
case DATK_LOCLISTS_BASE: printf("loc list base: "); break;
case DATK_RNGLISTS_BASE: printf("range list base: "); break;
+ case DATK_NAME: printf("name: "); break;
default: assert(0); break;
}
@@ -1108,6 +1296,7 @@ elf_inspect_file(Arena arena, str8 file)
}
printf("\n");
}
+ #endif
result = 1;
}
@@ -1115,6 +1304,31 @@ elf_inspect_file(Arena arena, str8 file)
return result;
}
+function Options
+parse_command_line(Arena *arena, s32 argc, char *argv[])
+{
+ Options result = {0};
+
+ #define shift(c, v) ((c)--, *(v)++)
+
+ char *c_arg = shift(argc, argv);
+ result.argv0 = (u8 *)c_arg;
+ while (argc) {
+ c_arg = shift(argc, argv);
+ str8 arg = str8_from_c_str((u8 *)c_arg);
+ if (str8_equal(arg, str8("--sections"))) {
+ result.sections = 1;
+ } else if (str8_equal(arg, str8("--header"))) {
+ result.header = 1;
+ } else {
+ *da_push(arena, &result.file_names) = (u8 *)c_arg;
+ }
+ }
+
+ #undef shift
+
+ return result;
+}
function b32
elfinspect(Arena arena, s32 argc, char *argv[])
@@ -1125,8 +1339,8 @@ elfinspect(Arena arena, s32 argc, char *argv[])
for (s32 file_index = 0; file_index < options.file_names.count; file_index++) {
u8 *file_name = options.file_names.data[file_index];
str8 file = os_map_file(file_name);
- printf("-----<%s>-----\n", file_name);
- result &= elf_inspect_file(arena, file);
+ if (!file.len) fprintf(stderr, "failed to open file: %s\n", file_name);
+ result &= elf_inspect_file(arena, file, &options);
}
return result;
diff --git a/main_posix.c b/main_posix.c
@@ -11,7 +11,7 @@ typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
-typedef ptrdiff_t iz;
+typedef ptrdiff_t sz;
#include <stdio.h>
@@ -23,10 +23,10 @@ typedef ptrdiff_t iz;
#include <unistd.h>
function Arena
-os_arena_new(iz size)
+os_arena_new(sz size)
{
Arena result = {0};
- iz page_size = sysconf(_SC_PAGESIZE);
+ sz page_size = sysconf(_SC_PAGESIZE);
if (size % page_size) size += page_size - (size % page_size);
u8 *mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (mem != MAP_FAILED) {