authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-03 14:11:07+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-04 13:09:32+02:00
log68ebc7cba0cb6089be3eb4511a05615830f132ae
treee2e15699a72659e32fd3602e9aedc67b7dd1a499
parent86ab6ca56c4e6d115b017eed40dc62815a6a8e3d

zld: rewrite symbol resolution


3 files changed, 173 insertions(+), 136 deletions(-)

src/link/MachO/Object.zig+1-1
...@@ -365,7 +365,7 @@ pub fn parseSymbols(self: *Object) !void {...@@ -365,7 +365,7 @@ pub fn parseSymbols(self: *Object) !void {
365 .@"type" = .regular,365 .@"type" = .regular,
366 .name = name,366 .name = name,
367 },367 },
368 .linkage = .translation_unit,368 .linkage = linkage,
369 .address = sym.n_value,369 .address = sym.n_value,
370 .section = sym.n_sect - 1,370 .section = sym.n_sect - 1,
371 .weak_ref = Symbol.isWeakRef(sym),371 .weak_ref = Symbol.isWeakRef(sym),
src/link/MachO/Symbol.zig+9-3
...@@ -19,6 +19,15 @@ pub const Type = enum {...@@ -19,6 +19,15 @@ pub const Type = enum {
19/// Symbol name. Owned slice.19/// Symbol name. Owned slice.
20name: []u8,20name: []u8,
2121
22/// Alias of.
23alias: ?*Symbol = null,
24
25/// Index in GOT table for indirection.
26got_index: ?u32 = null,
27
28/// Index in stubs table for late binding.
29stubs_index: ?u32 = null,
30
22pub const Regular = struct {31pub const Regular = struct {
23 base: Symbol,32 base: Symbol,
2433
...@@ -71,9 +80,6 @@ pub const Proxy = struct {...@@ -71,9 +80,6 @@ pub const Proxy = struct {
71pub const Unresolved = struct {80pub const Unresolved = struct {
72 base: Symbol,81 base: Symbol,
7382
74 /// Alias of.
75 alias: ?*Symbol = null,
76
77 /// File where this symbol was referenced.83 /// File where this symbol was referenced.
78 file: *Object,84 file: *Object,
7985
src/link/MachO/Zld.zig+163-132
...@@ -29,8 +29,8 @@ page_size: ?u16 = null,...@@ -29,8 +29,8 @@ page_size: ?u16 = null,
29file: ?fs.File = null,29file: ?fs.File = null,
30out_path: ?[]const u8 = null,30out_path: ?[]const u8 = null,
3131
32objects: std.ArrayListUnmanaged(Object) = .{},32objects: std.ArrayListUnmanaged(*Object) = .{},
33archives: std.ArrayListUnmanaged(Archive) = .{},33archives: std.ArrayListUnmanaged(*Archive) = .{},
3434
35load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},35load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3636
...@@ -74,30 +74,23 @@ data_section_index: ?u16 = null,...@@ -74,30 +74,23 @@ data_section_index: ?u16 = null,
74bss_section_index: ?u16 = null,74bss_section_index: ?u16 = null,
75common_section_index: ?u16 = null,75common_section_index: ?u16 = null,
7676
77symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{},77globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
78imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
79unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
80
78strtab: std.ArrayListUnmanaged(u8) = .{},81strtab: std.ArrayListUnmanaged(u8) = .{},
79strtab_dir: std.StringHashMapUnmanaged(u32) = .{},82strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
8083
81threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},84threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
82local_rebases: std.ArrayListUnmanaged(Pointer) = .{},85local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
83stubs: std.StringArrayHashMapUnmanaged(u32) = .{},86stubs: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
84got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{},87got_entries: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
8588
86stub_helper_stubs_start_off: ?u64 = null,89stub_helper_stubs_start_off: ?u64 = null,
8790
88mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},91mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
89unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},92unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
9093
91const GotEntry = struct {
92 tag: enum {
93 local,
94 import,
95 },
96 index: u32,
97 target_addr: u64,
98 file: u16,
99};
100
101const MappingKey = struct {94const MappingKey = struct {
102 object_id: u16,95 object_id: u16,
103 source_sect_id: u16,96 source_sect_id: u16,
...@@ -124,15 +117,7 @@ pub fn init(allocator: *Allocator) Zld {...@@ -124,15 +117,7 @@ pub fn init(allocator: *Allocator) Zld {
124pub fn deinit(self: *Zld) void {117pub fn deinit(self: *Zld) void {
125 self.threadlocal_offsets.deinit(self.allocator);118 self.threadlocal_offsets.deinit(self.allocator);
126 self.local_rebases.deinit(self.allocator);119 self.local_rebases.deinit(self.allocator);
127
128 for (self.stubs.items()) |entry| {
129 self.allocator.free(entry.key);
130 }
131 self.stubs.deinit(self.allocator);120 self.stubs.deinit(self.allocator);
132
133 for (self.got_entries.items()) |entry| {
134 self.allocator.free(entry.key);
135 }
136 self.got_entries.deinit(self.allocator);121 self.got_entries.deinit(self.allocator);
137122
138 for (self.load_commands.items) |*lc| {123 for (self.load_commands.items) |*lc| {
...@@ -140,23 +125,22 @@ pub fn deinit(self: *Zld) void {...@@ -140,23 +125,22 @@ pub fn deinit(self: *Zld) void {
140 }125 }
141 self.load_commands.deinit(self.allocator);126 self.load_commands.deinit(self.allocator);
142127
143 for (self.objects.items) |*object| {128 for (self.objects.items) |object| {
144 object.deinit();129 object.deinit();
130 self.allocator.destroy(object);
145 }131 }
146 self.objects.deinit(self.allocator);132 self.objects.deinit(self.allocator);
147133
148 for (self.archives.items) |*archive| {134 for (self.archives.items) |archive| {
149 archive.deinit();135 archive.deinit();
136 self.allocator.destroy(archive);
150 }137 }
151 self.archives.deinit(self.allocator);138 self.archives.deinit(self.allocator);
152139
153 self.mappings.deinit(self.allocator);140 self.mappings.deinit(self.allocator);
154 self.unhandled_sections.deinit(self.allocator);141 self.unhandled_sections.deinit(self.allocator);
155142
156 for (self.symtab.items()) |*entry| {143 self.globals.deinit(self.allocator);
157 entry.value.deinit(self.allocator);
158 }
159 self.symtab.deinit(self.allocator);
160 self.strtab.deinit(self.allocator);144 self.strtab.deinit(self.allocator);
161145
162 {146 {
...@@ -216,19 +200,21 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {...@@ -216,19 +200,21 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
216 try self.populateMetadata();200 try self.populateMetadata();
217 try self.parseInputFiles(files);201 try self.parseInputFiles(files);
218 try self.resolveSymbols();202 try self.resolveSymbols();
219 try self.resolveStubsAndGotEntries();203 self.printSymbols();
220 try self.updateMetadata();204 return error.Unfinished;
221 try self.sortSections();205 // try self.resolveStubsAndGotEntries();
222 try self.allocateTextSegment();206 // try self.updateMetadata();
223 try self.allocateDataConstSegment();207 // try self.sortSections();
224 try self.allocateDataSegment();208 // try self.allocateTextSegment();
225 self.allocateLinkeditSegment();209 // try self.allocateDataConstSegment();
226 try self.allocateSymbols();210 // try self.allocateDataSegment();
227 try self.allocateStubsAndGotEntries();211 // self.allocateLinkeditSegment();
228 try self.allocateCppStatics();212 // try self.allocateSymbols();
229 try self.writeStubHelperCommon();213 // try self.allocateStubsAndGotEntries();
230 try self.resolveRelocsAndWriteSections();214 // try self.allocateCppStatics();
231 try self.flush();215 // try self.writeStubHelperCommon();
216 // try self.resolveRelocsAndWriteSections();
217 // try self.flush();
232}218}
233219
234fn parseInputFiles(self: *Zld, files: []const []const u8) !void {220fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
...@@ -291,7 +277,10 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -291,7 +277,10 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
291 for (classified.items) |input| {277 for (classified.items) |input| {
292 switch (input.kind) {278 switch (input.kind) {
293 .object => {279 .object => {
294 var object = Object.init(self.allocator);280 const object = try self.allocator.create(Object);
281 errdefer self.allocator.destroy(object);
282
283 object.* = Object.init(self.allocator);
295 object.arch = self.arch.?;284 object.arch = self.arch.?;
296 object.name = try self.allocator.dupe(u8, input.name);285 object.name = try self.allocator.dupe(u8, input.name);
297 object.file = input.file;286 object.file = input.file;
...@@ -299,7 +288,10 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -299,7 +288,10 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
299 try self.objects.append(self.allocator, object);288 try self.objects.append(self.allocator, object);
300 },289 },
301 .archive => {290 .archive => {
302 var archive = Archive.init(self.allocator);291 const archive = try self.allocator.create(Archive);
292 errdefer self.allocator.destroy(archive);
293
294 archive.* = Archive.init(self.allocator);
303 archive.arch = self.arch.?;295 archive.arch = self.arch.?;
304 archive.name = try self.allocator.dupe(u8, input.name);296 archive.name = try self.allocator.dupe(u8, input.name);
305 archive.file = input.file;297 archive.file = input.file;
...@@ -1274,141 +1266,150 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {...@@ -1274,141 +1266,150 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1274 try self.file.?.pwriteAll(code, stub_off);1266 try self.file.?.pwriteAll(code, stub_off);
1275}1267}
12761268
1277fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {1269fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
1278 const object = self.objects.items[object_id];1270 log.warn("resolving symbols in '{s}'", .{object.name});
1279 log.debug("resolving symbols in '{s}'", .{object.name});
12801271
1281 for (object.symtab.items) |sym, sym_id| {1272 for (object.symbols.items) |sym| {
1282 if (Symbol.isLocal(sym)) {1273 if (sym.cast(Symbol.Regular)) |reg| {
1283 // If symbol is local to CU, we don't put it in the global symbol table.1274 if (reg.linkage == .translation_unit) continue; // Symbol local to TU.
1284 continue;1275
1285 } else if (Symbol.isGlobal(sym)) {1276 if (self.unresolved.swapRemove(sym.name)) |entry| {
1286 const sym_name = object.getString(sym.n_strx);1277 // Create link to the global.
1287 const is_weak = Symbol.isWeakDef(sym) or Symbol.isPext(sym);1278 entry.value.alias = sym;
1288 const global = self.symtab.getEntry(sym_name) orelse {1279 }
1280 const entry = self.globals.getEntry(sym.name) orelse {
1289 // Put new global symbol into the symbol table.1281 // Put new global symbol into the symbol table.
1290 const name = try self.allocator.dupe(u8, sym_name);1282 try self.globals.putNoClobber(self.allocator, sym.name, sym);
1291 try self.symtab.putNoClobber(self.allocator, name, .{
1292 .tag = if (is_weak) .weak else .strong,
1293 .name = name,
1294 .address = 0,
1295 .section = 0,
1296 .file = object_id,
1297 .index = @intCast(u32, sym_id),
1298 });
1299 continue;1283 continue;
1300 };1284 };
13011285 const g_sym = entry.value;
1302 switch (global.value.tag) {1286 const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable;
1303 .weak => {1287
1304 if (is_weak) continue; // Nothing to do for weak symbol.1288 switch (g_reg.linkage) {
1289 .translation_unit => unreachable,
1290 .linkage_unit => {
1291 if (reg.linkage == .linkage_unit) {
1292 // Create link to the first encountered linkage_unit symbol.
1293 sym.alias = g_sym;
1294 continue;
1295 }
1305 },1296 },
1306 .strong => {1297 .global => {
1307 if (!is_weak) {1298 if (reg.linkage == .global) {
1308 log.debug("strong symbol '{s}' defined multiple times", .{sym_name});1299 log.warn("symbol '{s}' defined multiple times", .{reg.base.name});
1309 return error.MultipleSymbolDefinitions;1300 return error.MultipleSymbolDefinitions;
1310 }1301 }
1302 sym.alias = g_sym;
1311 continue;1303 continue;
1312 },1304 },
1313 else => {},
1314 }1305 }
13151306
1316 global.value.tag = if (is_weak) .weak else .strong;1307 g_sym.alias = sym;
1317 global.value.file = object_id;1308 entry.value = sym;
1318 global.value.index = @intCast(u32, sym_id);1309 } else if (sym.cast(Symbol.Unresolved)) |und| {
1319 } else if (Symbol.isUndef(sym)) {1310 if (self.globals.get(sym.name)) |g_sym| {
1320 const sym_name = object.getString(sym.n_strx);1311 sym.alias = g_sym;
1321 if (self.symtab.contains(sym_name)) continue; // Nothing to do if we already found a definition.1312 continue;
13221313 }
1323 const name = try self.allocator.dupe(u8, sym_name);1314 if (self.unresolved.get(sym.name)) |u_sym| {
1324 try self.symtab.putNoClobber(self.allocator, name, .{1315 sym.alias = u_sym;
1325 .tag = .undef,1316 continue;
1326 .name = name,1317 }
1327 .address = 0,1318 try self.unresolved.putNoClobber(self.allocator, sym.name, sym);
1328 .section = 0,
1329 });
1330 } else unreachable;1319 } else unreachable;
1331 }1320 }
1332}1321}
13331322
1334fn resolveSymbols(self: *Zld) !void {1323fn resolveSymbols(self: *Zld) !void {
1335 // First pass, resolve symbols in provided objects.1324 // First pass, resolve symbols in provided objects.
1336 for (self.objects.items) |object, object_id| {1325 for (self.objects.items) |object| {
1337 try self.resolveSymbolsInObject(@intCast(u16, object_id));1326 try self.resolveSymbolsInObject(object);
1338 }1327 }
13391328
1340 // Second pass, resolve symbols in static libraries.1329 // Second pass, resolve symbols in static libraries.
1341 var next_sym: usize = 0;1330 var next_sym: usize = 0;
1342 var nsyms: usize = self.symtab.items().len;1331 while (true) {
1343 while (next_sym < nsyms) : (next_sym += 1) {1332 if (next_sym == self.unresolved.count()) break;
1344 const sym = self.symtab.items()[next_sym];1333
1345 if (sym.value.tag != .undef) continue;1334 const entry = self.unresolved.items()[next_sym];
1335 const sym = entry.value;
13461336
1347 const sym_name = sym.value.name;1337 var reset: bool = false;
1348 for (self.archives.items) |archive| {1338 for (self.archives.items) |archive| {
1349 // Check if the entry exists in a static archive.1339 // Check if the entry exists in a static archive.
1350 const offsets = archive.toc.get(sym_name) orelse {1340 const offsets = archive.toc.get(sym.name) orelse {
1351 // No hit.1341 // No hit.
1352 continue;1342 continue;
1353 };1343 };
1354 assert(offsets.items.len > 0);1344 assert(offsets.items.len > 0);
13551345
1356 const object = try archive.parseObject(offsets.items[0]);1346 const object = try self.allocator.create(Object);
1357 const object_id = @intCast(u16, self.objects.items.len);1347 errdefer self.allocator.destroy(object);
1348
1349 object.* = try archive.parseObject(offsets.items[0]);
1358 try self.objects.append(self.allocator, object);1350 try self.objects.append(self.allocator, object);
1359 try self.resolveSymbolsInObject(object_id);1351 try self.resolveSymbolsInObject(object);
13601352
1361 nsyms = self.symtab.items().len;1353 reset = true;
1362 break;1354 break;
1363 }1355 }
1356
1357 if (reset) {
1358 next_sym = 0;
1359 } else {
1360 next_sym += 1;
1361 }
1364 }1362 }
13651363
1366 // Third pass, resolve symbols in dynamic libraries.1364 // Third pass, resolve symbols in dynamic libraries.
1367 // TODO Implement libSystem as a hard-coded library, or ship with1365 // TODO Implement libSystem as a hard-coded library, or ship with
1368 // a libSystem.B.tbd definition file?1366 // a libSystem.B.tbd definition file?
1369 for (self.symtab.items()) |*entry| {1367 try self.imports.ensureCapacity(self.allocator, self.unresolved.count());
1370 if (entry.value.tag != .undef) continue;1368 for (self.unresolved.items()) |entry| {
1369 const proxy = try self.allocator.create(Symbol.Proxy);
1370 errdefer self.allocator.destroy(proxy);
1371
1372 proxy.* = .{
1373 .base = .{
1374 .@"type" = .proxy,
1375 .name = try self.allocator.dupe(u8, entry.key),
1376 },
1377 .dylib = 0,
1378 };
13711379
1372 entry.value.tag = .import;1380 self.imports.putAssumeCapacityNoClobber(proxy.base.name, &proxy.base);
1373 entry.value.file = 0;1381 entry.value.alias = &proxy.base;
1374 }1382 }
1383 self.unresolved.clearAndFree(self.allocator);
13751384
1376 // If there are any undefs left, flag an error.1385 // If there are any undefs left, flag an error.
1377 var has_unresolved = false;1386 if (self.unresolved.count() > 0) {
1378 for (self.symtab.items()) |entry| {1387 for (self.unresolved.items()) |entry| {
1379 if (entry.value.tag != .undef) continue;1388 log.err("undefined reference to symbol '{s}'", .{entry.key});
13801389 log.err(" | referenced in {s}", .{
1381 has_unresolved = true;1390 entry.value.cast(Symbol.Unresolved).?.file.name.?,
1382 log.err("undefined reference to symbol '{s}'", .{entry.value.name});1391 });
1383 }1392 }
1384 if (has_unresolved) {
1385 return error.UndefinedSymbolReference;1393 return error.UndefinedSymbolReference;
1386 }1394 }
13871395
1388 // Finally put dyld_stub_binder as an Import1396 // Finally put dyld_stub_binder as an Import
1389 var name = try self.allocator.dupe(u8, "dyld_stub_binder");1397 const dyld_stub_binder = try self.allocator.create(Symbol.Proxy);
1390 try self.symtab.putNoClobber(self.allocator, name, .{1398 errdefer self.allocator.destroy(dyld_stub_binder);
1391 .tag = .import,
1392 .name = name,
1393 .address = 0,
1394 .section = 0,
1395 .file = 0,
1396 });
13971399
1398 {1400 dyld_stub_binder.* = .{
1399 log.debug("symtab", .{});1401 .base = .{
1400 for (self.symtab.items()) |sym| {1402 .@"type" = .proxy,
1401 switch (sym.value.tag) {1403 .name = try self.allocator.dupe(u8, "dyld_stub_binder"),
1402 .weak, .strong => {1404 },
1403 log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? });1405 .dylib = 0,
1404 },1406 };
1405 .import => {1407
1406 log.debug(" | {s} => libSystem.B.dylib", .{sym.key});1408 try self.imports.putNoClobber(
1407 },1409 self.allocator,
1408 else => unreachable,1410 dyld_stub_binder.base.name,
1409 }1411 &dyld_stub_binder.base,
1410 }1412 );
1411 }
1412}1413}
14131414
1414fn resolveStubsAndGotEntries(self: *Zld) !void {1415fn resolveStubsAndGotEntries(self: *Zld) !void {
...@@ -2979,3 +2980,33 @@ pub fn parseName(name: *const [16]u8) []const u8 {...@@ -2979,3 +2980,33 @@ pub fn parseName(name: *const [16]u8) []const u8 {
2979 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;2980 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
2980 return name[0..len];2981 return name[0..len];
2981}2982}
2983
2984fn printSymbols(self: *Zld) void {
2985 log.warn("globals", .{});
2986 for (self.globals.items()) |entry| {
2987 const sym = entry.value.cast(Symbol.Regular) orelse unreachable;
2988 log.warn(" | {s} @ {*}", .{ sym.base.name, entry.value });
2989 log.warn(" => alias of {*}", .{sym.base.alias});
2990 log.warn(" => linkage {s}", .{sym.linkage});
2991 log.warn(" => defined in {s}", .{sym.file.name.?});
2992 }
2993 for (self.objects.items) |object| {
2994 log.warn("locals in {s}", .{object.name.?});
2995 for (object.symbols.items) |sym| {
2996 log.warn(" | {s} @ {*}", .{ sym.name, sym });
2997 log.warn(" => alias of {*}", .{sym.alias});
2998 if (sym.cast(Symbol.Regular)) |reg| {
2999 log.warn(" => linkage {s}", .{reg.linkage});
3000 } else {
3001 log.warn(" => unresolved", .{});
3002 }
3003 }
3004 }
3005 log.warn("proxies", .{});
3006 for (self.imports.items()) |entry| {
3007 const sym = entry.value.cast(Symbol.Proxy) orelse unreachable;
3008 log.warn(" | {s} @ {*}", .{ sym.base.name, entry.value });
3009 log.warn(" => alias of {*}", .{sym.base.alias});
3010 log.warn(" => defined in libSystem.B.dylib", .{});
3011 }
3012}