authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-18 15:05:52+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-18 15:05:52+02:00
log2828cd2983446b116dc8d543ca5e209e361f39d0
treed4df4ca4a00657490ebde1270e5a0fe74193c0bf
parent5aa9c0b4ab8ca00b0da3ce695924218984d11f11

zld: migrate symbol mgmt to incremental backend


4 files changed, 222 insertions(+), 256 deletions(-)

src/codegen.zig+15-8
......@@ -2500,7 +2500,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25002500 const got_addr = blk: {
25012501 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
25022502 const got = seg.sections.items[macho_file.got_section_index.?];
2503 break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2503 const got_index = macho_file.got_entries_map.get(.{
2504 .where = .local,
2505 .where_index = func.owner_decl.link.macho.local_sym_index,
2506 }) orelse unreachable;
2507 break :blk got.addr + got_index * @sizeOf(u64);
25042508 };
25052509 log.debug("got_addr = 0x{x}", .{got_addr});
25062510 switch (arch) {
......@@ -2521,11 +2525,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25212525 const decl = func_payload.data;
25222526 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
25232527 defer self.bin_file.allocator.free(decl_name);
2524 const already_defined = macho_file.lazy_imports.contains(decl_name);
2525 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
2526 @intCast(u32, index)
2527 else
2528 try macho_file.addExternSymbol(decl_name);
2528 const already_defined = macho_file.symbol_resolver.contains(decl_name);
2529 const resolv = macho_file.symbol_resolver.get(decl_name) orelse blk: {
2530 break :blk try macho_file.addExternFn(decl_name);
2531 };
25292532 const start = self.code.items.len;
25302533 const len: usize = blk: {
25312534 switch (arch) {
......@@ -2544,7 +2547,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25442547 }
25452548 };
25462549 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
2547 .symbol = symbol,
2550 .symbol = resolv.where_index,
25482551 .already_defined = already_defined,
25492552 .start = start,
25502553 .len = len,
......@@ -4351,7 +4354,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43514354 const got_addr = blk: {
43524355 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
43534356 const got = seg.sections.items[macho_file.got_section_index.?];
4354 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
4357 const got_index = macho_file.got_entries_map.get(.{
4358 .where = .local,
4359 .where_index = decl.link.macho.local_sym_index,
4360 }) orelse unreachable;
4361 break :blk got.addr + got_index * ptr_bytes;
43554362 };
43564363 return MCValue{ .memory = got_addr };
43574364 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
src/link/MachO.zig+205-226
......@@ -100,31 +100,29 @@ data_section_index: ?u16 = null,
100100/// The absolute address of the entry point.
101101entry_addr: ?u64 = null,
102102
103/// Table of all local symbols
104/// Internally references string table for names (which are optional).
105103locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106/// Table of all global symbols
107104globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all extern nonlazy symbols, indexed by name.
109nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
110/// Table of all extern lazy symbols, indexed by name.
111lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
105imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
112107
113108locals_free_list: std.ArrayListUnmanaged(u32) = .{},
114109globals_free_list: std.ArrayListUnmanaged(u32) = .{},
115offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
116110
117111stub_helper_stubs_start_off: ?u64 = null,
118112
119113strtab: std.ArrayListUnmanaged(u8) = .{},
120strtab_cache: std.StringHashMapUnmanaged(u32) = .{},
121114
122/// Table of GOT entries.
123offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
115got_entries: std.ArrayListUnmanaged(GotIndirectionKey) = .{},
116got_entries_map: std.AutoHashMapUnmanaged(GotIndirectionKey, u32) = .{},
117
118got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
119
120stubs: std.ArrayListUnmanaged(u32) = .{},
121stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
124122
125123error_flags: File.ErrorFlags = File.ErrorFlags{},
126124
127offset_table_count_dirty: bool = false,
125got_entries_count_dirty: bool = false,
128126load_commands_dirty: bool = false,
129127rebase_info_dirty: bool = false,
130128binding_info_dirty: bool = false,
......@@ -170,31 +168,25 @@ pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
170168/// rather than sitting in the global scope.
171169stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
172170
173pub const GOTEntry = struct {
174 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
175 kind: enum {
176 Local,
177 Extern,
171const SymbolWithLoc = struct {
172 // Table where the symbol can be found.
173 where: enum {
174 global,
175 import,
176 undef,
177 tentative,
178178 },
179
180 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
181 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
182 /// rather than 4 as we currently do, but I'll follow up in the future PR.
183 symbol: u32,
184
185 /// Index of this entry in the GOT.
186 index: u32,
179 where_index: u32,
180 local_sym_index: u32 = 0,
181 file: u16 = 0,
187182};
188183
189pub const Import = struct {
190 /// MachO symbol table entry.
191 symbol: macho.nlist_64,
192
193 /// Id of the dynamic library where the specified entries can be found.
194 dylib_ordinal: i64,
195
196 /// Index of this import within the import list.
197 index: u32,
184pub const GotIndirectionKey = struct {
185 where: enum {
186 local,
187 import,
188 },
189 where_index: u32,
198190};
199191
200192pub const PIEFixup = struct {
......@@ -253,9 +245,6 @@ pub const TextBlock = struct {
253245 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
254246 /// offset table entry.
255247 local_sym_index: u32,
256 /// Index into offset table
257 /// This field is undefined for symbols with size = 0.
258 offset_table_index: u32,
259248 /// Size of this text block
260249 /// Unlike in Elf, we need to store the size of this symbol as part of
261250 /// the TextBlock since macho.nlist_64 lacks this information.
......@@ -275,7 +264,6 @@ pub const TextBlock = struct {
275264
276265 pub const empty = TextBlock{
277266 .local_sym_index = 0,
278 .offset_table_index = undefined,
279267 .size = 0,
280268 .prev = null,
281269 .next = null,
......@@ -433,7 +421,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
433421 }
434422 }
435423
436 if (build_options.have_llvm) {
424 if (build_options.is_stage1) {
437425 return self.linkWithZld(comp);
438426 } else {
439427 switch (self.base.options.effectiveOutputMode()) {
......@@ -500,7 +488,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
500488 self.error_flags.no_entry_point_found = false;
501489 }
502490
503 assert(!self.offset_table_count_dirty);
491 assert(!self.got_entries_count_dirty);
504492 assert(!self.load_commands_dirty);
505493 assert(!self.rebase_info_dirty);
506494 assert(!self.binding_info_dirty);
......@@ -971,31 +959,27 @@ pub fn deinit(self: *MachO) void {
971959 if (self.d_sym) |*ds| {
972960 ds.deinit(self.base.allocator);
973961 }
974 for (self.lazy_imports.keys()) |*key| {
975 self.base.allocator.free(key.*);
976 }
977 self.lazy_imports.deinit(self.base.allocator);
978 for (self.nonlazy_imports.keys()) |*key| {
979 self.base.allocator.free(key.*);
980 }
981 self.nonlazy_imports.deinit(self.base.allocator);
962
982963 self.pie_fixups.deinit(self.base.allocator);
983964 self.stub_fixups.deinit(self.base.allocator);
984965 self.text_block_free_list.deinit(self.base.allocator);
985 self.offset_table.deinit(self.base.allocator);
986 self.offset_table_free_list.deinit(self.base.allocator);
987 {
988 var it = self.strtab_cache.keyIterator();
989 while (it.next()) |key| {
990 self.base.allocator.free(key.*);
991 }
992 }
993 self.strtab_cache.deinit(self.base.allocator);
966 self.got_entries.deinit(self.base.allocator);
967 self.got_entries_map.deinit(self.base.allocator);
968 self.got_entries_free_list.deinit(self.base.allocator);
969 self.stubs.deinit(self.base.allocator);
970 self.stubs_map.deinit(self.base.allocator);
994971 self.strtab.deinit(self.base.allocator);
972 self.imports.deinit(self.base.allocator);
995973 self.globals.deinit(self.base.allocator);
996974 self.globals_free_list.deinit(self.base.allocator);
997975 self.locals.deinit(self.base.allocator);
998976 self.locals_free_list.deinit(self.base.allocator);
977
978 for (self.symbol_resolver.keys()) |key| {
979 self.base.allocator.free(key);
980 }
981 self.symbol_resolver.deinit(self.base.allocator);
982
999983 for (self.load_commands.items) |*lc| {
1000984 lc.deinit(self.base.allocator);
1001985 }
......@@ -1086,8 +1070,8 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
10861070pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
10871071 if (decl.link.macho.local_sym_index != 0) return;
10881072
1089 try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1);
1090 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
1073 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
1074 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);
10911075
10921076 if (self.locals_free_list.popOrNull()) |i| {
10931077 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
......@@ -1098,16 +1082,19 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
10981082 _ = self.locals.addOneAssumeCapacity();
10991083 }
11001084
1101 if (self.offset_table_free_list.popOrNull()) |i| {
1102 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
1103 decl.link.macho.offset_table_index = i;
1104 } else {
1105 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
1106 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
1107 _ = self.offset_table.addOneAssumeCapacity();
1108 self.offset_table_count_dirty = true;
1109 self.rebase_info_dirty = true;
1110 }
1085 const got_index: u32 = blk: {
1086 if (self.got_entries_free_list.popOrNull()) |i| {
1087 log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name });
1088 break :blk i;
1089 } else {
1090 const got_index = @intCast(u32, self.got_entries.items.len);
1091 log.debug("allocating GOT entry index {d} for {s}", .{ got_index, decl.name });
1092 _ = self.got_entries.addOneAssumeCapacity();
1093 self.got_entries_count_dirty = true;
1094 self.rebase_info_dirty = true;
1095 break :blk got_index;
1096 }
1097 };
11111098
11121099 self.locals.items[decl.link.macho.local_sym_index] = .{
11131100 .n_strx = 0,
......@@ -1116,11 +1103,12 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11161103 .n_desc = 0,
11171104 .n_value = 0,
11181105 };
1119 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1120 .kind = .Local,
1121 .symbol = decl.link.macho.local_sym_index,
1122 .index = decl.link.macho.offset_table_index,
1106 const got_entry = GotIndirectionKey{
1107 .where = .local,
1108 .where_index = decl.link.macho.local_sym_index,
11231109 };
1110 self.got_entries.items[got_index] = got_entry;
1111 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index);
11241112}
11251113
11261114pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
......@@ -1191,13 +1179,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11911179 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
11921180
11931181 if (vaddr != symbol.n_value) {
1194 log.debug(" (writing new offset table entry)", .{});
1195 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1196 .kind = .Local,
1197 .symbol = decl.link.macho.local_sym_index,
1198 .index = decl.link.macho.offset_table_index,
1199 };
1200 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1182 log.debug(" (writing new GOT entry)", .{});
1183 const got_index = self.got_entries_map.get(.{
1184 .where = .local,
1185 .where_index = decl.link.macho.local_sym_index,
1186 }) orelse unreachable;
1187 try self.writeGotEntry(got_index);
12011188 }
12021189
12031190 symbol.n_value = vaddr;
......@@ -1235,16 +1222,17 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12351222 .n_desc = 0,
12361223 .n_value = addr,
12371224 };
1238 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1239 .kind = .Local,
1240 .symbol = decl.link.macho.local_sym_index,
1241 .index = decl.link.macho.offset_table_index,
1242 };
12431225
12441226 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1227
12451228 if (self.d_sym) |*ds|
12461229 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
1247 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1230
1231 const got_index = self.got_entries_map.get(.{
1232 .where = .local,
1233 .where_index = decl.link.macho.local_sym_index,
1234 }) orelse unreachable;
1235 try self.writeGotEntry(got_index);
12481236 }
12491237
12501238 // Calculate displacements to target addr (if any).
......@@ -1291,7 +1279,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12911279 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12921280 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12931281 for (self.stub_fixups.items) |fixup| {
1294 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
1282 const stubs_index = self.stubs_map.get(fixup.symbol) orelse unreachable;
1283 const stub_addr = stubs.addr + stubs_index * stubs.reserved2;
12951284 const text_addr = symbol.n_value + fixup.start;
12961285 switch (self.base.options.target.cpu.arch) {
12971286 .x86_64 => {
......@@ -1309,9 +1298,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
13091298 else => unreachable, // unsupported target architecture
13101299 }
13111300 if (!fixup.already_defined) {
1312 try self.writeStub(fixup.symbol);
1313 try self.writeStubInStubHelper(fixup.symbol);
1314 try self.writeLazySymbolPointer(fixup.symbol);
1301 try self.writeStub(stubs_index);
1302 try self.writeStubInStubHelper(stubs_index);
1303 try self.writeLazySymbolPointer(stubs_index);
13151304
13161305 self.rebase_info_dirty = true;
13171306 self.lazy_binding_info_dirty = true;
......@@ -1448,10 +1437,16 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14481437 self.freeTextBlock(&decl.link.macho);
14491438 if (decl.link.macho.local_sym_index != 0) {
14501439 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
1451 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
14521440
1453 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
1441 const got_key = GotIndirectionKey{
1442 .where = .local,
1443 .where_index = decl.link.macho.local_sym_index,
1444 };
1445 const got_index = self.got_entries_map.get(got_key) orelse unreachable;
1446 _ = self.got_entries_map.remove(got_key);
1447 self.got_entries_free_list.append(self.base.allocator, got_index) catch {};
14541448
1449 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
14551450 decl.link.macho.local_sym_index = 0;
14561451 }
14571452 if (self.d_sym) |*ds| {
......@@ -1506,8 +1501,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15061501 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
15071502
15081503 const program_code_size_hint = self.base.options.program_code_size_hint;
1509 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
1510 const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint;
1504 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
1505 const ideal_size = self.header_pad + program_code_size_hint + 3 * got_size_hint;
15111506 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
15121507
15131508 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
......@@ -1934,28 +1929,28 @@ pub fn populateMissingMetadata(self: *MachO) !void {
19341929 });
19351930 self.load_commands_dirty = true;
19361931 }
1937 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
1938 const index = @intCast(u32, self.nonlazy_imports.count());
1939 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
1940 const offset = try self.makeString("dyld_stub_binder");
1941 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
1942 .symbol = .{
1943 .n_strx = offset,
1944 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
1945 .n_sect = 0,
1946 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
1947 .n_value = 0,
1948 },
1949 .dylib_ordinal = 1, // TODO this is currently hardcoded.
1950 .index = index,
1932 if (!self.symbol_resolver.contains("dyld_stub_binder")) {
1933 const import_sym_index = @intCast(u32, self.imports.items.len);
1934 try self.imports.append(self.base.allocator, .{
1935 .n_strx = try self.makeString("dyld_stub_binder"),
1936 .n_type = macho.N_UNDF | macho.N_EXT,
1937 .n_sect = 0,
1938 .n_desc = packDylibOrdinal(1),
1939 .n_value = 0,
19511940 });
1952 const off_index = @intCast(u32, self.offset_table.items.len);
1953 try self.offset_table.append(self.base.allocator, .{
1954 .kind = .Extern,
1955 .symbol = index,
1956 .index = off_index,
1941 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
1942 try self.symbol_resolver.putNoClobber(self.base.allocator, name, .{
1943 .where = .import,
1944 .where_index = import_sym_index,
19571945 });
1958 try self.writeOffsetTableEntry(off_index);
1946 const got_key = GotIndirectionKey{
1947 .where = .import,
1948 .where_index = import_sym_index,
1949 };
1950 const got_index = @intCast(u32, self.got_entries.items.len);
1951 try self.got_entries.append(self.base.allocator, got_key);
1952 try self.got_entries_map.putNoClobber(self.base.allocator, got_key, got_index);
1953 try self.writeGotEntry(got_index);
19591954 self.binding_info_dirty = true;
19601955 }
19611956 if (self.stub_helper_stubs_start_off == null) {
......@@ -2068,24 +2063,25 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
20682063 return vaddr;
20692064}
20702065
2071pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2072 const index = @intCast(u32, self.lazy_imports.count());
2073 const offset = try self.makeString(name);
2074 const sym_name = try self.base.allocator.dupe(u8, name);
2075 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2076 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2077 .symbol = .{
2078 .n_strx = offset,
2079 .n_type = macho.N_UNDF | macho.N_EXT,
2080 .n_sect = 0,
2081 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
2082 .n_value = 0,
2083 },
2084 .dylib_ordinal = dylib_ordinal,
2085 .index = index,
2066pub fn addExternFn(self: *MachO, name: []const u8) !SymbolWithLoc {
2067 log.debug("adding new extern function '{s}' with dylib ordinal 1", .{name});
2068 const import_sym_index = @intCast(u32, self.imports.items.len);
2069 try self.imports.append(self.base.allocator, .{
2070 .n_strx = try self.makeString(name),
2071 .n_type = macho.N_UNDF | macho.N_EXT,
2072 .n_sect = 0,
2073 .n_desc = packDylibOrdinal(1),
2074 .n_value = 0,
20862075 });
2087 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
2088 return index;
2076 const resolv = .{
2077 .where = .import,
2078 .where_index = import_sym_index,
2079 };
2080 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, name), resolv);
2081 const stubs_index = @intCast(u32, self.stubs.items.len);
2082 try self.stubs.append(self.base.allocator, import_sym_index);
2083 try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index);
2084 return resolv;
20892085}
20902086
20912087const NextSegmentAddressAndOffset = struct {
......@@ -2239,29 +2235,26 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
22392235 return st;
22402236}
22412237
2242fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2238fn writeGotEntry(self: *MachO, index: usize) !void {
22432239 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
22442240 const sect = &seg.sections.items[self.got_section_index.?];
22452241 const off = sect.offset + @sizeOf(u64) * index;
22462242
2247 if (self.offset_table_count_dirty) {
2243 if (self.got_entries_count_dirty) {
22482244 // TODO relocate.
2249 self.offset_table_count_dirty = false;
2245 self.got_entries_count_dirty = false;
22502246 }
22512247
2252 const got_entry = self.offset_table.items[index];
2253 const sym = blk: {
2254 switch (got_entry.kind) {
2255 .Local => {
2256 break :blk self.locals.items[got_entry.symbol];
2257 },
2258 .Extern => {
2259 break :blk self.nonlazy_imports.values()[got_entry.symbol].symbol;
2260 },
2261 }
2248 const got_entry = self.got_entries.items[index];
2249 const sym = switch (got_entry.where) {
2250 .local => self.locals.items[got_entry.where_index],
2251 .import => self.imports.items[got_entry.where_index],
22622252 };
2263 const sym_name = self.getString(sym.n_strx) orelse unreachable;
2264 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2253 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{
2254 off,
2255 sym.n_value,
2256 self.getString(sym.n_strx),
2257 });
22652258 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
22662259}
22672260
......@@ -2539,7 +2532,7 @@ fn relocateSymbolTable(self: *MachO) !void {
25392532 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
25402533 const nlocals = self.locals.items.len;
25412534 const nglobals = self.globals.items.len;
2542 const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count();
2535 const nundefs = self.imports.items.len;
25432536 const nsyms = nlocals + nglobals + nundefs;
25442537
25452538 if (symtab.nsyms < nsyms) {
......@@ -2584,17 +2577,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
25842577 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
25852578 const nlocals = self.locals.items.len;
25862579 const nglobals = self.globals.items.len;
2587
2588 const nundefs = self.lazy_imports.count() + self.nonlazy_imports.count();
2589 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2590 defer undefs.deinit();
2591 try undefs.ensureCapacity(nundefs);
2592 for (self.lazy_imports.values()) |*value| {
2593 undefs.appendAssumeCapacity(value.symbol);
2594 }
2595 for (self.nonlazy_imports.values()) |*value| {
2596 undefs.appendAssumeCapacity(value.symbol);
2597 }
2580 const nundefs = self.imports.items.len;
25982581
25992582 const locals_off = symtab.symoff;
26002583 const locals_size = nlocals * @sizeOf(macho.nlist_64);
......@@ -2607,7 +2590,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26072590 const undefs_off = globals_off + globals_size;
26082591 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
26092592 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2610 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
2593 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off);
26112594
26122595 // Update dynamic symbol table.
26132596 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
......@@ -2633,10 +2616,10 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26332616 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
26342617 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26352618
2636 const lazy_count = self.lazy_imports.count();
2637 const got_entries = self.offset_table.items;
2619 const nstubs = @intCast(u32, self.stubs.items.len);
2620 const ngot_entries = @intCast(u32, self.got_entries.items.len);
26382621 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
2639 const nindirectsyms = @intCast(u32, lazy_count * 2 + got_entries.len);
2622 const nindirectsyms = nstubs * 2 + ngot_entries;
26402623 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26412624
26422625 if (needed_size > allocated_size) {
......@@ -2655,35 +2638,25 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26552638 var writer = stream.writer();
26562639
26572640 stubs.reserved1 = 0;
2658 {
2659 var i: usize = 0;
2660 while (i < lazy_count) : (i += 1) {
2661 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2662 try writer.writeIntLittle(u32, symtab_idx);
2663 }
2641 for (self.stubs.items) |id| {
2642 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
26642643 }
26652644
2666 const base_id = @intCast(u32, lazy_count);
2667 got.reserved1 = base_id;
2668 for (got_entries) |entry| {
2669 switch (entry.kind) {
2670 .Local => {
2671 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2645 got.reserved1 = nstubs;
2646 for (self.got_entries.items) |entry| {
2647 switch (entry.where) {
2648 .import => {
2649 try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index);
26722650 },
2673 .Extern => {
2674 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id);
2675 try writer.writeIntLittle(u32, symtab_idx);
2651 .local => {
2652 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
26762653 },
26772654 }
26782655 }
26792656
2680 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2681 {
2682 var i: usize = 0;
2683 while (i < lazy_count) : (i += 1) {
2684 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2685 try writer.writeIntLittle(u32, symtab_idx);
2686 }
2657 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
2658 for (self.stubs.items) |id| {
2659 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
26872660 }
26882661
26892662 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
......@@ -2756,13 +2729,18 @@ fn writeExportTrie(self: *MachO) !void {
27562729 defer trie.deinit();
27572730
27582731 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2759 for (self.globals.items) |symbol| {
2760 // TODO figure out if we should put all global symbols into the export trie
2761 const name = self.getString(symbol.n_strx) orelse unreachable;
2762 assert(symbol.n_value >= text_segment.inner.vmaddr);
2732 const base_address = text_segment.inner.vmaddr;
2733
2734 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
2735 log.debug("writing export trie", .{});
2736
2737 for (self.globals.items) |sym| {
2738 const sym_name = self.getString(sym.n_strx);
2739 log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
2740
27632741 try trie.put(.{
2764 .name = name,
2765 .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr,
2742 .name = sym_name,
2743 .vmaddr_offset = sym.n_value - base_address,
27662744 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
27672745 });
27682746 }
......@@ -2804,27 +2782,28 @@ fn writeRebaseInfoTable(self: *MachO) !void {
28042782 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
28052783 const sect = seg.sections.items[idx];
28062784 const base_offset = sect.addr - seg.inner.vmaddr;
2807 const segment_id = self.data_const_segment_cmd_index.?;
2785 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2786
2787 for (self.got_entries.items) |entry, i| {
2788 if (entry.where == .import) continue;
28082789
2809 for (self.offset_table.items) |entry| {
2810 if (entry.kind == .Extern) continue;
28112790 try pointers.append(.{
2812 .offset = base_offset + entry.index * @sizeOf(u64),
2791 .offset = base_offset + i * @sizeOf(u64),
28132792 .segment_id = segment_id,
28142793 });
28152794 }
28162795 }
28172796
28182797 if (self.la_symbol_ptr_section_index) |idx| {
2819 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.count());
28202798 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
28212799 const sect = seg.sections.items[idx];
28222800 const base_offset = sect.addr - seg.inner.vmaddr;
2823 const segment_id = self.data_segment_cmd_index.?;
2801 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
28242802
2825 for (self.lazy_imports.values()) |*value| {
2803 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2804 for (self.stubs.items) |_, i| {
28262805 pointers.appendAssumeCapacity(.{
2827 .offset = base_offset + value.index * @sizeOf(u64),
2806 .offset = base_offset + i * @sizeOf(u64),
28282807 .segment_id = segment_id,
28292808 });
28302809 }
......@@ -2872,15 +2851,15 @@ fn writeBindingInfoTable(self: *MachO) !void {
28722851 const base_offset = sect.addr - seg.inner.vmaddr;
28732852 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
28742853
2875 for (self.offset_table.items) |entry| {
2876 if (entry.kind == .Local) continue;
2877 const import_key = self.nonlazy_imports.keys()[entry.symbol];
2878 const import_ordinal = self.nonlazy_imports.values()[entry.symbol].dylib_ordinal;
2854 for (self.got_entries.items) |entry, i| {
2855 if (entry.where == .local) continue;
2856
2857 const sym = self.imports.items[entry.where_index];
28792858 try pointers.append(.{
2880 .offset = base_offset + entry.index * @sizeOf(u64),
2859 .offset = base_offset + i * @sizeOf(u64),
28812860 .segment_id = segment_id,
2882 .dylib_ordinal = import_ordinal,
2883 .name = import_key,
2861 .dylib_ordinal = unpackDylibOrdinal(sym.n_desc),
2862 .name = self.getString(sym.n_strx),
28842863 });
28852864 }
28862865 }
......@@ -2920,21 +2899,20 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
29202899 defer pointers.deinit();
29212900
29222901 if (self.la_symbol_ptr_section_index) |idx| {
2923 try pointers.ensureCapacity(self.lazy_imports.count());
29242902 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
29252903 const sect = seg.sections.items[idx];
29262904 const base_offset = sect.addr - seg.inner.vmaddr;
29272905 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
29282906
2929 const slice = self.lazy_imports.entries.slice();
2930 const keys = slice.items(.key);
2931 const values = slice.items(.value);
2932 for (keys) |*key, i| {
2907 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2908
2909 for (self.stubs.items) |import_id, i| {
2910 const sym = self.imports.items[import_id];
29332911 pointers.appendAssumeCapacity(.{
2934 .offset = base_offset + values[i].index * @sizeOf(u64),
2912 .offset = base_offset + i * @sizeOf(u64),
29352913 .segment_id = segment_id,
2936 .dylib_ordinal = values[i].dylib_ordinal,
2937 .name = key.*,
2914 .dylib_ordinal = unpackDylibOrdinal(sym.n_desc),
2915 .name = self.getString(sym.n_strx),
29382916 });
29392917 }
29402918 }
......@@ -2966,7 +2944,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
29662944}
29672945
29682946fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2969 if (self.lazy_imports.count() == 0) return;
2947 if (self.stubs.items.len == 0) return;
29702948
29712949 var stream = std.io.fixedBufferStream(buffer);
29722950 var reader = stream.reader();
......@@ -3011,7 +2989,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
30112989 else => {},
30122990 }
30132991 }
3014 assert(self.lazy_imports.count() <= offsets.items.len);
2992 assert(self.stubs.items.len <= offsets.items.len);
30152993
30162994 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
30172995 .x86_64 => 10,
......@@ -3024,9 +3002,9 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
30243002 else => unreachable,
30253003 };
30263004 var buf: [@sizeOf(u32)]u8 = undefined;
3027 for (offsets.items[0..self.lazy_imports.count()]) |offset, i| {
3028 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
3029 mem.writeIntLittle(u32, &buf, offset);
3005 for (self.stubs.items) |_, index| {
3006 const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off;
3007 mem.writeIntLittle(u32, &buf, offsets.items[index]);
30303008 try self.base.file.?.pwriteAll(&buf, placeholder_off);
30313009 }
30323010}
......@@ -3182,11 +3160,6 @@ fn hasTlvDescriptors(_: *MachO) bool {
31823160}
31833161
31843162pub fn makeString(self: *MachO, string: []const u8) !u32 {
3185 if (self.strtab_cache.get(string)) |off| {
3186 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
3187 return off;
3188 }
3189
31903163 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);
31913164 const new_off = @intCast(u32, self.strtab.items.len);
31923165
......@@ -3195,12 +3168,18 @@ pub fn makeString(self: *MachO, string: []const u8) !u32 {
31953168 self.strtab.appendSliceAssumeCapacity(string);
31963169 self.strtab.appendAssumeCapacity(0);
31973170
3198 try self.strtab_cache.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, string), new_off);
3199
32003171 return new_off;
32013172}
32023173
3203pub fn getString(self: *MachO, off: u32) ?[]const u8 {
3174pub fn getString(self: *MachO, off: u32) []const u8 {
32043175 assert(off < self.strtab.items.len);
32053176 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
32063177}
3178
3179fn packDylibOrdinal(ordinal: u16) u16 {
3180 return ordinal * macho.N_SYMBOL_RESOLVER;
3181}
3182
3183fn unpackDylibOrdinal(pack: u16) u16 {
3184 return @divExact(pack, macho.N_SYMBOL_RESOLVER);
3185}
src/link/MachO/TextBlock.zig-4
......@@ -1059,10 +1059,6 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
10591059 break :blk sym.n_value;
10601060 },
10611061 .import => {
1062 // TODO I think this will be autohandled by self.bindings.
1063 // if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
1064 // break :blk 0; // Dynamically bound by dyld.
1065 // }
10661062 const stubs_index = zld.stubs.getIndex(rel.where_index) orelse {
10671063 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
10681064 break :blk 0; // Dynamically bound by dyld.
src/link/MachO/Zld.zig+2-18
......@@ -2367,7 +2367,7 @@ fn writeRebaseInfoTable(self: *Zld) !void {
23672367 const base_offset = sect.addr - seg.inner.vmaddr;
23682368 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
23692369
2370 try pointers.ensureCapacity(pointers.items.len + self.stubs.count());
2370 try pointers.ensureUnusedCapacity(self.stubs.count());
23712371 for (self.stubs.keys()) |_, i| {
23722372 pointers.appendAssumeCapacity(.{
23732373 .offset = base_offset + i * @sizeOf(u64),
......@@ -2450,22 +2450,6 @@ fn writeBindInfoTable(self: *Zld) !void {
24502450 }
24512451 }
24522452
2453 // if (self.tlv_section_index) |idx| {
2454 // const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2455 // const sect = seg.sections.items[idx];
2456 // const base_offset = sect.addr - seg.inner.vmaddr;
2457 // const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2458
2459 // const sym = self.globals.get("__tlv_bootstrap") orelse unreachable;
2460 // const proxy = sym.payload.proxy;
2461 // try pointers.append(.{
2462 // .offset = base_offset,
2463 // .segment_id = segment_id,
2464 // .dylib_ordinal = proxy.dylibOrdinal(),
2465 // .name = self.getString(sym.strx),
2466 // });
2467 // }
2468
24692453 const size = try bindInfoSize(pointers.items);
24702454 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
24712455 defer self.allocator.free(buffer);
......@@ -2494,7 +2478,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
24942478 const base_offset = sect.addr - seg.inner.vmaddr;
24952479 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
24962480
2497 try pointers.ensureCapacity(self.stubs.count());
2481 try pointers.ensureUnusedCapacity(self.stubs.count());
24982482
24992483 for (self.stubs.keys()) |key, i| {
25002484 const sym = self.imports.items[key];