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...@@ -579,6 +579,7 @@ set(ZIG_STAGE2_SOURCES
579 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"579 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"580 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
581 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"581 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Stub.zig"
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"585 "${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 {...@@ -234,8 +234,11 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
234 return object;234 return object;
235}235}
236236
237pub fn isArchive(file: fs.File) bool {237pub fn isArchive(file: fs.File) !bool {
238 const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch return false;238 const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch |err| switch (err) {
239 file.seekTo(0) catch return false;239 error.EndOfStream => return false,
240 else => |e| return e,
241 };
242 try file.seekTo(0);
240 return mem.eql(u8, &magic, Archive.ARMAG);243 return mem.eql(u8, &magic, Archive.ARMAG);
241}244}
src/link/MachO/Dylib.zig+25-94
...@@ -9,7 +9,6 @@ const mem = std.mem;...@@ -9,7 +9,6 @@ const mem = std.mem;
99
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
11const Symbol = @import("Symbol.zig");11const Symbol = @import("Symbol.zig");
12const LibStub = @import("../tapi.zig").LibStub;
1312
14usingnamespace @import("commands.zig");13usingnamespace @import("commands.zig");
1514
...@@ -29,7 +28,10 @@ id_cmd_index: ?u16 = null,...@@ -29,7 +28,10 @@ id_cmd_index: ?u16 = null,
2928
30id: ?Id = null,29id: ?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
34pub const Id = struct {36pub const Id = struct {
35 name: []const u8,37 name: []const u8,
...@@ -52,9 +54,8 @@ pub fn deinit(self: *Dylib) void {...@@ -52,9 +54,8 @@ pub fn deinit(self: *Dylib) void {
52 }54 }
53 self.load_commands.deinit(self.allocator);55 self.load_commands.deinit(self.allocator);
5456
55 for (self.symbols.values()) |value| {57 for (self.symbols.keys()) |key| {
56 value.deinit(self.allocator);58 self.allocator.free(key);
57 self.allocator.destroy(value);
58 }59 }
59 self.symbols.deinit(self.allocator);60 self.symbols.deinit(self.allocator);
6061
...@@ -171,103 +172,33 @@ pub fn parseSymbols(self: *Dylib) !void {...@@ -171,103 +172,33 @@ pub fn parseSymbols(self: *Dylib) !void {
171 if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;172 if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;
172173
173 const name = try self.allocator.dupe(u8, sym_name);174 const name = try self.allocator.dupe(u8, sym_name);
174 const proxy = try self.allocator.create(Symbol.Proxy);175 try self.symbols.putNoClobber(self.allocator, name, {});
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);
186 }176 }
187}177}
188178
189pub fn isDylib(file: fs.File) bool {179pub fn isDylib(file: fs.File) !bool {
190 const header = file.reader().readStruct(macho.mach_header_64) catch return false;180 const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
191 file.seekTo(0) catch return false;181 error.EndOfStream => return false,
182 else => |e| return e,
183 };
184 try file.seekTo(0);
192 return header.filetype == macho.MH_DYLIB;185 return header.filetype == macho.MH_DYLIB;
193}186}
194187
195pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {188pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol {
196 assert(lib_stub.inner.len > 0);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];195 proxy.* = .{
201 self.id = .{196 .base = .{
202 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),197 .@"type" = .proxy,
203 // TODO parse from the stub198 .name = name,
204 .timestamp = 2,199 },
205 .current_version = 0,200 .file = .{ .dylib = self },
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,
213 };201 };
214202
215 for (lib_stub.inner) |stub| {203 return &proxy.base;
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;
273}204}
src/link/MachO/Object.zig+6-3
...@@ -534,8 +534,11 @@ pub fn parseDataInCode(self: *Object) !void {...@@ -534,8 +534,11 @@ pub fn parseDataInCode(self: *Object) !void {
534 }534 }
535}535}
536536
537pub fn isObject(file: fs.File) bool {537pub fn isObject(file: fs.File) !bool {
538 const header = file.reader().readStruct(macho.mach_header_64) catch return false;538 const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
539 file.seekTo(0) catch return false;539 error.EndOfStream => return false,
540 else => |e| return e,
541 };
542 try file.seekTo(0);
540 return header.filetype == macho.MH_OBJECT;543 return header.filetype == macho.MH_OBJECT;
541}544}
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;...@@ -7,6 +7,7 @@ const mem = std.mem;
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
8const Dylib = @import("Dylib.zig");8const Dylib = @import("Dylib.zig");
9const Object = @import("Object.zig");9const Object = @import("Object.zig");
10const Stub = @import("Stub.zig");
1011
11pub const Type = enum {12pub const Type = enum {
12 regular,13 regular,
...@@ -84,11 +85,22 @@ pub const Regular = struct {...@@ -84,11 +85,22 @@ pub const Regular = struct {
84pub const Proxy = struct {85pub const Proxy = struct {
85 base: Symbol,86 base: Symbol,
8687
87 /// Dylib where to locate this symbol.88 /// Dylib or stub where to locate this symbol.
88 /// null means self-reference.89 /// null means self-reference.
89 dylib: ?*Dylib = null,90 file: ?union(enum) {
91 dylib: *Dylib,
92 stub: *Stub,
93 } = null,
9094
91 pub const base_type: Symbol.Type = .proxy;95 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 }
92};104};
93105
94pub const Unresolved = struct {106pub const Unresolved = struct {
src/link/MachO/Zld.zig+163-123
...@@ -16,8 +16,8 @@ const Allocator = mem.Allocator;...@@ -16,8 +16,8 @@ const Allocator = mem.Allocator;
16const Archive = @import("Archive.zig");16const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");17const CodeSignature = @import("CodeSignature.zig");
18const Dylib = @import("Dylib.zig");18const Dylib = @import("Dylib.zig");
19const LibStub = @import("../tapi.zig").LibStub;
20const Object = @import("Object.zig");19const Object = @import("Object.zig");
20const Stub = @import("Stub.zig");
21const Symbol = @import("Symbol.zig");21const Symbol = @import("Symbol.zig");
22const Trie = @import("Trie.zig");22const Trie = @import("Trie.zig");
2323
...@@ -38,6 +38,10 @@ stack_size: u64 = 0,...@@ -38,6 +38,10 @@ stack_size: u64 = 0,
38objects: std.ArrayListUnmanaged(*Object) = .{},38objects: std.ArrayListUnmanaged(*Object) = .{},
39archives: std.ArrayListUnmanaged(*Archive) = .{},39archives: std.ArrayListUnmanaged(*Archive) = .{},
40dylibs: std.ArrayListUnmanaged(*Dylib) = .{},40dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
41lib_stubs: std.ArrayListUnmanaged(*Stub) = .{},
42
43libsystem_stub_index: ?u16 = null,
44next_dylib_ordinal: u16 = 1,
4145
42load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},46load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
4347
...@@ -153,9 +157,20 @@ pub fn deinit(self: *Zld) void {...@@ -153,9 +157,20 @@ pub fn deinit(self: *Zld) void {
153 }157 }
154 self.dylibs.deinit(self.allocator);158 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
156 self.tentatives.deinit(self.allocator);172 self.tentatives.deinit(self.allocator);
157 self.globals.deinit(self.allocator);173 self.globals.deinit(self.allocator);
158 self.imports.deinit(self.allocator);
159 self.unresolved.deinit(self.allocator);174 self.unresolved.deinit(self.allocator);
160 self.strtab.deinit(self.allocator);175 self.strtab.deinit(self.allocator);
161176
...@@ -245,9 +260,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -245,9 +260,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
245 dylib,260 dylib,
246 stub,261 stub,
247 },262 },
248 file: fs.File,263 origin: union {
264 file: fs.File,
265 stub: Stub.LibStub,
266 },
249 name: []const u8,267 name: []const u8,
250 stub: ?LibStub = null,
251 };268 };
252 var classified = std.ArrayList(Input).init(self.allocator);269 var classified = std.ArrayList(Input).init(self.allocator);
253 defer classified.deinit();270 defer classified.deinit();
...@@ -262,45 +279,45 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -262,45 +279,45 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
262 };279 };
263280
264 try_object: {281 try_object: {
265 if (!Object.isObject(file)) break :try_object;282 if (!(try Object.isObject(file))) break :try_object;
266 try classified.append(.{283 try classified.append(.{
267 .kind = .object,284 .kind = .object,
268 .file = file,285 .origin = .{ .file = file },
269 .name = full_path,286 .name = full_path,
270 });287 });
271 continue;288 continue;
272 }289 }
273290
274 try_archive: {291 try_archive: {
275 if (!Archive.isArchive(file)) break :try_archive;292 if (!(try Archive.isArchive(file))) break :try_archive;
276 try classified.append(.{293 try classified.append(.{
277 .kind = .archive,294 .kind = .archive,
278 .file = file,295 .origin = .{ .file = file },
279 .name = full_path,296 .name = full_path,
280 });297 });
281 continue;298 continue;
282 }299 }
283300
284 try_dylib: {301 try_dylib: {
285 if (!Dylib.isDylib(file)) break :try_dylib;302 if (!(try Dylib.isDylib(file))) break :try_dylib;
286 try classified.append(.{303 try classified.append(.{
287 .kind = .dylib,304 .kind = .dylib,
288 .file = file,305 .origin = .{ .file = file },
289 .name = full_path,306 .name = full_path,
290 });307 });
291 continue;308 continue;
292 }309 }
293310
294 try_stub: {311 try_stub: {
295 var lib_stub = LibStub.loadFromFile(self.allocator, file) catch {312 var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch {
296 break :try_stub;313 break :try_stub;
297 };314 };
298 try classified.append(.{315 try classified.append(.{
299 .kind = .stub,316 .kind = .stub,
300 .file = file,317 .origin = .{ .stub = lib_stub },
301 .name = full_path,318 .name = full_path,
302 .stub = lib_stub,
303 });319 });
320 file.close();
304 continue;321 continue;
305 }322 }
306323
...@@ -318,7 +335,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -318,7 +335,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
318 object.* = Object.init(self.allocator);335 object.* = Object.init(self.allocator);
319 object.arch = self.arch.?;336 object.arch = self.arch.?;
320 object.name = input.name;337 object.name = input.name;
321 object.file = input.file;338 object.file = input.origin.file;
339
322 try object.parse();340 try object.parse();
323 try self.objects.append(self.allocator, object);341 try self.objects.append(self.allocator, object);
324 },342 },
...@@ -329,40 +347,34 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -329,40 +347,34 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
329 archive.* = Archive.init(self.allocator);347 archive.* = Archive.init(self.allocator);
330 archive.arch = self.arch.?;348 archive.arch = self.arch.?;
331 archive.name = input.name;349 archive.name = input.name;
332 archive.file = input.file;350 archive.file = input.origin.file;
351
333 try archive.parse();352 try archive.parse();
334 try self.archives.append(self.allocator, archive);353 try self.archives.append(self.allocator, archive);
335 },354 },
336 .dylib, .stub => {355 .dylib => {
337 const dylib = try self.allocator.create(Dylib);356 const dylib = try self.allocator.create(Dylib);
338 errdefer self.allocator.destroy(dylib);357 errdefer self.allocator.destroy(dylib);
339358
340 dylib.* = Dylib.init(self.allocator);359 dylib.* = Dylib.init(self.allocator);
341 dylib.arch = self.arch.?;360 dylib.arch = self.arch.?;
342 dylib.name = input.name;361 dylib.name = input.name;
343 dylib.file = input.file;362 dylib.file = input.origin.file;
344 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
345363
346 // TODO Defer parsing of the dylibs until they are actually needed364 try dylib.parse();
347 if (input.stub) |stub| {
348 try dylib.parseFromStub(stub);
349 } else {
350 try dylib.parse();
351 }
352 try self.dylibs.append(self.allocator, dylib);365 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 command371 stub.* = Stub.init(self.allocator);
355 const dylib_id = dylib.id orelse unreachable;372 stub.arch = self.arch.?;
356 var dylib_cmd = try createLoadDylibCommand(373 stub.name = input.name;
357 self.allocator,374 stub.lib_stub = input.origin.stub;
358 dylib_id.name,375
359 dylib_id.timestamp,376 try stub.parse();
360 dylib_id.current_version,377 try self.lib_stubs.append(self.allocator, stub);
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 });
366 },378 },
367 }379 }
368 }380 }
...@@ -372,7 +384,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -372,7 +384,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
372 for (libs) |lib| {384 for (libs) |lib| {
373 const file = try fs.cwd().openFile(lib, .{});385 const file = try fs.cwd().openFile(lib, .{});
374386
375 if (Dylib.isDylib(file)) {387 if (try Dylib.isDylib(file)) {
376 const dylib = try self.allocator.create(Dylib);388 const dylib = try self.allocator.create(Dylib);
377 errdefer self.allocator.destroy(dylib);389 errdefer self.allocator.destroy(dylib);
378390
...@@ -380,57 +392,27 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -380,57 +392,27 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
380 dylib.arch = self.arch.?;392 dylib.arch = self.arch.?;
381 dylib.name = try self.allocator.dupe(u8, lib);393 dylib.name = try self.allocator.dupe(u8, lib);
382 dylib.file = file;394 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
386 try dylib.parse();396 try dylib.parse();
387 try self.dylibs.append(self.allocator, dylib);397 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 });
401 } else {398 } else {
402 // Try tbd stub file next.399 // Try tbd stub file next.
403 if (LibStub.loadFromFile(self.allocator, file)) |*lib_stub| {400 if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| {
404 defer lib_stub.deinit();401 const stub = try self.allocator.create(Stub);
405402 errdefer self.allocator.destroy(stub);
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;
414403
415 try dylib.parseFromStub(lib_stub.*);404 stub.* = Stub.init(self.allocator);
416 try self.dylibs.append(self.allocator, dylib);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 command409 try stub.parse();
419 const dylib_id = dylib.id orelse unreachable;410 try self.lib_stubs.append(self.allocator, stub);
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 });
430 } else |_| {411 } else |_| {
431 // TODO this entire logic has to be cleaned up.412 // TODO this entire logic has to be cleaned up.
432 try file.seekTo(0);413 try file.seekTo(0);
433 if (Archive.isArchive(file)) {414
415 if (try Archive.isArchive(file)) {
434 const archive = try self.allocator.create(Archive);416 const archive = try self.allocator.create(Archive);
435 errdefer self.allocator.destroy(archive);417 errdefer self.allocator.destroy(archive);
436418
...@@ -438,6 +420,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -438,6 +420,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
438 archive.arch = self.arch.?;420 archive.arch = self.arch.?;
439 archive.name = try self.allocator.dupe(u8, lib);421 archive.name = try self.allocator.dupe(u8, lib);
440 archive.file = file;422 archive.file = file;
423
441 try archive.parse();424 try archive.parse();
442 try self.archives.append(self.allocator, archive);425 try self.archives.append(self.allocator, archive);
443 } else {426 } else {
...@@ -449,26 +432,28 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -449,26 +432,28 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
449 }432 }
450}433}
451434
452fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {435fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
453 const file = try fs.cwd().openFile(lib_system_path, .{});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);441 const stub = try self.allocator.create(Stub);
456 defer lib_stub.deinit();442 errdefer self.allocator.destroy(stub);
457443
458 const dylib = try self.allocator.create(Dylib);444 stub.* = Stub.init(self.allocator);
459 errdefer self.allocator.destroy(dylib);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);449 try stub.parse();
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;
466450
467 try dylib.parseFromStub(lib_stub);451 self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len);
468 try self.dylibs.append(self.allocator, dylib);452 try self.lib_stubs.append(self.allocator, stub);
469453
470 // Add LC_LOAD_DYLIB command454 // Add LC_LOAD_DYLIB load command.
471 const dylib_id = dylib.id orelse unreachable;455 stub.ordinal = self.next_dylib_ordinal;
456 const dylib_id = stub.id orelse unreachable;
472 var dylib_cmd = try createLoadDylibCommand(457 var dylib_cmd = try createLoadDylibCommand(
473 self.allocator,458 self.allocator,
474 dylib_id.name,459 dylib_id.name,
...@@ -477,8 +462,8 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {...@@ -477,8 +462,8 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
477 dylib_id.compatibility_version,462 dylib_id.compatibility_version,
478 );463 );
479 errdefer dylib_cmd.deinit(self.allocator);464 errdefer dylib_cmd.deinit(self.allocator);
480
481 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });465 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
466 self.next_dylib_ordinal += 1;
482}467}
483468
484fn mapAndUpdateSections(469fn mapAndUpdateSections(
...@@ -1906,21 +1891,34 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1906,21 +1891,34 @@ fn resolveSymbols(self: *Zld) !void {
1906 for (self.unresolved.values()) |value| {1891 for (self.unresolved.values()) |value| {
1907 unresolved.appendAssumeCapacity(value);1892 unresolved.appendAssumeCapacity(value);
1908 }1893 }
1909 self.unresolved.clearAndFree(self.allocator);1894 self.unresolved.clearRetainingCapacity();
19101895
1911 var has_undefined = false;1896 var referenced = std.AutoHashMap(union(enum) {
1912 while (unresolved.popOrNull()) |undef| {1897 dylib: *Dylib,
1913 var found = false;1898 stub: *Stub,
1914 for (self.dylibs.items) |dylib| {1899 }, void).init(self.allocator);
1915 const proxy = dylib.symbols.get(undef.name) orelse continue;1900 defer referenced.deinit();
1916 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);1901
1917 undef.alias = proxy;1902 loop: while (unresolved.popOrNull()) |undef| {
1918 found = true;1903 const proxy = self.imports.get(undef.name) orelse outer: {
1919 }1904 const proxy = inner: {
19201905 for (self.dylibs.items) |dylib| {
1921 if (!found) {1906 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1922 if (mem.eql(u8, undef.name, "___dso_handle")) {1907 try referenced.put(.{ .dylib = dylib }, {});
1923 const proxy = self.imports.get(undef.name) orelse blk: {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.
1924 const name = try self.allocator.dupe(u8, undef.name);1922 const name = try self.allocator.dupe(u8, undef.name);
1925 const proxy = try self.allocator.create(Symbol.Proxy);1923 const proxy = try self.allocator.create(Symbol.Proxy);
1926 errdefer self.allocator.destroy(proxy);1924 errdefer self.allocator.destroy(proxy);
...@@ -1929,26 +1927,69 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1929,26 +1927,69 @@ fn resolveSymbols(self: *Zld) !void {
1929 .@"type" = .proxy,1927 .@"type" = .proxy,
1930 .name = name,1928 .name = name,
1931 },1929 },
1930 .file = null,
1932 };1931 };
1933 try self.imports.putNoClobber(self.allocator, name, &proxy.base);1932 break :inner &proxy.base;
1934 break :blk &proxy.base;1933 }
1935 };1934
1936 undef.alias = proxy;1935 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
1937 continue;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 },
1938 }1972 }
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| {
1940 log.err("undefined reference to symbol '{s}'", .{undef.name});1981 log.err("undefined reference to symbol '{s}'", .{undef.name});
1941 log.err(" | referenced in {s}", .{1982 log.err(" | referenced in {s}", .{
1942 undef.cast(Symbol.Unresolved).?.file.name.?,1983 undef.cast(Symbol.Unresolved).?.file.name.?,
1943 });1984 });
1944 has_undefined = true;
1945 }1985 }
1946 }
19471986
1948 if (has_undefined) return error.UndefinedSymbolReference;1987 return error.UndefinedSymbolReference;
1988 }
19491989
1950 // Finally put dyld_stub_binder as an Import1990 // 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 {
1952 log.err("undefined reference to symbol 'dyld_stub_binder'", .{});1993 log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
1953 return error.UndefinedSymbolReference;1994 return error.UndefinedSymbolReference;
1954 };1995 };
...@@ -2814,7 +2855,7 @@ fn writeBindInfoTable(self: *Zld) !void {...@@ -2814,7 +2855,7 @@ fn writeBindInfoTable(self: *Zld) !void {
2814 try pointers.append(.{2855 try pointers.append(.{
2815 .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),2856 .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),
2816 .segment_id = segment_id,2857 .segment_id = segment_id,
2817 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,2858 .dylib_ordinal = proxy.dylibOrdinal(),
2818 .name = proxy.base.name,2859 .name = proxy.base.name,
2819 });2860 });
2820 }2861 }
...@@ -2833,7 +2874,7 @@ fn writeBindInfoTable(self: *Zld) !void {...@@ -2833,7 +2874,7 @@ fn writeBindInfoTable(self: *Zld) !void {
2833 try pointers.append(.{2874 try pointers.append(.{
2834 .offset = base_offset,2875 .offset = base_offset,
2835 .segment_id = segment_id,2876 .segment_id = segment_id,
2836 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,2877 .dylib_ordinal = proxy.dylibOrdinal(),
2837 .name = proxy.base.name,2878 .name = proxy.base.name,
2838 });2879 });
2839 }2880 }
...@@ -2873,7 +2914,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {...@@ -2873,7 +2914,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
2873 pointers.appendAssumeCapacity(.{2914 pointers.appendAssumeCapacity(.{
2874 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),2915 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
2875 .segment_id = segment_id,2916 .segment_id = segment_id,
2876 .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,2917 .dylib_ordinal = proxy.dylibOrdinal(),
2877 .name = sym.name,2918 .name = sym.name,
2878 });2919 });
2879 }2920 }
...@@ -3181,12 +3222,11 @@ fn writeSymbolTable(self: *Zld) !void {...@@ -3181,12 +3222,11 @@ fn writeSymbolTable(self: *Zld) !void {
31813222
3182 for (self.imports.values()) |sym| {3223 for (self.imports.values()) |sym| {
3183 const proxy = sym.cast(Symbol.Proxy) orelse unreachable;3224 const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
3184 const dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0;
3185 try undefs.append(.{3225 try undefs.append(.{
3186 .n_strx = try self.makeString(sym.name),3226 .n_strx = try self.makeString(sym.name),
3187 .n_type = macho.N_UNDF | macho.N_EXT,3227 .n_type = macho.N_UNDF | macho.N_EXT,
3188 .n_sect = 0,3228 .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,
3190 .n_value = 0,3230 .n_value = 0,
3191 });3231 });
3192 }3232 }