| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| | 3 | const elf = std.elf; |
| 3 | const fs = std.fs; | 4 | const fs = std.fs; |
| 4 | const macho = std.macho; | 5 | const macho = std.macho; |
| 5 | const math = std.math; | 6 | const math = std.math; |
| ... | @@ -338,7 +339,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -338,7 +339,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 338 | .macho => try MachODumper.parseAndDump(step, contents, .{ | 339 | .macho => try MachODumper.parseAndDump(step, contents, .{ |
| 339 | .dump_symtab = self.dump_symtab, | 340 | .dump_symtab = self.dump_symtab, |
| 340 | }), | 341 | }), |
| 341 | .elf => @panic("TODO elf parser"), | 342 | .elf => try ElfDumper.parseAndDump(step, contents, .{ |
| | 343 | .dump_symtab = self.dump_symtab, |
| | 344 | }), |
| 342 | .coff => @panic("TODO coff parser"), | 345 | .coff => @panic("TODO coff parser"), |
| 343 | .wasm => try WasmDumper.parseAndDump(step, contents, .{ | 346 | .wasm => try WasmDumper.parseAndDump(step, contents, .{ |
| 344 | .dump_symtab = self.dump_symtab, | 347 | .dump_symtab = self.dump_symtab, |
| ... | @@ -695,6 +698,234 @@ const MachODumper = struct { | ... | @@ -695,6 +698,234 @@ const MachODumper = struct { |
| 695 | } | 698 | } |
| 696 | }; | 699 | }; |
| 697 | | 700 | |
| | 701 | const 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 | |
| 698 | const WasmDumper = struct { | 929 | const WasmDumper = struct { |
| 699 | const symtab_label = "symbols"; | 930 | const symtab_label = "symbols"; |
| 700 | | 931 | |