authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-07-14 06:38:33+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-14 06:38:33+02:00
log546212ff7baee86d3408c465e906b52e2e585fe1
treed0c7e9e7c25b691831fb536915bb356b6d22146a
parent3ec337484b80647ee23217533c1d62e914afa4f1
parent77026c67a42050cf6c15531d784afbf16c67c23c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16398 from ziglang/check-object-elf

std: add ELF parse'n'dump functionality to std.Build.Step.CheckObject

2 files changed, 292 insertions(+), 4 deletions(-)

lib/std/Build/Step/CheckObject.zig+232-1
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const assert = std.debug.assert;
3const elf = std.elf;
34const fs = std.fs;
45const macho = std.macho;
56const math = std.math;
......@@ -338,7 +339,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
338339 .macho => try MachODumper.parseAndDump(step, contents, .{
339340 .dump_symtab = self.dump_symtab,
340341 }),
341 .elf => @panic("TODO elf parser"),
342 .elf => try ElfDumper.parseAndDump(step, contents, .{
343 .dump_symtab = self.dump_symtab,
344 }),
342345 .coff => @panic("TODO coff parser"),
343346 .wasm => try WasmDumper.parseAndDump(step, contents, .{
344347 .dump_symtab = self.dump_symtab,
......@@ -695,6 +698,234 @@ const MachODumper = struct {
695698 }
696699};
697700
701const ElfDumper = struct {
702 const symtab_label = "symtab";
703
704 const Symtab = struct {
705 symbols: []align(1) const elf.Elf64_Sym,
706 strings: []const u8,
707
708 fn get(st: Symtab, index: usize) ?elf.Elf64_Sym {
709 if (index >= st.symbols.len) return null;
710 return st.symbols[index];
711 }
712
713 fn getName(st: Symtab, index: usize) ?[]const u8 {
714 const sym = st.get(index) orelse return null;
715 assert(sym.st_name < st.strings.len);
716 return mem.sliceTo(@ptrCast(st.strings.ptr + sym.st_name), 0);
717 }
718 };
719
720 const Context = struct {
721 gpa: Allocator,
722 data: []const u8,
723 hdr: elf.Elf64_Ehdr,
724 shdrs: []align(1) const elf.Elf64_Shdr,
725 phdrs: []align(1) const elf.Elf64_Phdr,
726 shstrtab: []const u8,
727 symtab: ?Symtab = null,
728 dysymtab: ?Symtab = null,
729 };
730
731 fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 {
732 const gpa = step.owner.allocator;
733 var stream = std.io.fixedBufferStream(bytes);
734 const reader = stream.reader();
735
736 const hdr = try reader.readStruct(elf.Elf64_Ehdr);
737 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) {
738 return error.InvalidMagicNumber;
739 }
740
741 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum];
742 const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum];
743
744 var ctx = Context{
745 .gpa = gpa,
746 .data = bytes,
747 .hdr = hdr,
748 .shdrs = shdrs,
749 .phdrs = phdrs,
750 .shstrtab = undefined,
751 };
752 ctx.shstrtab = getSectionContents(ctx, ctx.hdr.e_shstrndx);
753
754 if (opts.dump_symtab) {
755 for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
756 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
757 const raw = getSectionContents(ctx, i);
758 const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym));
759 const symbols = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw.ptr))[0..nsyms];
760 const strings = getSectionContents(ctx, shdr.sh_link);
761
762 switch (shdr.sh_type) {
763 elf.SHT_SYMTAB => {
764 ctx.symtab = .{
765 .symbols = symbols,
766 .strings = strings,
767 };
768 },
769 elf.SHT_DYNSYM => {
770 ctx.dysymtab = .{
771 .symbols = symbols,
772 .strings = strings,
773 };
774 },
775 else => unreachable,
776 }
777 },
778
779 else => {},
780 };
781 }
782
783 var output = std.ArrayList(u8).init(gpa);
784 const writer = output.writer();
785
786 try dumpHeader(ctx, writer);
787 try dumpShdrs(ctx, writer);
788 try dumpPhdrs(ctx, writer);
789
790 return output.toOwnedSlice();
791 }
792
793 fn getSectionName(ctx: Context, shndx: usize) []const u8 {
794 const shdr = ctx.shdrs[shndx];
795 assert(shdr.sh_name < ctx.shstrtab.len);
796 return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.shstrtab.ptr + shdr.sh_name)), 0);
797 }
798
799 fn getSectionContents(ctx: Context, shndx: usize) []const u8 {
800 const shdr = ctx.shdrs[shndx];
801 assert(shdr.sh_offset < ctx.data.len);
802 assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len);
803 return ctx.data[shdr.sh_offset..][0..shdr.sh_size];
804 }
805
806 fn dumpHeader(ctx: Context, writer: anytype) !void {
807 try writer.writeAll("header\n");
808 try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)});
809 try writer.print("entry {x}\n", .{ctx.hdr.e_entry});
810 }
811
812 fn dumpShdrs(ctx: Context, writer: anytype) !void {
813 if (ctx.shdrs.len == 0) return;
814
815 for (ctx.shdrs, 0..) |shdr, shndx| {
816 try writer.print("shdr {d}\n", .{shndx});
817 try writer.print("name {s}\n", .{getSectionName(ctx, shndx)});
818 try writer.print("type {s}\n", .{fmtShType(shdr.sh_type)});
819 try writer.print("addr {x}\n", .{shdr.sh_addr});
820 try writer.print("offset {x}\n", .{shdr.sh_offset});
821 try writer.print("size {x}\n", .{shdr.sh_size});
822 try writer.print("addralign {x}\n", .{shdr.sh_addralign});
823 // TODO dump formatted sh_flags
824 }
825 }
826
827 fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) {
828 return .{ .data = sh_type };
829 }
830
831 fn formatShType(
832 sh_type: u32,
833 comptime unused_fmt_string: []const u8,
834 options: std.fmt.FormatOptions,
835 writer: anytype,
836 ) !void {
837 _ = unused_fmt_string;
838 _ = options;
839 if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) {
840 try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS});
841 } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) {
842 try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC});
843 } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) {
844 try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER});
845 } else {
846 const name = switch (sh_type) {
847 elf.SHT_NULL => "NULL",
848 elf.SHT_PROGBITS => "PROGBITS",
849 elf.SHT_SYMTAB => "SYMTAB",
850 elf.SHT_STRTAB => "STRTAB",
851 elf.SHT_RELA => "RELA",
852 elf.SHT_HASH => "HASH",
853 elf.SHT_DYNAMIC => "DYNAMIC",
854 elf.SHT_NOTE => "NOTE",
855 elf.SHT_NOBITS => "NOBITS",
856 elf.SHT_REL => "REL",
857 elf.SHT_SHLIB => "SHLIB",
858 elf.SHT_DYNSYM => "DYNSYM",
859 elf.SHT_INIT_ARRAY => "INIT_ARRAY",
860 elf.SHT_FINI_ARRAY => "FINI_ARRAY",
861 elf.SHT_PREINIT_ARRAY => "PREINIT_ARRAY",
862 elf.SHT_GROUP => "GROUP",
863 elf.SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX",
864 elf.SHT_X86_64_UNWIND => "X86_64_UNWIND",
865 elf.SHT_LLVM_ADDRSIG => "LLVM_ADDRSIG",
866 elf.SHT_GNU_HASH => "GNU_HASH",
867 elf.SHT_GNU_VERDEF => "VERDEF",
868 elf.SHT_GNU_VERNEED => "VERNEED",
869 elf.SHT_GNU_VERSYM => "VERSYM",
870 else => "UNKNOWN",
871 };
872 try writer.writeAll(name);
873 }
874 }
875
876 fn dumpPhdrs(ctx: Context, writer: anytype) !void {
877 if (ctx.phdrs.len == 0) return;
878
879 for (ctx.phdrs, 0..) |phdr, phndx| {
880 try writer.print("phdr {d}\n", .{phndx});
881 try writer.print("type {s}\n", .{fmtPhType(phdr.p_type)});
882 try writer.print("vaddr {x}\n", .{phdr.p_vaddr});
883 try writer.print("paddr {x}\n", .{phdr.p_paddr});
884 try writer.print("offset {x}\n", .{phdr.p_offset});
885 try writer.print("memsz {x}\n", .{phdr.p_memsz});
886 try writer.print("filesz {x}\n", .{phdr.p_filesz});
887 try writer.print("align {x}\n", .{phdr.p_align});
888 // TODO dump formatted p_flags
889 }
890 }
891
892 fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) {
893 return .{ .data = ph_type };
894 }
895
896 fn formatPhType(
897 ph_type: u32,
898 comptime unused_fmt_string: []const u8,
899 options: std.fmt.FormatOptions,
900 writer: anytype,
901 ) !void {
902 _ = unused_fmt_string;
903 _ = options;
904 if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) {
905 try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS});
906 } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) {
907 try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC});
908 } else {
909 const p_type = switch (ph_type) {
910 elf.PT_NULL => "NULL",
911 elf.PT_LOAD => "LOAD",
912 elf.PT_DYNAMIC => "DYNAMIC",
913 elf.PT_INTERP => "INTERP",
914 elf.PT_NOTE => "NOTE",
915 elf.PT_SHLIB => "SHLIB",
916 elf.PT_PHDR => "PHDR",
917 elf.PT_TLS => "TLS",
918 elf.PT_NUM => "NUM",
919 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",
920 elf.PT_GNU_STACK => "GNU_STACK",
921 elf.PT_GNU_RELRO => "GNU_RELRO",
922 else => "UNKNOWN",
923 };
924 try writer.writeAll(p_type);
925 }
926 }
927};
928
698929const WasmDumper = struct {
699930 const symtab_label = "symbols";
700931
lib/std/elf.zig+60-3
......@@ -221,6 +221,58 @@ pub const DT_IA_64_NUM = 1;
221221
222222pub const DT_NIOS2_GP = 0x70000002;
223223
224pub const DF_ORIGIN = 0x00000001;
225pub const DF_SYMBOLIC = 0x00000002;
226pub const DF_TEXTREL = 0x00000004;
227pub const DF_BIND_NOW = 0x00000008;
228pub const DF_STATIC_TLS = 0x00000010;
229
230pub const DF_1_NOW = 0x00000001;
231pub const DF_1_GLOBAL = 0x00000002;
232pub const DF_1_GROUP = 0x00000004;
233pub const DF_1_NODELETE = 0x00000008;
234pub const DF_1_LOADFLTR = 0x00000010;
235pub const DF_1_INITFIRST = 0x00000020;
236pub const DF_1_NOOPEN = 0x00000040;
237pub const DF_1_ORIGIN = 0x00000080;
238pub const DF_1_DIRECT = 0x00000100;
239pub const DF_1_TRANS = 0x00000200;
240pub const DF_1_INTERPOSE = 0x00000400;
241pub const DF_1_NODEFLIB = 0x00000800;
242pub const DF_1_NODUMP = 0x00001000;
243pub const DF_1_CONFALT = 0x00002000;
244pub const DF_1_ENDFILTEE = 0x00004000;
245pub const DF_1_DISPRELDNE = 0x00008000;
246pub const DF_1_DISPRELPND = 0x00010000;
247pub const DF_1_NODIRECT = 0x00020000;
248pub const DF_1_IGNMULDEF = 0x00040000;
249pub const DF_1_NOKSYMS = 0x00080000;
250pub const DF_1_NOHDR = 0x00100000;
251pub const DF_1_EDITED = 0x00200000;
252pub const DF_1_NORELOC = 0x00400000;
253pub const DF_1_SYMINTPOSE = 0x00800000;
254pub const DF_1_GLOBAUDIT = 0x01000000;
255pub const DF_1_SINGLETON = 0x02000000;
256pub const DF_1_STUB = 0x04000000;
257pub const DF_1_PIE = 0x08000000;
258
259pub const VERSYM_HIDDEN = 0x8000;
260pub const VERSYM_VERSION = 0x7fff;
261
262/// Symbol is local
263pub const VER_NDX_LOCAL = 0;
264/// Symbol is global
265pub const VER_NDX_GLOBAL = 1;
266/// Beginning of reserved entries
267pub const VER_NDX_LORESERVE = 0xff00;
268/// Symbol is to be eliminated
269pub const VER_NDX_ELIMINATE = 0xff01;
270
271/// Version definition of the file itself
272pub const VER_FLG_BASE = 1;
273/// Weak version identifier
274pub const VER_FLG_WEAK = 2;
275
224276/// Program header table entry unused
225277pub const PT_NULL = 0;
226278/// Loadable program segment
......@@ -298,6 +350,14 @@ pub const SHT_SYMTAB_SHNDX = 18;
298350pub const SHT_LOOS = 0x60000000;
299351/// LLVM address-significance table
300352pub const SHT_LLVM_ADDRSIG = 0x6fff4c03;
353/// GNU hash table
354pub const SHT_GNU_HASH = 0x6ffffff6;
355/// GNU version definition table
356pub const SHT_GNU_VERDEF = 0x6ffffffd;
357/// GNU needed versions table
358pub const SHT_GNU_VERNEED = 0x6ffffffe;
359/// GNU symbol version table
360pub const SHT_GNU_VERSYM = 0x6fffffff;
301361/// End of OS-specific
302362pub const SHT_HIOS = 0x6fffffff;
303363/// Start of processor-specific
......@@ -369,9 +429,6 @@ pub const STT_HP_STUB = (STT_LOOS + 0x2);
369429pub const STT_ARM_TFUNC = STT_LOPROC;
370430pub const STT_ARM_16BIT = STT_HIPROC;
371431
372pub const VER_FLG_BASE = 0x1;
373pub const VER_FLG_WEAK = 0x2;
374
375432pub const MAGIC = "\x7fELF";
376433
377434/// File types