authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-08 23:45:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-08 23:45:25-07:00
log04d44db6cc764c1fceca27fe8f0d3651142ba5f3
tree4f4cd6a5b0ef012ce9c8b5f5281dcd92268d6195
parent808b9239a3f5d32ffb5207ce48ef417e56a80720

tools/gen_stubs: consolidate symbol properties into MultiSym


1 files changed, 212 insertions(+), 81 deletions(-)

tools/gen_stubs.zig+212-81
......@@ -6,51 +6,143 @@
66//!
77//! ...each with 'lib/libc.so' inside of them.
88
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
914const std = @import("std");
1015const builtin = std.builtin;
1116const mem = std.mem;
17const log = std.log;
1218const elf = std.elf;
1319const native_endian = @import("builtin").target.cpu.arch.endian();
1420
21const 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
29const 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
38const 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
1547pub fn main() !void {
1648 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1749 defer arena_instance.deinit();
1850 const arena = arena_instance.allocator();
1951
2052 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 }
4393 }
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 //}
44140}
45141
46fn 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;
142fn 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;
54146 const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym;
55147 const S = struct {
56148 fn endianSwap(x: anytype) @TypeOf(x) {
......@@ -73,17 +165,26 @@ fn finishMain(
73165
74166 // Obtain the section header string table.
75167 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});
77169 const shstrtab = elf_bytes[shstrtab_offset..];
78170
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
79174 // 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");
85186
86 std.log.debug("found .dynsym section at index {d}", .{dynsym_index});
187 log.debug("found .dynsym section at index {d}", .{dynsym_index});
87188
88189 // Read the dynamic symbols into a list.
89190 const dyn_syms_off = s(shdrs[dynsym_index].sh_offset);
......@@ -96,25 +197,25 @@ fn finishMain(
96197 // Sort the list by address, ascending.
97198 std.sort.sort(Sym, dyn_syms, {}, S.symbolAddrLessThan);
98199
99 const stdout = std.io.getStdOut().writer();
100
101 var prev_section: u16 = 0;
102200 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));
104203 const ty = @truncate(u4, sym.st_info);
105204 const binding = @truncate(u4, sym.st_info >> 4);
106205 const visib = @intToEnum(elf.STV, @truncate(u2, sym.st_other));
107206 const size = s(sym.st_size);
108207
109208 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 });
111210 continue;
112211 }
113212
114213 switch (binding) {
115214 elf.STB_GLOBAL, elf.STB_WEAK => {},
116215 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 });
118219 continue;
119220 },
120221 }
......@@ -122,7 +223,9 @@ fn finishMain(
122223 switch (ty) {
123224 elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {},
124225 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 });
126229 continue;
127230 },
128231 }
......@@ -130,51 +233,79 @@ fn finishMain(
130233 switch (visib) {
131234 .DEFAULT, .PROTECTED => {},
132235 .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),
135238 });
136239 continue;
137240 },
138241 }
139242
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 };
155279 }
280 gop.value_ptr.present[archIndex(parse.arch)] = true;
281 gop.value_ptr.size[archIndex(parse.arch)] = size;
282 }
283}
156284
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 }
285fn 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}
171298
172 switch (visib) {
173 .DEFAULT => {},
174 .PROTECTED => try stdout.print(".protected {s}\n", .{name}),
175 .INTERNAL, .HIDDEN => unreachable,
299fn archSetName(arch_set: [arches.len]bool) []const u8 {
300 for (arches) |arch, i| {
301 if (arch_set[i]) {
302 return @tagName(arch);
176303 }
177
178 try stdout.print("{s}:\n", .{name});
179304 }
305 return "(none)";
306}
307
308fn fatal(comptime format: []const u8, args: anytype) noreturn {
309 log.err(format, args);
310 std.process.exit(1);
180311}