diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 36d522f1ee12b8474b92c38d440da23203e67765..b34d3324c662342f1c18784b09fe3eac990fcb3d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -384,6 +384,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node error.MalformedArchive, error.InvalidCpuArch, error.InvalidTarget, + error.UnknownFileType, => continue, // already reported else => |e| try self.reportParseError( obj.path, @@ -393,6 +394,81 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node }; } + var system_libs = std.ArrayList(SystemLib).init(gpa); + defer system_libs.deinit(); + + // libs + try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); + for (comp.system_libs.values()) |info| { + system_libs.appendAssumeCapacity(.{ + .needed = info.needed, + .weak = info.weak, + .path = info.path.?, + }); + } + + // frameworks + try system_libs.ensureUnusedCapacity(self.frameworks.len); + for (self.frameworks) |info| { + system_libs.appendAssumeCapacity(.{ + .needed = info.needed, + .weak = info.weak, + .path = info.path, + }); + } + + // libc++ dep + if (comp.config.link_libcpp) { + try system_libs.ensureUnusedCapacity(2); + system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); + system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); + } + + // libc/libSystem dep + self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { + error.MissingLibSystem => {}, // already reported + else => |e| return e, // TODO: convert into an error + }; + + for (system_libs.items) |lib| { + self.parseLibrary(lib, false) catch |err| switch (err) { + error.MalformedDylib, + error.MalformedArchive, + error.InvalidCpuArch, + error.UnknownFileType, + => continue, // already reported + else => |e| try self.reportParseError( + lib.path, + "unexpected error: parsing library failed with error {s}", + .{@errorName(e)}, + ), + }; + } + + // Finally, link against compiler_rt. + const compiler_rt_path: ?[]const u8 = blk: { + if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; + if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + break :blk null; + }; + if (compiler_rt_path) |path| { + self.parsePositional(path, false) catch |err| switch (err) { + error.MalformedObject, + error.MalformedArchive, + error.InvalidCpuArch, + error.InvalidTarget, + error.UnknownFileType, + => {}, // already reported + else => |e| try self.reportParseError( + path, + "unexpected error: parsing input file failed with error {s}", + .{@errorName(e)}, + ), + }; + } + + if (comp.link_errors.items.len > 0) return error.FlushFailure; + state_log.debug("{}", .{self.dumpState()}); @panic("TODO"); @@ -626,13 +702,12 @@ pub fn resolveLibSystem( }; try self.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); - return; + return error.MissingLibSystem; } const libsystem_path = try arena.dupe(u8, test_path.items); - try out_libs.put(libsystem_path, .{ + try out_libs.append(.{ .needed = true, - .weak = false, .path = libsystem_path, }); } @@ -685,6 +760,7 @@ fn accessLibPath( const ParseError = error{ MalformedObject, MalformedArchive, + MalformedDylib, NotLibStub, InvalidCpuArch, InvalidTarget, @@ -696,6 +772,8 @@ const ParseError = error{ EndOfStream, FileSystem, NotSupported, + Unhandled, + UnknownFileType, } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError; fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void { @@ -709,9 +787,25 @@ fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!v } fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void { - _ = self; - _ = lib; - _ = must_link; + const tracy = trace(@src()); + defer tracy.end(); + if (try fat.isFatLibrary(lib.path)) { + const fat_arch = try self.parseFatLibrary(lib.path); + if (try Archive.isArchive(lib.path, fat_arch)) { + try self.parseArchive(lib, must_link, fat_arch); + } else if (try Dylib.isDylib(lib.path, fat_arch)) { + try self.parseDylib(lib, true, fat_arch); + } else { + try self.reportParseError(lib.path, "unknown file type for a library", .{}); + return error.UnknownFileType; + } + } else if (try Archive.isArchive(lib.path, null)) { + try self.parseArchive(lib, must_link, null); + } else if (try Dylib.isDylib(lib.path, null)) { + try self.parseDylib(lib, true, null); + } else { + try self.parseTbd(lib, true); + } } fn parseObject(self: *MachO, path: []const u8) ParseError!void { @@ -739,6 +833,40 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void { try object.parse(self); } +fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch { + var buffer: [2]fat.Arch = undefined; + const fat_archs = try fat.parseArchs(path, &buffer); + const cpu_arch = self.getTarget().cpu.arch; + for (fat_archs) |arch| { + if (arch.tag == cpu_arch) return arch; + } + try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)}); + return error.InvalidCpuArch; +} + +fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Arch) ParseError!void { + _ = self; + _ = lib; + _ = must_link; + _ = fat_arch; + return error.Unhandled; +} + +fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!void { + _ = self; + _ = lib; + _ = explicit; + _ = fat_arch; + return error.Unhandled; +} + +fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!void { + _ = self; + _ = lib; + _ = explicit; + return error.Unhandled; +} + fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { _ = self; _ = atom_index; diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index c31278ce1cbe50a1161bca074c45e352faa87797..2451874eb0eafba3e5417cbff9bcc92f7d9b78e4 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -61,6 +61,17 @@ const ar_hdr = extern struct { } }; +pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + if (fat_arch) |arch| { + try file.seekTo(arch.offset); + } + const magic = file.reader().readBytesNoEof(SARMAG) catch return false; + if (!mem.eql(u8, &magic, ARMAG)) return false; + return true; +} + pub fn deinit(self: *Archive, allocator: Allocator) void { self.objects.deinit(allocator); } @@ -117,6 +128,7 @@ pub fn parse(self: *Archive, arena: Allocator, macho_file: *MachO) !void { } } +const fat = @import("fat.zig"); const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index b57d6eb911df074ce4b969adc80460a719d3b503..f0454d91c3302cbe412d78665fdc5979692335e8 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -23,6 +23,16 @@ referenced: bool = false, output_symtab_ctx: MachO.SymtabCtx = .{}, +pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + if (fat_arch) |arch| { + try file.seekTo(arch.offset); + } + const header = file.reader().readStruct(macho.mach_header_64) catch return false; + return header.filetype == macho.MH_DYLIB; +} + pub fn deinit(self: *Dylib, allocator: Allocator) void { self.exports.deinit(allocator); self.strtab.deinit(allocator); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 53334be6abca540f7bab49c012d5276dc42c7377..9b453fad4948e2f2b4bf4b824bc217f50ace4283 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -34,8 +34,7 @@ output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn isObject(path: []const u8) !bool { const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); - const reader = file.reader(); - const header = reader.readStruct(macho.mach_header_64) catch return false; + const header = file.reader().readStruct(macho.mach_header_64) catch return false; return header.filetype == macho.MH_OBJECT; } @@ -175,8 +174,12 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { }); return error.InvalidTarget; } - if (macho_file.platform.version.order(platform.version) != .lt) { - try macho_file.reportParseError2(self.index, "object file built for newer platform: {}", .{platform}); + if (macho_file.platform.version.order(platform.version) == .lt) { + try macho_file.reportParseError2(self.index, "object file built for newer platform: {}: {} < {}", .{ + macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch), + macho_file.platform.version, + platform.version, + }); return error.InvalidTarget; } } diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 46cf0139df37e9d14f0db3c9cbac5709ac120206..5542d70dc06d3a9e2a8fff464d1c536de2bbd4a8 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -8,10 +8,10 @@ const native_endian = builtin.target.cpu.arch.endian(); const MachO = @import("../MachO.zig"); -pub fn isFatLibrary(file: std.fs.File) bool { - const reader = file.reader(); - const hdr = reader.readStructEndian(macho.fat_header, .big) catch return false; - defer file.seekTo(0) catch {}; +pub fn isFatLibrary(path: []const u8) !bool { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false; return hdr.magic == macho.FAT_MAGIC; } @@ -21,7 +21,9 @@ pub const Arch = struct { size: u32, }; -pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch { +pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); const reader = file.reader(); const fat_header = try reader.readStructEndian(macho.fat_header, .big); assert(fat_header.magic == macho.FAT_MAGIC);