diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index f47228077c..13dadb1dd2 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -234,8 +234,8 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { return object; } -pub fn isArchive(file: fs.File) !bool { - const magic = try file.reader().readBytesNoEof(Archive.SARMAG); - try file.seekTo(0); +pub fn isArchive(file: fs.File) bool { + const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch return false; + file.seekTo(0) catch return false; return mem.eql(u8, &magic, Archive.ARMAG); } diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index a2a703dc08..3ac1ff5ed2 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -1,6 +1,7 @@ const Dylib = @This(); const std = @import("std"); +const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.dylib); const macho = std.macho; @@ -8,6 +9,7 @@ const mem = std.mem; const Allocator = mem.Allocator; const Symbol = @import("Symbol.zig"); +const LibStub = @import("../tapi.zig").LibStub; usingnamespace @import("commands.zig"); @@ -184,8 +186,88 @@ pub fn parseSymbols(self: *Dylib) !void { } } -pub fn isDylib(file: fs.File) !bool { - const header = try file.reader().readStruct(macho.mach_header_64); - try file.seekTo(0); +pub fn isDylib(file: fs.File) bool { + const header = file.reader().readStruct(macho.mach_header_64) catch return false; + file.seekTo(0) catch return false; return header.filetype == macho.MH_DYLIB; } + +pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { + assert(lib_stub.inner.len > 0); + + log.debug("parsing shared library from stub '{s}'", .{self.name.?}); + + const umbrella_lib = lib_stub.inner[0]; + self.id = .{ + .name = try self.allocator.dupe(u8, umbrella_lib.install_name), + // TODO parse from the stub + .timestamp = 2, + .current_version = 0, + .compatibility_version = 0, + }; + + const target_string: []const u8 = switch (self.arch.?) { + .aarch64 => "arm64-macos", + .x86_64 => "x86_64-macos", + else => unreachable, + }; + + for (lib_stub.inner) |stub| { + if (!hasTarget(stub.targets, target_string)) continue; + + if (stub.exports) |exports| { + for (exports) |exp| { + if (!hasTarget(exp.targets, target_string)) continue; + + for (exp.symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + + const name = try self.allocator.dupe(u8, sym_name); + const proxy = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(proxy); + + proxy.* = .{ + .base = .{ + .@"type" = .proxy, + .name = name, + }, + .dylib = self, + }; + + try self.symbols.putNoClobber(self.allocator, name, &proxy.base); + } + } + } + + if (stub.reexports) |reexports| { + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + for (reexp.symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + + const name = try self.allocator.dupe(u8, sym_name); + const proxy = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(proxy); + + proxy.* = .{ + .base = .{ + .@"type" = .proxy, + .name = name, + }, + .dylib = self, + }; + + try self.symbols.putNoClobber(self.allocator, name, &proxy.base); + } + } + } + } +} + +fn hasTarget(targets: []const []const u8, target: []const u8) bool { + for (targets) |t| { + if (mem.eql(u8, t, target)) return true; + } + return false; +} diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index f9c33bfa5c..5b19d81698 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -534,8 +534,8 @@ pub fn parseDataInCode(self: *Object) !void { } } -pub fn isObject(file: fs.File) !bool { - const header = try file.reader().readStruct(macho.mach_header_64); - try file.seekTo(0); +pub fn isObject(file: fs.File) bool { + const header = file.reader().readStruct(macho.mach_header_64) catch return false; + file.seekTo(0) catch return false; return header.filetype == macho.MH_OBJECT; } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 58cf51663c..4226943353 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -243,14 +243,16 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { object, archive, dylib, + stub, }, file: fs.File, name: []const u8, + stub: ?LibStub = null, }; var classified = std.ArrayList(Input).init(self.allocator); defer classified.deinit(); - // First, classify input files: object, archive or dylib. + // First, classify input files: object, archive, dylib or stub (tbd). for (files) |file_name| { const file = try fs.cwd().openFile(file_name, .{}); const full_path = full_path: { @@ -260,7 +262,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { }; try_object: { - if (!(try Object.isObject(file))) break :try_object; + if (!Object.isObject(file)) break :try_object; try classified.append(.{ .kind = .object, .file = file, @@ -270,7 +272,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } try_archive: { - if (!(try Archive.isArchive(file))) break :try_archive; + if (!Archive.isArchive(file)) break :try_archive; try classified.append(.{ .kind = .archive, .file = file, @@ -280,7 +282,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } try_dylib: { - if (!(try Dylib.isDylib(file))) break :try_dylib; + if (!Dylib.isDylib(file)) break :try_dylib; try classified.append(.{ .kind = .dylib, .file = file, @@ -289,6 +291,19 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { continue; } + try_stub: { + var lib_stub = LibStub.loadFromFile(self.allocator, file) catch |_| { + break :try_stub; + }; + try classified.append(.{ + .kind = .stub, + .file = file, + .name = full_path, + .stub = lib_stub, + }); + continue; + } + file.close(); log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); } @@ -318,7 +333,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try archive.parse(); try self.archives.append(self.allocator, archive); }, - .dylib => { + .dylib, .stub => { const dylib = try self.allocator.create(Dylib); errdefer self.allocator.destroy(dylib); @@ -329,7 +344,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1; // TODO Defer parsing of the dylibs until they are actually needed - try dylib.parse(); + if (input.stub) |stub| { + try dylib.parseFromStub(stub); + } else { + try dylib.parse(); + } try self.dylibs.append(self.allocator, dylib); // Add LC_LOAD_DYLIB command @@ -353,7 +372,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { for (libs) |lib| { const file = try fs.cwd().openFile(lib, .{}); - if (try Dylib.isDylib(file)) { + if (Dylib.isDylib(file)) { const dylib = try self.allocator.create(Dylib); errdefer self.allocator.destroy(dylib); @@ -379,30 +398,57 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { errdefer dylib_cmd.deinit(self.allocator); try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); - } else if (try Archive.isArchive(file)) { - const archive = try self.allocator.create(Archive); - errdefer self.allocator.destroy(archive); - - archive.* = Archive.init(self.allocator); - archive.arch = self.arch.?; - archive.name = try self.allocator.dupe(u8, lib); - archive.file = file; - try archive.parse(); - try self.archives.append(self.allocator, archive); } else { - file.close(); - log.warn("unknown filetype for a library: '{s}'", .{lib}); + // Try tbd stub file next. + if (LibStub.loadFromFile(self.allocator, file)) |*lib_stub| { + defer lib_stub.deinit(); + + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); + + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, lib); + dylib.file = file; + dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1; + + try dylib.parseFromStub(lib_stub.*); + try self.dylibs.append(self.allocator, dylib); + + // Add LC_LOAD_DYLIB command + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try createLoadDylibCommand( + self.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); + errdefer dylib_cmd.deinit(self.allocator); + + try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); + } else |_| { + // TODO this entire logic has to be cleaned up. + try file.seekTo(0); + if (Archive.isArchive(file)) { + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); + + archive.* = Archive.init(self.allocator); + archive.arch = self.arch.?; + archive.name = try self.allocator.dupe(u8, lib); + archive.file = file; + try archive.parse(); + try self.archives.append(self.allocator, archive); + } else { + file.close(); + log.warn("unknown filetype for a library: '{s}'", .{lib}); + } + } } } } -fn hasTarget(targets: []const []const u8, target: []const u8) bool { - for (targets) |t| { - if (mem.eql(u8, t, target)) return true; - } - return false; -} - fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void { const file = try fs.cwd().openFile(lib_system_path, .{}); @@ -418,73 +464,7 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void { dylib.file = file; dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1; - const umbrella_lib = lib_stub.inner[0]; - dylib.id = .{ - .name = try self.allocator.dupe(u8, umbrella_lib.install_name), - // TODO parse from the stub - .timestamp = 2, - .current_version = 0, - .compatibility_version = 0, - }; - - const target_string: []const u8 = switch (self.arch.?) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; - - for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; - - if (stub.exports) |exports| { - for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; - - for (exp.symbols) |sym_name| { - if (dylib.symbols.contains(sym_name)) continue; - - const name = try self.allocator.dupe(u8, sym_name); - const proxy = try self.allocator.create(Symbol.Proxy); - errdefer self.allocator.destroy(proxy); - - proxy.* = .{ - .base = .{ - .@"type" = .proxy, - .name = name, - }, - .dylib = dylib, - }; - - try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base); - } - } - } - - if (stub.reexports) |reexports| { - for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; - - for (reexp.symbols) |sym_name| { - if (dylib.symbols.contains(sym_name)) continue; - - const name = try self.allocator.dupe(u8, sym_name); - const proxy = try self.allocator.create(Symbol.Proxy); - errdefer self.allocator.destroy(proxy); - - proxy.* = .{ - .base = .{ - .@"type" = .proxy, - .name = name, - }, - .dylib = dylib, - }; - - try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base); - } - } - } - } - + try dylib.parseFromStub(lib_stub); try self.dylibs.append(self.allocator, dylib); // Add LC_LOAD_DYLIB command diff --git a/src/link/tapi.zig b/src/link/tapi.zig index 05a23a3aff..efa7227def 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -58,7 +58,17 @@ pub const LibStub = struct { .inner = undefined, }; - lib_stub.inner = try lib_stub.yaml.parse([]Tbd); + lib_stub.inner = lib_stub.yaml.parse([]Tbd) catch |err| blk: { + switch (err) { + error.TypeMismatch => { + // TODO clean this up. + var out = try lib_stub.yaml.arena.allocator.alloc(Tbd, 1); + out[0] = try lib_stub.yaml.parse(Tbd); + break :blk out; + }, + else => |e| return e, + } + }; return lib_stub; }