| ... | ... | @@ -6,51 +6,143 @@ |
| 6 | 6 | //! |
| 7 | 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 | 14 | const std = @import("std"); |
| 10 | 15 | const builtin = std.builtin; |
| 11 | 16 | const mem = std.mem; |
| 17 | const log = std.log; |
| 12 | 18 | const elf = std.elf; |
| 13 | 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 | 47 | pub fn main() !void { |
| 16 | 48 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 17 | 49 | defer arena_instance.deinit(); |
| 18 | 50 | const arena = arena_instance.allocator(); |
| 19 | 51 | |
| 20 | 52 | const args = try std.process.argsAlloc(arena); |
| 21 | | const libc_so_path = args[1]; |
| 22 | | |
| 23 | | // Read the ELF header. |
| 24 | | const elf_bytes = try std.fs.cwd().readFileAllocOptions( |
| 25 | | arena, |
| 26 | | libc_so_path, |
| 27 | | 100 * 1024 * 1024, |
| 28 | | 1 * 1024 * 1024, |
| 29 | | @alignOf(elf.Elf64_Ehdr), |
| 30 | | null, |
| 31 | | ); |
| 32 | | const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]); |
| 33 | | |
| 34 | | switch (header.is_64) { |
| 35 | | true => switch (header.endian) { |
| 36 | | .Big => return finishMain(arena, elf_bytes, header, true, .Big), |
| 37 | | .Little => return finishMain(arena, elf_bytes, header, true, .Little), |
| 38 | | }, |
| 39 | | false => switch (header.endian) { |
| 40 | | .Big => return finishMain(arena, elf_bytes, header, false, .Big), |
| 41 | | .Little => return finishMain(arena, elf_bytes, header, false, .Little), |
| 42 | | }, |
| 53 | const build_all_path = args[1]; |
| 54 | |
| 55 | var build_all_dir = try std.fs.cwd().openDir(build_all_path, .{}); |
| 56 | |
| 57 | for (arches) |arch| { |
| 58 | const libc_so_path = try std.fmt.allocPrint(arena, "{s}/lib/libc.so", .{@tagName(arch)}); |
| 59 | |
| 60 | // Read the ELF header. |
| 61 | const elf_bytes = try build_all_dir.readFileAllocOptions( |
| 62 | arena, |
| 63 | libc_so_path, |
| 64 | 100 * 1024 * 1024, |
| 65 | 1 * 1024 * 1024, |
| 66 | @alignOf(elf.Elf64_Ehdr), |
| 67 | null, |
| 68 | ); |
| 69 | const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]); |
| 70 | |
| 71 | var sym_table = std.StringArrayHashMap(MultiSym).init(arena); |
| 72 | var sections = std.StringArrayHashMap(void).init(arena); |
| 73 | |
| 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( |
| 47 | | arena: mem.Allocator, |
| 48 | | elf_bytes: []align(@alignOf(elf.Elf64_Ehdr)) u8, |
| 49 | | header: elf.Header, |
| 50 | | comptime is_64: bool, |
| 51 | | comptime endian: builtin.Endian, |
| 52 | | ) !void { |
| 53 | | _ = arena; |
| 142 | fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void { |
| 143 | const arena = parse.arena; |
| 144 | const elf_bytes = parse.elf_bytes; |
| 145 | const header = parse.header; |
| 54 | 146 | const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; |
| 55 | 147 | const S = struct { |
| 56 | 148 | fn endianSwap(x: anytype) @TypeOf(x) { |
| ... | ... | @@ -73,17 +165,26 @@ fn finishMain( |
| 73 | 165 | |
| 74 | 166 | // Obtain the section header string table. |
| 75 | 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 | 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 | 174 | // Find the offset of the dynamic symbol table. |
| 80 | | const dynsym_index = for (shdrs) |shdr, i| { |
| 81 | | const sh_name = mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0); |
| 82 | | std.log.debug("found section: {s}", .{sh_name}); |
| 83 | | if (mem.eql(u8, sh_name, ".dynsym")) break @intCast(u16, i); |
| 84 | | } else @panic("did not find the .dynsym section"); |
| 175 | var dynsym_index: u16 = 0; |
| 176 | for (shdrs) |shdr, i| { |
| 177 | const sh_name = try arena.dupe(u8, mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0)); |
| 178 | log.debug("found section: {s}", .{sh_name}); |
| 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 | 189 | // Read the dynamic symbols into a list. |
| 89 | 190 | const dyn_syms_off = s(shdrs[dynsym_index].sh_offset); |
| ... | ... | @@ -96,25 +197,25 @@ fn finishMain( |
| 96 | 197 | // Sort the list by address, ascending. |
| 97 | 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 | 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 | 203 | const ty = @truncate(u4, sym.st_info); |
| 105 | 204 | const binding = @truncate(u4, sym.st_info >> 4); |
| 106 | 205 | const visib = @intToEnum(elf.STV, @truncate(u2, sym.st_other)); |
| 107 | 206 | const size = s(sym.st_size); |
| 108 | 207 | |
| 109 | 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 | 210 | continue; |
| 112 | 211 | } |
| 113 | 212 | |
| 114 | 213 | switch (binding) { |
| 115 | 214 | elf.STB_GLOBAL, elf.STB_WEAK => {}, |
| 116 | 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 | 219 | continue; |
| 119 | 220 | }, |
| 120 | 221 | } |
| ... | ... | @@ -122,7 +223,9 @@ fn finishMain( |
| 122 | 223 | switch (ty) { |
| 123 | 224 | elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {}, |
| 124 | 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 | 229 | continue; |
| 127 | 230 | }, |
| 128 | 231 | } |
| ... | ... | @@ -130,51 +233,79 @@ fn finishMain( |
| 130 | 233 | switch (visib) { |
| 131 | 234 | .DEFAULT, .PROTECTED => {}, |
| 132 | 235 | .INTERNAL, .HIDDEN => { |
| 133 | | std.log.debug("skipping '{s}' due to it having visibility '{s}'", .{ |
| 134 | | name, @tagName(visib), |
| 236 | log.debug("{s}: skipping '{s}' due to it having visibility '{s}'", .{ |
| 237 | @tagName(parse.arch), name, @tagName(visib), |
| 135 | 238 | }); |
| 136 | 239 | continue; |
| 137 | 240 | }, |
| 138 | 241 | } |
| 139 | 242 | |
| 140 | | const this_section = s(sym.st_shndx); |
| 141 | | if (this_section != prev_section) { |
| 142 | | prev_section = this_section; |
| 143 | | const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); |
| 144 | | try stdout.print("{s}\n", .{sh_name}); |
| 145 | | } |
| 146 | | |
| 147 | | switch (binding) { |
| 148 | | elf.STB_GLOBAL => { |
| 149 | | try stdout.print(".globl {s}\n", .{name}); |
| 150 | | }, |
| 151 | | elf.STB_WEAK => { |
| 152 | | try stdout.print(".weak {s}\n", .{name}); |
| 153 | | }, |
| 154 | | else => unreachable, |
| 243 | const gop = try parse.sym_table.getOrPut(name); |
| 244 | if (gop.found_existing) { |
| 245 | if (gop.value_ptr.section != section_index_map[this_section]) { |
| 246 | const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); |
| 247 | fatal("symbol '{s}' in arch {s} is in section {s} but in arch {s} is in section {s}", .{ |
| 248 | name, @tagName(parse.arch), sh_name, |
| 249 | archSetName(gop.value_ptr.present), parse.sections.keys()[gop.value_ptr.section], |
| 250 | }); |
| 251 | } |
| 252 | if (gop.value_ptr.ty != ty) { |
| 253 | fatal("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}", .{ |
| 254 | name, @tagName(parse.arch), ty, |
| 255 | archSetName(gop.value_ptr.present), gop.value_ptr.ty, |
| 256 | }); |
| 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) { |
| 158 | | elf.STT_NOTYPE => {}, |
| 159 | | elf.STT_FUNC => { |
| 160 | | try stdout.print(".type {s}, %function;\n", .{name}); |
| 161 | | // omitting the size is OK for functions |
| 162 | | }, |
| 163 | | elf.STT_OBJECT => { |
| 164 | | try stdout.print(".type {s}, %object;\n", .{name}); |
| 165 | | if (size != 0) { |
| 166 | | try stdout.print(".size {s}, {d}\n", .{ name, size }); |
| 167 | | } |
| 168 | | }, |
| 169 | | else => unreachable, |
| 170 | | } |
| 285 | fn archIndex(arch: std.Target.Cpu.Arch) u8 { |
| 286 | return switch (arch) { |
| 287 | // zig fmt: off |
| 288 | .riscv64 => 0, |
| 289 | .mips => 1, |
| 290 | .i386 => 2, |
| 291 | .x86_64 => 3, |
| 292 | .powerpc => 4, |
| 293 | .powerpc64 => 5, |
| 294 | else => unreachable, |
| 295 | // zig fmt: on |
| 296 | }; |
| 297 | } |
| 171 | 298 | |
| 172 | | switch (visib) { |
| 173 | | .DEFAULT => {}, |
| 174 | | .PROTECTED => try stdout.print(".protected {s}\n", .{name}), |
| 175 | | .INTERNAL, .HIDDEN => unreachable, |
| 299 | fn archSetName(arch_set: [arches.len]bool) []const u8 { |
| 300 | for (arches) |arch, i| { |
| 301 | if (arch_set[i]) { |
| 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 | } |