| ... | @@ -6,51 +6,143 @@ | ... | @@ -6,51 +6,143 @@ |
| 6 | //! | 6 | //! |
| 7 | //! ...each with 'lib/libc.so' inside of them. | 7 | //! ...each with 'lib/libc.so' inside of them. |
| 8 | | 8 | |
| | 9 | // TODO: pick the best index to put them into instead of at the end |
| | 10 | // - e.g. find a common previous symbol and put it after that one |
| | 11 | // - they definitely need to go into the correct section |
| | 12 | // TODO: emit MultiSyms to use the preprocessor |
| | 13 | |
| 9 | const std = @import("std"); | 14 | const std = @import("std"); |
| 10 | const builtin = std.builtin; | 15 | const builtin = std.builtin; |
| 11 | const mem = std.mem; | 16 | const mem = std.mem; |
| | 17 | const log = std.log; |
| 12 | const elf = std.elf; | 18 | const elf = std.elf; |
| 13 | const native_endian = @import("builtin").target.cpu.arch.endian(); | 19 | const native_endian = @import("builtin").target.cpu.arch.endian(); |
| 14 | | 20 | |
| | 21 | const arches: [6]std.Target.Cpu.Arch = blk: { |
| | 22 | var result: [6]std.Target.Cpu.Arch = undefined; |
| | 23 | for (.{ .riscv64, .mips, .i386, .x86_64, .powerpc, .powerpc64 }) |arch| { |
| | 24 | result[archIndex(arch)] = arch; |
| | 25 | } |
| | 26 | break :blk result; |
| | 27 | }; |
| | 28 | |
| | 29 | const MultiSym = struct { |
| | 30 | size: [arches.len]u64, |
| | 31 | present: [arches.len]bool, |
| | 32 | section: u16, |
| | 33 | ty: u4, |
| | 34 | binding: u4, |
| | 35 | visib: elf.STV, |
| | 36 | }; |
| | 37 | |
| | 38 | const Parse = struct { |
| | 39 | arena: mem.Allocator, |
| | 40 | sym_table: *std.StringArrayHashMap(MultiSym), |
| | 41 | sections: *std.StringArrayHashMap(void), |
| | 42 | elf_bytes: []align(@alignOf(elf.Elf64_Ehdr)) u8, |
| | 43 | header: elf.Header, |
| | 44 | arch: std.Target.Cpu.Arch, |
| | 45 | }; |
| | 46 | |
| 15 | pub fn main() !void { | 47 | pub fn main() !void { |
| 16 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 48 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 17 | defer arena_instance.deinit(); | 49 | defer arena_instance.deinit(); |
| 18 | const arena = arena_instance.allocator(); | 50 | const arena = arena_instance.allocator(); |
| 19 | | 51 | |
| 20 | const args = try std.process.argsAlloc(arena); | 52 | const args = try std.process.argsAlloc(arena); |
| 21 | const libc_so_path = args[1]; | 53 | const build_all_path = args[1]; |
| 22 | | 54 | |
| 23 | // Read the ELF header. | 55 | var build_all_dir = try std.fs.cwd().openDir(build_all_path, .{}); |
| 24 | const elf_bytes = try std.fs.cwd().readFileAllocOptions( | 56 | |
| 25 | arena, | 57 | for (arches) |arch| { |
| 26 | libc_so_path, | 58 | const libc_so_path = try std.fmt.allocPrint(arena, "{s}/lib/libc.so", .{@tagName(arch)}); |
| 27 | 100 * 1024 * 1024, | 59 | |
| 28 | 1 * 1024 * 1024, | 60 | // Read the ELF header. |
| 29 | @alignOf(elf.Elf64_Ehdr), | 61 | const elf_bytes = try build_all_dir.readFileAllocOptions( |
| 30 | null, | 62 | arena, |
| 31 | ); | 63 | libc_so_path, |
| 32 | const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]); | 64 | 100 * 1024 * 1024, |
| 33 | | 65 | 1 * 1024 * 1024, |
| 34 | switch (header.is_64) { | 66 | @alignOf(elf.Elf64_Ehdr), |
| 35 | true => switch (header.endian) { | 67 | null, |
| 36 | .Big => return finishMain(arena, elf_bytes, header, true, .Big), | 68 | ); |
| 37 | .Little => return finishMain(arena, elf_bytes, header, true, .Little), | 69 | const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]); |
| 38 | }, | 70 | |
| 39 | false => switch (header.endian) { | 71 | var sym_table = std.StringArrayHashMap(MultiSym).init(arena); |
| 40 | .Big => return finishMain(arena, elf_bytes, header, false, .Big), | 72 | var sections = std.StringArrayHashMap(void).init(arena); |
| 41 | .Little => return finishMain(arena, elf_bytes, header, false, .Little), | 73 | |
| 42 | }, | 74 | const parse: Parse = .{ |
| | 75 | .arena = arena, |
| | 76 | .sym_table = &sym_table, |
| | 77 | .sections = &sections, |
| | 78 | .elf_bytes = elf_bytes, |
| | 79 | .header = header, |
| | 80 | .arch = arch, |
| | 81 | }; |
| | 82 | |
| | 83 | switch (header.is_64) { |
| | 84 | true => switch (header.endian) { |
| | 85 | .Big => try parseElf(parse, true, .Big), |
| | 86 | .Little => try parseElf(parse, true, .Little), |
| | 87 | }, |
| | 88 | false => switch (header.endian) { |
| | 89 | .Big => try parseElf(parse, false, .Big), |
| | 90 | .Little => try parseElf(parse, false, .Little), |
| | 91 | }, |
| | 92 | } |
| 43 | } | 93 | } |
| | 94 | |
| | 95 | const stdout = std.io.getStdOut().writer(); |
| | 96 | _ = stdout; |
| | 97 | |
| | 98 | //var prev_section: u16 = 0; |
| | 99 | //for (all_syms) |sym| { |
| | 100 | // const this_section = s(sym.st_shndx); |
| | 101 | // if (this_section != prev_section) { |
| | 102 | // prev_section = this_section; |
| | 103 | // const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); |
| | 104 | // try stdout.print("{s}\n", .{sh_name}); |
| | 105 | // } |
| | 106 | |
| | 107 | // switch (binding) { |
| | 108 | // elf.STB_GLOBAL => { |
| | 109 | // try stdout.print(".globl {s}\n", .{name}); |
| | 110 | // }, |
| | 111 | // elf.STB_WEAK => { |
| | 112 | // try stdout.print(".weak {s}\n", .{name}); |
| | 113 | // }, |
| | 114 | // else => unreachable, |
| | 115 | // } |
| | 116 | |
| | 117 | // switch (ty) { |
| | 118 | // elf.STT_NOTYPE => {}, |
| | 119 | // elf.STT_FUNC => { |
| | 120 | // try stdout.print(".type {s}, %function;\n", .{name}); |
| | 121 | // // omitting the size is OK for functions |
| | 122 | // }, |
| | 123 | // elf.STT_OBJECT => { |
| | 124 | // try stdout.print(".type {s}, %object;\n", .{name}); |
| | 125 | // if (size != 0) { |
| | 126 | // try stdout.print(".size {s}, {d}\n", .{ name, size }); |
| | 127 | // } |
| | 128 | // }, |
| | 129 | // else => unreachable, |
| | 130 | // } |
| | 131 | |
| | 132 | // switch (visib) { |
| | 133 | // .DEFAULT => {}, |
| | 134 | // .PROTECTED => try stdout.print(".protected {s}\n", .{name}), |
| | 135 | // .INTERNAL, .HIDDEN => unreachable, |
| | 136 | // } |
| | 137 | |
| | 138 | // try stdout.print("{s}:\n", .{name}); |
| | 139 | //} |
| 44 | } | 140 | } |
| 45 | | 141 | |
| 46 | fn finishMain( | 142 | fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void { |
| 47 | arena: mem.Allocator, | 143 | const arena = parse.arena; |
| 48 | elf_bytes: []align(@alignOf(elf.Elf64_Ehdr)) u8, | 144 | const elf_bytes = parse.elf_bytes; |
| 49 | header: elf.Header, | 145 | const header = parse.header; |
| 50 | comptime is_64: bool, | | |
| 51 | comptime endian: builtin.Endian, | | |
| 52 | ) !void { | | |
| 53 | _ = arena; | | |
| 54 | const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; | 146 | const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; |
| 55 | const S = struct { | 147 | const S = struct { |
| 56 | fn endianSwap(x: anytype) @TypeOf(x) { | 148 | fn endianSwap(x: anytype) @TypeOf(x) { |
| ... | @@ -73,17 +165,26 @@ fn finishMain( | ... | @@ -73,17 +165,26 @@ fn finishMain( |
| 73 | | 165 | |
| 74 | // Obtain the section header string table. | 166 | // Obtain the section header string table. |
| 75 | const shstrtab_offset = s(shdrs[header.shstrndx].sh_offset); | 167 | const shstrtab_offset = s(shdrs[header.shstrndx].sh_offset); |
| 76 | std.log.debug("shstrtab is at offset {d}", .{shstrtab_offset}); | 168 | log.debug("shstrtab is at offset {d}", .{shstrtab_offset}); |
| 77 | const shstrtab = elf_bytes[shstrtab_offset..]; | 169 | const shstrtab = elf_bytes[shstrtab_offset..]; |
| 78 | | 170 | |
| | 171 | // Maps this ELF file's section header index to the multi arch section ArrayHashMap index. |
| | 172 | const section_index_map = try arena.alloc(u16, shdrs.len); |
| | 173 | |
| 79 | // Find the offset of the dynamic symbol table. | 174 | // Find the offset of the dynamic symbol table. |
| 80 | const dynsym_index = for (shdrs) |shdr, i| { | 175 | var dynsym_index: u16 = 0; |
| 81 | const sh_name = mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0); | 176 | for (shdrs) |shdr, i| { |
| 82 | std.log.debug("found section: {s}", .{sh_name}); | 177 | const sh_name = try arena.dupe(u8, mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0)); |
| 83 | if (mem.eql(u8, sh_name, ".dynsym")) break @intCast(u16, i); | 178 | log.debug("found section: {s}", .{sh_name}); |
| 84 | } else @panic("did not find the .dynsym section"); | 179 | if (mem.eql(u8, sh_name, ".dynsym")) { |
| | 180 | dynsym_index = @intCast(u16, i); |
| | 181 | } |
| | 182 | const gop = try parse.sections.getOrPut(sh_name); |
| | 183 | section_index_map[i] = @intCast(u16, gop.index); |
| | 184 | } |
| | 185 | if (dynsym_index == 0) @panic("did not find the .dynsym section"); |
| 85 | | 186 | |
| 86 | std.log.debug("found .dynsym section at index {d}", .{dynsym_index}); | 187 | log.debug("found .dynsym section at index {d}", .{dynsym_index}); |
| 87 | | 188 | |
| 88 | // Read the dynamic symbols into a list. | 189 | // Read the dynamic symbols into a list. |
| 89 | const dyn_syms_off = s(shdrs[dynsym_index].sh_offset); | 190 | const dyn_syms_off = s(shdrs[dynsym_index].sh_offset); |
| ... | @@ -96,25 +197,25 @@ fn finishMain( | ... | @@ -96,25 +197,25 @@ fn finishMain( |
| 96 | // Sort the list by address, ascending. | 197 | // Sort the list by address, ascending. |
| 97 | std.sort.sort(Sym, dyn_syms, {}, S.symbolAddrLessThan); | 198 | std.sort.sort(Sym, dyn_syms, {}, S.symbolAddrLessThan); |
| 98 | | 199 | |
| 99 | const stdout = std.io.getStdOut().writer(); | | |
| 100 | | | |
| 101 | var prev_section: u16 = 0; | | |
| 102 | for (dyn_syms) |sym| { | 200 | for (dyn_syms) |sym| { |
| 103 | const name = mem.sliceTo(dynstr[s(sym.st_name)..], 0); | 201 | const this_section = s(sym.st_shndx); |
| | 202 | const name = try arena.dupe(u8, mem.sliceTo(dynstr[s(sym.st_name)..], 0)); |
| 104 | const ty = @truncate(u4, sym.st_info); | 203 | const ty = @truncate(u4, sym.st_info); |
| 105 | const binding = @truncate(u4, sym.st_info >> 4); | 204 | const binding = @truncate(u4, sym.st_info >> 4); |
| 106 | const visib = @intToEnum(elf.STV, @truncate(u2, sym.st_other)); | 205 | const visib = @intToEnum(elf.STV, @truncate(u2, sym.st_other)); |
| 107 | const size = s(sym.st_size); | 206 | const size = s(sym.st_size); |
| 108 | | 207 | |
| 109 | if (size == 0) { | 208 | if (size == 0) { |
| 110 | std.log.warn("symbol '{s}' has size 0", .{name}); | 209 | log.warn("{s}: symbol '{s}' has size 0", .{ @tagName(parse.arch), name }); |
| 111 | continue; | 210 | continue; |
| 112 | } | 211 | } |
| 113 | | 212 | |
| 114 | switch (binding) { | 213 | switch (binding) { |
| 115 | elf.STB_GLOBAL, elf.STB_WEAK => {}, | 214 | elf.STB_GLOBAL, elf.STB_WEAK => {}, |
| 116 | else => { | 215 | else => { |
| 117 | std.log.debug("skipping '{s}' due to it having binding '{d}'", .{ name, binding }); | 216 | log.debug("{s}: skipping '{s}' due to it having binding '{d}'", .{ |
| | 217 | @tagName(parse.arch), name, binding, |
| | 218 | }); |
| 118 | continue; | 219 | continue; |
| 119 | }, | 220 | }, |
| 120 | } | 221 | } |
| ... | @@ -122,7 +223,9 @@ fn finishMain( | ... | @@ -122,7 +223,9 @@ fn finishMain( |
| 122 | switch (ty) { | 223 | switch (ty) { |
| 123 | elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {}, | 224 | elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {}, |
| 124 | else => { | 225 | else => { |
| 125 | std.log.debug("skipping '{s}' due to it having type '{d}'", .{ name, ty }); | 226 | log.debug("{s}: skipping '{s}' due to it having type '{d}'", .{ |
| | 227 | @tagName(parse.arch), name, ty, |
| | 228 | }); |
| 126 | continue; | 229 | continue; |
| 127 | }, | 230 | }, |
| 128 | } | 231 | } |
| ... | @@ -130,51 +233,79 @@ fn finishMain( | ... | @@ -130,51 +233,79 @@ fn finishMain( |
| 130 | switch (visib) { | 233 | switch (visib) { |
| 131 | .DEFAULT, .PROTECTED => {}, | 234 | .DEFAULT, .PROTECTED => {}, |
| 132 | .INTERNAL, .HIDDEN => { | 235 | .INTERNAL, .HIDDEN => { |
| 133 | std.log.debug("skipping '{s}' due to it having visibility '{s}'", .{ | 236 | log.debug("{s}: skipping '{s}' due to it having visibility '{s}'", .{ |
| 134 | name, @tagName(visib), | 237 | @tagName(parse.arch), name, @tagName(visib), |
| 135 | }); | 238 | }); |
| 136 | continue; | 239 | continue; |
| 137 | }, | 240 | }, |
| 138 | } | 241 | } |
| 139 | | 242 | |
| 140 | const this_section = s(sym.st_shndx); | 243 | const gop = try parse.sym_table.getOrPut(name); |
| 141 | if (this_section != prev_section) { | 244 | if (gop.found_existing) { |
| 142 | prev_section = this_section; | 245 | if (gop.value_ptr.section != section_index_map[this_section]) { |
| 143 | const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); | 246 | const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); |
| 144 | try stdout.print("{s}\n", .{sh_name}); | 247 | fatal("symbol '{s}' in arch {s} is in section {s} but in arch {s} is in section {s}", .{ |
| 145 | } | 248 | name, @tagName(parse.arch), sh_name, |
| 146 | | 249 | archSetName(gop.value_ptr.present), parse.sections.keys()[gop.value_ptr.section], |
| 147 | switch (binding) { | 250 | }); |
| 148 | elf.STB_GLOBAL => { | 251 | } |
| 149 | try stdout.print(".globl {s}\n", .{name}); | 252 | if (gop.value_ptr.ty != ty) { |
| 150 | }, | 253 | fatal("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}", .{ |
| 151 | elf.STB_WEAK => { | 254 | name, @tagName(parse.arch), ty, |
| 152 | try stdout.print(".weak {s}\n", .{name}); | 255 | archSetName(gop.value_ptr.present), gop.value_ptr.ty, |
| 153 | }, | 256 | }); |
| 154 | else => unreachable, | 257 | } |
| | 258 | if (gop.value_ptr.binding != binding) { |
| | 259 | fatal("symbol '{s}' in arch {s} has binding {d} but in arch {s} has binding {d}", .{ |
| | 260 | name, @tagName(parse.arch), binding, |
| | 261 | archSetName(gop.value_ptr.present), gop.value_ptr.binding, |
| | 262 | }); |
| | 263 | } |
| | 264 | if (gop.value_ptr.visib != visib) { |
| | 265 | fatal("symbol '{s}' in arch {s} has visib {s} but in arch {s} has visib {s}", .{ |
| | 266 | name, @tagName(parse.arch), @tagName(visib), |
| | 267 | archSetName(gop.value_ptr.present), @tagName(gop.value_ptr.visib), |
| | 268 | }); |
| | 269 | } |
| | 270 | } else { |
| | 271 | gop.value_ptr.* = .{ |
| | 272 | .present = [1]bool{false} ** arches.len, |
| | 273 | .section = section_index_map[this_section], |
| | 274 | .ty = ty, |
| | 275 | .binding = binding, |
| | 276 | .visib = visib, |
| | 277 | .size = [1]u64{0} ** arches.len, |
| | 278 | }; |
| 155 | } | 279 | } |
| | 280 | gop.value_ptr.present[archIndex(parse.arch)] = true; |
| | 281 | gop.value_ptr.size[archIndex(parse.arch)] = size; |
| | 282 | } |
| | 283 | } |
| 156 | | 284 | |
| 157 | switch (ty) { | 285 | fn archIndex(arch: std.Target.Cpu.Arch) u8 { |
| 158 | elf.STT_NOTYPE => {}, | 286 | return switch (arch) { |
| 159 | elf.STT_FUNC => { | 287 | // zig fmt: off |
| 160 | try stdout.print(".type {s}, %function;\n", .{name}); | 288 | .riscv64 => 0, |
| 161 | // omitting the size is OK for functions | 289 | .mips => 1, |
| 162 | }, | 290 | .i386 => 2, |
| 163 | elf.STT_OBJECT => { | 291 | .x86_64 => 3, |
| 164 | try stdout.print(".type {s}, %object;\n", .{name}); | 292 | .powerpc => 4, |
| 165 | if (size != 0) { | 293 | .powerpc64 => 5, |
| 166 | try stdout.print(".size {s}, {d}\n", .{ name, size }); | 294 | else => unreachable, |
| 167 | } | 295 | // zig fmt: on |
| 168 | }, | 296 | }; |
| 169 | else => unreachable, | 297 | } |
| 170 | } | | |
| 171 | | 298 | |
| 172 | switch (visib) { | 299 | fn archSetName(arch_set: [arches.len]bool) []const u8 { |
| 173 | .DEFAULT => {}, | 300 | for (arches) |arch, i| { |
| 174 | .PROTECTED => try stdout.print(".protected {s}\n", .{name}), | 301 | if (arch_set[i]) { |
| 175 | .INTERNAL, .HIDDEN => unreachable, | 302 | return @tagName(arch); |
| 176 | } | 303 | } |
| 177 | | | |
| 178 | try stdout.print("{s}:\n", .{name}); | | |
| 179 | } | 304 | } |
| | 305 | return "(none)"; |
| | 306 | } |
| | 307 | |
| | 308 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| | 309 | log.err(format, args); |
| | 310 | std.process.exit(1); |
| 180 | } | 311 | } |