elfinspect

ELF and DWARF Binary Inspector
git clone anongit@rnpnr.xyz:elfinspect.git
Log | Files | Refs | Feed | README | LICENSE

elfinspect.c (38774B)


      1 /* See LICENSE for license details. */
      2 
      3 #define local_persist static
      4 #define global        static
      5 #define function      static
      6 
      7 #ifdef __ARM_ARCH_ISA_A64
      8 #define debugbreak() asm("brk 0xf000")
      9 #elif __x86_64__
     10 #define debugbreak() asm("int3; nop")
     11 #else
     12 #error Unsupported Architecture
     13 #endif
     14 #define assert(c) do { if (!(c)) debugbreak(); } while (0)
     15 #define TODO(c) assert(c)
     16 
     17 #ifdef __clang__
     18 #define read_only __attribute__((section(".rodata")))
     19 #else
     20 /* TODO(rnp): how to do this with gcc */
     21 #define read_only
     22 #endif
     23 
     24 #define countof(a) (sizeof(a) / sizeof(*a))
     25 
     26 #define static_assert(c, msg) _Static_assert(c, msg)
     27 
     28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
     29 #define MAX(a, b) ((a) < (b) ? (b) : (a))
     30 
     31 #define KB(a) ((u64)a << 10ULL)
     32 #define MB(a) ((u64)a << 20ULL)
     33 
     34 typedef struct {u8 *beg, *end;} Arena;
     35 
     36 #define str8(s) (str8){.len = countof(s) - 1, .data = (u8 *)s}
     37 typedef struct { sz len; u8 *data;} str8;
     38 
     39 typedef struct {
     40 	u8  *data;
     41 	sz   index;
     42 	sz   count;
     43 	b32  errors;
     44 } str8_stream;
     45 
     46 typedef struct {
     47 	sz *max_column_widths;
     48 
     49 	str8 *data;
     50 	s16   capacity;
     51 	s16   count;
     52 
     53 	u16  columns;
     54 } Table;
     55 
     56 typedef struct {
     57 	u8 *argv0;
     58 	struct {
     59 		u8 **data;
     60 		s16  capacity;
     61 		s16  count;
     62 	} file_names;
     63 
     64 	b32 sections;
     65 	b32 header;
     66 } Options;
     67 
     68 /* NOTE: platform api; defined here so that other platform symbols are not visible to this TU */
     69 function str8 os_map_file(u8 *);
     70 
     71 /* X(name, value, pretty) */
     72 #define ELF_FORMATS \
     73 	X(EF_NONE, 0, "Unknown") \
     74 	X(EF_32,   1, "ELF32")   \
     75 	X(EF_64,   2, "ELF64")
     76 
     77 #define ELF_ENDIANNESS \
     78 	X(EEK_NONE,   0, "Unknown")       \
     79 	X(EEK_LITTLE, 1, "Little-Endian") \
     80 	X(EEK_BIG,    2, "Big-Endian")
     81 
     82 #define ELF_OS_ABI \
     83 	X(ELF_ABI_SYSV,       0x00, "System-V")       \
     84 	X(ELF_ABI_HPUX,       0x01, "HP-UX")          \
     85 	X(ELF_ABI_NETBSD,     0x02, "NetBSD")         \
     86 	X(ELF_ABI_GNU,        0x03, "GNU Hurd")       \
     87 	X(ELF_ABI_SOLARIS,    0x06, "Solaris")        \
     88 	X(ELF_ABI_AIX,        0x07, "AIX")            \
     89 	X(ELF_ABI_IRIX,       0x08, "IRIX")           \
     90 	X(ELF_ABI_FREEBSD,    0x09, "FreeBSD")        \
     91 	X(ELF_ABI_TRU64,      0x0a, "Tru64")          \
     92 	X(ELF_ABI_MODESTO,    0x0b, "Novel Modesto")  \
     93 	X(ELF_ABI_OPENBSD,    0x0c, "OpenBSD")        \
     94 	X(ELF_ABI_OPENVMS,    0x0d, "OpenVMS")        \
     95 	X(ELF_ABI_NONSTOP,    0x0e, "NonStop Kernel") \
     96 	X(ELF_ABI_AROS,       0x0f, "AROS")           \
     97 	X(ELF_ABI_FENIX,      0x10, "Fenix")          \
     98 	X(ELF_ABI_ARM,        0x61, "ARM")            \
     99 	X(ELF_ABI_STANDALONE, 0xff, "Free Standing")
    100 
    101 #define ELF_KINDS \
    102 	X(ELF_KIND_NONE,   0x0000, "Unknown")       \
    103 	X(ELF_KIND_REL,    0x0001, "Relocatable")   \
    104 	X(ELF_KIND_EXEC,   0x0002, "Executable")    \
    105 	X(ELF_KIND_DYN,    0x0003, "Shared Object") \
    106 	X(ELF_KIND_CORE,   0x0004, "Core Dump")
    107 
    108 #define X(name, value, pretty) name = value,
    109 typedef enum { ELF_FORMATS }    ELFFormat;
    110 typedef enum { ELF_ENDIANNESS } ELFEndianKind;
    111 typedef enum { ELF_OS_ABI }     ELFABIKind;
    112 typedef enum { ELF_KINDS }      ELFKind;
    113 #undef X
    114 
    115 /* X(ctype, name) */
    116 #define ELF_HEADER_MEMBERS \
    117 	X(ELFFormat,     format)                          \
    118 	X(ELFEndianKind, endianness)                      \
    119 	X(ELFABIKind,    abi)                             \
    120 	X(ELFKind,       kind)                            \
    121 	X(u16,           abi_version)                     \
    122 	X(u16,           machine)                         \
    123 	X(u32,           version)                         \
    124 	X(u64,           entry_point_offset)              \
    125 	X(u64,           program_header_offset)           \
    126 	X(u64,           section_header_offset)           \
    127 	X(u32,           flags)                           \
    128 	X(u16,           elf_header_size)                 \
    129 	X(u16,           program_header_entry_size)       \
    130 	X(u16,           program_header_count)            \
    131 	X(u16,           section_header_entry_size)       \
    132 	X(u16,           section_header_count)            \
    133 	X(u16,           section_header_name_table_index)
    134 
    135 #define X(ctype, name) ctype name;
    136 typedef struct {ELF_HEADER_MEMBERS} ELFHeader;
    137 #undef X
    138 
    139 /* X(name, value, pretty) */
    140 /* NOTE: base section kinds; extended kinds handled directly */
    141 #define ELFSectionKinds \
    142 	X(NULL,         0x00, "null")            \
    143 	X(PROGBITS,     0x01, "progdata")        \
    144 	X(SYMBOL_TABLE, 0x02, "symtab")          \
    145 	X(STR_TABLE,    0x03, "strtab")          \
    146 	X(RELA,         0x04, "rela")            \
    147 	X(HASH,         0x05, "symhashtab")      \
    148 	X(DYNAMIC,      0x06, "dynamic")         \
    149 	X(NOTE,         0x07, "notes")           \
    150 	X(NOBITS,       0x08, "nobits")          \
    151 	X(REL,          0x09, "rel")             \
    152 	X(SHLIB,        0x0A, "shlib")           \
    153 	X(DYNSYM,       0x0B, "dynsym")          \
    154 	X(INIT_ARRAY,   0x0E, "init_array")      \
    155 	X(FINI_ARRAY,   0x0F, "fini_array")      \
    156 	X(PREINIT_ARR,  0x10, "preinit_array")   \
    157 	X(GROUP,        0x11, "group")           \
    158 	X(SYMTAB_SHND,  0x12, "extended symtab") \
    159 	X(RELR,         0x13, "relr")            \
    160 	X(NUM,          0x14, "number")
    161 
    162 
    163 #define X(name, value, pretty) ELFSectionKind_ ## name = value,
    164 typedef enum {ELFSectionKinds ELFSectionKind_COUNT} ELFSectionKind;
    165 #undef X
    166 
    167 /* X(ctype, read32, read64, name) */
    168 #define ELF_SECTION_HEADER_MEMBERS \
    169 	X(u32,            u32, u32, name_table_offset) \
    170 	X(ELFSectionKind, u32, u32, kind)              \
    171 	X(u64,            u32, u64, flags)             \
    172 	X(u64,            u32, u64, addr)              \
    173 	X(u64,            u32, u64, offset)            \
    174 	X(u64,            u32, u64, size)              \
    175 	X(u32,            u32, u32, link)              \
    176 	X(u32,            u32, u32, info)              \
    177 	X(u64,            u32, u64, addralign)         \
    178 	X(u64,            u32, u64, entsize)
    179 
    180 #define X(ctype, r32, r64, name) ctype name;
    181 typedef struct {
    182 	str8 name;
    183 	ELF_SECTION_HEADER_MEMBERS
    184 } ELFSectionHeader;
    185 #undef X
    186 
    187 typedef struct {
    188 	ELFSectionHeader header;
    189 	str8 store;
    190 } ELFSection;
    191 
    192 typedef enum {
    193 	DUT_UNKNOWN       = 0x00,
    194 	DUT_COMPILE       = 0x01,
    195 	DUT_TYPE          = 0x02,
    196 	DUT_PARTIAL       = 0x03,
    197 	DUT_SKELETON      = 0x04,
    198 	DUT_SPLIT_COMPILE = 0x05,
    199 	DUT_SPLIT_TYPE    = 0x06,
    200 	DUT_LO_USER       = 0x80,
    201 	DUT_HI_USER       = 0xff
    202 } DWARFUnitKind;
    203 
    204 typedef struct {
    205 	u64 length;
    206 	u64 abbreviation_offset;
    207 	DWARFUnitKind kind;
    208 	u16 version;
    209 	u8  address_size;
    210 	u8  dwarf_64;
    211 } DWARFUnitHeader;
    212 
    213 typedef enum {
    214 	DATK_SIBLING                 = 0x01,
    215 	DATK_LOCATION                = 0x02,
    216 	DATK_NAME                    = 0x03,
    217 	DATK_ORDERING                = 0x09,
    218 	DATK_BYTE_SIZE               = 0x0b,
    219 	DATK_BIT_OFFSET              = 0x0c,
    220 	DATK_BIT_SIZE                = 0x0d,
    221 	DATK_STMT_LIST               = 0x10,
    222 	DATK_LOW_PC                  = 0x11,
    223 	DATK_HIGH_PC                 = 0x12,
    224 	DATK_LANGUAGE                = 0x13,
    225 	DATK_DISCR                   = 0x15,
    226 	DATK_DISCR_VALUE             = 0x16,
    227 	DATK_VISIBILITY              = 0x17,
    228 	DATK_IMPORT                  = 0x18,
    229 	DATK_STRING_LENGTH           = 0x19,
    230 	DATK_COMMON_REFERENCE        = 0x1a,
    231 	DATK_COMP_DIR                = 0x1b,
    232 	DATK_CONST_VALUE             = 0x1c,
    233 	DATK_CONTAINING_TYPE         = 0x1d,
    234 	DATK_DEFAULT_VALUE           = 0x1e,
    235 	DATK_INLINE                  = 0x20,
    236 	DATK_IS_OPTIONAL             = 0x21,
    237 	DATK_LOWER_BOUND             = 0x22,
    238 	DATK_PRODUCER                = 0x25,
    239 	DATK_PROTOTYPED              = 0x27,
    240 	DATK_RETURN_ADDR             = 0x2a,
    241 	DATK_START_SCOPE             = 0x2c,
    242 	DATK_BIT_STRIDE              = 0x2e,
    243 	DATK_UPPER_BOUND             = 0x2f,
    244 	DATK_ABSTRACT_ORIGIN         = 0x31,
    245 	DATK_ACCESSIBILITY           = 0x32,
    246 	DATK_ADDRESS_CLASS           = 0x33,
    247 	DATK_ARTIFICIAL              = 0x34,
    248 	DATK_BASE_TYPES              = 0x35,
    249 	DATK_CALLING_CONVENTION      = 0x36,
    250 	DATK_COUNT                   = 0x37,
    251 	DATK_DATA_MEMBER_LOCATION    = 0x38,
    252 	DATK_DECL_COLUMN             = 0x39,
    253 	DATK_DECL_FILE               = 0x3a,
    254 	DATK_DECL_LINE               = 0x3b,
    255 	DATK_DECLARATION             = 0x3c,
    256 	DATK_DISCR_LIST              = 0x3d,
    257 	DATK_ENCODING                = 0x3e,
    258 	DATK_EXTERNAL                = 0x3f,
    259 	DATK_FRAME_BASE              = 0x40,
    260 	DATK_FRIEND                  = 0x41,
    261 	DATK_IDENTIFIER_CASE         = 0x42,
    262 	DATK_MACRO_INFO              = 0x43,
    263 	DATK_NAMELIST_ITEM           = 0x44,
    264 	DATK_PRIORITY                = 0x45,
    265 	DATK_SEGMENT                 = 0x46,
    266 	DATK_SPECIFICATION           = 0x47,
    267 	DATK_STATIC_LINK             = 0x48,
    268 	DATK_TYPE                    = 0x49,
    269 	DATK_USE_LOCATION            = 0x4a,
    270 	DATK_VARIABLE_PARAMETER      = 0x4b,
    271 	DATK_VIRTUALITY              = 0x4c,
    272 	DATK_VTABLE_ELEM_LOCATION    = 0x4d,
    273 	DATK_ALLOCATED               = 0x4e,
    274 	DATK_ASSOCIATED              = 0x4f,
    275 	DATK_DATA_LOCATION           = 0x50,
    276 	DATK_BYTE_STRIDE             = 0x51,
    277 	DATK_ENTRY_PC                = 0x52,
    278 	DATK_USE_UTF8                = 0x53,
    279 	DATK_EXTENSION               = 0x54,
    280 	DATK_RANGES                  = 0x55,
    281 	DATK_TRAMPOLINE              = 0x56,
    282 	DATK_CALL_COLUMN             = 0x57,
    283 	DATK_CALL_FILE               = 0x58,
    284 	DATK_CALL_LINE               = 0x59,
    285 	DATK_DESCRIPTION             = 0x5a,
    286 	DATK_BINARY_SCALE            = 0x5b,
    287 	DATK_DECIMAL_SCALE           = 0x5c,
    288 	DATK_SMALL                   = 0x5d,
    289 	DATK_DECIMAL_SIGN            = 0x5e,
    290 	DATK_DIGIT_COUNT             = 0x5f,
    291 	DATK_PICTURE_STRING          = 0x60,
    292 	DATK_MUTABLE                 = 0x61,
    293 	DATK_THREADS_SCALED          = 0x62,
    294 	DATK_EXPLICIT                = 0x63,
    295 	DATK_OBJECT_POINTER          = 0x64,
    296 	DATK_ENDIANITY               = 0x65,
    297 	DATK_ELEMENTAL               = 0x66,
    298 	DATK_PURE                    = 0x67,
    299 	DATK_RECURSIVE               = 0x68,
    300 	DATK_SIGNATURE               = 0x69,
    301 	DATK_MAIN_SUBPROGRAM         = 0x6a,
    302 	DATK_DATA_BIT_OFFSET         = 0x6b,
    303 	DATK_CONST_EXPR              = 0x6c,
    304 	DATK_ENUM_CLASS              = 0x6d,
    305 	DATK_LINKAGE_NAME            = 0x6e,
    306 	DATK_STRING_LENGTH_BIT_SIZE  = 0x6f,
    307 	DATK_STRING_LENGTH_BYTE_SIZE = 0x70,
    308 	DATK_RANK                    = 0x71,
    309 	DATK_STR_OFFSETS_BASE        = 0x72,
    310 	DATK_ADDR_BASE               = 0x73,
    311 	DATK_RNGLISTS_BASE           = 0x74,
    312 	DATK_DWO_NAME                = 0x76,
    313 	DATK_REFERENCE               = 0x77,
    314 	DATK_RVALUE_REFERENCE        = 0x78,
    315 	DATK_MACROS                  = 0x79,
    316 	DATK_CALL_ALL_CALLS          = 0x7a,
    317 	DATK_CALL_ALL_SOURCE_CALLS   = 0x7b,
    318 	DATK_CALL_ALL_TAIL_CALLS     = 0x7c,
    319 	DATK_CALL_RETURN_PC          = 0x7d,
    320 	DATK_CALL_VALUE              = 0x7e,
    321 	DATK_CALL_ORIGIN             = 0x7f,
    322 	DATK_CALL_PARAMETER          = 0x80,
    323 	DATK_CALL_PC                 = 0x81,
    324 	DATK_CALL_TAIL_CALL          = 0x82,
    325 	DATK_CALL_TARGET             = 0x83,
    326 	DATK_CALL_TARGET_CLOBBERED   = 0x84,
    327 	DATK_CALL_DATA_LOCATION      = 0x85,
    328 	DATK_CALL_DATA_VALUE         = 0x86,
    329 	DATK_NORETURN                = 0x87,
    330 	DATK_ALIGNMENT               = 0x88,
    331 	DATK_EXPORT_SYMBOLS          = 0x89,
    332 	DATK_DELETED                 = 0x8a,
    333 	DATK_DEFAULTED               = 0x8b,
    334 	DATK_LOCLISTS_BASE           = 0x8c,
    335 	DATK_LO_USER                 = 0x2000,
    336 	DATK_HI_USER                 = 0x3fff,
    337 } DWARFAttributeKind;
    338 
    339 typedef enum {
    340 	DFK_ADDR           = 0x01,
    341 	DFK_BLOCK2         = 0x03,
    342 	DFK_BLOCK4         = 0x04,
    343 	DFK_DATA2          = 0x05,
    344 	DFK_DATA4          = 0x06,
    345 	DFK_DATA8          = 0x07,
    346 	DFK_STRING         = 0x08,
    347 	DFK_BLOCK          = 0x09,
    348 	DFK_BLOCK1         = 0x0a,
    349 	DFK_DATA1          = 0x0b,
    350 	DFK_FLAG           = 0x0c,
    351 	DFK_SDATA          = 0x0d,
    352 	DFK_STRP           = 0x0e,
    353 	DFK_UDATA          = 0x0f,
    354 	DFK_REF_ADDR       = 0x10,
    355 	DFK_REF1           = 0x11,
    356 	DFK_REF2           = 0x12,
    357 	DFK_REF4           = 0x13,
    358 	DFK_REF8           = 0x14,
    359 	DFK_REF_UDATA      = 0x15,
    360 	DFK_INDIRECT       = 0x16,
    361 	DFK_SEC_OFFSET     = 0x17,
    362 	DFK_EXPRLOC        = 0x18,
    363 	DFK_FLAG_PRESENT   = 0x19,
    364 	DFK_STRX           = 0x1a,
    365 	DFK_ADDRX          = 0x1b,
    366 	DFK_REF_SUP4       = 0x1c,
    367 	DFK_STRP_SUP       = 0x1d,
    368 	DFK_DATA16         = 0x1e,
    369 	DFK_LINE_STRP      = 0x1f,
    370 	DFK_REF_SIG8       = 0x20,
    371 	DFK_IMPLICIT_CONST = 0x21,
    372 	DFK_LOCLISTX       = 0x22,
    373 	DFK_RNGLISTX       = 0x23,
    374 	DFK_REF_SUP8       = 0x24,
    375 	DFK_STRX1          = 0x25,
    376 	DFK_STRX2          = 0x26,
    377 	DFK_STRX3          = 0x27,
    378 	DFK_STRX4          = 0x28,
    379 	DFK_ADDRX1         = 0x29,
    380 	DFK_ADDRX2         = 0x2a,
    381 	DFK_ADDRX3         = 0x2b,
    382 	DFK_ADDRX4         = 0x2c,
    383 } DWARFFormKind;
    384 
    385 typedef struct {
    386 	DWARFAttributeKind kind;
    387 	DWARFFormKind      form_kind;
    388 } DWARFAttribute;
    389 
    390 typedef enum {
    391 	DAK_ARRAY_TYPE               = 0x01,
    392 	DAK_CLASS_TYPE               = 0x02,
    393 	DAK_ENTRY_POINT              = 0x03,
    394 	DAK_ENUMERATION_TYPE         = 0x04,
    395 	DAK_FORMAL_PARAMETER         = 0x05,
    396 	DAK_IMPORTED_DECLARATION     = 0x08,
    397 	DAK_LABEL                    = 0x0a,
    398 	DAK_LEXICAL_BLOCK            = 0x0b,
    399 	DAK_MEMBER                   = 0x0d,
    400 	DAK_POINTER_TYPE             = 0x0f,
    401 	DAK_REFERENCE_TYPE           = 0x10,
    402 	DAK_COMPILE_UNIT             = 0x11,
    403 	DAK_STRING_TYPE              = 0x12,
    404 	DAK_STRUCTURE_TYPE           = 0x13,
    405 	DAK_SUBROUTINE_TYPE          = 0x15,
    406 	DAK_TYPEDEF                  = 0x16,
    407 	DAK_UNION_TYPE               = 0x17,
    408 	DAK_UNSPECIFIED_PARAMETERS   = 0x18,
    409 	DAK_VARIANT                  = 0x19,
    410 	DAK_COMMON_BLOCK             = 0x1a,
    411 	DAK_COMMON_INCLUSION         = 0x1b,
    412 	DAK_INHERITANCE              = 0x1c,
    413 	DAK_INLINED_SUBROUTINE       = 0x1d,
    414 	DAK_MODULE                   = 0x1e,
    415 	DAK_PTR_TO_MEMBER_TYPE       = 0x1f,
    416 	DAK_SET_TYPE                 = 0x20,
    417 	DAK_SUBRANGE_TYPE            = 0x21,
    418 	DAK_WITH_STMT                = 0x22,
    419 	DAK_ACCESS_DECLARATION       = 0x23,
    420 	DAK_BASE_TYPE                = 0x24,
    421 	DAK_CATCH_BLOCK              = 0x25,
    422 	DAK_CONST_TYPE               = 0x26,
    423 	DAK_CONSTANT                 = 0x27,
    424 	DAK_ENUMERATOR               = 0x28,
    425 	DAK_FILE_TYPE                = 0x29,
    426 	DAK_FRIEND                   = 0x2a,
    427 	DAK_NAMELIST                 = 0x2b,
    428 	DAK_NAMELIST_ITEM            = 0x2c,
    429 	DAK_PACKED_TYPE              = 0x2d,
    430 	DAK_SUBPROGRAM               = 0x2e,
    431 	DAK_TEMPLATE_TYPE_PARAMETER  = 0x2f,
    432 	DAK_TEMPLATE_VALUE_PARAMETER = 0x30,
    433 	DAK_THROWN_TYPE              = 0x31,
    434 	DAK_TRY_BLOCK                = 0x32,
    435 	DAK_VARIANT_PART             = 0x33,
    436 	DAK_VARIABLE                 = 0x34,
    437 	DAK_VOLATILE_TYPE            = 0x35,
    438 	DAK_DWARF_PROCEDURE          = 0x36,
    439 	DAK_RESTRICT_TYPE            = 0x37,
    440 	DAK_INTERFACE_TYPE           = 0x38,
    441 	DAK_NAMESPACE                = 0x39,
    442 	DAK_IMPORTED_MODULE          = 0x3a,
    443 	DAK_UNSPECIFIED_TYPE         = 0x3b,
    444 	DAK_PARTIAL_UNIT             = 0x3c,
    445 	DAK_IMPORTED_UNIT            = 0x3d,
    446 	DAK_CONDITION                = 0x3f,
    447 	DAK_SHARED_TYPE              = 0x40,
    448 	DAK_TYPE_UNIT                = 0x41,
    449 	DAK_RVALUE_REFERENCE_TYPE    = 0x42,
    450 	DAK_TEMPLATE_ALIAS           = 0x43,
    451 	DAK_COARRAY_TYPE             = 0x44,
    452 	DAK_GENERIC_SUBRANGE         = 0x45,
    453 	DAK_DYNAMIC_TYPE             = 0x46,
    454 	DAK_ATOMIC_TYPE              = 0x47,
    455 	DAK_CALL_SITE                = 0x48,
    456 	DAK_CALL_SITE_PARAMETER      = 0x49,
    457 	DAK_SKELETON_UNIT            = 0x4a,
    458 	DAK_IMMUTABLE_TYPE           = 0x4b,
    459 	DAK_LO_USER                  = 0x4080,
    460 	DAK_HI_USER                  = 0xffff
    461 } DWARFAbbreviationKind;
    462 
    463 typedef struct DWARFAbbreviation {
    464 	DWARFAbbreviationKind kind;
    465 	b32    has_children;
    466 	u64    abbreviation_code;
    467 
    468 	DWARFAttribute *data;
    469 	s16             count;
    470 	s16             capacity;
    471 
    472 	struct DWARFAbbreviation *next;
    473 } DWARFAbbreviation;
    474 
    475 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    476 function u16
    477 u16_host_from_be(u8 *s)
    478 {
    479 	u16 result = 0;
    480 	result |= s[1]; result <<= 8;
    481 	result |= s[0];
    482 	return result;
    483 }
    484 
    485 function u16
    486 u16_host_from_le(u8 *s)
    487 {
    488 	u16 result = 0;
    489 	result |= s[0]; result <<= 8;
    490 	result |= s[1];
    491 	return result;
    492 }
    493 
    494 function u32
    495 u32_host_from_be(u8 *s)
    496 {
    497 	u32 result = 0;
    498 	result |= s[3]; result <<= 8;
    499 	result |= s[2]; result <<= 8;
    500 	result |= s[1]; result <<= 8;
    501 	result |= s[0];
    502 	return result;
    503 }
    504 
    505 function u32
    506 u32_host_from_le(u8 *s)
    507 {
    508 	u32 result = 0;
    509 	result |= s[0]; result <<= 8;
    510 	result |= s[1]; result <<= 8;
    511 	result |= s[2]; result <<= 8;
    512 	result |= s[3];
    513 	return result;
    514 }
    515 
    516 function u64
    517 u64_host_from_be(u8 *s)
    518 {
    519 	u64 result = 0;
    520 	result |= s[7]; result <<= 8;
    521 	result |= s[6]; result <<= 8;
    522 	result |= s[5]; result <<= 8;
    523 	result |= s[4]; result <<= 8;
    524 	result |= s[3]; result <<= 8;
    525 	result |= s[2]; result <<= 8;
    526 	result |= s[1]; result <<= 8;
    527 	result |= s[0];
    528 	return result;
    529 }
    530 
    531 function u64
    532 u64_host_from_le(u8 *s)
    533 {
    534 	u64 result = 0;
    535 	result |= s[0]; result <<= 8;
    536 	result |= s[1]; result <<= 8;
    537 	result |= s[2]; result <<= 8;
    538 	result |= s[3]; result <<= 8;
    539 	result |= s[4]; result <<= 8;
    540 	result |= s[5]; result <<= 8;
    541 	result |= s[6]; result <<= 8;
    542 	result |= s[7];
    543 	return result;
    544 }
    545 #else
    546 function u16
    547 u16_host_from_be(u8 *s)
    548 {
    549 	u16 result = 0;
    550 	result |= s[0]; result <<= 8;
    551 	result |= s[1];
    552 	return result;
    553 }
    554 
    555 function u16
    556 u16_host_from_le(u8 *s)
    557 {
    558 	u16 result = 0;
    559 	result |= s[1]; result <<= 8;
    560 	result |= s[0];
    561 	return result;
    562 }
    563 
    564 function u32
    565 u32_host_from_be(u8 *s)
    566 {
    567 	u32 result = 0;
    568 	result |= s[0]; result <<= 8;
    569 	result |= s[1]; result <<= 8;
    570 	result |= s[2]; result <<= 8;
    571 	result |= s[3];
    572 	return result;
    573 }
    574 
    575 function u32
    576 u32_host_from_le(u8 *s)
    577 {
    578 	u32 result = 0;
    579 	result |= s[3]; result <<= 8;
    580 	result |= s[2]; result <<= 8;
    581 	result |= s[1]; result <<= 8;
    582 	result |= s[0];
    583 	return result;
    584 }
    585 
    586 function u64
    587 u64_host_from_be(u8 *s)
    588 {
    589 	u64 result = 0;
    590 	result |= s[0]; result <<= 8;
    591 	result |= s[1]; result <<= 8;
    592 	result |= s[2]; result <<= 8;
    593 	result |= s[3]; result <<= 8;
    594 	result |= s[4]; result <<= 8;
    595 	result |= s[5]; result <<= 8;
    596 	result |= s[6]; result <<= 8;
    597 	result |= s[7];
    598 	return result;
    599 }
    600 
    601 function u64
    602 u64_host_from_le(u8 *s)
    603 {
    604 	u64 result = 0;
    605 	result |= s[7]; result <<= 8;
    606 	result |= s[6]; result <<= 8;
    607 	result |= s[5]; result <<= 8;
    608 	result |= s[4]; result <<= 8;
    609 	result |= s[3]; result <<= 8;
    610 	result |= s[2]; result <<= 8;
    611 	result |= s[1]; result <<= 8;
    612 	result |= s[0];
    613 	return result;
    614 }
    615 #endif
    616 
    617 #define zero_struct(s) mem_clear(s, 0, sizeof(*s));
    618 function void *
    619 mem_clear(void *restrict _s, u8 byte, sz size)
    620 {
    621 	u8 *s = _s;
    622 	for (sz i = 0; i < size; i++)
    623 		s[i] = byte;
    624 	return s;
    625 }
    626 
    627 function void *
    628 mem_copy(void *restrict dest, void *restrict src, sz size)
    629 {
    630 	u8 *s = src, *d = dest;
    631 	for (sz i = 0; i < size; i++)
    632 		d[i] = s[i];
    633 	return dest;
    634 }
    635 
    636 #define push_array(a, t, c) (t *)alloc_(a, _Alignof(t), sizeof(t), c)
    637 function void *
    638 alloc_(Arena *a, sz alignment, sz size, sz count)
    639 {
    640 	sz capacity = a->end - a->beg;
    641 	sz padding  = -(uintptr_t)a->beg & (alignment - 1);
    642 	if ((capacity - padding) / size  < count) {
    643 		assert(0 && "OOM: buy more ram lol");
    644 	}
    645 	u8 *result = a->beg + padding;
    646 	a->beg += padding + size * count;
    647 	return mem_clear(result, 0, size * count);
    648 }
    649 
    650 enum { DA_INITIAL_CAP = 4 };
    651 #define da_reserve(a, s, n) \
    652   (s)->data = da_reserve_((a), (s)->data, &(s)->capacity, (s)->count + n, \
    653                           _Alignof(typeof(*(s)->data)), sizeof(*(s)->data))
    654 
    655 #define da_push(a, s) \
    656   ((s)->count == (s)->capacity  \
    657     ? da_reserve(a, s, 1),      \
    658       (s)->data + (s)->count++  \
    659     : (s)->data + (s)->count++)
    660 
    661 function void *
    662 da_reserve_(Arena *a, void *data, s16 *capacity, sz needed, sz align, sz size)
    663 {
    664 	sz cap = *capacity;
    665 
    666 	/* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started
    667 	 * on the stack or someone allocated something in the middle of the arena during usage) */
    668 	if (!data || a->beg != (u8 *)data + cap * size) {
    669 		void *copy = alloc_(a, align, size, cap);
    670 		if (data) mem_copy(copy, data, cap * size);
    671 		data = copy;
    672 	}
    673 
    674 	if (!cap) cap = DA_INITIAL_CAP;
    675 	while (cap < needed) cap *= 2;
    676 	alloc_(a, align, size, cap - *capacity);
    677 	*capacity = cap;
    678 	return data;
    679 }
    680 
    681 function str8
    682 arena_commit_str8_stream(Arena *a, str8_stream *s)
    683 {
    684 	assert(s->data == a->beg);
    685 	str8 result = {.data = s->data, .len = s->index};
    686 	a->beg   += s->index;
    687 	s->count -= s->index;
    688 	s->data   = a->beg;
    689 	s->index  = 0;
    690 	return result;
    691 }
    692 
    693 function str8_stream
    694 str8_stream_from_arena(Arena arena)
    695 {
    696 	str8_stream result = {0};
    697 	result.data  = arena.beg;
    698 	result.count = arena.end - arena.beg;
    699 	return result;
    700 }
    701 
    702 function str8
    703 str8_from_c_str(u8 *c_str)
    704 {
    705 	str8 result = {.data = c_str};
    706 	if (c_str) while (*c_str) c_str++;
    707 	result.len = c_str - result.data;
    708 	return result;
    709 }
    710 
    711 function b32
    712 str8_equal(str8 a, str8 b)
    713 {
    714 	b32 result = a.len == b.len;
    715 	for (sz i = 0; result && i < a.len; i++)
    716 		result &= a.data[i] == b.data[i];
    717 	return result;
    718 }
    719 
    720 function str8
    721 str8_chop_at(str8 a, sz length)
    722 {
    723 	str8 result = {0};
    724 	if (length < a.len) {
    725 		result.data = a.data + length;
    726 		result.len  = a.len  - length;
    727 	}
    728 	return result;
    729 }
    730 
    731 function str8_stream
    732 str8_reader_from_str8(str8 s)
    733 {
    734 	str8_stream result = {0};
    735 	result.data  = s.data;
    736 	result.count = s.len;
    737 	return result;
    738 }
    739 
    740 function u8
    741 str8_read_u8(str8_stream *r)
    742 {
    743 	u8 result = 0;
    744 	r->errors |= r->index + 1 > r->count;
    745 	if (!r->errors) result = r->data[r->index++];
    746 	return result;
    747 }
    748 
    749 function u16
    750 str8_read_u16(str8_stream *r, b32 big_endian)
    751 {
    752 	u16 result = 0;
    753 	r->errors |= r->index + 2 > r->count;
    754 	if (!r->errors) {
    755 		if (big_endian) result = u16_host_from_be(r->data + r->index);
    756 		else            result = u16_host_from_le(r->data + r->index);
    757 		r->index += 2;
    758 	}
    759 	return result;
    760 }
    761 
    762 function u32
    763 str8_read_u32(str8_stream *r, b32 big_endian)
    764 {
    765 	u32 result = 0;
    766 	r->errors |= r->index + 4 > r->count;
    767 	if (!r->errors) {
    768 		if (big_endian) result = u32_host_from_be(r->data + r->index);
    769 		else            result = u32_host_from_le(r->data + r->index);
    770 		r->index += 4;
    771 	}
    772 	return result;
    773 }
    774 
    775 function u64
    776 str8_read_u64(str8_stream *r, b32 big_endian)
    777 {
    778 	u64 result = 0;
    779 	r->errors |= r->index + 8 > r->count;
    780 	if (!r->errors) {
    781 		if (big_endian) result = u32_host_from_be(r->data + r->index);
    782 		else            result = u32_host_from_le(r->data + r->index);
    783 		r->index += 8;
    784 	}
    785 	return result;
    786 }
    787 
    788 function u64
    789 str8_read_uleb128(str8_stream *r)
    790 {
    791 	/* TODO(rnp): check for overflow ... */
    792 	sz  shift  = 0;
    793 	u64 result = 0;
    794 	while (r->index < r->count) {
    795 		u8 byte = r->data[r->index++];
    796 		result |= (u64)(byte & 0x7F) << shift;
    797 		if ((byte & 0x80) == 0)
    798 			break;
    799 		shift += 7;
    800 	}
    801 	return result;
    802 }
    803 
    804 function void
    805 print_u64(u64 v)
    806 {
    807 	printf("%zu", v);
    808 }
    809 
    810 function void print_u16(u16 v) { print_u64(v); }
    811 function void print_u32(u32 v) { print_u64(v); }
    812 
    813 function void
    814 print_ELFFormat(ELFFormat format)
    815 {
    816 	#define X(name, value, pretty) [name] = str8(pretty),
    817 	local_persist str8 format_pretty[] = {ELF_FORMATS};
    818 	#undef X
    819 	if (format < countof(format_pretty) && format_pretty[format].len) {
    820 		printf("%.*s", (s32)format_pretty[format].len, format_pretty[format].data);
    821 	} else {
    822 		printf("Invalid");
    823 	}
    824 }
    825 
    826 function void
    827 print_ELFEndianKind(ELFEndianKind kind)
    828 {
    829 	#define X(name, value, pretty) [name] = str8(pretty),
    830 	local_persist str8 kind_pretty[] = {ELF_ENDIANNESS};
    831 	#undef X
    832 	if (kind < countof(kind_pretty) && kind_pretty[kind].len) {
    833 		printf("%.*s", (s32)kind_pretty[kind].len, kind_pretty[kind].data);
    834 	} else {
    835 		printf("Invalid");
    836 	}
    837 }
    838 
    839 function void
    840 print_ELFABIKind(ELFABIKind kind)
    841 {
    842 	#define X(name, value, pretty) [name] = str8(pretty),
    843 	local_persist str8 kind_pretty[] = {ELF_OS_ABI};
    844 	#undef X
    845 	if (kind < countof(kind_pretty) && kind_pretty[kind].len) {
    846 		printf("%.*s", (s32)kind_pretty[kind].len, kind_pretty[kind].data);
    847 	} else {
    848 		printf("Invalid");
    849 	}
    850 }
    851 
    852 function void
    853 print_ELFKind(ELFKind kind)
    854 {
    855 	#define X(name, value, pretty) [name] = str8(pretty),
    856 	local_persist str8 kind_pretty[] = {ELF_KINDS};
    857 	#undef X
    858 	if (kind < countof(kind_pretty) && kind_pretty[kind].len) {
    859 		printf("%.*s", (s32)kind_pretty[kind].len, kind_pretty[kind].data);
    860 	} else {
    861 		printf("Invalid");
    862 	}
    863 }
    864 
    865 function void
    866 print_elf_header(ELFHeader *eh)
    867 {
    868 	printf("ELF Header:\n");
    869 	s32 max_name_len = 0;
    870 	#define X(ctype, name) if (max_name_len < sizeof(#name) - 1) max_name_len = sizeof(#name) - 1;
    871 	ELF_HEADER_MEMBERS
    872 	#undef X
    873 
    874 	#define X(ctype, name) printf(#name ": %*s", (s32)(max_name_len - sizeof(#name) + 1), ""); \
    875 	                       print_##ctype(eh->name); printf("\n");
    876 	ELF_HEADER_MEMBERS
    877 	#undef X
    878 }
    879 
    880 function void
    881 str8_stream_append(str8_stream *s, void *restrict data, sz size)
    882 {
    883 	s->errors |= s->count - s->index < size;
    884 	if (!s->errors) {
    885 		mem_copy(s->data + s->index, data, size);
    886 		s->index += size;
    887 	}
    888 }
    889 
    890 function void
    891 str8_stream_print_byte(str8_stream *s, u8 byte)
    892 {
    893 	str8_stream_append(s, &byte, 1);
    894 }
    895 
    896 function void
    897 str8_stream_print_str8(str8_stream *s, str8 str)
    898 {
    899 	str8_stream_append(s, str.data, str.len);
    900 }
    901 
    902 function void
    903 str8_stream_print_u64(str8_stream *s, u64 n)
    904 {
    905 	u8 buffer[64];
    906 	u8 *end = buffer + sizeof(buffer);
    907 	u8 *beg = end;
    908 	do { *--beg = '0' + (n % 10); } while (n /= 10);
    909 	str8_stream_append(s, beg, end - beg);
    910 }
    911 
    912 function void
    913 str8_stream_print_u64_hex(str8_stream *s, u64 n)
    914 {
    915 	u8 buffer[16];
    916 	u8 *end = buffer + sizeof(buffer);
    917 	u8 *beg = end;
    918 	while (n) {
    919 		*--beg = "0123456789abcdef"[n & 0x0Fu];
    920 		n >>= 4;
    921 	}
    922 	while (end - beg < 2) *--beg = '0';
    923 	str8_stream_append(s, beg, end - beg);
    924 }
    925 
    926 function void
    927 str8_stream_print_elf_section_header_kind(str8_stream *s, ELFSectionKind kind)
    928 {
    929 	#define X(name, value, pretty) [ELFSectionKind_##name] = str8(pretty),
    930 	read_only local_persist str8 kind_pretty[ELFSectionKind_COUNT] = {ELFSectionKinds};
    931 	#undef X
    932 	if (kind_pretty[MIN(kind, ELFSectionKind_COUNT - 1)].len) {
    933 		str8_stream_print_str8(s, kind_pretty[MIN(kind, ELFSectionKind_COUNT - 1)]);
    934 	} else {
    935 		str8_stream_print_str8(s, str8("0x"));
    936 		str8_stream_print_u64_hex(s, kind);
    937 	}
    938 }
    939 
    940 function void
    941 print_table_line_marker(Table *t, str8_stream *s)
    942 {
    943 	str8_stream_print_byte(s, '+');
    944 	for (s16 column = 0; column < t->columns; column++) {
    945 		for (sz i = 0; i < t->max_column_widths[column] + 2; i++)
    946 			str8_stream_print_byte(s, '-');
    947 		str8_stream_print_byte(s, '+');
    948 	}
    949 	str8_stream_print_byte(s, '\n');
    950 }
    951 
    952 function void
    953 print_table_row(Table *t, str8 *cells, str8_stream *s)
    954 {
    955 	str8_stream_print_byte(s, '|');
    956 	for (s16 column = 0; column < t->columns; column++) {
    957 		str8_stream_print_byte(s, ' ');
    958 		str8_stream_print_str8(s, cells[column]);
    959 		for (s32 i = cells[column].len; i < t->max_column_widths[column]; i++)
    960 			str8_stream_print_byte(s, ' ');
    961 		str8_stream_print_byte(s, ' ');
    962 		str8_stream_print_byte(s, '|');
    963 	}
    964 	str8_stream_print_byte(s, '\n');
    965 }
    966 
    967 function b32
    968 is_elf(str8 file)
    969 {
    970 	b32 result = file.len >= 16;
    971 	if (result) {
    972 		result &= file.data[0] == 0x7F;
    973 		result &= file.data[1] == 'E';
    974 		result &= file.data[2] == 'L';
    975 		result &= file.data[3] == 'F';
    976 	}
    977 	return result;
    978 }
    979 
    980 function b32
    981 elf_header_from_file(ELFHeader *eh, str8 file)
    982 {
    983 	b32 result = 0;
    984 	if (is_elf(file)) {
    985 		eh->format      = file.data[4];
    986 		eh->endianness  = file.data[5];
    987 		eh->abi         = file.data[7];
    988 		eh->abi_version = file.data[8];
    989 
    990 		str8_stream reader = str8_reader_from_str8(str8_chop_at(file, 16));
    991 		b32 big_endian     = eh->endianness == EEK_BIG;
    992 		eh->kind                            = str8_read_u16(&reader, big_endian);
    993 		eh->machine                         = str8_read_u16(&reader, big_endian);
    994 		eh->version                         = str8_read_u32(&reader, big_endian);
    995 		if (eh->format == EF_64) {
    996 			eh->entry_point_offset      = str8_read_u64(&reader, big_endian);
    997 			eh->program_header_offset   = str8_read_u64(&reader, big_endian);
    998 			eh->section_header_offset   = str8_read_u64(&reader, big_endian);
    999 		} else {
   1000 			eh->entry_point_offset      = str8_read_u32(&reader, big_endian);
   1001 			eh->program_header_offset   = str8_read_u32(&reader, big_endian);
   1002 			eh->section_header_offset   = str8_read_u32(&reader, big_endian);
   1003 		}
   1004 		eh->flags                           = str8_read_u32(&reader, big_endian);
   1005 		eh->elf_header_size                 = str8_read_u16(&reader, big_endian);
   1006 		eh->program_header_entry_size       = str8_read_u16(&reader, big_endian);
   1007 		eh->program_header_count            = str8_read_u16(&reader, big_endian);
   1008 		eh->section_header_entry_size       = str8_read_u16(&reader, big_endian);
   1009 		eh->section_header_count            = str8_read_u16(&reader, big_endian);
   1010 		eh->section_header_name_table_index = str8_read_u16(&reader, big_endian);
   1011 		result = !reader.errors && file.data[6] == eh->version;
   1012 	}
   1013 	return result;
   1014 }
   1015 
   1016 function void
   1017 elf_section_header32_from_str8(ELFSectionHeader *sh, str8 s, b32 big_endian)
   1018 {
   1019 	assert(s.len >= 0x28);
   1020 	u8 *data = s.data;
   1021 	if (big_endian) {
   1022 		#define X(ctype, r32, r64, name) \
   1023 			sh->name = r32 ##_host_from_be(data); data += sizeof(r32);
   1024 		ELF_SECTION_HEADER_MEMBERS
   1025 		#undef X
   1026 	} else {
   1027 		#define X(ctype, r32, r64, name) \
   1028 			sh->name = r32 ##_host_from_le(data); data += sizeof(r32);
   1029 		ELF_SECTION_HEADER_MEMBERS
   1030 		#undef X
   1031 	}
   1032 }
   1033 
   1034 function void
   1035 elf_section_header64_from_str8(ELFSectionHeader *sh, str8 s, b32 big_endian)
   1036 {
   1037 	assert(s.len >= 0x40);
   1038 	u8 *data = s.data;
   1039 	if (big_endian) {
   1040 		#define X(ctype, r32, r64, name) \
   1041 			sh->name = r64 ##_host_from_be(data); data += sizeof(r64);
   1042 		ELF_SECTION_HEADER_MEMBERS
   1043 		#undef X
   1044 	} else {
   1045 		#define X(ctype, r32, r64, name) \
   1046 			sh->name = r64 ##_host_from_le(data); data += sizeof(r64);
   1047 		ELF_SECTION_HEADER_MEMBERS
   1048 		#undef X
   1049 	}
   1050 }
   1051 
   1052 function b32
   1053 elf_section_header_from_str8(ELFSectionHeader *sh, str8 s, b32 big_endian, b32 is32bit)
   1054 {
   1055 	b32 result = s.len >= 0x40 || (is32bit && s.len >= 0x28);
   1056 	if (result) {
   1057 		if (is32bit) elf_section_header32_from_str8(sh, s, big_endian);
   1058 		else         elf_section_header64_from_str8(sh, s, big_endian);
   1059 	}
   1060 	return result;
   1061 }
   1062 
   1063 function ELFSectionHeader *
   1064 elf_extract_section_headers(Arena *a, str8 file, ELFHeader *eh)
   1065 {
   1066 	/* TODO(rnp): */
   1067 	assert(file.len >= eh->section_header_offset + eh->section_header_entry_size * eh->section_header_count);
   1068 
   1069 	u32 sections = eh->section_header_count;
   1070 	ELFSectionHeader *result = push_array(a, ELFSectionHeader, sections);
   1071 	for (u32 i = 0; i < sections; i++) {
   1072 		sz offset = eh->section_header_offset + eh->section_header_entry_size * i;
   1073 		elf_section_header_from_str8(result + i, str8_chop_at(file, offset),
   1074 		                             eh->endianness == EEK_BIG, eh->format == EF_32);
   1075 	}
   1076 
   1077 	u8 *str_tab = file.data + result[eh->section_header_name_table_index].offset;
   1078 	for (u32 i = 0; i < sections; i++)
   1079 		result[i].name = str8_from_c_str(str_tab + result[i].name_table_offset);
   1080 
   1081 	return result;
   1082 }
   1083 
   1084 function ELFSection
   1085 elf_lookup_section(str8 name, str8 file, ELFSectionHeader *shs, u32 sections_count)
   1086 {
   1087 	ELFSection result = {0};
   1088 	for (u32 i = 0; i < sections_count; i++) {
   1089 		if (str8_equal(shs[i].name, name)) {
   1090 			result.header     = shs[i];
   1091 			result.store.data = file.data + shs[i].offset;
   1092 			result.store.len  = shs[i].size;
   1093 			break;
   1094 		}
   1095 	}
   1096 	return result;
   1097 }
   1098 
   1099 function void
   1100 dwarf_read_unit_header(str8_stream *store, DWARFUnitHeader *duh)
   1101 {
   1102 	/* TODO(rnp): context containing endianess, dwarf size */
   1103 	u32 test_length = str8_read_u32(store, 0);
   1104 	duh->dwarf_64 = test_length == 0xffffffff;
   1105 	if (duh->dwarf_64) duh->length = str8_read_u64(store, 0);
   1106 	else               duh->length = test_length;
   1107 	duh->version = str8_read_u16(store, 0);
   1108 	if (duh->version == 5) duh->kind = str8_read_u8(store);
   1109 	duh->address_size = str8_read_u8(store);
   1110 	if (duh->dwarf_64) duh->abbreviation_offset = str8_read_u64(store, 0);
   1111 	else               duh->abbreviation_offset = str8_read_u32(store, 0);
   1112 }
   1113 
   1114 function sz
   1115 dwarf_parse_abbrevation(Arena *a, DWARFAbbreviation *abbrv, str8 table)
   1116 {
   1117 	str8_stream table_reader = str8_reader_from_str8(table);
   1118 	sz table_start_size = table.len;
   1119 	abbrv->abbreviation_code = str8_read_uleb128(&table_reader);
   1120 	abbrv->kind              = str8_read_uleb128(&table_reader);
   1121 	if (table_reader.count - table_reader.index < 1)
   1122 		return table_start_size;
   1123 	abbrv->has_children = str8_read_u8(&table_reader);
   1124 	for (;;) {
   1125 		u64 attr_kind = str8_read_uleb128(&table_reader);
   1126 		u64 form_kind = str8_read_uleb128(&table_reader);
   1127 		TODO(form_kind != DFK_INDIRECT);
   1128 		TODO(form_kind != DFK_IMPLICIT_CONST);
   1129 		if (attr_kind) {
   1130 			*da_push(a, abbrv) = (DWARFAttribute){attr_kind, form_kind};
   1131 		} else {
   1132 			assert(form_kind == 0);
   1133 			break;
   1134 		}
   1135 	}
   1136 	return table_reader.index;
   1137 }
   1138 
   1139 function DWARFAbbreviation
   1140 dwarf_lookup_abbreviation(Arena *a, str8 table, u64 key)
   1141 {
   1142 	DWARFAbbreviation result = {0};
   1143 	while (key != result.abbreviation_code && table.len > 1) {
   1144 		result.count = 0;
   1145 		table = str8_chop_at(table, dwarf_parse_abbrevation(a, &result, table));
   1146 	}
   1147 	return result;
   1148 }
   1149 
   1150 function Table
   1151 table_new(Arena *arena, u16 column_count, u16 reserved_rows)
   1152 {
   1153 	Table result = {0};
   1154 	result.max_column_widths = push_array(arena, typeof(*result.max_column_widths), column_count);
   1155 	result.columns           = column_count;
   1156 	assert(reserved_rows < 0xFFFF / column_count);
   1157 	result.data = da_reserve(arena, &result, reserved_rows * column_count);
   1158 	return result;
   1159 }
   1160 
   1161 #define table_push_row(t, a, ...) \
   1162 	table_push_row_(t, a, (str8 []){__VA_ARGS__}, sizeof((str8 []){__VA_ARGS__}) / (sizeof(str8)))
   1163 function void
   1164 table_push_row_(Table *table, Arena *arena, str8 *cells, sz cells_count)
   1165 {
   1166 	assert(table->columns == cells_count);
   1167 	for (sz i = 0; i < cells_count; i++) {
   1168 		table->max_column_widths[i] = MAX(table->max_column_widths[i], cells[i].len);
   1169 		str8 *out  = da_push(arena, table);
   1170 		str8  cell = cells[i];
   1171 		*out = cell;
   1172 	}
   1173 }
   1174 
   1175 function void
   1176 print_section_table(Arena arena, ELFSectionHeader *sections, u32 section_count)
   1177 {
   1178 	str8 header_row[] = {str8("name"), str8("kind"), str8("size"), str8("offset"), str8("flags"), str8("align")};
   1179 	Table table = table_new(&arena, countof(header_row), section_count);
   1180 	for (u32 i = 0; i < countof(header_row); i++)
   1181 		table.max_column_widths[i] = header_row[i].len;
   1182 
   1183 	for (u32 i = 0; i < section_count; i++) {
   1184 		if (sections[i].size) {
   1185 			str8_stream sb = str8_stream_from_arena(arena);
   1186 			str8 name = sections[i].name;
   1187 
   1188 			str8_stream_print_elf_section_header_kind(&sb, sections[i].kind);
   1189 			str8 kind = arena_commit_str8_stream(&arena, &sb);
   1190 
   1191 			str8_stream_print_u64(&sb, sections[i].size);
   1192 			str8 size = arena_commit_str8_stream(&arena, &sb);
   1193 
   1194 			str8_stream_print_str8(&sb, str8("0x"));
   1195 			str8_stream_print_u64_hex(&sb, sections[i].offset);
   1196 			str8 offset = arena_commit_str8_stream(&arena, &sb);
   1197 
   1198 			str8_stream_print_str8(&sb, str8("0x"));
   1199 			str8_stream_print_u64_hex(&sb, sections[i].flags);
   1200 			str8 flags = arena_commit_str8_stream(&arena, &sb);
   1201 
   1202 			str8_stream_print_u64(&sb, sections[i].addralign);
   1203 			str8 align = arena_commit_str8_stream(&arena, &sb);
   1204 
   1205 			table_push_row(&table, &arena, name, kind, size, offset, flags, align);
   1206 		}
   1207 	}
   1208 
   1209 	str8_stream sb = str8_stream_from_arena(arena);
   1210 	print_table_line_marker(&table, &sb);
   1211 	print_table_row(&table, header_row, &sb);
   1212 	print_table_line_marker(&table, &sb);
   1213 	for (s16 index = 0; index < table.count; index += table.columns)
   1214 		print_table_row(&table, table.data + index, &sb);
   1215 	print_table_line_marker(&table, &sb);
   1216 	fwrite(sb.data, 1, sb.index, stdout);
   1217 	fflush(stdout);
   1218 }
   1219 
   1220 function b32
   1221 elf_inspect_file(Arena arena, str8 file, Options *options)
   1222 {
   1223 	b32 result = is_elf(file);
   1224 	if (result) {
   1225 		ELFHeader header = {0};
   1226 		if (!elf_header_from_file(&header, file)) {
   1227 			return 1;
   1228 		}
   1229 
   1230 		if (options->header)
   1231 			print_elf_header(&header);
   1232 
   1233 		ELFSectionHeader *sections = elf_extract_section_headers(&arena, file, &header);
   1234 
   1235 		if (options->sections)
   1236 			print_section_table(arena, sections, header.section_header_count);
   1237 
   1238 		#if 0 /* TODO(rnp): fix this. it broke when da_reserve was fixed (i.e. it was always busted)*/
   1239 		ELFSection debug_info  = elf_lookup_section(str8(".debug_info"), file,
   1240 		                                            sections, header.section_header_count);
   1241 		ELFSection debug_abbrv = elf_lookup_section(str8(".debug_abbrev"), file,
   1242 		                                            sections, header.section_header_count);
   1243 		ELFSection debug_str_offsets = elf_lookup_section(str8(".debug_str_offsets"), file,
   1244 		                                                  sections, header.section_header_count);
   1245 		ELFSection debug_str = elf_lookup_section(str8(".debug_str"), file,
   1246 		                                          sections, header.section_header_count);
   1247 
   1248 		/* TODO(rnp): cleanup */
   1249 		if (debug_info.store.len == 0) {
   1250 			printf("No Debug Info!\n");
   1251 			return 0;
   1252 		}
   1253 
   1254 		str8_stream d_info_reader = str8_reader_from_str8(debug_info.store);
   1255 		DWARFUnitHeader d_info_header = {0};
   1256 		dwarf_read_unit_header(&d_info_reader, &d_info_header);
   1257 		str8 abbreviation_table = str8_chop_at(debug_abbrv.store, d_info_header.abbreviation_offset);
   1258 		u64  abbreviation_code  = str8_read_uleb128(&d_info_reader);
   1259 		DWARFAbbreviation abbrv = dwarf_lookup_abbreviation(&arena, abbreviation_table, abbreviation_code);
   1260 
   1261 		printf("\nFirst DWARF DIE:\n");
   1262 		for (s16 i = 0; i < abbrv.count; i++) {
   1263 			DWARFAttribute attr = abbrv.data[i];
   1264 			if (attr.kind == 0)
   1265 				continue;
   1266 
   1267 			switch (attr.kind) {
   1268 			case DATK_PRODUCER:      printf("producer:        "); break;
   1269 			case DATK_LANGUAGE:      printf("language:        "); break;
   1270 			case DATK_ADDR_BASE:     printf("addr base:       "); break;
   1271 			case DATK_LOCLISTS_BASE: printf("loc list base:   "); break;
   1272 			case DATK_RNGLISTS_BASE: printf("range list base: "); break;
   1273 			case DATK_NAME:          printf("name:            "); break;
   1274 			default: assert(0); break;
   1275 			}
   1276 
   1277 			switch (attr.form_kind) {
   1278 			case DFK_STRX1: {
   1279 				u32 str_offset_offset = str8_read_u8(&d_info_reader);
   1280 				u32 str_offset;
   1281 				if (header.endianness == EEK_BIG)
   1282 					str_offset = u32_host_from_be(debug_str_offsets.store.data + str_offset_offset);
   1283 				else
   1284 					str_offset = u32_host_from_le(debug_str_offsets.store.data + str_offset_offset);
   1285 				printf("%s", (char *)debug_str.store.data + str_offset);
   1286 			} break;
   1287 			case DFK_DATA2: {
   1288 				u32 data = str8_read_u16(&d_info_reader, header.endianness == EEK_BIG);
   1289 				printf("%u", data);
   1290 			} break;
   1291 			case DFK_SEC_OFFSET: {
   1292 				u32 data = str8_read_u32(&d_info_reader, header.endianness == EEK_BIG);
   1293 				printf("%u", data);
   1294 			} break;
   1295 			default: assert(0); break;
   1296 			}
   1297 			printf("\n");
   1298 		}
   1299 		#endif
   1300 
   1301 		result = 1;
   1302 	}
   1303 
   1304 	return result;
   1305 }
   1306 
   1307 function Options
   1308 parse_command_line(Arena *arena, s32 argc, char *argv[])
   1309 {
   1310 	Options result = {0};
   1311 
   1312 	#define shift(c, v) ((c)--, *(v)++)
   1313 
   1314 	char *c_arg  = shift(argc, argv);
   1315 	result.argv0 = (u8 *)c_arg;
   1316 	while (argc) {
   1317 		c_arg = shift(argc, argv);
   1318 		str8 arg = str8_from_c_str((u8 *)c_arg);
   1319 		if (str8_equal(arg, str8("--sections"))) {
   1320 			result.sections = 1;
   1321 		} else if (str8_equal(arg, str8("--header"))) {
   1322 			result.header = 1;
   1323 		} else {
   1324 			*da_push(arena, &result.file_names) = (u8 *)c_arg;
   1325 		}
   1326 	}
   1327 
   1328 	#undef shift
   1329 
   1330 	return result;
   1331 }
   1332 
   1333 function b32
   1334 elfinspect(Arena arena, s32 argc, char *argv[])
   1335 {
   1336 	Options options = parse_command_line(&arena, argc, argv);
   1337 
   1338 	b32 result = 1;
   1339 	for (s32 file_index = 0; file_index < options.file_names.count; file_index++) {
   1340 		u8 *file_name = options.file_names.data[file_index];
   1341 		str8 file     = os_map_file(file_name);
   1342 		if (!file.len) fprintf(stderr, "failed to open file: %s\n", file_name);
   1343 		result &= elf_inspect_file(arena, file, &options);
   1344 	}
   1345 
   1346 	return result;
   1347 }