authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-19 16:12:29+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-24 14:45:45+02:00
log96a0479db2d876bc1b3136b26cff4004a212ba6a
tree569a646a7ad922fc0876c88c7f0fe86533c3854e
parent089577a71df6e22bf1cc3f83489fd2af548c5b81

zld: parse lib stubs as tbds on the linker line


5 files changed, 175 insertions(+), 103 deletions(-)

src/link/MachO/Archive.zig+3-3
......@@ -234,8 +234,8 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
234234 return object;
235235}
236236
237pub fn isArchive(file: fs.File) !bool {
238 const magic = try file.reader().readBytesNoEof(Archive.SARMAG);
239 try file.seekTo(0);
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;
240240 return mem.eql(u8, &magic, Archive.ARMAG);
241241}
src/link/MachO/Dylib.zig+85-3
......@@ -1,6 +1,7 @@
11const Dylib = @This();
22
33const std = @import("std");
4const assert = std.debug.assert;
45const fs = std.fs;
56const log = std.log.scoped(.dylib);
67const macho = std.macho;
......@@ -8,6 +9,7 @@ const mem = std.mem;
89
910const Allocator = mem.Allocator;
1011const Symbol = @import("Symbol.zig");
12const LibStub = @import("../tapi.zig").LibStub;
1113
1214usingnamespace @import("commands.zig");
1315
......@@ -184,8 +186,88 @@ pub fn parseSymbols(self: *Dylib) !void {
184186 }
185187}
186188
187pub fn isDylib(file: fs.File) !bool {
188 const header = try file.reader().readStruct(macho.mach_header_64);
189 try file.seekTo(0);
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;
190192 return header.filetype == macho.MH_DYLIB;
191193}
194
195pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
196 assert(lib_stub.inner.len > 0);
197
198 log.debug("parsing shared library from stub '{s}'", .{self.name.?});
199
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,
213 };
214
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;
273}
src/link/MachO/Object.zig+3-3
......@@ -534,8 +534,8 @@ pub fn parseDataInCode(self: *Object) !void {
534534 }
535535}
536536
537pub fn isObject(file: fs.File) !bool {
538 const header = try file.reader().readStruct(macho.mach_header_64);
539 try file.seekTo(0);
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;
540540 return header.filetype == macho.MH_OBJECT;
541541}
src/link/MachO/Zld.zig+73-93
......@@ -243,14 +243,16 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
243243 object,
244244 archive,
245245 dylib,
246 stub,
246247 },
247248 file: fs.File,
248249 name: []const u8,
250 stub: ?LibStub = null,
249251 };
250252 var classified = std.ArrayList(Input).init(self.allocator);
251253 defer classified.deinit();
252254
253 // First, classify input files: object, archive or dylib.
255 // First, classify input files: object, archive, dylib or stub (tbd).
254256 for (files) |file_name| {
255257 const file = try fs.cwd().openFile(file_name, .{});
256258 const full_path = full_path: {
......@@ -260,7 +262,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
260262 };
261263
262264 try_object: {
263 if (!(try Object.isObject(file))) break :try_object;
265 if (!Object.isObject(file)) break :try_object;
264266 try classified.append(.{
265267 .kind = .object,
266268 .file = file,
......@@ -270,7 +272,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
270272 }
271273
272274 try_archive: {
273 if (!(try Archive.isArchive(file))) break :try_archive;
275 if (!Archive.isArchive(file)) break :try_archive;
274276 try classified.append(.{
275277 .kind = .archive,
276278 .file = file,
......@@ -280,7 +282,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
280282 }
281283
282284 try_dylib: {
283 if (!(try Dylib.isDylib(file))) break :try_dylib;
285 if (!Dylib.isDylib(file)) break :try_dylib;
284286 try classified.append(.{
285287 .kind = .dylib,
286288 .file = file,
......@@ -289,6 +291,19 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
289291 continue;
290292 }
291293
294 try_stub: {
295 var lib_stub = LibStub.loadFromFile(self.allocator, file) catch |_| {
296 break :try_stub;
297 };
298 try classified.append(.{
299 .kind = .stub,
300 .file = file,
301 .name = full_path,
302 .stub = lib_stub,
303 });
304 continue;
305 }
306
292307 file.close();
293308 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
294309 }
......@@ -318,7 +333,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
318333 try archive.parse();
319334 try self.archives.append(self.allocator, archive);
320335 },
321 .dylib => {
336 .dylib, .stub => {
322337 const dylib = try self.allocator.create(Dylib);
323338 errdefer self.allocator.destroy(dylib);
324339
......@@ -329,7 +344,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
329344 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
330345
331346 // TODO Defer parsing of the dylibs until they are actually needed
332 try dylib.parse();
347 if (input.stub) |stub| {
348 try dylib.parseFromStub(stub);
349 } else {
350 try dylib.parse();
351 }
333352 try self.dylibs.append(self.allocator, dylib);
334353
335354 // Add LC_LOAD_DYLIB command
......@@ -353,7 +372,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
353372 for (libs) |lib| {
354373 const file = try fs.cwd().openFile(lib, .{});
355374
356 if (try Dylib.isDylib(file)) {
375 if (Dylib.isDylib(file)) {
357376 const dylib = try self.allocator.create(Dylib);
358377 errdefer self.allocator.destroy(dylib);
359378
......@@ -379,28 +398,55 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
379398 errdefer dylib_cmd.deinit(self.allocator);
380399
381400 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
382 } else if (try Archive.isArchive(file)) {
383 const archive = try self.allocator.create(Archive);
384 errdefer self.allocator.destroy(archive);
385
386 archive.* = Archive.init(self.allocator);
387 archive.arch = self.arch.?;
388 archive.name = try self.allocator.dupe(u8, lib);
389 archive.file = file;
390 try archive.parse();
391 try self.archives.append(self.allocator, archive);
392401 } else {
393 file.close();
394 log.warn("unknown filetype for a library: '{s}'", .{lib});
395 }
396 }
397}
402 // 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;
414
415 try dylib.parseFromStub(lib_stub.*);
416 try self.dylibs.append(self.allocator, dylib);
417
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);
398428
399fn hasTarget(targets: []const []const u8, target: []const u8) bool {
400 for (targets) |t| {
401 if (mem.eql(u8, t, target)) return true;
429 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
430 } else |_| {
431 // TODO this entire logic has to be cleaned up.
432 try file.seekTo(0);
433 if (Archive.isArchive(file)) {
434 const archive = try self.allocator.create(Archive);
435 errdefer self.allocator.destroy(archive);
436
437 archive.* = Archive.init(self.allocator);
438 archive.arch = self.arch.?;
439 archive.name = try self.allocator.dupe(u8, lib);
440 archive.file = file;
441 try archive.parse();
442 try self.archives.append(self.allocator, archive);
443 } else {
444 file.close();
445 log.warn("unknown filetype for a library: '{s}'", .{lib});
446 }
447 }
448 }
402449 }
403 return false;
404450}
405451
406452fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
......@@ -418,73 +464,7 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
418464 dylib.file = file;
419465 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
420466
421 const umbrella_lib = lib_stub.inner[0];
422 dylib.id = .{
423 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
424 // TODO parse from the stub
425 .timestamp = 2,
426 .current_version = 0,
427 .compatibility_version = 0,
428 };
429
430 const target_string: []const u8 = switch (self.arch.?) {
431 .aarch64 => "arm64-macos",
432 .x86_64 => "x86_64-macos",
433 else => unreachable,
434 };
435
436 for (lib_stub.inner) |stub| {
437 if (!hasTarget(stub.targets, target_string)) continue;
438
439 if (stub.exports) |exports| {
440 for (exports) |exp| {
441 if (!hasTarget(exp.targets, target_string)) continue;
442
443 for (exp.symbols) |sym_name| {
444 if (dylib.symbols.contains(sym_name)) continue;
445
446 const name = try self.allocator.dupe(u8, sym_name);
447 const proxy = try self.allocator.create(Symbol.Proxy);
448 errdefer self.allocator.destroy(proxy);
449
450 proxy.* = .{
451 .base = .{
452 .@"type" = .proxy,
453 .name = name,
454 },
455 .dylib = dylib,
456 };
457
458 try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base);
459 }
460 }
461 }
462
463 if (stub.reexports) |reexports| {
464 for (reexports) |reexp| {
465 if (!hasTarget(reexp.targets, target_string)) continue;
466
467 for (reexp.symbols) |sym_name| {
468 if (dylib.symbols.contains(sym_name)) continue;
469
470 const name = try self.allocator.dupe(u8, sym_name);
471 const proxy = try self.allocator.create(Symbol.Proxy);
472 errdefer self.allocator.destroy(proxy);
473
474 proxy.* = .{
475 .base = .{
476 .@"type" = .proxy,
477 .name = name,
478 },
479 .dylib = dylib,
480 };
481
482 try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base);
483 }
484 }
485 }
486 }
487
467 try dylib.parseFromStub(lib_stub);
488468 try self.dylibs.append(self.allocator, dylib);
489469
490470 // Add LC_LOAD_DYLIB command
src/link/tapi.zig+11-1
......@@ -58,7 +58,17 @@ pub const LibStub = struct {
5858 .inner = undefined,
5959 };
6060
61 lib_stub.inner = try lib_stub.yaml.parse([]Tbd);
61 lib_stub.inner = lib_stub.yaml.parse([]Tbd) catch |err| blk: {
62 switch (err) {
63 error.TypeMismatch => {
64 // TODO clean this up.
65 var out = try lib_stub.yaml.arena.allocator.alloc(Tbd, 1);
66 out[0] = try lib_stub.yaml.parse(Tbd);
67 break :blk out;
68 },
69 else => |e| return e,
70 }
71 };
6272
6373 return lib_stub;
6474 }