3131
3232const std = @import ("std" );
3333const dwarf = std .dwarf ;
34+ const elf = std .elf ;
3435
3536// ── helpers ──────────────────────────────────────────────────────────────────
3637
@@ -45,34 +46,15 @@ fn readU32Le(data: []const u8, off: usize) u32 {
4546 (@as (u32 , data [off + 3 ]) << 24 );
4647}
4748
48- // ── ELF constants ─────────────────────────────────────────────────────────────
49-
50- const SHT_NULL : u32 = 0 ;
51- const SHT_SYMTAB : u32 = 2 ;
52- const SHT_STRTAB : u32 = 3 ;
53- const SHT_NOBITS : u32 = 8 ;
54- const SHT_DYNSYM : u32 = 11 ;
55-
56- const SHF_WRITE : u32 = 0x1 ;
57- const SHF_ALLOC : u32 = 0x2 ;
58- const SHF_EXECINSTR : u32 = 0x4 ;
59- const SHF_MERGE : u32 = 0x10 ;
60- const SHF_STRINGS : u32 = 0x20 ;
61- const SHF_LINK_ORDER : u32 = 0x80 ;
62- const SHF_GROUP : u32 = 0x200 ;
63- const SHF_TLS : u32 = 0x400 ;
64-
65- const SHN_UNDEF : u16 = 0 ;
66- const SHN_ABS : u16 = 0xFFF1 ;
67- const SHN_COMMON : u16 = 0xFFF2 ;
68-
69- const STB_LOCAL : u8 = 0 ;
70- const STB_GLOBAL : u8 = 1 ;
71- const STB_WEAK : u8 = 2 ;
72-
73- fn elfMachName (m : u16 ) []const u8 {
74- return switch (m ) {
75- 0x1966 = > "MOS6502" , // EM_MOS (llvm-mos)
49+ fn readVal (comptime T : type , data : []const u8 , offset : usize ) T {
50+ var v : T = undefined ;
51+ @memcpy (std .mem .asBytes (& v ), data [offset .. ][0.. @sizeOf (T )]);
52+ return v ;
53+ }
54+
55+ fn elfMachName (m : elf.EM ) []const u8 {
56+ return switch (@intFromEnum (m )) {
57+ 0x1966 = > "MOS6502" ,
7658 3 = > "x86" ,
7759 0x3E = > "x86-64" ,
7860 0xB7 = > "AArch64" ,
@@ -81,29 +63,29 @@ fn elfMachName(m: u16) []const u8 {
8163 };
8264}
8365
84- fn elfTypeName (t : u16 ) []const u8 {
66+ fn elfTypeName (t : elf.ET ) []const u8 {
8567 return switch (t ) {
86- 0 = > "NONE" ,
87- 1 = > "REL" ,
88- 2 = > "EXEC" ,
89- 3 = > "DYN" ,
90- 4 = > "CORE" ,
68+ .NONE = > "NONE" ,
69+ .REL = > "REL" ,
70+ .EXEC = > "EXEC" ,
71+ .DYN = > "DYN" ,
72+ .CORE = > "CORE" ,
9173 else = > "?" ,
9274 };
9375}
9476
9577// nm-style type character derived from section attributes and symbol binding.
9678fn nmTypeChar (sh_type : u32 , sh_flags : u32 , shndx : u16 , binding : u8 ) u8 {
97- const is_local = binding == STB_LOCAL ;
98- const is_weak = binding == STB_WEAK ;
99- if (shndx == SHN_UNDEF ) return if (is_weak ) 'w' else 'U' ;
100- if (shndx == SHN_ABS ) return if (is_local ) 'a' else 'A' ;
101- if (shndx == SHN_COMMON ) return 'C' ;
102- const c : u8 = if (sh_type == SHT_NOBITS and (sh_flags & SHF_ALLOC ) != 0 )
79+ const is_local = binding == elf . STB_LOCAL ;
80+ const is_weak = binding == elf . STB_WEAK ;
81+ if (shndx == elf . SHN_UNDEF ) return if (is_weak ) 'w' else 'U' ;
82+ if (shndx == elf . SHN_ABS ) return if (is_local ) 'a' else 'A' ;
83+ if (shndx == elf . SHN_COMMON ) return 'C' ;
84+ const c : u8 = if (sh_type == elf . SHT_NOBITS and (sh_flags & elf . SHF_ALLOC ) != 0 )
10385 'b' // BSS
104- else if ((sh_flags & SHF_EXECINSTR ) != 0 )
86+ else if ((sh_flags & elf . SHF_EXECINSTR ) != 0 )
10587 't' // text
106- else if ((sh_flags & SHF_WRITE ) != 0 )
88+ else if ((sh_flags & elf . SHF_WRITE ) != 0 )
10789 'd' // data
10890 else
10991 'r' ; // read-only
@@ -277,7 +259,7 @@ fn dumpDebugLine(out: anytype, debug_line: []const u8) void {
277259// ── ELF inspector ─────────────────────────────────────────────────────────────
278260
279261fn checkElf (out : anytype , gpa : std.mem.Allocator , path : []const u8 , data : []const u8 , opts : Opts ) bool {
280- if (data .len < 52 ) {
262+ if (data .len < @sizeOf ( elf . Elf32_Ehdr ) ) {
281263 out .print ("{s}: [ELF] ERROR: header truncated ({d} B)\n " , .{ path , data .len }) catch {};
282264 return false ;
283265 }
@@ -292,13 +274,14 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
292274 return true ;
293275 }
294276
295- const e_type = readU16Le (data , 16 );
296- const e_machine = readU16Le (data , 18 );
297- const e_entry = readU32Le (data , 24 );
298- const e_shoff = readU32Le (data , 32 );
299- const e_shentsize = readU16Le (data , 46 );
300- const e_shnum = readU16Le (data , 48 );
301- const e_shstrndx = readU16Le (data , 50 );
277+ const ehdr = readVal (elf .Elf32_Ehdr , data , 0 );
278+ const e_type = ehdr .e_type ; // elf.ET
279+ const e_machine = ehdr .e_machine ; // elf.EM
280+ const e_entry = ehdr .e_entry ;
281+ const e_shoff = ehdr .e_shoff ;
282+ const e_shentsize = ehdr .e_shentsize ;
283+ const e_shnum = ehdr .e_shnum ;
284+ const e_shstrndx = ehdr .e_shstrndx ;
302285
303286 if (e_shoff == 0 or e_shentsize < 40 or
304287 @as (usize , e_shoff ) + @as (usize , e_shnum ) * e_shentsize > data .len )
@@ -309,18 +292,10 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
309292 return true ;
310293 }
311294
312- // Build a helper: return a slice of a section header's bytes.
313- const shdr = struct {
314- fn get (d : []const u8 , shoff : u32 , shentsz : u16 , idx : u16 ) []const u8 {
315- const off : usize = shoff + @as (usize , idx ) * shentsz ;
316- return d [off .. off + shentsz ];
317- }
318- };
319-
320295 // Locate shstrtab for section name lookup.
321- const shstr_hdr = shdr . get ( data , e_shoff , e_shentsize , e_shstrndx );
322- const shstr_off = readU32Le ( shstr_hdr , 16 ) ;
323- const shstr_size = readU32Le ( shstr_hdr , 20 ) ;
296+ const shstr_hdr = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + @as ( usize , e_shstrndx ) * e_shentsize );
297+ const shstr_off = shstr_hdr . sh_offset ;
298+ const shstr_size = shstr_hdr . sh_size ;
324299 const shstr : []const u8 = if (shstr_off + shstr_size <= data .len )
325300 data [shstr_off .. shstr_off + shstr_size ]
326301 else
@@ -329,8 +304,8 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
329304 // Count allocatable sections (for the summary).
330305 var alloc_count : u16 = 0 ;
331306 for (0.. e_shnum ) | i | {
332- const sh = shdr . get ( data , e_shoff , e_shentsize , @truncate ( i ) );
333- if ((readU32Le ( sh , 8 ) & SHF_ALLOC ) != 0 ) alloc_count += 1 ;
307+ const sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + i * e_shentsize );
308+ if ((sh . sh_flags & elf . SHF_ALLOC ) != 0 ) alloc_count += 1 ;
334309 }
335310
336311 // Find .symtab; fall back to .dynsym for stripped binaries.
@@ -340,14 +315,14 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
340315 var sym_local_end : u32 = 0 ;
341316 var found_symtab = false ;
342317 for (0.. e_shnum ) | i | {
343- const sh = shdr . get ( data , e_shoff , e_shentsize , @truncate ( i ) );
344- const sht = readU32Le ( sh , 4 ) ;
345- if (sht == SHT_SYMTAB or (sht == SHT_DYNSYM and ! found_symtab )) {
346- symtab_off = readU32Le ( sh , 16 ) ;
347- symtab_size = readU32Le ( sh , 20 ) ;
348- symtab_stridx = readU32Le ( sh , 24 ) ;
349- sym_local_end = readU32Le ( sh , 28 ) ; // sh_info = one past last LOCAL
350- if (sht == SHT_SYMTAB ) {
318+ const sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + i * e_shentsize );
319+ const sht = sh . sh_type ;
320+ if (sht == elf . SHT_SYMTAB or (sht == elf . SHT_DYNSYM and ! found_symtab )) {
321+ symtab_off = sh . sh_offset ;
322+ symtab_size = sh . sh_size ;
323+ symtab_stridx = sh . sh_link ;
324+ sym_local_end = sh . sh_info ; // sh_info = one past last LOCAL
325+ if (sht == elf . SHT_SYMTAB ) {
351326 found_symtab = true ;
352327 break ;
353328 }
@@ -357,14 +332,14 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
357332 // Locate symbol string table.
358333 var strtab_data : []const u8 = &[_ ]u8 {};
359334 if (symtab_stridx < e_shnum ) {
360- const sh = shdr . get ( data , e_shoff , e_shentsize , @truncate ( symtab_stridx ));
361- const st_off = readU32Le ( sh , 16 ) ;
362- const st_sz = readU32Le ( sh , 20 ) ;
335+ const sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + @as ( usize , symtab_stridx ) * e_shentsize );
336+ const st_off = sh . sh_offset ;
337+ const st_sz = sh . sh_size ;
363338 if (st_off + st_sz <= data .len )
364339 strtab_data = data [st_off .. st_off + st_sz ];
365340 }
366341
367- const sym_count = if (symtab_size >= 16 ) symtab_size / 16 else 0 ;
342+ const sym_count = if (symtab_size >= @sizeOf ( elf . Elf32_Sym )) symtab_size / @sizeOf ( elf . Elf32_Sym ) else 0 ;
368343
369344 // One-line summary — always printed.
370345 out .print ("{s}: [ELF32 {s}] type={s} entry=${x:0>4} alloc-sections={d} symbols={d}\n " , .{
@@ -376,13 +351,13 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
376351 out .print (" -- Sections " ++ "-" ** 42 ++ "\n " , .{}) catch {};
377352 out .print (" {s:<22} {s:<10} {s:>8} {s}\n " , .{ "Name" , "Type" , "Size" , "Flags/VMA" }) catch {};
378353 for (0.. e_shnum ) | i | {
379- const sh = shdr . get ( data , e_shoff , e_shentsize , @truncate ( i ) );
380- const sh_type = readU32Le ( sh , 4 ) ;
381- if (sh_type == SHT_NULL ) continue ;
382- const sh_flags = readU32Le ( sh , 8 ) ;
383- const sh_addr = readU32Le ( sh , 12 ) ;
384- const sh_size = readU32Le ( sh , 20 ) ;
385- const sh_name_off = readU32Le ( sh , 0 ) ;
354+ const sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + i * e_shentsize );
355+ const sh_type = sh . sh_type ;
356+ if (sh_type == elf . SHT_NULL ) continue ;
357+ const sh_flags = sh . sh_flags ;
358+ const sh_addr = sh . sh_addr ;
359+ const sh_size = sh . sh_size ;
360+ const sh_name_off = sh . sh_name ;
386361
387362 const name : []const u8 = if (sh_name_off < shstr .len ) blk : {
388363 const start = sh_name_off ;
@@ -393,44 +368,44 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
393368
394369 var fbuf : [12 ]u8 = undefined ;
395370 var flen : usize = 0 ;
396- if ((sh_flags & SHF_ALLOC ) != 0 ) {
371+ if ((sh_flags & elf . SHF_ALLOC ) != 0 ) {
397372 fbuf [flen ] = 'A' ;
398373 flen += 1 ;
399374 }
400- if ((sh_flags & SHF_EXECINSTR ) != 0 ) {
375+ if ((sh_flags & elf . SHF_EXECINSTR ) != 0 ) {
401376 fbuf [flen ] = 'X' ;
402377 flen += 1 ;
403378 }
404- if ((sh_flags & SHF_WRITE ) != 0 ) {
379+ if ((sh_flags & elf . SHF_WRITE ) != 0 ) {
405380 fbuf [flen ] = 'W' ;
406381 flen += 1 ;
407382 }
408- if (sh_type == SHT_NOBITS ) {
383+ if (sh_type == elf . SHT_NOBITS ) {
409384 fbuf [flen ] = 'B' ;
410385 flen += 1 ;
411386 }
412- if ((sh_flags & SHF_MERGE ) != 0 ) {
387+ if ((sh_flags & elf . SHF_MERGE ) != 0 ) {
413388 fbuf [flen ] = 'M' ;
414389 flen += 1 ;
415390 }
416- if ((sh_flags & SHF_STRINGS ) != 0 ) {
391+ if ((sh_flags & elf . SHF_STRINGS ) != 0 ) {
417392 fbuf [flen ] = 'S' ;
418393 flen += 1 ;
419394 }
420- if ((sh_flags & SHF_TLS ) != 0 ) {
395+ if ((sh_flags & elf . SHF_TLS ) != 0 ) {
421396 fbuf [flen ] = 'T' ;
422397 flen += 1 ;
423398 }
424- if ((sh_flags & SHF_GROUP ) != 0 ) {
399+ if ((sh_flags & elf . SHF_GROUP ) != 0 ) {
425400 fbuf [flen ] = 'G' ;
426401 flen += 1 ;
427402 }
428- if ((sh_flags & SHF_LINK_ORDER ) != 0 ) {
403+ if ((sh_flags & elf . SHF_LINK_ORDER ) != 0 ) {
429404 fbuf [flen ] = 'l' ;
430405 flen += 1 ;
431406 }
432407
433- if ((sh_flags & SHF_ALLOC ) != 0 ) {
408+ if ((sh_flags & elf . SHF_ALLOC ) != 0 ) {
434409 out .print (" {s:<22} {s:<10} {d:>8}B {s} @${x:0>4}\n " , .{
435410 name , shtypeName (sh_type ), sh_size , fbuf [0.. flen ], sh_addr ,
436411 }) catch {};
@@ -455,46 +430,41 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
455430 var printed : usize = 0 ;
456431
457432 for (0.. sym_count ) | si | {
458- const sym = data [sym_base + si * 16 .. sym_base + si * 16 + 16 ];
459- const st_name = readU32Le (sym , 0 );
460- const st_value = readU32Le (sym , 4 );
461- const st_info = sym [12 ];
462- const st_shndx : u16 = readU16Le (sym , 14 );
463- const binding : u8 = st_info >> 4 ;
464- const stype : u8 = st_info & 0xF ;
465-
466- // Skip FILE/SECTION symbols and empty names.
467- if (stype == 3 or stype == 4 ) continue ;
468- if (! show_locals and binding == STB_LOCAL ) continue ;
469-
470- const name : []const u8 = if (st_name < strtab_data .len ) blk : {
471- var end = st_name ;
433+ const sym = readVal (elf .Elf32_Sym , data , sym_base + si * @sizeOf (elf .Elf32_Sym ));
434+ if (sym .st_type () == elf .STT_SECTION or sym .st_type () == elf .STT_FILE ) continue ;
435+ const binding : u8 = @as (u8 , sym .st_bind ());
436+
437+ // Skip local symbols unless object is small.
438+ if (! show_locals and binding == elf .STB_LOCAL ) continue ;
439+
440+ const name : []const u8 = if (sym .st_name < strtab_data .len ) blk : {
441+ var end = sym .st_name ;
472442 while (end < strtab_data .len and strtab_data [end ] != 0 ) end += 1 ;
473- if (end == st_name ) continue ; // empty name
474- break :blk strtab_data [st_name .. end ];
443+ if (end == sym . st_name ) continue ; // empty name
444+ break :blk strtab_data [sym . st_name .. end ];
475445 } else continue ;
476446
477447 // Look up the section's type and flags for this symbol.
478448 var sh_type_for_sym : u32 = 0 ;
479449 var sh_flags_for_sym : u32 = 0 ;
480- if (st_shndx != SHN_UNDEF and st_shndx != SHN_ABS and st_shndx != SHN_COMMON and
481- st_shndx < e_shnum )
450+ if (sym . st_shndx != elf . SHN_UNDEF and sym . st_shndx != elf . SHN_ABS and sym . st_shndx != elf . SHN_COMMON and
451+ sym . st_shndx < e_shnum )
482452 {
483- const sym_sh = shdr . get ( data , e_shoff , e_shentsize , st_shndx );
484- sh_type_for_sym = readU32Le ( sym_sh , 4 ) ;
485- sh_flags_for_sym = readU32Le ( sym_sh , 8 ) ;
453+ const sym_sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + @as ( usize , sym . st_shndx ) * e_shentsize );
454+ sh_type_for_sym = sym_sh . sh_type ;
455+ sh_flags_for_sym = sym_sh . sh_flags ;
486456 }
487457
488- const tc = nmTypeChar (sh_type_for_sym , sh_flags_for_sym , st_shndx , binding );
489- out .print (" ${x:0>4} {c} {s}\n " , .{ st_value , tc , name }) catch {};
458+ const tc = nmTypeChar (sh_type_for_sym , sh_flags_for_sym , sym . st_shndx , binding );
459+ out .print (" ${x:0>4} {c} {s}\n " , .{ sym . st_value , tc , name }) catch {};
490460 printed += 1 ;
491461 }
492462 if (printed == 0 )
493463 out .print (" (no symbols — binary may be stripped)\n " , .{}) catch {};
494464 }
495465
496466 // ── MOS DWARF inventory ───────────────────────────────────────────────
497- if (opts .dwarf and e_machine == 0x1966 ) {
467+ if (opts .dwarf and @intFromEnum ( e_machine ) == 0x1966 ) {
498468 const dwarf_names = [_ ][]const u8 {
499469 ".debug_info" ,
500470 ".debug_abbrev" ,
@@ -510,17 +480,17 @@ fn checkElf(out: anytype, gpa: std.mem.Allocator, path: []const u8, data: []cons
510480 var dwarf_sizes = [_ ]u32 {0 } ** dwarf_names .len ;
511481
512482 for (0.. e_shnum ) | i | {
513- const sh = shdr . get ( data , e_shoff , e_shentsize , @truncate ( i ) );
514- if ((readU32Le ( sh , 8 ) & SHF_ALLOC ) != 0 ) continue ; // DWARF is non-ALLOC
515- const name_off = readU32Le ( sh , 0 ) ;
483+ const sh = readVal ( elf . Elf32_Shdr , data , @as ( usize , e_shoff ) + i * e_shentsize );
484+ if ((sh . sh_flags & elf . SHF_ALLOC ) != 0 ) continue ; // DWARF is non-ALLOC
485+ const name_off = sh . sh_name ;
516486 if (name_off >= shstr .len ) continue ;
517487 var name_end = name_off ;
518488 while (name_end < shstr .len and shstr [name_end ] != 0 ) name_end += 1 ;
519489 const sec_name = shstr [name_off .. name_end ];
520490 for (dwarf_names , 0.. ) | dn , di | {
521491 if (std .mem .eql (u8 , sec_name , dn )) {
522- dwarf_offsets [di ] = readU32Le ( sh , 16 ) ;
523- dwarf_sizes [di ] = readU32Le ( sh , 20 ) ;
492+ dwarf_offsets [di ] = sh . sh_offset ;
493+ dwarf_sizes [di ] = sh . sh_size ;
524494 break ;
525495 }
526496 }
0 commit comments