authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-17 17:20:55+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-17 21:01:52+01:00
log81183365852e7f871500a3f71810ae3b468f0027
tree96a4de9d67ed5cf4330c81564c23973db381a7d3
parent3562edf13772bca66b4f04fc87be25b518393221

macho: refactor undef symbol handling

Now, we don't erroneously write to the string table on every write of global and undef symbols.

3 files changed, 80 insertions(+), 108 deletions(-)

src/codegen.zig+5-13
......@@ -1920,21 +1920,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19201920 }
19211921 } else if (func_value.castTag(.extern_fn)) |func_payload| {
19221922 const decl = func_payload.data;
1923 // We don't free the decl_name immediately unless it already exists.
1924 // If it doesn't, it will get autofreed when we clean up the extern symbol table.
19251923 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
1924 defer self.bin_file.allocator.free(decl_name);
19261925 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
1927 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index| blk: {
1928 self.bin_file.allocator.free(decl_name);
1929 break :blk @intCast(u32, index);
1930 } else blk: {
1931 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
1932 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
1933 .name = decl_name,
1934 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
1935 });
1936 break :blk index;
1937 };
1926 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|
1927 @intCast(u32, index)
1928 else
1929 try macho_file.addExternSymbol(decl_name);
19381930 const start = self.code.items.len;
19391931 const len: usize = blk: {
19401932 switch (arch) {
src/link/MachO.zig+39-50
......@@ -1011,11 +1011,11 @@ pub fn deinit(self: *MachO) void {
10111011 ds.deinit(self.base.allocator);
10121012 }
10131013 for (self.extern_lazy_symbols.items()) |*entry| {
1014 entry.value.deinit(self.base.allocator);
1014 self.base.allocator.free(entry.key);
10151015 }
10161016 self.extern_lazy_symbols.deinit(self.base.allocator);
10171017 for (self.extern_nonlazy_symbols.items()) |*entry| {
1018 entry.value.deinit(self.base.allocator);
1018 self.base.allocator.free(entry.key);
10191019 }
10201020 self.extern_nonlazy_symbols.deinit(self.base.allocator);
10211021 self.pie_fixups.deinit(self.base.allocator);
......@@ -2042,9 +2042,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20422042 }
20432043 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
20442044 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2045 const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
2045 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
2046 const offset = try self.makeString("dyld_stub_binder");
20462047 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2047 .name = name,
2048 .inner = .{
2049 .n_strx = offset,
2050 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2051 .n_sect = 0,
2052 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2053 .n_value = 0,
2054 },
20482055 .dylib_ordinal = 1, // TODO this is currently hardcoded.
20492056 .segment = self.data_const_segment_cmd_index.?,
20502057 .offset = index * @sizeOf(u64),
......@@ -2222,15 +2229,15 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
22222229 return buf;
22232230}
22242231
2225pub fn makeString(self: *MachO, bytes: []const u8) !u32 {
2232fn makeString(self: *MachO, bytes: []const u8) !u32 {
22262233 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
2227 const result = @intCast(u32, self.string_table.items.len);
2234 const offset = @intCast(u32, self.string_table.items.len);
22282235 self.string_table.appendSliceAssumeCapacity(bytes);
22292236 self.string_table.appendAssumeCapacity(0);
22302237 self.string_table_dirty = true;
22312238 if (self.d_sym) |*ds|
22322239 ds.string_table_dirty = true;
2233 return result;
2240 return offset;
22342241}
22352242
22362243fn getString(self: *MachO, str_off: u32) []const u8 {
......@@ -2246,6 +2253,23 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
22462253 return self.makeString(new_name);
22472254}
22482255
2256pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2257 const index = @intCast(u32, self.extern_lazy_symbols.items().len);
2258 const offset = try self.makeString(name);
2259 const sym_name = try self.base.allocator.dupe(u8, name);
2260 try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{
2261 .inner = .{
2262 .n_strx = offset,
2263 .n_type = macho.N_UNDF | macho.N_EXT,
2264 .n_sect = 0,
2265 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
2266 .n_value = 0,
2267 },
2268 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
2269 });
2270 return index;
2271}
2272
22492273const NextSegmentAddressAndOffset = struct {
22502274 address: u64,
22512275 offset: u64,
......@@ -2585,24 +2609,10 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
25852609 defer undefs.deinit();
25862610 try undefs.ensureCapacity(nundefs);
25872611 for (self.extern_lazy_symbols.items()) |entry| {
2588 const name = try self.makeString(entry.key);
2589 undefs.appendAssumeCapacity(.{
2590 .n_strx = name,
2591 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2592 .n_sect = 0,
2593 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2594 .n_value = 0,
2595 });
2612 undefs.appendAssumeCapacity(entry.value.inner);
25962613 }
25972614 for (self.extern_nonlazy_symbols.items()) |entry| {
2598 const name = try self.makeString(entry.key);
2599 undefs.appendAssumeCapacity(.{
2600 .n_strx = name,
2601 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2602 .n_sect = 0,
2603 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2604 .n_value = 0,
2605 });
2615 undefs.appendAssumeCapacity(entry.value.inner);
26062616 }
26072617
26082618 const locals_off = symtab.symoff;
......@@ -2781,19 +2791,12 @@ fn writeRebaseInfoTable(self: *MachO) !void {
27812791 const tracy = trace(@src());
27822792 defer tracy.end();
27832793
2784 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2785 defer self.base.allocator.free(symbols);
2786
2787 for (self.extern_lazy_symbols.items()) |*entry, i| {
2788 symbols[i] = &entry.value;
2789 }
2790
2791 const size = try rebaseInfoSize(symbols);
2794 const size = try rebaseInfoSize(self.extern_lazy_symbols.items());
27922795 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
27932796 defer self.base.allocator.free(buffer);
27942797
27952798 var stream = std.io.fixedBufferStream(buffer);
2796 try writeRebaseInfo(symbols, stream.writer());
2799 try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer());
27972800
27982801 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
27992802 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2820,19 +2823,12 @@ fn writeBindingInfoTable(self: *MachO) !void {
28202823 const tracy = trace(@src());
28212824 defer tracy.end();
28222825
2823 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len);
2824 defer self.base.allocator.free(symbols);
2825
2826 for (self.extern_nonlazy_symbols.items()) |*entry, i| {
2827 symbols[i] = &entry.value;
2828 }
2829
2830 const size = try bindInfoSize(symbols);
2826 const size = try bindInfoSize(self.extern_nonlazy_symbols.items());
28312827 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28322828 defer self.base.allocator.free(buffer);
28332829
28342830 var stream = std.io.fixedBufferStream(buffer);
2835 try writeBindInfo(symbols, stream.writer());
2831 try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer());
28362832
28372833 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28382834 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2856,19 +2852,12 @@ fn writeBindingInfoTable(self: *MachO) !void {
28562852fn writeLazyBindingInfoTable(self: *MachO) !void {
28572853 if (!self.lazy_binding_info_dirty) return;
28582854
2859 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2860 defer self.base.allocator.free(symbols);
2861
2862 for (self.extern_lazy_symbols.items()) |*entry, i| {
2863 symbols[i] = &entry.value;
2864 }
2865
2866 const size = try lazyBindInfoSize(symbols);
2855 const size = try lazyBindInfoSize(self.extern_lazy_symbols.items());
28672856 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28682857 defer self.base.allocator.free(buffer);
28692858
28702859 var stream = std.io.fixedBufferStream(buffer);
2871 try writeLazyBindInfo(symbols, stream.writer());
2860 try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer());
28722861
28732862 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28742863 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
src/link/MachO/imports.zig+36-45
......@@ -7,12 +7,8 @@ const assert = std.debug.assert;
77const Allocator = mem.Allocator;
88
99pub const ExternSymbol = struct {
10 /// Symbol name.
11 /// We own the memory, therefore we'll need to free it by calling `deinit`.
12 /// In self-hosted, we don't expect it to be null ever.
13 /// However, this is for backwards compatibility with LLD when
14 /// we'll be patching things up post mortem.
15 name: ?[]u8 = null,
10 /// MachO symbol table entry.
11 inner: macho.nlist_64,
1612
1713 /// Id of the dynamic library where the specified entries can be found.
1814 /// Id of 0 means self.
......@@ -26,22 +22,16 @@ pub const ExternSymbol = struct {
2622
2723 /// Offset relative to the start address of the `segment`.
2824 offset: u32 = 0,
29
30 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
31 if (self.name) |name| {
32 allocator.free(name);
33 }
34 }
3525};
3626
37pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
27pub fn rebaseInfoSize(symbols: anytype) !u64 {
3828 var stream = std.io.countingWriter(std.io.null_writer);
3929 var writer = stream.writer();
4030 var size: u64 = 0;
4131
42 for (symbols) |symbol| {
32 for (symbols) |entry| {
4333 size += 2;
44 try leb.writeILEB128(writer, symbol.offset);
34 try leb.writeILEB128(writer, entry.value.offset);
4535 size += 1;
4636 }
4737
......@@ -49,8 +39,9 @@ pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
4939 return size;
5040}
5141
52pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
53 for (symbols) |symbol| {
42pub fn writeRebaseInfo(symbols: anytype, writer: anytype) !void {
43 for (symbols) |entry| {
44 const symbol = entry.value;
5445 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
5546 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
5647 try leb.writeILEB128(writer, symbol.offset);
......@@ -59,23 +50,23 @@ pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
5950 try writer.writeByte(macho.REBASE_OPCODE_DONE);
6051}
6152
62pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
53pub fn bindInfoSize(symbols: anytype) !u64 {
6354 var stream = std.io.countingWriter(std.io.null_writer);
6455 var writer = stream.writer();
6556 var size: u64 = 0;
6657
67 for (symbols) |symbol| {
58 for (symbols) |entry| {
59 const symbol = entry.value;
60
6861 size += 1;
6962 if (symbol.dylib_ordinal > 15) {
7063 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
7164 }
7265 size += 1;
7366
74 if (symbol.name) |name| {
75 size += 1;
76 size += name.len;
77 size += 1;
78 }
67 size += 1;
68 size += entry.key.len;
69 size += 1;
7970
8071 size += 1;
8172 try leb.writeILEB128(writer, symbol.offset);
......@@ -86,8 +77,10 @@ pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
8677 return size;
8778}
8879
89pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
90 for (symbols) |symbol| {
80pub fn writeBindInfo(symbols: anytype, writer: anytype) !void {
81 for (symbols) |entry| {
82 const symbol = entry.value;
83
9184 if (symbol.dylib_ordinal > 15) {
9285 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
9386 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
......@@ -98,11 +91,9 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
9891 }
9992 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
10093
101 if (symbol.name) |name| {
102 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
103 try writer.writeAll(name);
104 try writer.writeByte(0);
105 }
94 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
95 try writer.writeAll(entry.key);
96 try writer.writeByte(0);
10697
10798 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
10899 try leb.writeILEB128(writer, symbol.offset);
......@@ -111,23 +102,24 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
111102 }
112103}
113104
114pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
105pub fn lazyBindInfoSize(symbols: anytype) !u64 {
115106 var stream = std.io.countingWriter(std.io.null_writer);
116107 var writer = stream.writer();
117108 var size: u64 = 0;
118109
119 for (symbols) |symbol| {
110 for (symbols) |entry| {
111 const symbol = entry.value;
120112 size += 1;
121113 try leb.writeILEB128(writer, symbol.offset);
122114 size += 1;
123115 if (symbol.dylib_ordinal > 15) {
124116 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
125117 }
126 if (symbol.name) |name| {
127 size += 1;
128 size += name.len;
129 size += 1;
130 }
118
119 size += 1;
120 size += entry.key.len;
121 size += 1;
122
131123 size += 2;
132124 }
133125
......@@ -135,8 +127,9 @@ pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
135127 return size;
136128}
137129
138pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
139 for (symbols) |symbol| {
130pub fn writeLazyBindInfo(symbols: anytype, writer: anytype) !void {
131 for (symbols) |entry| {
132 const symbol = entry.value;
140133 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
141134 try leb.writeILEB128(writer, symbol.offset);
142135
......@@ -149,11 +142,9 @@ pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void
149142 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
150143 }
151144
152 if (symbol.name) |name| {
153 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
154 try writer.writeAll(name);
155 try writer.writeByte(0);
156 }
145 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
146 try writer.writeAll(entry.key);
147 try writer.writeByte(0);
157148
158149 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
159150 try writer.writeByte(macho.BIND_OPCODE_DONE);