Something went wrong. Try again.
A fork of https://github.com/kubkon/zelf.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444arena: Allocator,data: []const u8,path: []const u8,opts: @import("main.zig").Options,
header: elf.Elf64_Ehdr = undefined,shdrs: std.ArrayList(elf.Elf64_Shdr) = .empty,phdrs: std.ArrayList(elf.Elf64.Phdr) = .empty,shstrtab: []const u8 = &[0]u8{},
symtab_index: ?u32 = null,symtab: std.ArrayList(elf.Elf64_Sym) = .empty,strtab: []const u8 = &[0]u8{},
dynamic_index: ?u32 = null,
dynsymtab_index: ?u32 = null,dynsymtab: std.ArrayList(elf.Elf64_Sym) = .empty,dynstrtab: []const u8 = &[0]u8{},
versymtab_index: ?u32 = null,versymtab: std.ArrayList(elf.Versym) = .empty,
verdef_index: ?u32 = null,verdefsyms: std.ArrayList(VersionSym(elf.Verdef)) = .empty,/// Lookup to verdefsyms.verdefsyms_lookup: std.AutoHashMapUnmanaged(elf.VER_NDX, u32) = .empty,verdefaux: std.ArrayList(VersionSymAux(elf.Verdaux)) = .empty,
verneed_index: ?u32 = null,verneedsyms: std.ArrayList(VersionSym(elf.Elf64_Verneed)) = .empty,/// Lookup to verneedaux.verneedsyms_lookup: std.AutoHashMapUnmanaged(u32, u32) = .empty,verneedaux: std.ArrayList(VersionSymAux(elf.Vernaux)) = .empty,
pub fn parse(self: *Object) !void { if (self.data.len < @sizeOf(elf.Elf64_Ehdr)) return error.InvalidMagic; if (!mem.eql(u8, self.data[0..4], "\x7fELF")) return error.InvalidMagic;
self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(self.data.ptr)).*;
if (self.is32Bit()) { const header = @as(*align(1) const elf.Elf32_Ehdr, @ptrCast(self.data.ptr)).*; @memcpy(&self.header.e_ident, &header.e_ident); self.header.e_type = header.e_type; self.header.e_machine = header.e_machine; self.header.e_version = header.e_version; self.header.e_entry = header.e_entry; self.header.e_phoff = header.e_phoff; self.header.e_shoff = header.e_shoff; self.header.e_flags = header.e_flags; self.header.e_ehsize = header.e_ehsize; self.header.e_phentsize = header.e_phentsize; self.header.e_phnum = header.e_phnum; self.header.e_shentsize = header.e_shentsize; self.header.e_shnum = header.e_shnum; self.header.e_shstrndx = header.e_shstrndx; }
try self.phdrs.ensureTotalCapacityPrecise(self.arena, self.header.e_phnum); for (0..self.header.e_phnum) |i| { const data = self.data[self.header.e_phoff + i * self.header.e_phentsize ..][0..self.header.e_phentsize]; const phdr: elf.Elf64.Phdr = if (self.is32Bit()) blk: { const phdr = @as(*align(1) const elf.Elf32.Phdr, @ptrCast(data)); break :blk .{ .type = phdr.type, .offset = phdr.offset, .vaddr = phdr.vaddr, .paddr = phdr.paddr, .filesz = phdr.filesz, .memsz = phdr.memsz, .flags = phdr.flags, .@"align" = phdr.@"align", }; } else @as(*align(1) const elf.Elf64.Phdr, @ptrCast(data)).*; self.phdrs.appendAssumeCapacity(phdr); }
const e_shnum = if (self.header.e_shnum == 0) blk: { if (self.is32Bit()) { const shdr = @as(*align(1) const elf.Elf32_Shdr, @ptrCast(self.data.ptr + self.header.e_shoff)); break :blk shdr.sh_size; } else { const shdr = @as(*align(1) const elf.Elf64_Shdr, @ptrCast(self.data.ptr + self.header.e_shoff)); break :blk shdr.sh_size; } } else self.header.e_shnum;
try self.shdrs.ensureTotalCapacityPrecise(self.arena, e_shnum); for (0..e_shnum) |i| { const data = self.data[self.header.e_shoff + i * self.header.e_shentsize ..][0..self.header.e_shentsize]; const shdr: elf.Elf64_Shdr = if (self.is32Bit()) blk: { const shdr = @as(*align(1) const elf.Elf32_Shdr, @ptrCast(data)); break :blk .{ .sh_name = shdr.sh_name, .sh_type = shdr.sh_type, .sh_flags = shdr.sh_flags, .sh_addr = shdr.sh_addr, .sh_offset = shdr.sh_offset, .sh_size = shdr.sh_size, .sh_link = shdr.sh_link, .sh_info = shdr.sh_info, .sh_addralign = shdr.sh_addralign, .sh_entsize = shdr.sh_entsize, }; } else @as(*align(1) const elf.Elf64_Shdr, @ptrCast(data)).*; self.shdrs.appendAssumeCapacity(shdr); }
const e_shstrndx = if (self.header.e_shstrndx == elf.SHN_HIRESERVE) blk: { if (self.is32Bit()) { const shdr = @as(*align(1) const elf.Elf32_Shdr, @ptrCast(self.data.ptr + self.header.e_shoff)); break :blk shdr.sh_link; } else { const shdr = @as(*align(1) const elf.Elf64_Shdr, @ptrCast(self.data.ptr + self.header.e_shoff)); break :blk shdr.sh_link; } } else self.header.e_shstrndx; self.shstrtab = self.getSectionContentsByIndex(e_shstrndx);
for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB, elf.SHT_DYNSYM => { const raw = self.getSectionContents(shdr); const sym_size: usize = if (self.is32Bit()) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); const nsyms = @divExact(raw.len, sym_size); switch (shdr.sh_type) { elf.SHT_SYMTAB => try self.symtab.ensureTotalCapacityPrecise(self.arena, nsyms), elf.SHT_DYNSYM => try self.dynsymtab.ensureTotalCapacityPrecise(self.arena, nsyms), else => unreachable, } for (0..nsyms) |si| { const data = raw[si * sym_size ..][0..sym_size]; const out_sym: elf.Elf64_Sym = if (self.is32Bit()) blk: { const sym = @as(*align(1) const elf.Elf32_Sym, @ptrCast(data)); break :blk .{ .st_name = sym.st_name, .st_value = sym.st_value, .st_size = sym.st_size, .st_info = sym.st_info, .st_other = sym.st_other, .st_shndx = sym.st_shndx, }; } else @as(*align(1) const elf.Elf64_Sym, @ptrCast(data)).*; switch (shdr.sh_type) { elf.SHT_SYMTAB => self.symtab.appendAssumeCapacity(out_sym), elf.SHT_DYNSYM => self.dynsymtab.appendAssumeCapacity(out_sym), else => unreachable, } } const strtab = self.getSectionContentsByIndex(shdr.sh_link); switch (shdr.sh_type) { elf.SHT_SYMTAB => { self.symtab_index = @as(u32, @intCast(i)); self.strtab = strtab; }, elf.SHT_DYNSYM => { self.dynsymtab_index = @as(u32, @intCast(i)); self.dynstrtab = strtab; }, else => unreachable, } },
elf.SHT_DYNAMIC => { self.dynamic_index = @as(u32, @intCast(i)); },
elf.SHT_GNU_VERDEF => { self.verdef_index = @as(u32, @intCast(i)); },
elf.SHT_GNU_VERNEED => { self.verneed_index = @as(u32, @intCast(i)); },
elf.SHT_GNU_VERSYM => { self.versymtab_index = @as(u32, @intCast(i)); const raw = self.getSectionContents(shdr); const nsyms = @divExact(raw.len, @sizeOf(elf.Versym)); try self.versymtab.ensureTotalCapacityPrecise(self.arena, nsyms); const versymtab = @as([*]align(1) const elf.Versym, @ptrCast(raw.ptr))[0..nsyms]; self.versymtab.appendUnalignedSliceAssumeCapacity(versymtab); },
else => {}, };
if (self.verdef_index) |shndx| { const shdr = self.shdrs.items[shndx]; const raw = self.getSectionContents(shdr); const nsyms = @as(u32, @intCast(self.getVerdefNum())); try self.verdefsyms.ensureTotalCapacityPrecise(self.arena, nsyms); try self.verdefsyms_lookup.ensureTotalCapacity(self.arena, nsyms);
{ var i: u32 = 0; var offset: u32 = 0; while (i < nsyms) : (i += 1) { const verdefsym: elf.Verdef = @as(*align(1) const elf.Verdef, @ptrCast(raw.ptr + offset)).*; self.verdefsyms.appendAssumeCapacity(.{ .sym = verdefsym, .off = offset, .aux = undefined, }); offset += verdefsym.next; } }
for (self.verdefsyms.items, 0..) |*verdefsym, i| { const aux = @as(u32, @intCast(self.verdefaux.items.len)); verdefsym.aux = aux;
self.verdefsyms_lookup.putAssumeCapacityNoClobber(verdefsym.sym.ndx, @as(u32, @intCast(i))); try self.verdefaux.ensureUnusedCapacity(self.arena, verdefsym.sym.cnt);
var j: u32 = 0; var offset: u32 = verdefsym.off + verdefsym.sym.aux; while (j < verdefsym.sym.cnt) : (j += 1) { const verdefaux: elf.Verdaux = @as(*align(1) const elf.Verdaux, @ptrCast(raw.ptr + offset)).*; self.verdefaux.appendAssumeCapacity(.{ .off = offset, .sym = verdefaux }); offset += verdefaux.next; } } }
if (self.verneed_index) |shndx| { const shdr = self.shdrs.items[shndx]; const raw = self.getSectionContents(shdr); const nsyms = @as(u32, @intCast(self.getVerneedNum())); try self.verneedsyms.ensureTotalCapacityPrecise(self.arena, nsyms);
{ var i: u32 = 0; var offset: u32 = 0; while (i < nsyms) : (i += 1) { const verneedsym: elf.Elf64_Verneed = if (self.is32Bit()) blk: { const verneedsym = @as(*align(1) const elf.Elf32_Verneed, @ptrCast(raw.ptr + offset)); break :blk .{ .vn_version = verneedsym.vn_version, .vn_cnt = verneedsym.vn_cnt, .vn_file = verneedsym.vn_file, .vn_aux = verneedsym.vn_aux, .vn_next = verneedsym.vn_next, }; } else @as(*align(1) const elf.Elf64_Verneed, @ptrCast(raw.ptr + offset)).*; self.verneedsyms.appendAssumeCapacity(.{ .sym = verneedsym, .off = offset, .aux = undefined, }); offset += verneedsym.vn_next; } }
for (self.verneedsyms.items) |*verneedsym| { const aux = @as(u32, @intCast(self.verneedaux.items.len)); verneedsym.aux = aux;
try self.verneedaux.ensureUnusedCapacity(self.arena, verneedsym.sym.vn_cnt); try self.verneedsyms_lookup.ensureUnusedCapacity(self.arena, verneedsym.sym.vn_cnt);
var i: u32 = 0; var offset: u32 = verneedsym.off + verneedsym.sym.vn_aux; while (i < verneedsym.sym.vn_cnt) : (i += 1) { const verneedaux: elf.Vernaux = @as(*align(1) const elf.Vernaux, @ptrCast(raw.ptr + offset)).*; self.verneedaux.appendAssumeCapacity(.{ .off = offset, .sym = verneedaux }); offset += verneedaux.next; self.verneedsyms_lookup.putAssumeCapacityNoClobber(verneedaux.other, aux + i); } } }}
pub fn printHeader(self: Object, writer: *Io.Writer) !void { try writer.print("ELF Header:\n", .{});
try writer.writeAll(" Magic: "); for (self.header.e_ident) |byte| { try writer.print(" {x:0>2}", .{byte}); } try writer.writeByte('\n');
try writer.print(" {s: <34} {s}\n", .{ "Class:", switch (self.header.e_ident[elf.EI_CLASS]) { elf.ELFCLASS32 => "ELF32", elf.ELFCLASS64 => "ELF64", else => "Unknown", } }); try writer.print(" {s: <34} 2's complement, {s} endian\n", .{ "Data:", switch (self.header.e_ident[elf.EI_DATA]) { elf.ELFDATA2LSB => "little", elf.ELFDATA2MSB => "big", else => "unknown", } }); try writer.print(" {s: <34} {d} ({s})\n", .{ "Version:", self.header.e_ident[elf.EI_VERSION], if (self.header.e_ident[elf.EI_VERSION] == 1) "current" else "unknown", }); try writer.print(" {s: <34} {s}\n", .{ "OS/ABI:", switch (self.header.e_ident[EI_OSABI]) { ELFOSABI_SYSV => "UNIX - System V", ELFOSABI_HPUX => "HP UNIX", ELFOSABI_NETBSD => "NetBSD", ELFOSABI_LINUX => "Linux GNU", ELFOSABI_SOLARIS => "Solaris UNIX", ELFOSABI_AIX => "AIX", ELFOSABI_IRIX => "IRIX", ELFOSABI_FREEBSD => "FreeBSD", ELFOSABI_TRU64 => "TRU64", ELFOSABI_MODESTO => "Modesto", ELFOSABI_OPENBSD => "OpenBSD", ELFOSABI_ARM => "ARM", ELFOSABI_STANDALONE => "Standalone", else => "Unknown", } }); try writer.print(" {s: <34} {d}\n", .{ "ABI Version:", self.header.e_ident[EI_ABIVERSION] }); if (elf.ET.LOPROC <= @intFromEnum(self.header.e_type) and @intFromEnum(self.header.e_type) < elf.ET.HIPROC) { try writer.print(" {s: <34} {s}+{x} (Processor-specific)\n", .{ "Type:", "LOPROC", @intFromEnum(self.header.e_type), }); } else { try writer.print(" {s: <34} {s} ({s})\n", .{ "Type:", @tagName(self.header.e_type), switch (self.header.e_type) { .NONE => "No file type", .REL => "Relocatable file", .EXEC => "Executable file", .DYN => "Shared object file", .CORE => "Core file", else => "Unknown file type", } }); } try writer.print(" {s: <34} {s}\n", .{ "Machine:", switch (self.header.e_machine) { .NONE => "None", .X86_64 => "Advanced Micro Devices X86-64", .AARCH64 => "Aarch64", .RISCV => "RISC-V", else => "Unknown", } }); try writer.print(" {s: <34} 0x{x}\n", .{ "Version:", self.header.e_version }); try writer.print(" {s: <34} 0x{x}\n", .{ "Entry point address:", self.header.e_entry }); try writer.print(" {s: <34} {d} (bytes into file)\n", .{ "Start of program headers:", self.header.e_phoff }); try writer.print(" {s: <34} {d} (bytes into file)\n", .{ "Start of section headers:", self.header.e_shoff }); try writer.print(" {s: <34} 0x{x}\n", .{ "Flags:", self.header.e_flags }); try writer.print(" {s: <34} {d} (bytes)\n", .{ "Size of this header:", self.header.e_ehsize }); try writer.print(" {s: <34} {d} (bytes)\n", .{ "Size of program headers:", self.header.e_phentsize }); try writer.print(" {s: <34} {d}\n", .{ "Number of program headers:", self.header.e_phnum }); try writer.print(" {s: <34} {d} (bytes)\n", .{ "Size of section headers:", self.header.e_shentsize }); try writer.print(" {s: <34} {d}\n", .{ "Number of section headers:", self.header.e_shnum }); try writer.print(" {s: <34} {d}\n", .{ "Section header string table index:", self.header.e_shstrndx });}
const EI_OSABI = 7;const ELFOSABI_NONE = 0;const ELFOSABI_SYSV = 0;const ELFOSABI_HPUX = 1;const ELFOSABI_NETBSD = 2;const ELFOSABI_LINUX = 3;const ELFOSABI_GNU = 3;const ELFOSABI_SOLARIS = 6;const ELFOSABI_AIX = 7;const ELFOSABI_IRIX = 8;const ELFOSABI_FREEBSD = 9;const ELFOSABI_TRU64 = 10;const ELFOSABI_MODESTO = 11;const ELFOSABI_OPENBSD = 12;const ELFOSABI_ARM = 97;const ELFOSABI_STANDALONE = 255;
const EI_ABIVERSION = 8;
pub fn printShdrs(self: Object, writer: *Io.Writer) !void { const legend = \\Key to Flags: \\ W (write), A (alloc), X (execute), M (merge), S (strings), I (info), \\ L (link order), O (extra OS processing required), G (group), T (TLS), \\ C (compressed), x (unknown), o (OS specific), E (exclude), \\ D (mbind), l (large), p (processor specific) \\ ;
try writer.print("There are {d} section headers, starting at offset 0x{x}:\n\n", .{ self.header.e_shnum, self.header.e_shoff, }); try writer.print("Section Headers:\n", .{}); try writer.print(" [Nr] Name{s: <14}Type{s: <14}Address{s: <11}Offset\n", .{ "", "", "" }); try writer.print(" Size{s: <14}EntSize{s: <11}Flags Link Info Align\n", .{ "", "" });
var sh_name_fmt = FormatName(max_name_len){ .wide = self.opts.wide };
for (self.shdrs.items, 0..) |shdr, i| { const sh_flags = shdr.sh_flags; const sh_name = self.getShString(shdr.sh_name); const sh_type = switch (shdr.sh_type) { elf.SHT_NULL => "NULL", elf.SHT_PROGBITS => "PROGBITS", elf.SHT_SYMTAB => "SYMTAB", elf.SHT_STRTAB => "STRTAB", elf.SHT_RELA => "RELA", elf.SHT_HASH => "HASH", elf.SHT_DYNAMIC => "DYNAMIC", elf.SHT_NOTE => "NOTE", elf.SHT_NOBITS => "NOBITS", elf.SHT_REL => "REL", elf.SHT_SHLIB => "SHLIB", elf.SHT_DYNSYM => "DYNSYM", elf.SHT_INIT_ARRAY => "INIT_ARRAY", elf.SHT_FINI_ARRAY => "FINI_ARRAY", elf.SHT_PREINIT_ARRAY => "PREINIT_ARRAY", elf.SHT_GROUP => "GROUP", elf.SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX", elf.SHT_X86_64_UNWIND => "X86_64_UNWIND", elf.SHT_LLVM_ADDRSIG => "LLVM_ADDRSIG", 0x6ffffff6 => "GNU_HASH", // SHT_GNU_HASH 0x6ffffffd => "VERDEF", // SHT_GNU_verdef 0x6ffffffe => "VERNEED", // SHT_GNU_verneed 0x6fffffff => "VERSYM", // SHT_GNU_versym else => |sht| blk: { if (elf.SHT_LOOS <= sht and sht < elf.SHT_HIOS) { break :blk try fmt.allocPrint(self.arena, "LOOS+0x{x}", .{sht - elf.SHT_LOOS}); } if (elf.SHT_LOPROC <= sht and sht < elf.SHT_HIPROC) { break :blk try fmt.allocPrint(self.arena, "LOPROC+0x{x}", .{sht - elf.SHT_LOPROC}); } if (elf.SHT_LOUSER <= sht and sht < elf.SHT_HIUSER) { break :blk try fmt.allocPrint(self.arena, "LOUSER+0x{x}", .{sht - elf.SHT_LOUSER}); } break :blk "UNKNOWN"; }, }; const flags = blk: { var flags: std.ArrayList(u8) = .empty; if (elf.SHF_WRITE & sh_flags != 0) { try flags.append(self.arena, 'W'); } if (elf.SHF_ALLOC & sh_flags != 0) { try flags.append(self.arena, 'A'); } if (elf.SHF_EXECINSTR & sh_flags != 0) { try flags.append(self.arena, 'X'); } if (elf.SHF_MERGE & sh_flags != 0) { try flags.append(self.arena, 'M'); } if (elf.SHF_STRINGS & sh_flags != 0) { try flags.append(self.arena, 'S'); } if (elf.SHF_INFO_LINK & sh_flags != 0) { try flags.append(self.arena, 'I'); } if (elf.SHF_LINK_ORDER & sh_flags != 0) { try flags.append(self.arena, 'L'); } if (elf.SHF_EXCLUDE & sh_flags != 0) { try flags.append(self.arena, 'E'); } if (elf.SHF_COMPRESSED & sh_flags != 0) { try flags.append(self.arena, 'C'); } if (elf.SHF_GROUP & sh_flags != 0) { try flags.append(self.arena, 'G'); } if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) { try flags.append(self.arena, 'O'); } if (elf.SHF_TLS & sh_flags != 0) { try flags.append(self.arena, 'T'); } if (elf.SHF_X86_64_LARGE & sh_flags != 0) { try flags.append(self.arena, 'l'); } if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) { try flags.append(self.arena, 'p'); } // TODO parse more flags break :blk try flags.toOwnedSlice(self.arena); }; try writer.print(" [{d: >2}] {s: <16} {s: <16} {x:0>16} {x:0>16}\n", .{ i, sh_name_fmt.fmt(sh_name), sh_type, shdr.sh_addr, shdr.sh_offset, }); try writer.print(" {x:0>16} {x:0>16} {s: <5} {d: >4} {d: >4} {d: >4}\n", .{ shdr.sh_size, shdr.sh_entsize, flags, shdr.sh_link, shdr.sh_info, shdr.sh_addralign, }); }
try writer.writeAll(legend);}
pub fn printPhdrs(self: Object, writer: *Io.Writer) !void { if (self.phdrs.items.len == 0) return writer.print("There are no program headers in this file.\n", .{});
try writer.print("Entry point 0x{x}\n", .{self.header.e_entry}); try writer.print("There are {d} program headers, starting at offset {d}\n\n", .{ self.phdrs.items.len, self.header.e_phoff, }); try writer.print("Program Headers:\n", .{}); try writer.print(" Type{s: <12} Offset{s: <10} VirtAddr{s: <8} PhysAddr{s: <8}\n", .{ "", "", "", "" }); try writer.print(" {s: <16} FileSiz{s: <9} MemSiz{s: <10} Flags Align\n", .{ "", "", "" });
var section_to_segment = try self.arena.alloc(std.ArrayList(usize), self.phdrs.items.len); for (self.phdrs.items, 0..) |_, i| { section_to_segment[i] = .empty; }
for (self.phdrs.items, 0..) |phdr, i| { const p_type = switch (phdr.type) { .NULL => "NULL", .LOAD => "LOAD", .DYNAMIC => "DYNAMIC", .INTERP => "INTERP", .NOTE => "NOTE", .SHLIB => "SHLIB", .PHDR => "PHDR", .TLS => "TLS", @as(elf.PT, @enumFromInt(elf.PT.NUM)) => "NUM", elf.PT.GNU_EH_FRAME => "GNU_EH_FRAME", elf.PT.GNU_STACK => "GNU_STACK", elf.PT.GNU_RELRO => "GNU_RELRO", else => |pt| blk: { const value = @intFromEnum(pt); if (@intFromEnum(elf.PT.LOOS) <= value and value < @intFromEnum(elf.PT.HIOS)) { break :blk try fmt.allocPrint(self.arena, "LOOS+0x{x}", .{value - @intFromEnum(elf.PT.LOOS)}); } if (@intFromEnum(elf.PT.LOPROC) <= value and value < @intFromEnum(elf.PT.HIPROC)) { break :blk try fmt.allocPrint(self.arena, "LOPROC+0x{x}", .{value - @intFromEnum(elf.PT.LOPROC)}); } break :blk "UNKNOWN"; }, }; const p_flags = blk: { var p_flags: std.ArrayList(u8) = .empty; if (phdr.flags.R) { try p_flags.append(self.arena, 'R'); } if (phdr.flags.W) { try p_flags.append(self.arena, 'W'); } if (phdr.flags.X) { try p_flags.append(self.arena, 'E'); } if (phdr.flags.OS.MASK != 0) { try p_flags.appendSlice(self.arena, "OS"); } if (phdr.flags.PROC.MASK != 0) { try p_flags.appendSlice(self.arena, "PROC"); } break :blk try p_flags.toOwnedSlice(self.arena); }; try writer.print(" {s: <16} {x:0>16} {x:0>16} {x:0>16}\n", .{ p_type, phdr.offset, phdr.vaddr, phdr.paddr, }); try writer.print(" {s: >16} {x:0>16} {x:0>16} {s: <6} {x:0>6}\n", .{ "", phdr.filesz, phdr.memsz, p_flags, phdr.@"align", });
const start_addr = phdr.vaddr; const end_addr = start_addr + phdr.memsz; for (self.shdrs.items, 0..) |shdr, j| { if (start_addr <= shdr.sh_addr and shdr.sh_addr < end_addr) { try section_to_segment[i].append(self.arena, j); } } }
try writer.writeAll("\n"); try writer.print(" Section to Segment mapping:\n", .{}); try writer.print(" Segment Sections...\n", .{});
for (section_to_segment, 0..) |ss, i| { try writer.print(" {d:0>2} ", .{i});
for (ss.items, 0..) |shdr_ndx, x| { const shdr = self.shdrs.items[shdr_ndx]; if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; const shdr_name = self.getShString(shdr.sh_name); try writer.print("{s}", .{shdr_name}); if (x < ss.items.len - 1) { try writer.writeAll(" "); } }
try writer.writeAll("\n"); }}
pub fn printSectionGroups(self: Object, writer: *Io.Writer) !void { const has_section_groups = for (self.shdrs.items) |shdr| switch (shdr.sh_type) { elf.SHT_GROUP => break true, else => {}, } else false; if (!has_section_groups) return writer.print("There are no section groups in this file.\n", .{});
var first = true; for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_GROUP => { const raw = self.getSectionContents(shdr); const num_members = std.math.divExact(usize, raw.len, @sizeOf(u32)) catch { // TODO convert into an error return writer.print("Section {d}, '{s}' does not divide by group member size.\n", .{ i, self.getShString(shdr.sh_name), }); }; if (num_members == 0) return writer.print("Empty section {d}, '{s}'.\n", .{ i, self.getShString(shdr.sh_name), }); const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[0..num_members]; if (members[0] != elf.GRP_COMDAT) return writer.print("Unknown SHT_GROUP format in section {d}, '{s}'.\n", .{ i, self.getShString(shdr.sh_name), }); const group_info_sym = self.symtab.items[shdr.sh_info]; const group_signature = if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) self.getShString(self.shdrs.items[group_info_sym.st_shndx].sh_name) else getString(self.strtab, group_info_sym.st_name); if (!first) try writer.writeByte('\n'); try writer.print("COMDAT group section [{d: >5}] `{s}' [{s}] contains {d} sections:\n", .{ i, self.getShString(shdr.sh_name), group_signature, num_members - 1, }); try writer.writeAll(" [Index] Name\n"); for (members[1..], 1..) |shndx, n| { try writer.print(" [{d: >5}] {s}", .{ shndx, self.getShString(self.shdrs.items[shndx].sh_name), }); if (n < members.len - 1) try writer.writeByte('\n'); } first = false; }, else => {}, };}
pub fn printRelocs(self: Object, writer: *Io.Writer) !void { const has_relocs = for (self.shdrs.items) |shdr| switch (shdr.sh_type) { elf.SHT_RELA, elf.SHT_REL => break true, else => {}, } else false; if (!has_relocs) return writer.print("There is no relocation info in this file.\n", .{});
var last_shndx: usize = 0; for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_RELA, elf.SHT_REL => last_shndx = i, else => {}, };
for (self.shdrs.items, 0..) |shdr, i| { const is_rela = switch (shdr.sh_type) { elf.SHT_RELA => true, elf.SHT_REL => false, else => continue, }; const raw = self.getSectionContents(shdr); const nrelocs = @divExact(shdr.sh_size, shdr.sh_entsize);
try writer.print("Relocation section '{s}' at offset 0x{x} contains {d} entries:\n", .{ self.getShString(shdr.sh_name), shdr.sh_offset, nrelocs, }); try writer.print( " Offset{s: <8}Info{s: <12}Type{s: <20}Sym. Value{s: <2}Sym. Name + Addend\n", .{ "", "", "", "" }, ); if (!is_rela) { try writer.writeAll("\n\nWARNING: TODO implement parsing addend for SHT_REL records\n\n"); }
for (0..nrelocs) |ireloc| { const data = raw[ireloc * shdr.sh_entsize ..][0..shdr.sh_entsize]; const reloc: elf.Elf64_Rela = if (self.is32Bit()) blk: { if (is_rela) { const reloc = @as(*align(1) const elf.Elf32_Rela, @ptrCast(data)).*; break :blk .{ .r_offset = reloc.r_offset, .r_info = reloc.r_info, .r_addend = reloc.r_addend, }; } const reloc = @as(*align(1) const elf.Elf32_Rel, @ptrCast(data)).*; const addend: u64 = 0; // TODO get addend based on machine type and reloc type break :blk .{ .r_offset = reloc.r_offset, .r_info = reloc.r_info, .r_addend = addend, }; } else blk: { if (is_rela) { break :blk @as(*align(1) const elf.Elf64_Rela, @ptrCast(data)).*; } const reloc = @as(*align(1) const elf.Elf64_Rel, @ptrCast(data)).*; const addend: u64 = 0; // TODO get addend based on machine type and reloc type break :blk .{ .r_offset = reloc.r_offset, .r_info = reloc.r_info, .r_addend = addend, }; }; var sym: elf.Elf64_Sym = undefined; var sym_name: []const u8 = undefined; if (self.symtab_index != null and shdr.sh_link == self.symtab_index.?) { sym = self.symtab.items[reloc.r_sym()]; sym_name = blk: { if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) { const target_shdr = self.shdrs.items[sym.st_shndx]; break :blk self.getShString(target_shdr.sh_name); } break :blk getString(self.strtab, sym.st_name); }; } else if (self.dynsymtab_index != null and shdr.sh_link == self.dynsymtab_index.?) { sym = self.dynsymtab.items[reloc.r_sym()]; sym_name = getString(self.dynstrtab, sym.st_name); } else unreachable; try writer.print("{x:0>12} {x:0>12} {f} {x:0>16} {s} ", .{ reloc.r_offset, reloc.r_info, fmtRelocType(reloc.r_type(), self), sym.st_value, sym_name, }); if (reloc.r_addend >= 0) { try writer.print("+ {x}", .{reloc.r_addend}); } else { try writer.print("- {x}", .{@abs(reloc.r_addend)}); } try writer.writeByte('\n'); }
if (i != last_shndx) try writer.writeByte('\n'); }}
const FmtRelocTypeCtx = struct { r_type: u32, object: Object,};
fn fmtRelocType(r_type: u32, object: Object) fmt.Alt(FmtRelocTypeCtx, formatRelocType) { return .{ .data = .{ .r_type = r_type, .object = object, } };}
fn formatRelocType( ctx: FmtRelocTypeCtx, writer: *Io.Writer,) Io.Writer.Error!void { const r_type = ctx.r_type; const object = ctx.object; const prefix = switch (object.header.e_machine) { .X86_64 => "R_X86_64_", .AARCH64 => "R_AARCH64_", .RISCV => "R_RISCV_", else => unreachable, }; const suffix = switch (object.header.e_machine) { .X86_64 => @tagName(@as(elf.R_X86_64, @enumFromInt(r_type))), .AARCH64 => @tagName(@as(elf.R_AARCH64, @enumFromInt(r_type))), .RISCV => @tagName(@as(elf.R_RISCV, @enumFromInt(r_type))), else => unreachable, }; const width: usize = 24; if (object.opts.wide) { return writer.print("{s}{s}", .{ prefix, suffix }); } const total_len = prefix.len + suffix.len; if (total_len > width) { try writer.print("{s}{s}", .{ prefix, suffix[0 .. width - prefix.len] }); } else { try writer.print("{s}{s}", .{ prefix, suffix }); const padding = width - total_len; if (padding > 0) { try writer.splatByteAll(' ', padding); } }}
pub fn printSymbolTable(self: Object, writer: *Io.Writer) !void { const ndx = self.symtab_index orelse return writer.print("There is no symbol table in this file.", .{}); try self.printSymtab(ndx, self.symtab.items, self.strtab, writer);}
pub fn printDynamicSymbolTable(self: Object, writer: *Io.Writer) !void { const ndx = self.dynsymtab_index orelse return writer.print("There is no dynamic symbol table in this file.", .{}); try self.printSymtab(ndx, self.dynsymtab.items, self.dynstrtab, writer);}
fn printSymtab( self: Object, shdr_ndx: u32, symtab: []align(1) const elf.Elf64_Sym, strtab: []align(1) const u8, writer: *Io.Writer,) !void { const shdr = self.shdrs.items[shdr_ndx]; const is_dynsym = shdr.sh_type == elf.SHT_DYNSYM;
try writer.print("Symbol table '{s}' contains {d} entries:\n", .{ self.getShString(shdr.sh_name), symtab.len, }); try writer.print( " Num:{s: <12}Value{s: <2}Size Type{s: <3} Bind{s: <2} Vis{s: <5} Ndx{s: <2} Name\n", .{ "", "", "", "", "", "" }, );
var sym_name_fmt = FormatName(max_name_len){ .wide = self.opts.wide };
for (symtab, 0..) |sym, i| { const sym_name = blk: { switch (sym.st_type()) { elf.STT_SECTION => { const sym_shdr = self.shdrs.items[sym.st_shndx]; break :blk self.getShString(sym_shdr.sh_name); }, else => { const base_name = getString(strtab, sym.st_name); if (is_dynsym and self.versymtab_index != null) { const versym = self.versymtab.items[@as(u32, @intCast(i))].VERSION; if (self.verdefsyms_lookup.get(@enumFromInt(versym))) |verdef_index| { const verdef = self.verdefsyms.items[verdef_index]; const verdaux = self.verdefaux.items[verdef.aux]; break :blk try std.fmt.allocPrint(self.arena, "{s}@{s} ({d})", .{ base_name, getString(strtab, verdaux.sym.name), versym, }); } if (self.verneedsyms_lookup.get(versym)) |verneed_index| { const vernaux = self.verneedaux.items[verneed_index]; break :blk try std.fmt.allocPrint(self.arena, "{s}@{s} ({d})", .{ base_name, getString(strtab, vernaux.sym.name), versym, }); } } break :blk base_name; }, } }; const sym_type = switch (sym.st_type()) { elf.STT_NOTYPE => "NOTYPE", elf.STT_OBJECT => "OBJECT", elf.STT_FUNC => "FUNC", elf.STT_SECTION => "SECTION", elf.STT_FILE => "FILE", elf.STT_COMMON => "COMMON", elf.STT_TLS => "TLS", elf.STT_NUM => "NUM", else => |tt| blk: { if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { break :blk try fmt.allocPrint(self.arena, "LOPROC+{d}", .{tt - elf.STT_LOPROC}); } if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { break :blk try fmt.allocPrint(self.arena, "LOOS+{d}", .{tt - elf.STT_LOOS}); } break :blk "UNK"; }, }; const sym_bind = switch (sym.st_bind()) { elf.STB_LOCAL => "LOCAL", elf.STB_GLOBAL => "GLOBAL", elf.STB_WEAK => "WEAK", elf.STB_NUM => "NUM", else => |bind| blk: { if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { break :blk try fmt.allocPrint(self.arena, "LOPROC+{d}", .{bind - elf.STB_LOPROC}); } if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { break :blk try fmt.allocPrint(self.arena, "LOOS+{d}", .{bind - elf.STB_LOOS}); } break :blk "UNKNOWN"; }, }; const sym_vis = @as(elf.STV, @enumFromInt(sym.st_other)); const sym_ndx = blk: { if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { break :blk try fmt.allocPrint(self.arena, "LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); }
const sym_ndx = &switch (sym.st_shndx) { elf.SHN_ABS => "ABS", elf.SHN_COMMON => "COM", elf.SHN_LIVEPATCH => "LIV", else => "UNK", }; break :blk sym_ndx.*; } else if (sym.st_shndx == elf.SHN_UNDEF) { break :blk "UND"; } break :blk try fmt.allocPrint(self.arena, "{d}", .{sym.st_shndx}); }; try writer.print( " {d: >3}: {x:0>16} {d: >5} {s: <7} {s: <6} {s: <8} {s: <5} {s}\n", .{ i, sym.st_value, sym.st_size, sym_type, sym_bind, @tagName(sym_vis), sym_ndx, sym_name_fmt.fmt(sym_name) }, ); }}
pub fn printDynamicSection(self: Object, writer: *Io.Writer) !void { const shdr = self.getShdrByType(elf.SHT_DYNAMIC) orelse return writer.writeAll("There is no dynamic section in this file."); const data = self.getSectionContents(shdr); const nentries = @divExact(data.len, @sizeOf(elf.Elf64_Dyn)); const entries = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(data.ptr))[0..nentries];
try writer.print(" {s:<18} {s:<24} {s}\n", .{ "Tag", "Type", "Name/Value" });
for (entries) |entry| { const key = @as(u64, @bitCast(entry.d_tag)); const value = entry.d_val;
try writer.print("0x{x:0>16} {f:<22}", .{ key, fmtDynamicSectionType(key) });
switch (key) { elf.DT_NEEDED, elf.DT_SONAME, elf.DT_RPATH, elf.DT_RUNPATH, => { const name = getString(self.dynstrtab, @as(u32, @intCast(value))); switch (key) { elf.DT_NEEDED => try writer.writeAll(" Shared library: "), elf.DT_SONAME => try writer.writeAll(" Library soname: "), elf.DT_RPATH => try writer.writeAll(" Library rpath: "), elf.DT_RUNPATH => try writer.writeAll(" Library runpath: "), else => unreachable, } try writer.print("[{s}]", .{name}); },
elf.DT_INIT_ARRAY, elf.DT_FINI_ARRAY, elf.DT_HASH, elf.DT_GNU_HASH, elf.DT_STRTAB, elf.DT_SYMTAB, elf.DT_PLTGOT, elf.DT_JMPREL, elf.DT_RELA, elf.DT_VERDEF, elf.DT_VERNEED, elf.DT_VERSYM, elf.DT_INIT, elf.DT_FINI, elf.DT_NULL, => try writer.print(" 0x{x}", .{value}),
elf.DT_INIT_ARRAYSZ, elf.DT_FINI_ARRAYSZ, elf.DT_STRSZ, elf.DT_SYMENT, elf.DT_PLTRELSZ, elf.DT_RELASZ, elf.DT_RELAENT, => try writer.print(" {d} (bytes)", .{value}),
elf.DT_PLTREL => try writer.print(" {f}", .{fmtDynamicSectionType(value)}),
elf.DT_FLAGS => if (value > 0) { if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); } else try writer.print(" {x}", .{value}),
elf.DT_FLAGS_1 => if (value > 0) { try writer.writeAll(" Flags:"); if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); } else try writer.print(" {x}", .{value}),
elf.DT_RELACOUNT => try writer.print(" {d}", .{value}),
else => try writer.print(" {x}", .{value}), }
try writer.writeByte('\n'); }}
fn fmtDynamicSectionType(@"type": u64) fmt.Alt(u64, formatDynamicSectionType) { return .{ .data = @"type" };}
fn formatDynamicSectionType( @"type": u64, writer: *Io.Writer,) Io.Writer.Error!void { const str = switch (@"type") { elf.DT_NEEDED => "NEEDED", elf.DT_SONAME => "SONAME", elf.DT_INIT_ARRAY => "INIT_ARRAY", elf.DT_INIT_ARRAYSZ => "INIT_ARRAYSZ", elf.DT_FINI_ARRAY => "FINI_ARRAY", elf.DT_FINI_ARRAYSZ => "FINI_ARRAYSZ", elf.DT_HASH => "HASH", elf.DT_GNU_HASH => "GNU_HASH", elf.DT_STRTAB => "STRTAB", elf.DT_SYMTAB => "SYMTAB", elf.DT_STRSZ => "STRSZ", elf.DT_SYMENT => "SYMENT", elf.DT_PLTGOT => "PLTGOT", elf.DT_PLTRELSZ => "PLTRELSZ", elf.DT_PLTREL => "PLTREL", elf.DT_JMPREL => "JMPREL", elf.DT_RELA => "RELA", elf.DT_RELASZ => "RELASZ", elf.DT_RELAENT => "RELAENT", elf.DT_VERDEF => "VERDEF", elf.DT_VERDEFNUM => "VERDEFNUM", elf.DT_FLAGS => "FLAGS", elf.DT_FLAGS_1 => "FLAGS_1", elf.DT_VERNEED => "VERNEED", elf.DT_VERNEEDNUM => "VERNEEDNUM", elf.DT_VERSYM => "VERSYM", elf.DT_RELACOUNT => "RELACOUNT", elf.DT_RPATH => "RPATH", elf.DT_RUNPATH => "RUNPATH", elf.DT_INIT => "INIT", elf.DT_FINI => "FINI", elf.DT_NULL => "NULL", else => "UNKNOWN", }; try writer.writeAll(str);}
pub fn printInitializers(self: Object, writer: *Io.Writer) !void { var no_inits = true; for (self.shdrs.items) |shdr| switch (shdr.sh_type) { elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY, elf.SHT_PREINIT_ARRAY => { try writer.print("{s}:\n", .{self.getShString(shdr.sh_name)}); const entry_size = shdr.sh_entsize; const entries = self.getSectionContents(shdr); const nentries = @divExact(shdr.sh_size, entry_size); var ientry: usize = 0; while (ientry < nentries) : (ientry += 1) { const off = ientry * entry_size; const entry = entries[off..][0..entry_size]; const value = switch (entry_size) { 4 => mem.readInt(u32, entry[0..4], .little), 8 => mem.readInt(u64, entry[0..8], .little), else => unreachable, }; const sym_index = self.findSymbolByAddress(value).?; const name = getString(self.strtab, self.symtab.items[sym_index].st_name); try writer.print(" {x:0>16}: {x:0>16} {s}\n", .{ shdr.sh_addr + off, value, name }); } no_inits = false; }, else => {}, };
if (no_inits) { try writer.writeAll("There is no .init_array, .fini_array or .preinit_array section in this file."); }}
pub fn printVersionSections(self: Object, writer: *Io.Writer) !void { if (self.versymtab_index == null) { return writer.writeAll("There are no version sections in this file."); }
if (self.versymtab_index) |shndx| { const shdr = self.shdrs.items[shndx]; try writer.print("Version symbols section '{s}' contains {d} entries:\n", .{ self.getShString(shdr.sh_name), self.versymtab.items.len, }); try writer.print(" Addr: 0x{x:0>16} Offset: 0x{x:0>8} Link: {d} ({s})\n", .{ shdr.sh_addr, shdr.sh_offset, shdr.sh_link, self.getShString(self.shdrs.items[shdr.sh_link].sh_name), });
var count: usize = 0; while (count < self.versymtab.items.len) : (count += 4) { const remaining = self.versymtab.items[count..]; const num = @min(remaining.len, 4);
try writer.print(" {x:0>4}", .{count});
for (remaining[0..num]) |versym| { const actual_versym = versym.VERSION; const name = switch (@as(elf.VER_NDX, @enumFromInt(actual_versym))) { .LOCAL => "*local*", .GLOBAL => "*global*", else => blk: { if (self.verdefsyms_lookup.get(@enumFromInt(actual_versym))) |verdef_index| { const verdef = self.verdefsyms.items[verdef_index]; const verauxs = self.verdefaux.items[verdef.aux..][0..verdef.sym.cnt]; break :blk getString(self.dynstrtab, verauxs[0].sym.name); } if (self.verneedsyms_lookup.get(actual_versym)) |vernaux_index| { const vernaux = self.verneedaux.items[vernaux_index]; break :blk getString(self.dynstrtab, vernaux.sym.name); } break :blk try std.fmt.allocPrint(self.arena, "unknown({d})", .{actual_versym}); }, }; const hidden = versym.HIDDEN; try writer.print(" {d: >4}{s}({s})", .{ actual_versym, if (hidden) "h" else " ", name }); }
try writer.writeByte('\n'); } try writer.writeByte('\n'); }
if (self.verdef_index) |shndx| { const shdr = self.shdrs.items[shndx]; try writer.print("Version definition section '{s}' contains {d} entries:\n", .{ self.getShString(shdr.sh_name), self.verdefsyms.items.len, }); try writer.print(" Addr: 0x{x:0>16} Offset: 0x{x:0>8} Link: {d} ({s})\n", .{ shdr.sh_addr, shdr.sh_offset, shdr.sh_link, self.getShString(self.shdrs.items[shdr.sh_link].sh_name), });
for (self.verdefsyms.items) |verdef| { const verauxs = self.verdefaux.items[verdef.aux..][0..verdef.sym.cnt]; try writer.print(" 0x{x:0>8}: Rev: {d} Flags: {s} Index: {d: >2} Cnt: {d: >2} Name: {s}\n", .{ verdef.off, verdef.sym.version, switch (verdef.sym.flags) { 0 => "none", elf.VER_FLG_BASE => "BASE", elf.VER_FLG_WEAK => "WEAK", else => "unknown", }, verdef.sym.ndx, verdef.sym.cnt, getString(self.dynstrtab, verauxs[0].sym.name), });
for (verauxs[1..], 1..) |veraux, i| { try writer.print(" 0x{x:0>8}: Parent {d}: {s}\n", .{ veraux.off, i, getString(self.dynstrtab, veraux.sym.name), }); } }
try writer.writeByte('\n'); }
if (self.verneed_index) |shndx| { const shdr = self.shdrs.items[shndx]; try writer.print("Version needs section '{s}' contains {d} entries:\n", .{ self.getShString(shdr.sh_name), self.verneedsyms.items.len, }); try writer.print(" Addr: 0x{x:0>16} Offset: 0x{x:0>8} Link: {d} ({s})\n", .{ shdr.sh_addr, shdr.sh_offset, shdr.sh_link, self.getShString(self.shdrs.items[shdr.sh_link].sh_name), });
for (self.verneedsyms.items) |verneed| { const verauxs = self.verneedaux.items[verneed.aux..][0..verneed.sym.vn_cnt]; try writer.print(" 0x{x:0>8}: Version: {d} File: {s} Cnt: {d}\n", .{ verneed.off, verneed.sym.vn_version, getString(self.dynstrtab, verneed.sym.vn_file), verneed.sym.vn_cnt, });
for (verauxs) |veraux| { try writer.print(" 0x{x:0>8}: Name: {s} Flags: {s} Version: {d}\n", .{ veraux.off, getString(self.dynstrtab, veraux.sym.name), switch (veraux.sym.flags) { 0 => "none", elf.VER_FLG_BASE => "BASE", elf.VER_FLG_WEAK => "WEAK", else => "unknown", }, veraux.sym.other, }); } } }}
pub fn dumpSectionHex(self: Object, shndx: u32, writer: *Io.Writer) !void { const shdr = self.shdrs.items[shndx]; const name = self.getShString(shdr.sh_name); const data = self.getSectionContentsByIndex(shndx); try writer.print("Hex dump of section '{s}':\n", .{name}); try fmtBlobHex(data, writer);}
// Format as 4 hex columns and 1 ascii column.// xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxfn fmtBlobHex(blob: []const u8, writer: *Io.Writer) !void { const step = 16; var hex_buf: [step]u8 = undefined; var str_buf: [step]u8 = undefined; var i: usize = 0; while (i < blob.len) : (i += step) { try writer.print(" 0x{x:0>8} ", .{i}); const end = if (blob[i..].len >= step) step else blob[i..].len; @memset(&hex_buf, 0); @memcpy(hex_buf[0..end], blob[i .. i + end]); var j: usize = 0; while (j < step) : (j += 4) { try writer.print("{x:<8} ", .{hex_buf[j .. j + 4]}); } @memcpy(&str_buf, &hex_buf); std.mem.replaceScalar(u8, &str_buf, 0, '.'); try printEscapedLower(writer, &str_buf); try writer.writeByte('\n'); }}
fn printEscapedLower(writer: *Io.Writer, bytes: []const u8) !void { for (bytes) |b| { if (std.ascii.isPrint(b)) { try writer.writeByte(b); } else { try writer.print("\\x{x:0>2}", .{b}); } }}
pub fn dumpSectionStr(self: Object, shndx: u32, writer: *Io.Writer) !void { const shdr = self.shdrs.items[shndx]; const name = self.getShString(shdr.sh_name); const data = self.getSectionContentsByIndex(shndx); try writer.print("String dump of section '{s}':\n", .{name});
if (shdr.sh_flags & elf.SHF_STRINGS != 0) { const entsize: usize = switch (shdr.sh_entsize) { 0 => 1, else => |x| @intCast(x), }; const isNull = struct { fn isNull(slice: []const u8) bool { for (slice) |x| if (x != 0) return false; return true; } }.isNull;
var start: usize = 0; while (start < data.len) { var end = start; while (end + entsize <= data.len and !isNull(data[end .. end + entsize])) : (end += entsize) {} // end now points at either the null terminator or one past the end of the // section (meaning the final string is unterminated — some linkers emit this // in .comment; readelf treats end-of-section as an implicit terminator). const string = data[start..end]; if (string.len > 0) { try writer.print(" [{x: >6}] ", .{start}); try printEscapedLower(writer, string); try writer.writeByte('\n'); } start = end + entsize; } } else { const entsize = shdr.sh_entsize; assert(entsize > 0); // TODO error if (shdr.sh_size % entsize != 0) { @panic("size not multiple of sh_entsize"); // TODO error } var pos: usize = 0; while (pos < data.len) : (pos += entsize) { try writer.print(" [{x: >6}] ", .{pos}); const string = data.ptr[pos..][0..entsize]; try printEscapedLower(writer, string); try writer.writeByte('\n'); } }}
fn getDynamicTable(self: Object) []align(1) const elf.Elf64_Dyn { const shndx = self.dynamic_index orelse return &[0]elf.Elf64_Dyn{}; const raw = self.getSectionContentsByIndex(shndx); const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn)); return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num];}
fn getVerdefNum(self: Object) u64 { const dynamic = self.getDynamicTable(); for (dynamic) |entry| switch (entry.d_tag) { elf.DT_VERDEFNUM => return entry.d_val, else => {}, }; return 0;}
fn getVerneedNum(self: Object) u64 { const dynamic = self.getDynamicTable(); for (dynamic) |entry| switch (entry.d_tag) { elf.DT_VERNEEDNUM => return entry.d_val, else => {}, }; return 0;}
fn findSymbolByAddress(self: Object, addr: u64) ?u32 { for (self.symtab.items, 0..) |sym, idx| { if (sym.st_value <= addr and addr < sym.st_value + sym.st_size) return @as(u32, @intCast(idx)); } return null;}
fn getShdrByType(self: Object, sh_type: u32) ?elf.Elf64_Shdr { for (self.shdrs.items) |shdr| if (shdr.sh_type == sh_type) { return shdr; }; return null;}
fn getShString(self: Object, off: u32) []const u8 { if (self.shstrtab.len == 0) return "<no-strings>"; assert(off < self.shstrtab.len); return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.ptr + off)), 0);}
fn getString(strtab: []const u8, off: u32) []const u8 { if (strtab.len == 0) return "<no-strings>"; assert(off < strtab.len); return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0);}
inline fn getSectionContents(self: Object, shdr: elf.Elf64_Shdr) []const u8 { return self.data[shdr.sh_offset..][0..shdr.sh_size];}
fn getSectionContentsByIndex(self: Object, shdr_index: u32) []const u8 { if (self.shdrs.items.len == 0) return &[0]u8{}; assert(shdr_index < self.shdrs.items.len); const shdr = self.shdrs.items[shdr_index]; return self.getSectionContents(shdr);}
pub fn getSectionByName(self: Object, name: []const u8) ?u32 { for (self.shdrs.items, 0..) |shdr, shdr_index| { if (mem.eql(u8, self.getShString(shdr.sh_name), name)) return @intCast(shdr_index); } return null;}
fn is32Bit(self: Object) bool { return self.header.e_ident[elf.EI_CLASS] == elf.ELFCLASS32;}
const max_name_len = 16;
fn FormatName(comptime max_len: comptime_int) type { return struct { buffer: [max_len]u8 = undefined, wide: bool = false,
fn fmt(this: *@This(), name: []const u8) []const u8 { if (this.wide) return name; if (name.len <= max_len) return name; @memcpy(this.buffer[0 .. max_len - 4], name[0 .. max_len - 4]); @memcpy(this.buffer[max_len - 4 ..], "[..]"); return &this.buffer; } };}
fn VersionSym(comptime Inner: type) type { return struct { sym: Inner, off: u32, aux: u32, };}
fn VersionSymAux(comptime Inner: type) type { return struct { sym: Inner, off: u32, };}
const Object = @This();
const std = @import("std");const Io = std.Io;const assert = std.debug.assert;const elf = std.elf;const fmt = std.fmt;const fs = std.fs;const mem = std.mem;
const Allocator = mem.Allocator;