aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Build/Step/CheckObject.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-07-14 06:38:33 +0200
committerGitHub <noreply@github.com>2023-07-14 06:38:33 +0200
commit546212ff7baee86d3408c465e906b52e2e585fe1 (patch)
treed0c7e9e7c25b691831fb536915bb356b6d22146a /lib/std/Build/Step/CheckObject.zig
parent3ec337484b80647ee23217533c1d62e914afa4f1 (diff)
parent77026c67a42050cf6c15531d784afbf16c67c23c (diff)
downloadzig-546212ff7baee86d3408c465e906b52e2e585fe1.tar.gz
zig-546212ff7baee86d3408c465e906b52e2e585fe1.zip
Merge pull request #16398 from ziglang/check-object-elf
std: add ELF parse'n'dump functionality to std.Build.Step.CheckObject
Diffstat (limited to 'lib/std/Build/Step/CheckObject.zig')
-rw-r--r--lib/std/Build/Step/CheckObject.zig233
1 files changed, 232 insertions, 1 deletions
diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig
index 171734c450..5c24d53722 100644
--- a/lib/std/Build/Step/CheckObject.zig
+++ b/lib/std/Build/Step/CheckObject.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const assert = std.debug.assert;
+const elf = std.elf;
const fs = std.fs;
const macho = std.macho;
const math = std.math;
@@ -338,7 +339,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
.macho => try MachODumper.parseAndDump(step, contents, .{
.dump_symtab = self.dump_symtab,
}),
- .elf => @panic("TODO elf parser"),
+ .elf => try ElfDumper.parseAndDump(step, contents, .{
+ .dump_symtab = self.dump_symtab,
+ }),
.coff => @panic("TODO coff parser"),
.wasm => try WasmDumper.parseAndDump(step, contents, .{
.dump_symtab = self.dump_symtab,
@@ -695,6 +698,234 @@ const MachODumper = struct {
}
};
+const ElfDumper = struct {
+ const symtab_label = "symtab";
+
+ const Symtab = struct {
+ symbols: []align(1) const elf.Elf64_Sym,
+ strings: []const u8,
+
+ fn get(st: Symtab, index: usize) ?elf.Elf64_Sym {
+ if (index >= st.symbols.len) return null;
+ return st.symbols[index];
+ }
+
+ fn getName(st: Symtab, index: usize) ?[]const u8 {
+ const sym = st.get(index) orelse return null;
+ assert(sym.st_name < st.strings.len);
+ return mem.sliceTo(@ptrCast(st.strings.ptr + sym.st_name), 0);
+ }
+ };
+
+ const Context = struct {
+ gpa: Allocator,
+ data: []const u8,
+ hdr: elf.Elf64_Ehdr,
+ shdrs: []align(1) const elf.Elf64_Shdr,
+ phdrs: []align(1) const elf.Elf64_Phdr,
+ shstrtab: []const u8,
+ symtab: ?Symtab = null,
+ dysymtab: ?Symtab = null,
+ };
+
+ fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 {
+ const gpa = step.owner.allocator;
+ var stream = std.io.fixedBufferStream(bytes);
+ const reader = stream.reader();
+
+ const hdr = try reader.readStruct(elf.Elf64_Ehdr);
+ if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) {
+ return error.InvalidMagicNumber;
+ }
+
+ const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum];
+ const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum];
+
+ var ctx = Context{
+ .gpa = gpa,
+ .data = bytes,
+ .hdr = hdr,
+ .shdrs = shdrs,
+ .phdrs = phdrs,
+ .shstrtab = undefined,
+ };
+ ctx.shstrtab = getSectionContents(ctx, ctx.hdr.e_shstrndx);
+
+ if (opts.dump_symtab) {
+ for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
+ elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
+ const raw = getSectionContents(ctx, i);
+ const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym));
+ const symbols = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw.ptr))[0..nsyms];
+ const strings = getSectionContents(ctx, shdr.sh_link);
+
+ switch (shdr.sh_type) {
+ elf.SHT_SYMTAB => {
+ ctx.symtab = .{
+ .symbols = symbols,
+ .strings = strings,
+ };
+ },
+ elf.SHT_DYNSYM => {
+ ctx.dysymtab = .{
+ .symbols = symbols,
+ .strings = strings,
+ };
+ },
+ else => unreachable,
+ }
+ },
+
+ else => {},
+ };
+ }
+
+ var output = std.ArrayList(u8).init(gpa);
+ const writer = output.writer();
+
+ try dumpHeader(ctx, writer);
+ try dumpShdrs(ctx, writer);
+ try dumpPhdrs(ctx, writer);
+
+ return output.toOwnedSlice();
+ }
+
+ fn getSectionName(ctx: Context, shndx: usize) []const u8 {
+ const shdr = ctx.shdrs[shndx];
+ assert(shdr.sh_name < ctx.shstrtab.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.shstrtab.ptr + shdr.sh_name)), 0);
+ }
+
+ fn getSectionContents(ctx: Context, shndx: usize) []const u8 {
+ const shdr = ctx.shdrs[shndx];
+ assert(shdr.sh_offset < ctx.data.len);
+ assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len);
+ return ctx.data[shdr.sh_offset..][0..shdr.sh_size];
+ }
+
+ fn dumpHeader(ctx: Context, writer: anytype) !void {
+ try writer.writeAll("header\n");
+ try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)});
+ try writer.print("entry {x}\n", .{ctx.hdr.e_entry});
+ }
+
+ fn dumpShdrs(ctx: Context, writer: anytype) !void {
+ if (ctx.shdrs.len == 0) return;
+
+ for (ctx.shdrs, 0..) |shdr, shndx| {
+ try writer.print("shdr {d}\n", .{shndx});
+ try writer.print("name {s}\n", .{getSectionName(ctx, shndx)});
+ try writer.print("type {s}\n", .{fmtShType(shdr.sh_type)});
+ try writer.print("addr {x}\n", .{shdr.sh_addr});
+ try writer.print("offset {x}\n", .{shdr.sh_offset});
+ try writer.print("size {x}\n", .{shdr.sh_size});
+ try writer.print("addralign {x}\n", .{shdr.sh_addralign});
+ // TODO dump formatted sh_flags
+ }
+ }
+
+ fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) {
+ return .{ .data = sh_type };
+ }
+
+ fn formatShType(
+ sh_type: u32,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) {
+ try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS});
+ } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) {
+ try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC});
+ } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) {
+ try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER});
+ } else {
+ const name = switch (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",
+ elf.SHT_GNU_HASH => "GNU_HASH",
+ elf.SHT_GNU_VERDEF => "VERDEF",
+ elf.SHT_GNU_VERNEED => "VERNEED",
+ elf.SHT_GNU_VERSYM => "VERSYM",
+ else => "UNKNOWN",
+ };
+ try writer.writeAll(name);
+ }
+ }
+
+ fn dumpPhdrs(ctx: Context, writer: anytype) !void {
+ if (ctx.phdrs.len == 0) return;
+
+ for (ctx.phdrs, 0..) |phdr, phndx| {
+ try writer.print("phdr {d}\n", .{phndx});
+ try writer.print("type {s}\n", .{fmtPhType(phdr.p_type)});
+ try writer.print("vaddr {x}\n", .{phdr.p_vaddr});
+ try writer.print("paddr {x}\n", .{phdr.p_paddr});
+ try writer.print("offset {x}\n", .{phdr.p_offset});
+ try writer.print("memsz {x}\n", .{phdr.p_memsz});
+ try writer.print("filesz {x}\n", .{phdr.p_filesz});
+ try writer.print("align {x}\n", .{phdr.p_align});
+ // TODO dump formatted p_flags
+ }
+ }
+
+ fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) {
+ return .{ .data = ph_type };
+ }
+
+ fn formatPhType(
+ ph_type: u32,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) {
+ try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS});
+ } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) {
+ try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC});
+ } else {
+ const p_type = switch (ph_type) {
+ elf.PT_NULL => "NULL",
+ elf.PT_LOAD => "LOAD",
+ elf.PT_DYNAMIC => "DYNAMIC",
+ elf.PT_INTERP => "INTERP",
+ elf.PT_NOTE => "NOTE",
+ elf.PT_SHLIB => "SHLIB",
+ elf.PT_PHDR => "PHDR",
+ elf.PT_TLS => "TLS",
+ 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 => "UNKNOWN",
+ };
+ try writer.writeAll(p_type);
+ }
+ }
+};
+
const WasmDumper = struct {
const symtab_label = "symbols";