authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-20 12:45:51+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-24 14:45:45+02:00
logbc78b02c04143b7e1b32aceacdcf44130026a7a7
treed0d20c362872c1dea62a50f694e309ca7c685645
parent09b46198ff8d64c9884a0cf13788855b4d4bbe2d

zld: introduce Stub.zig which represents parsed stub file

Instead of trying to fit a stub file into the frame of a Dylib struct, I think it makes more sense to keep them as separate entities with possibly shared interface (which would be added in the future). This cleaned up a lot of logic in Dylib as well as Stub. Also, while here I've made creating actual *Symbols lazy in the sense Dylib and Stub only store hash maps of symbol names that they expose but we defer create and referencing given dylib/stub until link time when a symbol is actually referenced. This should reduce memory usage and speed things up a bit.

7 files changed, 345 insertions(+), 225 deletions(-)

CMakeLists.txt+1
......@@ -579,6 +579,7 @@ set(ZIG_STAGE2_SOURCES
579579 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
580580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
581581 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Stub.zig"
582583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
583584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
584585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
src/link/MachO/Archive.zig+6-3
......@@ -234,8 +234,11 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
234234 return object;
235235}
236236
237pub fn isArchive(file: fs.File) bool {
238 const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch return false;
239 file.seekTo(0) catch return false;
237pub fn isArchive(file: fs.File) !bool {
238 const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch |err| switch (err) {
239 error.EndOfStream => return false,
240 else => |e| return e,
241 };
242 try file.seekTo(0);
240243 return mem.eql(u8, &magic, Archive.ARMAG);
241244}
src/link/MachO/Dylib.zig+25-94
......@@ -9,7 +9,6 @@ const mem = std.mem;
99
1010const Allocator = mem.Allocator;
1111const Symbol = @import("Symbol.zig");
12const LibStub = @import("../tapi.zig").LibStub;
1312
1413usingnamespace @import("commands.zig");
1514
......@@ -29,7 +28,10 @@ id_cmd_index: ?u16 = null,
2928
3029id: ?Id = null,
3130
32symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
31/// Parsed symbol table represented as hash map of symbols'
32/// names. We can and should defer creating *Symbols until
33/// a symbol is referenced by an object file.
34symbols: std.StringArrayHashMapUnmanaged(void) = .{},
3335
3436pub const Id = struct {
3537 name: []const u8,
......@@ -52,9 +54,8 @@ pub fn deinit(self: *Dylib) void {
5254 }
5355 self.load_commands.deinit(self.allocator);
5456
55 for (self.symbols.values()) |value| {
56 value.deinit(self.allocator);
57 self.allocator.destroy(value);
57 for (self.symbols.keys()) |key| {
58 self.allocator.free(key);
5859 }
5960 self.symbols.deinit(self.allocator);
6061
......@@ -171,103 +172,33 @@ pub fn parseSymbols(self: *Dylib) !void {
171172 if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;
172173
173174 const name = try self.allocator.dupe(u8, sym_name);
174 const proxy = try self.allocator.create(Symbol.Proxy);
175 errdefer self.allocator.destroy(proxy);
176
177 proxy.* = .{
178 .base = .{
179 .@"type" = .proxy,
180 .name = name,
181 },
182 .dylib = self,
183 };
184
185 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
175 try self.symbols.putNoClobber(self.allocator, name, {});
186176 }
187177}
188178
189pub fn isDylib(file: fs.File) bool {
190 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
191 file.seekTo(0) catch return false;
179pub fn isDylib(file: fs.File) !bool {
180 const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
181 error.EndOfStream => return false,
182 else => |e| return e,
183 };
184 try file.seekTo(0);
192185 return header.filetype == macho.MH_DYLIB;
193186}
194187
195pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
196 assert(lib_stub.inner.len > 0);
188pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol {
189 if (!self.symbols.contains(sym_name)) return null;
197190
198 log.debug("parsing shared library from stub '{s}'", .{self.name.?});
191 const name = try self.allocator.dupe(u8, sym_name);
192 const proxy = try self.allocator.create(Symbol.Proxy);
193 errdefer self.allocator.destroy(proxy);
199194
200 const umbrella_lib = lib_stub.inner[0];
201 self.id = .{
202 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
203 // TODO parse from the stub
204 .timestamp = 2,
205 .current_version = 0,
206 .compatibility_version = 0,
207 };
208
209 const target_string: []const u8 = switch (self.arch.?) {
210 .aarch64 => "arm64-macos",
211 .x86_64 => "x86_64-macos",
212 else => unreachable,
195 proxy.* = .{
196 .base = .{
197 .@"type" = .proxy,
198 .name = name,
199 },
200 .file = .{ .dylib = self },
213201 };
214202
215 for (lib_stub.inner) |stub| {
216 if (!hasTarget(stub.targets, target_string)) continue;
217
218 if (stub.exports) |exports| {
219 for (exports) |exp| {
220 if (!hasTarget(exp.targets, target_string)) continue;
221
222 for (exp.symbols) |sym_name| {
223 if (self.symbols.contains(sym_name)) continue;
224
225 const name = try self.allocator.dupe(u8, sym_name);
226 const proxy = try self.allocator.create(Symbol.Proxy);
227 errdefer self.allocator.destroy(proxy);
228
229 proxy.* = .{
230 .base = .{
231 .@"type" = .proxy,
232 .name = name,
233 },
234 .dylib = self,
235 };
236
237 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
238 }
239 }
240 }
241
242 if (stub.reexports) |reexports| {
243 for (reexports) |reexp| {
244 if (!hasTarget(reexp.targets, target_string)) continue;
245
246 for (reexp.symbols) |sym_name| {
247 if (self.symbols.contains(sym_name)) continue;
248
249 const name = try self.allocator.dupe(u8, sym_name);
250 const proxy = try self.allocator.create(Symbol.Proxy);
251 errdefer self.allocator.destroy(proxy);
252
253 proxy.* = .{
254 .base = .{
255 .@"type" = .proxy,
256 .name = name,
257 },
258 .dylib = self,
259 };
260
261 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
262 }
263 }
264 }
265 }
266}
267
268fn hasTarget(targets: []const []const u8, target: []const u8) bool {
269 for (targets) |t| {
270 if (mem.eql(u8, t, target)) return true;
271 }
272 return false;
203 return &proxy.base;
273204}
src/link/MachO/Object.zig+6-3
......@@ -534,8 +534,11 @@ pub fn parseDataInCode(self: *Object) !void {
534534 }
535535}
536536
537pub fn isObject(file: fs.File) bool {
538 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
539 file.seekTo(0) catch return false;
537pub fn isObject(file: fs.File) !bool {
538 const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
539 error.EndOfStream => return false,
540 else => |e| return e,
541 };
542 try file.seekTo(0);
540543 return header.filetype == macho.MH_OBJECT;
541544}
src/link/MachO/Stub.zig created+130
......@@ -0,0 +1,130 @@
1const Stub = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.stub);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Symbol = @import("Symbol.zig");
12pub const LibStub = @import("../tapi.zig").LibStub;
13
14allocator: *Allocator,
15arch: ?std.Target.Cpu.Arch = null,
16lib_stub: ?LibStub = null,
17name: ?[]const u8 = null,
18
19ordinal: ?u16 = null,
20
21id: ?Id = null,
22
23/// Parsed symbol table represented as hash map of symbols'
24/// names. We can and should defer creating *Symbols until
25/// a symbol is referenced by an object file.
26symbols: std.StringArrayHashMapUnmanaged(void) = .{},
27
28pub const Id = struct {
29 name: []const u8,
30 timestamp: u32,
31 current_version: u32,
32 compatibility_version: u32,
33
34 pub fn deinit(id: *Id, allocator: *Allocator) void {
35 allocator.free(id.name);
36 }
37};
38
39pub fn init(allocator: *Allocator) Stub {
40 return .{ .allocator = allocator };
41}
42
43pub fn deinit(self: *Stub) void {
44 self.symbols.deinit(self.allocator);
45
46 if (self.lib_stub) |*lib_stub| {
47 lib_stub.deinit();
48 }
49
50 if (self.name) |name| {
51 self.allocator.free(name);
52 }
53
54 if (self.id) |*id| {
55 id.deinit(self.allocator);
56 }
57}
58
59pub fn parse(self: *Stub) !void {
60 const lib_stub = self.lib_stub orelse return error.EmptyStubFile;
61 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
62
63 log.debug("parsing shared library from stub '{s}'", .{self.name.?});
64
65 const umbrella_lib = lib_stub.inner[0];
66 self.id = .{
67 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
68 // TODO parse from the stub
69 .timestamp = 2,
70 .current_version = 0,
71 .compatibility_version = 0,
72 };
73
74 const target_string: []const u8 = switch (self.arch.?) {
75 .aarch64 => "arm64-macos",
76 .x86_64 => "x86_64-macos",
77 else => unreachable,
78 };
79
80 for (lib_stub.inner) |stub| {
81 if (!hasTarget(stub.targets, target_string)) continue;
82
83 if (stub.exports) |exports| {
84 for (exports) |exp| {
85 if (!hasTarget(exp.targets, target_string)) continue;
86
87 for (exp.symbols) |sym_name| {
88 if (self.symbols.contains(sym_name)) continue;
89 try self.symbols.putNoClobber(self.allocator, sym_name, {});
90 }
91 }
92 }
93
94 if (stub.reexports) |reexports| {
95 for (reexports) |reexp| {
96 if (!hasTarget(reexp.targets, target_string)) continue;
97
98 for (reexp.symbols) |sym_name| {
99 if (self.symbols.contains(sym_name)) continue;
100 try self.symbols.putNoClobber(self.allocator, sym_name, {});
101 }
102 }
103 }
104 }
105}
106
107fn hasTarget(targets: []const []const u8, target: []const u8) bool {
108 for (targets) |t| {
109 if (mem.eql(u8, t, target)) return true;
110 }
111 return false;
112}
113
114pub fn createProxy(self: *Stub, sym_name: []const u8) !?*Symbol {
115 if (!self.symbols.contains(sym_name)) return null;
116
117 const name = try self.allocator.dupe(u8, sym_name);
118 const proxy = try self.allocator.create(Symbol.Proxy);
119 errdefer self.allocator.destroy(proxy);
120
121 proxy.* = .{
122 .base = .{
123 .@"type" = .proxy,
124 .name = name,
125 },
126 .file = .{ .stub = self },
127 };
128
129 return &proxy.base;
130}
src/link/MachO/Symbol.zig+14-2
......@@ -7,6 +7,7 @@ const mem = std.mem;
77const Allocator = mem.Allocator;
88const Dylib = @import("Dylib.zig");
99const Object = @import("Object.zig");
10const Stub = @import("Stub.zig");
1011
1112pub const Type = enum {
1213 regular,
......@@ -84,11 +85,22 @@ pub const Regular = struct {
8485pub const Proxy = struct {
8586 base: Symbol,
8687
87 /// Dylib where to locate this symbol.
88 /// Dylib or stub where to locate this symbol.
8889 /// null means self-reference.
89 dylib: ?*Dylib = null,
90 file: ?union(enum) {
91 dylib: *Dylib,
92 stub: *Stub,
93 } = null,
9094
9195 pub const base_type: Symbol.Type = .proxy;
96
97 pub fn dylibOrdinal(proxy: *Proxy) u16 {
98 const file = proxy.file orelse return 0;
99 return switch (file) {
100 .dylib => |dylib| dylib.ordinal.?,
101 .stub => |stub| stub.ordinal.?,
102 };
103 }
92104};
93105
94106pub const Unresolved = struct {
src/link/MachO/Zld.zig+163-123
......@@ -16,8 +16,8 @@ const Allocator = mem.Allocator;
1616const Archive = @import("Archive.zig");
1717const CodeSignature = @import("CodeSignature.zig");
1818const Dylib = @import("Dylib.zig");
19const LibStub = @import("../tapi.zig").LibStub;
2019const Object = @import("Object.zig");
20const Stub = @import("Stub.zig");
2121const Symbol = @import("Symbol.zig");
2222const Trie = @import("Trie.zig");
2323
......@@ -38,6 +38,10 @@ stack_size: u64 = 0,
3838objects: std.ArrayListUnmanaged(*Object) = .{},
3939archives: std.ArrayListUnmanaged(*Archive) = .{},
4040dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
41lib_stubs: std.ArrayListUnmanaged(*Stub) = .{},
42
43libsystem_stub_index: ?u16 = null,
44next_dylib_ordinal: u16 = 1,
4145
4246load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
4347
......@@ -153,9 +157,20 @@ pub fn deinit(self: *Zld) void {
153157 }
154158 self.dylibs.deinit(self.allocator);
155159
160 for (self.lib_stubs.items) |stub| {
161 stub.deinit();
162 self.allocator.destroy(stub);
163 }
164 self.lib_stubs.deinit(self.allocator);
165
166 for (self.imports.values()) |proxy| {
167 proxy.deinit(self.allocator);
168 self.allocator.destroy(proxy);
169 }
170 self.imports.deinit(self.allocator);
171
156172 self.tentatives.deinit(self.allocator);
157173 self.globals.deinit(self.allocator);
158 self.imports.deinit(self.allocator);
159174 self.unresolved.deinit(self.allocator);
160175 self.strtab.deinit(self.allocator);
161176
......@@ -245,9 +260,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
245260 dylib,
246261 stub,
247262 },
248 file: fs.File,
263 origin: union {
264 file: fs.File,
265 stub: Stub.LibStub,
266 },
249267 name: []const u8,
250 stub: ?LibStub = null,
251268 };
252269 var classified = std.ArrayList(Input).init(self.allocator);
253270 defer classified.deinit();
......@@ -262,45 +279,45 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
262279 };
263280
264281 try_object: {
265 if (!Object.isObject(file)) break :try_object;
282 if (!(try Object.isObject(file))) break :try_object;
266283 try classified.append(.{
267284 .kind = .object,
268 .file = file,
285 .origin = .{ .file = file },
269286 .name = full_path,
270287 });
271288 continue;
272289 }
273290
274291 try_archive: {
275 if (!Archive.isArchive(file)) break :try_archive;
292 if (!(try Archive.isArchive(file))) break :try_archive;
276293 try classified.append(.{
277294 .kind = .archive,
278 .file = file,
295 .origin = .{ .file = file },
279296 .name = full_path,
280297 });
281298 continue;
282299 }
283300
284301 try_dylib: {
285 if (!Dylib.isDylib(file)) break :try_dylib;
302 if (!(try Dylib.isDylib(file))) break :try_dylib;
286303 try classified.append(.{
287304 .kind = .dylib,
288 .file = file,
305 .origin = .{ .file = file },
289306 .name = full_path,
290307 });
291308 continue;
292309 }
293310
294311 try_stub: {
295 var lib_stub = LibStub.loadFromFile(self.allocator, file) catch {
312 var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch {
296313 break :try_stub;
297314 };
298315 try classified.append(.{
299316 .kind = .stub,
300 .file = file,
317 .origin = .{ .stub = lib_stub },
301318 .name = full_path,
302 .stub = lib_stub,
303319 });
320 file.close();
304321 continue;
305322 }
306323
......@@ -318,7 +335,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
318335 object.* = Object.init(self.allocator);
319336 object.arch = self.arch.?;
320337 object.name = input.name;
321 object.file = input.file;
338 object.file = input.origin.file;
339
322340 try object.parse();
323341 try self.objects.append(self.allocator, object);
324342 },
......@@ -329,40 +347,34 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
329347 archive.* = Archive.init(self.allocator);
330348 archive.arch = self.arch.?;
331349 archive.name = input.name;
332 archive.file = input.file;
350 archive.file = input.origin.file;
351
333352 try archive.parse();
334353 try self.archives.append(self.allocator, archive);
335354 },
336 .dylib, .stub => {
355 .dylib => {
337356 const dylib = try self.allocator.create(Dylib);
338357 errdefer self.allocator.destroy(dylib);
339358
340359 dylib.* = Dylib.init(self.allocator);
341360 dylib.arch = self.arch.?;
342361 dylib.name = input.name;
343 dylib.file = input.file;
344 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
362 dylib.file = input.origin.file;
345363
346 // TODO Defer parsing of the dylibs until they are actually needed
347 if (input.stub) |stub| {
348 try dylib.parseFromStub(stub);
349 } else {
350 try dylib.parse();
351 }
364 try dylib.parse();
352365 try self.dylibs.append(self.allocator, dylib);
366 },
367 .stub => {
368 const stub = try self.allocator.create(Stub);
369 errdefer self.allocator.destroy(stub);
353370
354 // Add LC_LOAD_DYLIB command
355 const dylib_id = dylib.id orelse unreachable;
356 var dylib_cmd = try createLoadDylibCommand(
357 self.allocator,
358 dylib_id.name,
359 dylib_id.timestamp,
360 dylib_id.current_version,
361 dylib_id.compatibility_version,
362 );
363 errdefer dylib_cmd.deinit(self.allocator);
364
365 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
371 stub.* = Stub.init(self.allocator);
372 stub.arch = self.arch.?;
373 stub.name = input.name;
374 stub.lib_stub = input.origin.stub;
375
376 try stub.parse();
377 try self.lib_stubs.append(self.allocator, stub);
366378 },
367379 }
368380 }
......@@ -372,7 +384,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
372384 for (libs) |lib| {
373385 const file = try fs.cwd().openFile(lib, .{});
374386
375 if (Dylib.isDylib(file)) {
387 if (try Dylib.isDylib(file)) {
376388 const dylib = try self.allocator.create(Dylib);
377389 errdefer self.allocator.destroy(dylib);
378390
......@@ -380,57 +392,27 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
380392 dylib.arch = self.arch.?;
381393 dylib.name = try self.allocator.dupe(u8, lib);
382394 dylib.file = file;
383 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
384395
385 // TODO Defer parsing of the dylibs until they are actually needed
386396 try dylib.parse();
387397 try self.dylibs.append(self.allocator, dylib);
388
389 // Add LC_LOAD_DYLIB command
390 const dylib_id = dylib.id orelse unreachable;
391 var dylib_cmd = try createLoadDylibCommand(
392 self.allocator,
393 dylib_id.name,
394 dylib_id.timestamp,
395 dylib_id.current_version,
396 dylib_id.compatibility_version,
397 );
398 errdefer dylib_cmd.deinit(self.allocator);
399
400 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
401398 } else {
402399 // Try tbd stub file next.
403 if (LibStub.loadFromFile(self.allocator, file)) |*lib_stub| {
404 defer lib_stub.deinit();
405
406 const dylib = try self.allocator.create(Dylib);
407 errdefer self.allocator.destroy(dylib);
408
409 dylib.* = Dylib.init(self.allocator);
410 dylib.arch = self.arch.?;
411 dylib.name = try self.allocator.dupe(u8, lib);
412 dylib.file = file;
413 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
400 if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| {
401 const stub = try self.allocator.create(Stub);
402 errdefer self.allocator.destroy(stub);
414403
415 try dylib.parseFromStub(lib_stub.*);
416 try self.dylibs.append(self.allocator, dylib);
404 stub.* = Stub.init(self.allocator);
405 stub.arch = self.arch.?;
406 stub.name = try self.allocator.dupe(u8, lib);
407 stub.lib_stub = lib_stub;
417408
418 // Add LC_LOAD_DYLIB command
419 const dylib_id = dylib.id orelse unreachable;
420 var dylib_cmd = try createLoadDylibCommand(
421 self.allocator,
422 dylib_id.name,
423 dylib_id.timestamp,
424 dylib_id.current_version,
425 dylib_id.compatibility_version,
426 );
427 errdefer dylib_cmd.deinit(self.allocator);
428
429 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
409 try stub.parse();
410 try self.lib_stubs.append(self.allocator, stub);
430411 } else |_| {
431412 // TODO this entire logic has to be cleaned up.
432413 try file.seekTo(0);
433 if (Archive.isArchive(file)) {
414
415 if (try Archive.isArchive(file)) {
434416 const archive = try self.allocator.create(Archive);
435417 errdefer self.allocator.destroy(archive);
436418
......@@ -438,6 +420,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
438420 archive.arch = self.arch.?;
439421 archive.name = try self.allocator.dupe(u8, lib);
440422 archive.file = file;
423
441424 try archive.parse();
442425 try self.archives.append(self.allocator, archive);
443426 } else {
......@@ -449,26 +432,28 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
449432 }
450433}
451434
452fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
453 const file = try fs.cwd().openFile(lib_system_path, .{});
435fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
436 const file = try fs.cwd().openFile(libc_stub_path, .{});
437 defer file.close();
438
439 var lib_stub = try Stub.LibStub.loadFromFile(self.allocator, file);
454440
455 var lib_stub = try LibStub.loadFromFile(self.allocator, file);
456 defer lib_stub.deinit();
441 const stub = try self.allocator.create(Stub);
442 errdefer self.allocator.destroy(stub);
457443
458 const dylib = try self.allocator.create(Dylib);
459 errdefer self.allocator.destroy(dylib);
444 stub.* = Stub.init(self.allocator);
445 stub.arch = self.arch.?;
446 stub.name = try self.allocator.dupe(u8, libc_stub_path);
447 stub.lib_stub = lib_stub;
460448
461 dylib.* = Dylib.init(self.allocator);
462 dylib.arch = self.arch.?;
463 dylib.name = try self.allocator.dupe(u8, lib_system_path);
464 dylib.file = file;
465 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
449 try stub.parse();
466450
467 try dylib.parseFromStub(lib_stub);
468 try self.dylibs.append(self.allocator, dylib);
451 self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len);
452 try self.lib_stubs.append(self.allocator, stub);
469453
470 // Add LC_LOAD_DYLIB command
471 const dylib_id = dylib.id orelse unreachable;
454 // Add LC_LOAD_DYLIB load command.
455 stub.ordinal = self.next_dylib_ordinal;
456 const dylib_id = stub.id orelse unreachable;
472457 var dylib_cmd = try createLoadDylibCommand(
473458 self.allocator,
474459 dylib_id.name,
......@@ -477,8 +462,8 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
477462 dylib_id.compatibility_version,
478463 );
479464 errdefer dylib_cmd.deinit(self.allocator);
480
481465 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
466 self.next_dylib_ordinal += 1;
482467}
483468
484469fn mapAndUpdateSections(
......@@ -1906,21 +1891,34 @@ fn resolveSymbols(self: *Zld) !void {
19061891 for (self.unresolved.values()) |value| {
19071892 unresolved.appendAssumeCapacity(value);
19081893 }
1909 self.unresolved.clearAndFree(self.allocator);
1910
1911 var has_undefined = false;
1912 while (unresolved.popOrNull()) |undef| {
1913 var found = false;
1914 for (self.dylibs.items) |dylib| {
1915 const proxy = dylib.symbols.get(undef.name) orelse continue;
1916 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
1917 undef.alias = proxy;
1918 found = true;
1919 }
1920
1921 if (!found) {
1922 if (mem.eql(u8, undef.name, "___dso_handle")) {
1923 const proxy = self.imports.get(undef.name) orelse blk: {
1894 self.unresolved.clearRetainingCapacity();
1895
1896 var referenced = std.AutoHashMap(union(enum) {
1897 dylib: *Dylib,
1898 stub: *Stub,
1899 }, void).init(self.allocator);
1900 defer referenced.deinit();
1901
1902 loop: while (unresolved.popOrNull()) |undef| {
1903 const proxy = self.imports.get(undef.name) orelse outer: {
1904 const proxy = inner: {
1905 for (self.dylibs.items) |dylib| {
1906 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1907 try referenced.put(.{ .dylib = dylib }, {});
1908 break :inner proxy;
1909 }
1910 for (self.lib_stubs.items) |stub, i| {
1911 const proxy = (try stub.createProxy(undef.name)) orelse continue;
1912 if (self.libsystem_stub_index.? != @intCast(u16, i)) {
1913 // LibSystem gets its load command separately.
1914 try referenced.put(.{ .stub = stub }, {});
1915 }
1916 break :inner proxy;
1917 }
1918 if (mem.eql(u8, undef.name, "___dso_handle")) {
1919 // TODO this is just a temp patch until I work out what to actually
1920 // do with ___dso_handle and __mh_execute_header symbols which are
1921 // synthetically created by the linker on macOS.
19241922 const name = try self.allocator.dupe(u8, undef.name);
19251923 const proxy = try self.allocator.create(Symbol.Proxy);
19261924 errdefer self.allocator.destroy(proxy);
......@@ -1929,26 +1927,69 @@ fn resolveSymbols(self: *Zld) !void {
19291927 .@"type" = .proxy,
19301928 .name = name,
19311929 },
1930 .file = null,
19321931 };
1933 try self.imports.putNoClobber(self.allocator, name, &proxy.base);
1934 break :blk &proxy.base;
1935 };
1936 undef.alias = proxy;
1937 continue;
1932 break :inner &proxy.base;
1933 }
1934
1935 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
1936 continue :loop;
1937 };
1938
1939 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
1940 break :outer proxy;
1941 };
1942 undef.alias = proxy;
1943 }
1944
1945 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
1946 var it = referenced.iterator();
1947 while (it.next()) |key| {
1948 var dylib_cmd = blk: {
1949 switch (key.key_ptr.*) {
1950 .dylib => |dylib| {
1951 dylib.ordinal = self.next_dylib_ordinal;
1952 const dylib_id = dylib.id orelse unreachable;
1953 break :blk try createLoadDylibCommand(
1954 self.allocator,
1955 dylib_id.name,
1956 dylib_id.timestamp,
1957 dylib_id.current_version,
1958 dylib_id.compatibility_version,
1959 );
1960 },
1961 .stub => |stub| {
1962 stub.ordinal = self.next_dylib_ordinal;
1963 const dylib_id = stub.id orelse unreachable;
1964 break :blk try createLoadDylibCommand(
1965 self.allocator,
1966 dylib_id.name,
1967 dylib_id.timestamp,
1968 dylib_id.current_version,
1969 dylib_id.compatibility_version,
1970 );
1971 },
19381972 }
1973 };
1974 errdefer dylib_cmd.deinit(self.allocator);
1975 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
1976 self.next_dylib_ordinal += 1;
1977 }
19391978
1979 if (self.unresolved.count() > 0) {
1980 for (self.unresolved.values()) |undef| {
19401981 log.err("undefined reference to symbol '{s}'", .{undef.name});
19411982 log.err(" | referenced in {s}", .{
19421983 undef.cast(Symbol.Unresolved).?.file.name.?,
19431984 });
1944 has_undefined = true;
19451985 }
1946 }
19471986
1948 if (has_undefined) return error.UndefinedSymbolReference;
1987 return error.UndefinedSymbolReference;
1988 }
19491989
19501990 // Finally put dyld_stub_binder as an Import
1951 const proxy = self.dylibs.items[self.dylibs.items.len - 1].symbols.get("dyld_stub_binder") orelse {
1991 const libsystem_stub = self.lib_stubs.items[self.libsystem_stub_index.?];
1992 const proxy = (try libsystem_stub.createProxy("dyld_stub_binder")) orelse {
19521993 log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
19531994 return error.UndefinedSymbolReference;
19541995 };
......@@ -2814,7 +2855,7 @@ fn writeBindInfoTable(self: *Zld) !void {
28142855 try pointers.append(.{
28152856 .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),
28162857 .segment_id = segment_id,
2817 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
2858 .dylib_ordinal = proxy.dylibOrdinal(),
28182859 .name = proxy.base.name,
28192860 });
28202861 }
......@@ -2833,7 +2874,7 @@ fn writeBindInfoTable(self: *Zld) !void {
28332874 try pointers.append(.{
28342875 .offset = base_offset,
28352876 .segment_id = segment_id,
2836 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
2877 .dylib_ordinal = proxy.dylibOrdinal(),
28372878 .name = proxy.base.name,
28382879 });
28392880 }
......@@ -2873,7 +2914,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
28732914 pointers.appendAssumeCapacity(.{
28742915 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
28752916 .segment_id = segment_id,
2876 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
2917 .dylib_ordinal = proxy.dylibOrdinal(),
28772918 .name = sym.name,
28782919 });
28792920 }
......@@ -3181,12 +3222,11 @@ fn writeSymbolTable(self: *Zld) !void {
31813222
31823223 for (self.imports.values()) |sym| {
31833224 const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
3184 const dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0;
31853225 try undefs.append(.{
31863226 .n_strx = try self.makeString(sym.name),
31873227 .n_type = macho.N_UNDF | macho.N_EXT,
31883228 .n_sect = 0,
3189 .n_desc = (dylib_ordinal * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
3229 .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
31903230 .n_value = 0,
31913231 });
31923232 }