Skip to content

Commit fe34b5b

Browse files
committed
fix(tools): bininfo hotfix
1 parent d4a388d commit fe34b5b

1 file changed

Lines changed: 91 additions & 121 deletions

File tree

tools/bininfo.zig

Lines changed: 91 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
const std = @import("std");
3333
const 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.
9678
fn 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

279261
fn 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

Comments
 (0)