authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-20 23:37:22+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-20 23:37:22+02:00
log5276ce8e638d3d295c443d8bc33e5c51538ae540
tree27f3a465a4a5f95d39d857c29c7ba7a6be617873
parenta442b165f1e219b429e497e1de26780612762871

macho: use adapters to directly reference strtab

Thanks to this, we no longer need to do allocs per symbol name landing in the symbol resolver, plus we do not need to actively track if the string was already inserted into the string table.

2 files changed, 106 insertions(+), 55 deletions(-)

src/link/MachO.zig+98-53
......@@ -127,7 +127,7 @@ globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
127127imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
128128undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
129129tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
130symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
130symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
131131
132132locals_free_list: std.ArrayListUnmanaged(u32) = .{},
133133globals_free_list: std.ArrayListUnmanaged(u32) = .{},
......@@ -135,6 +135,7 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},
135135stub_helper_stubs_start_off: ?u64 = null,
136136
137137strtab: std.ArrayListUnmanaged(u8) = .{},
138strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
138139
139140got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{},
140141got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{},
......@@ -199,6 +200,33 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
199200/// backends.
200201pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
201202
203const StringIndexContext = struct {
204 strtab: *std.ArrayListUnmanaged(u8),
205
206 pub fn eql(self: StringIndexContext, a: u32, b: u32) bool {
207 return a == b;
208 }
209
210 pub fn hash(self: StringIndexContext, x: u32) u64 {
211 const x_slice = mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr) + x);
212 return std.hash_map.hashString(x_slice);
213 }
214};
215
216pub const StringSliceAdapter = struct {
217 strtab: *std.ArrayListUnmanaged(u8),
218
219 pub fn eql(self: StringSliceAdapter, a_slice: []const u8, b: u32) bool {
220 const b_slice = mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr) + b);
221 return mem.eql(u8, a_slice, b_slice);
222 }
223
224 pub fn hash(self: StringSliceAdapter, adapted_key: []const u8) u64 {
225 _ = self;
226 return std.hash_map.hashString(adapted_key);
227 }
228};
229
202230const SymbolWithLoc = struct {
203231 // Table where the symbol can be found.
204232 where: enum {
......@@ -882,7 +910,10 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
882910
883911 {
884912 // Add dyld_stub_binder as the final GOT entry.
885 const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
913 const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{
914 .strtab = &self.strtab,
915 }) orelse unreachable;
916 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
886917 const got_index = @intCast(u32, self.got_entries.items.len);
887918 const got_entry = GotIndirectionKey{
888919 .where = .import,
......@@ -1722,14 +1753,17 @@ fn allocateTextBlocks(self: *MachO) !void {
17221753 }
17231754
17241755 // Update globals
1725 for (self.symbol_resolver.values()) |resolv| {
1726 if (resolv.where != .global) continue;
1727
1728 assert(resolv.local_sym_index != 0);
1729 const local_sym = self.locals.items[resolv.local_sym_index];
1730 const sym = &self.globals.items[resolv.where_index];
1731 sym.n_value = local_sym.n_value;
1732 sym.n_sect = local_sym.n_sect;
1756 {
1757 var sym_it = self.symbol_resolver.valueIterator();
1758 while (sym_it.next()) |resolv| {
1759 if (resolv.where != .global) continue;
1760
1761 assert(resolv.local_sym_index != 0);
1762 const local_sym = self.locals.items[resolv.local_sym_index];
1763 const sym = &self.globals.items[resolv.where_index];
1764 sym.n_value = local_sym.n_value;
1765 sym.n_sect = local_sym.n_sect;
1766 }
17331767 }
17341768}
17351769
......@@ -1821,7 +1855,10 @@ fn writeStubHelperCommon(self: *MachO) !void {
18211855 code[9] = 0xff;
18221856 code[10] = 0x25;
18231857 {
1824 const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
1858 const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{
1859 .strtab = &self.strtab,
1860 }) orelse unreachable;
1861 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
18251862 const got_index = self.got_entries_map.get(.{
18261863 .where = .import,
18271864 .where_index = resolv.where_index,
......@@ -1869,7 +1906,10 @@ fn writeStubHelperCommon(self: *MachO) !void {
18691906 code[10] = 0xbf;
18701907 code[11] = 0xa9;
18711908 binder_blk_outer: {
1872 const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
1909 const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{
1910 .strtab = &self.strtab,
1911 }) orelse unreachable;
1912 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
18731913 const got_index = self.got_entries_map.get(.{
18741914 .where = .import,
18751915 .where_index = resolv.where_index,
......@@ -1965,19 +2005,9 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
19652005 return error.UnhandledSymbolType;
19662006 }
19672007
2008 const n_strx = try self.makeString(sym_name);
19682009 if (symbolIsSect(sym)) {
19692010 // Defined symbol regardless of scope lands in the locals symbol table.
1970 const n_strx = blk: {
1971 if (self.symbol_resolver.get(sym_name)) |resolv| {
1972 switch (resolv.where) {
1973 .global => break :blk self.globals.items[resolv.where_index].n_strx,
1974 .tentative => break :blk self.tentatives.items[resolv.where_index].n_strx,
1975 .undef => break :blk self.undefs.items[resolv.where_index].n_strx,
1976 .import => unreachable,
1977 }
1978 }
1979 break :blk try self.makeString(sym_name);
1980 };
19812011 const local_sym_index = @intCast(u32, self.locals.items.len);
19822012 try self.locals.append(self.base.allocator, .{
19832013 .n_strx = n_strx,
......@@ -1993,7 +2023,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
19932023 if (!symbolIsExt(sym)) continue;
19942024
19952025 const local = self.locals.items[local_sym_index];
1996 const resolv = self.symbol_resolver.getPtr(sym_name) orelse {
2026 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
19972027 const global_sym_index = @intCast(u32, self.globals.items.len);
19982028 try self.globals.append(self.base.allocator, .{
19992029 .n_strx = n_strx,
......@@ -2002,7 +2032,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
20022032 .n_desc = sym.n_desc,
20032033 .n_value = sym.n_value,
20042034 });
2005 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{
2035 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
20062036 .where = .global,
20072037 .where_index = global_sym_index,
20082038 .local_sym_index = local_sym_index,
......@@ -2072,7 +2102,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
20722102 };
20732103 } else if (symbolIsTentative(sym)) {
20742104 // Symbol is a tentative definition.
2075 const resolv = self.symbol_resolver.getPtr(sym_name) orelse {
2105 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
20762106 const tent_sym_index = @intCast(u32, self.tentatives.items.len);
20772107 try self.tentatives.append(self.base.allocator, .{
20782108 .n_strx = try self.makeString(sym_name),
......@@ -2081,7 +2111,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
20812111 .n_desc = sym.n_desc,
20822112 .n_value = sym.n_value,
20832113 });
2084 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{
2114 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
20852115 .where = .tentative,
20862116 .where_index = tent_sym_index,
20872117 .file = object_id,
......@@ -2126,7 +2156,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
21262156 }
21272157 } else {
21282158 // Symbol is undefined.
2129 if (self.symbol_resolver.contains(sym_name)) continue;
2159 if (self.symbol_resolver.contains(n_strx)) continue;
21302160
21312161 const undef_sym_index = @intCast(u32, self.undefs.items.len);
21322162 try self.undefs.append(self.base.allocator, .{
......@@ -2136,7 +2166,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
21362166 .n_desc = 0,
21372167 .n_value = 0,
21382168 });
2139 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{
2169 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
21402170 .where = .undef,
21412171 .where_index = undef_sym_index,
21422172 .file = object_id,
......@@ -2214,7 +2244,7 @@ fn resolveSymbols(self: *MachO) !void {
22142244 mem.set(u8, code, 0);
22152245 const alignment = (sym.n_desc >> 8) & 0x0f;
22162246
2217 const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable;
2247 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;
22182248 const local_sym_index = @intCast(u32, self.locals.items.len);
22192249 var nlist = macho.nlist_64{
22202250 .n_strx = sym.n_strx,
......@@ -2263,15 +2293,16 @@ fn resolveSymbols(self: *MachO) !void {
22632293 // Third pass, resolve symbols in dynamic libraries.
22642294 {
22652295 // Put dyld_stub_binder as an undefined special symbol.
2296 const n_strx = try self.makeString("dyld_stub_binder");
22662297 const undef_sym_index = @intCast(u32, self.undefs.items.len);
22672298 try self.undefs.append(self.base.allocator, .{
2268 .n_strx = try self.makeString("dyld_stub_binder"),
2299 .n_strx = n_strx,
22692300 .n_type = macho.N_UNDF,
22702301 .n_sect = 0,
22712302 .n_desc = 0,
22722303 .n_value = 0,
22732304 });
2274 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, "dyld_stub_binder"), .{
2305 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
22752306 .where = .undef,
22762307 .where_index = undef_sym_index,
22772308 });
......@@ -2304,7 +2335,7 @@ fn resolveSymbols(self: *MachO) !void {
23042335 try referenced.putNoClobber(dylib, {});
23052336 }
23062337
2307 const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable;
2338 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;
23082339 const undef = &self.undefs.items[resolv.where_index];
23092340 const import_sym_index = @intCast(u32, self.imports.items.len);
23102341 try self.imports.append(self.base.allocator, .{
......@@ -2331,7 +2362,10 @@ fn resolveSymbols(self: *MachO) !void {
23312362 }
23322363
23332364 // Fourth pass, handle synthetic symbols and flag any undefined references.
2334 if (self.symbol_resolver.getPtr("___dso_handle")) |resolv| blk: {
2365 if (self.strtab_dir.getAdapted(@as([]const u8, "___dso_handle"), StringSliceAdapter{
2366 .strtab = &self.strtab,
2367 })) |n_strx| blk: {
2368 const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk;
23352369 if (resolv.where != .undef) break :blk;
23362370
23372371 const undef = &self.undefs.items[resolv.where_index];
......@@ -2390,7 +2424,7 @@ fn resolveSymbols(self: *MachO) !void {
23902424 if (symbolIsNull(sym)) continue;
23912425
23922426 const sym_name = self.getString(sym.n_strx);
2393 const resolv = self.symbol_resolver.get(sym_name) orelse unreachable;
2427 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
23942428
23952429 log.err("undefined reference to symbol '{s}'", .{sym_name});
23962430 log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?});
......@@ -2830,10 +2864,13 @@ fn setEntryPoint(self: *MachO) !void {
28302864 // TODO we should respect the -entry flag passed in by the user to set a custom
28312865 // entrypoint. For now, assume default of `_main`.
28322866 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2833 const resolv = self.symbol_resolver.get("_main") orelse {
2867 const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "_main"), StringSliceAdapter{
2868 .strtab = &self.strtab,
2869 }) orelse {
28342870 log.err("'_main' export not found", .{});
28352871 return error.MissingMainEntrypoint;
28362872 };
2873 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
28372874 assert(resolv.where == .global);
28382875 const sym = self.globals.items[resolv.where_index];
28392876 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
......@@ -3235,6 +3272,7 @@ pub fn deinit(self: *MachO) void {
32353272 self.got_entries_free_list.deinit(self.base.allocator);
32363273 self.stubs.deinit(self.base.allocator);
32373274 self.stubs_map.deinit(self.base.allocator);
3275 self.strtab_dir.deinit(self.base.allocator);
32383276 self.strtab.deinit(self.base.allocator);
32393277 self.undefs.deinit(self.base.allocator);
32403278 self.tentatives.deinit(self.base.allocator);
......@@ -3243,10 +3281,6 @@ pub fn deinit(self: *MachO) void {
32433281 self.globals_free_list.deinit(self.base.allocator);
32443282 self.locals.deinit(self.base.allocator);
32453283 self.locals_free_list.deinit(self.base.allocator);
3246
3247 for (self.symbol_resolver.keys()) |key| {
3248 self.base.allocator.free(key);
3249 }
32503284 self.symbol_resolver.deinit(self.base.allocator);
32513285
32523286 for (self.objects.items) |object| {
......@@ -3715,9 +3749,7 @@ pub fn updateDeclExports(
37153749 .n_desc = n_desc,
37163750 .n_value = decl_sym.n_value,
37173751 };
3718 const resolv_name = try self.base.allocator.dupe(u8, exp_name);
3719 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, resolv_name);
3720 defer if (resolv.found_existing) self.base.allocator.free(resolv_name);
3752 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, name_str_index);
37213753 resolv.value_ptr.* = .{
37223754 .where = .global,
37233755 .where_index = i,
......@@ -4233,17 +4265,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
42334265 });
42344266 self.load_commands_dirty = true;
42354267 }
4236 if (!self.symbol_resolver.contains("dyld_stub_binder")) {
4268 if (!self.strtab_dir.containsAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{
4269 .strtab = &self.strtab,
4270 })) {
42374271 const import_sym_index = @intCast(u32, self.imports.items.len);
4272 const n_strx = try self.makeString("dyld_stub_binder");
42384273 try self.imports.append(self.base.allocator, .{
4239 .n_strx = try self.makeString("dyld_stub_binder"),
4274 .n_strx = n_strx,
42404275 .n_type = macho.N_UNDF | macho.N_EXT,
42414276 .n_sect = 0,
42424277 .n_desc = packDylibOrdinal(1),
42434278 .n_value = 0,
42444279 });
4245 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
4246 try self.symbol_resolver.putNoClobber(self.base.allocator, name, .{
4280 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
42474281 .where = .import,
42484282 .where_index = import_sym_index,
42494283 });
......@@ -4370,24 +4404,26 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
43704404
43714405pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
43724406 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4373 const already_defined = self.symbol_resolver.contains(sym_name);
4407 defer self.base.allocator.free(sym_name);
43744408
4375 if (already_defined) {
4376 const resolv = self.symbol_resolver.get(sym_name) orelse unreachable;
4377 self.base.allocator.free(sym_name);
4409 if (self.strtab_dir.getAdapted(@as([]const u8, sym_name), StringSliceAdapter{
4410 .strtab = &self.strtab,
4411 })) |n_strx| {
4412 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
43784413 return resolv.where_index;
43794414 }
43804415
43814416 log.debug("adding new extern function '{s}' with dylib ordinal 1", .{sym_name});
43824417 const import_sym_index = @intCast(u32, self.imports.items.len);
4418 const n_strx = try self.makeString(sym_name);
43834419 try self.imports.append(self.base.allocator, .{
4384 .n_strx = try self.makeString(sym_name),
4420 .n_strx = n_strx,
43854421 .n_type = macho.N_UNDF | macho.N_EXT,
43864422 .n_sect = 0,
43874423 .n_desc = packDylibOrdinal(1),
43884424 .n_value = 0,
43894425 });
4390 try self.symbol_resolver.putNoClobber(self.base.allocator, sym_name, .{
4426 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
43914427 .where = .import,
43924428 .where_index = import_sym_index,
43934429 });
......@@ -5609,6 +5645,11 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
56095645}
56105646
56115647pub fn makeString(self: *MachO, string: []const u8) !u32 {
5648 if (self.strtab_dir.getAdapted(@as([]const u8, string), StringSliceAdapter{ .strtab = &self.strtab })) |off| {
5649 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
5650 return off;
5651 }
5652
56125653 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);
56135654 const new_off = @intCast(u32, self.strtab.items.len);
56145655
......@@ -5617,6 +5658,10 @@ pub fn makeString(self: *MachO, string: []const u8) !u32 {
56175658 self.strtab.appendSliceAssumeCapacity(string);
56185659 self.strtab.appendAssumeCapacity(0);
56195660
5661 try self.strtab_dir.putContext(self.base.allocator, new_off, new_off, StringIndexContext{
5662 .strtab = &self.strtab,
5663 });
5664
56205665 return new_off;
56215666}
56225667
src/link/MachO/TextBlock.zig+8-2
......@@ -640,7 +640,10 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
640640 parsed_rel.where = .local;
641641 parsed_rel.where_index = where_index;
642642 } else {
643 const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable;
643 const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
644 .strtab = &ctx.macho_file.strtab,
645 }) orelse unreachable;
646 const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
644647 switch (resolv.where) {
645648 .global => {
646649 parsed_rel.where = .local;
......@@ -704,7 +707,10 @@ pub fn parseRelocsFromObject(
704707 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
705708 subtractor = where_index;
706709 } else {
707 const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable;
710 const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
711 .strtab = &ctx.macho_file.strtab,
712 }) orelse unreachable;
713 const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
708714 assert(resolv.where == .global);
709715 subtractor = resolv.local_sym_index;
710716 }