| author | |
| committer | |
| log | 5ac5cd9de7c5387e37baa4f287d609c5d2f34564 |
| tree | 65d97ee06e85f411493dbef7a5b1688e627f70c6 |
| parent | 3cb6b6bd90c3b304bf771b37e974dd943c060e2b |
6 files changed, 263 insertions(+), 234 deletions(-)
src/link/MachO.zig+1| ... | @@ -789,6 +789,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -789,6 +789,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 789 | zld.deinit(); | 789 | zld.deinit(); |
| 790 | } | 790 | } |
| 791 | zld.arch = target.cpu.arch; | 791 | zld.arch = target.cpu.arch; |
| 792 | zld.syslibroot = self.base.options.syslibroot; | ||
| 792 | zld.stack_size = stack_size; | 793 | zld.stack_size = stack_size; |
| 793 | 794 | ||
| 794 | // Positional arguments to the linker such as object files and static archives. | 795 | // Positional arguments to the linker such as object files and static archives. |
src/link/MachO/Archive.zig+41-22| ... | @@ -8,12 +8,13 @@ const macho = std.macho; | ... | @@ -8,12 +8,13 @@ const macho = std.macho; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | 9 | ||
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const Arch = std.Target.Cpu.Arch; | ||
| 11 | const Object = @import("Object.zig"); | 12 | const Object = @import("Object.zig"); |
| 12 | 13 | ||
| 13 | usingnamespace @import("commands.zig"); | 14 | usingnamespace @import("commands.zig"); |
| 14 | 15 | ||
| 15 | allocator: *Allocator, | 16 | allocator: *Allocator, |
| 16 | arch: ?std.Target.Cpu.Arch = null, | 17 | arch: ?Arch = null, |
| 17 | file: ?fs.File = null, | 18 | file: ?fs.File = null, |
| 18 | header: ?ar_hdr = null, | 19 | header: ?ar_hdr = null, |
| 19 | name: ?[]const u8 = null, | 20 | name: ?[]const u8 = null, |
| ... | @@ -85,10 +86,36 @@ const ar_hdr = extern struct { | ... | @@ -85,10 +86,36 @@ const ar_hdr = extern struct { |
| 85 | } | 86 | } |
| 86 | }; | 87 | }; |
| 87 | 88 | ||
| 88 | pub fn init(allocator: *Allocator) Archive { | 89 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Archive { |
| 89 | return .{ | 90 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 91 | error.FileNotFound => return null, | ||
| 92 | else => |e| return e, | ||
| 93 | }; | ||
| 94 | errdefer file.close(); | ||
| 95 | |||
| 96 | const archive = try allocator.create(Archive); | ||
| 97 | errdefer allocator.destroy(archive); | ||
| 98 | |||
| 99 | const name = try allocator.dupe(u8, path); | ||
| 100 | errdefer allocator.free(name); | ||
| 101 | |||
| 102 | archive.* = .{ | ||
| 90 | .allocator = allocator, | 103 | .allocator = allocator, |
| 104 | .arch = arch, | ||
| 105 | .name = name, | ||
| 106 | .file = file, | ||
| 107 | }; | ||
| 108 | |||
| 109 | archive.parse() catch |err| switch (err) { | ||
| 110 | error.EndOfStream, error.NotArchive => { | ||
| 111 | archive.deinit(); | ||
| 112 | allocator.destroy(archive); | ||
| 113 | return null; | ||
| 114 | }, | ||
| 115 | else => |e| return e, | ||
| 91 | }; | 116 | }; |
| 117 | |||
| 118 | return archive; | ||
| 92 | } | 119 | } |
| 93 | 120 | ||
| 94 | pub fn deinit(self: *Archive) void { | 121 | pub fn deinit(self: *Archive) void { |
| ... | @@ -116,15 +143,15 @@ pub fn parse(self: *Archive) !void { | ... | @@ -116,15 +143,15 @@ pub fn parse(self: *Archive) !void { |
| 116 | const magic = try reader.readBytesNoEof(SARMAG); | 143 | const magic = try reader.readBytesNoEof(SARMAG); |
| 117 | 144 | ||
| 118 | if (!mem.eql(u8, &magic, ARMAG)) { | 145 | if (!mem.eql(u8, &magic, ARMAG)) { |
| 119 | log.err("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); | 146 | log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); |
| 120 | return error.MalformedArchive; | 147 | return error.NotArchive; |
| 121 | } | 148 | } |
| 122 | 149 | ||
| 123 | self.header = try reader.readStruct(ar_hdr); | 150 | self.header = try reader.readStruct(ar_hdr); |
| 124 | 151 | ||
| 125 | if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) { | 152 | if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) { |
| 126 | log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); | 153 | log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); |
| 127 | return error.MalformedArchive; | 154 | return error.NotArchive; |
| 128 | } | 155 | } |
| 129 | 156 | ||
| 130 | var embedded_name = try parseName(self.allocator, self.header.?, reader); | 157 | var embedded_name = try parseName(self.allocator, self.header.?, reader); |
| ... | @@ -222,23 +249,15 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { | ... | @@ -222,23 +249,15 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { |
| 222 | var object = try self.allocator.create(Object); | 249 | var object = try self.allocator.create(Object); |
| 223 | errdefer self.allocator.destroy(object); | 250 | errdefer self.allocator.destroy(object); |
| 224 | 251 | ||
| 225 | object.* = Object.init(self.allocator); | 252 | object.* = .{ |
| 226 | object.arch = self.arch.?; | 253 | .allocator = self.allocator, |
| 227 | object.file = try fs.cwd().openFile(self.name.?, .{}); | 254 | .arch = self.arch.?, |
| 228 | object.name = name; | 255 | .file = try fs.cwd().openFile(self.name.?, .{}), |
| 229 | object.file_offset = @intCast(u32, try reader.context.getPos()); | 256 | .name = name, |
| 257 | .file_offset = @intCast(u32, try reader.context.getPos()), | ||
| 258 | }; | ||
| 230 | try object.parse(); | 259 | try object.parse(); |
| 231 | |||
| 232 | try reader.context.seekTo(0); | 260 | try reader.context.seekTo(0); |
| 233 | 261 | ||
| 234 | return object; | 262 | return object; |
| 235 | } | 263 | } |
| 236 | |||
| 237 | pub 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); | ||
| 243 | return mem.eql(u8, &magic, Archive.ARMAG); | ||
| 244 | } |
src/link/MachO/Dylib.zig+129-19| ... | @@ -8,6 +8,7 @@ const macho = std.macho; | ... | @@ -8,6 +8,7 @@ const macho = std.macho; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | 9 | ||
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const Arch = std.Target.Cpu.Arch; | ||
| 11 | const Symbol = @import("Symbol.zig"); | 12 | const Symbol = @import("Symbol.zig"); |
| 12 | const LibStub = @import("../tapi.zig").LibStub; | 13 | const LibStub = @import("../tapi.zig").LibStub; |
| 13 | 14 | ||
| ... | @@ -15,10 +16,11 @@ usingnamespace @import("commands.zig"); | ... | @@ -15,10 +16,11 @@ usingnamespace @import("commands.zig"); |
| 15 | 16 | ||
| 16 | allocator: *Allocator, | 17 | allocator: *Allocator, |
| 17 | 18 | ||
| 18 | arch: ?std.Target.Cpu.Arch = null, | 19 | arch: ?Arch = null, |
| 19 | header: ?macho.mach_header_64 = null, | 20 | header: ?macho.mach_header_64 = null, |
| 20 | file: ?fs.File = null, | 21 | file: ?fs.File = null, |
| 21 | name: ?[]const u8 = null, | 22 | name: ?[]const u8 = null, |
| 23 | syslibroot: ?[]const u8 = null, | ||
| 22 | 24 | ||
| 23 | ordinal: ?u16 = null, | 25 | ordinal: ?u16 = null, |
| 24 | 26 | ||
| ... | @@ -35,6 +37,11 @@ id: ?Id = null, | ... | @@ -35,6 +37,11 @@ id: ?Id = null, |
| 35 | /// a symbol is referenced by an object file. | 37 | /// a symbol is referenced by an object file. |
| 36 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, | 38 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 37 | 39 | ||
| 40 | // TODO we should keep track of already parsed dylibs so that | ||
| 41 | // we don't unnecessarily reparse them again. | ||
| 42 | // TODO add dylib dep analysis and extraction for .dylib files. | ||
| 43 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | ||
| 44 | |||
| 38 | pub const Id = struct { | 45 | pub const Id = struct { |
| 39 | name: []const u8, | 46 | name: []const u8, |
| 40 | timestamp: u32, | 47 | timestamp: u32, |
| ... | @@ -46,8 +53,57 @@ pub const Id = struct { | ... | @@ -46,8 +53,57 @@ pub const Id = struct { |
| 46 | } | 53 | } |
| 47 | }; | 54 | }; |
| 48 | 55 | ||
| 49 | pub fn init(allocator: *Allocator) Dylib { | 56 | pub const Error = error{ |
| 50 | return .{ .allocator = allocator }; | 57 | OutOfMemory, |
| 58 | EmptyStubFile, | ||
| 59 | MismatchedCpuArchitecture, | ||
| 60 | UnsupportedCpuArchitecture, | ||
| 61 | } || fs.File.OpenError || std.os.PReadError; | ||
| 62 | |||
| 63 | pub fn createAndParseFromPath( | ||
| 64 | allocator: *Allocator, | ||
| 65 | arch: Arch, | ||
| 66 | path: []const u8, | ||
| 67 | syslibroot: ?[]const u8, | ||
| 68 | recurse_libs: bool, | ||
| 69 | ) Error!?*Dylib { | ||
| 70 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | ||
| 71 | error.FileNotFound => return null, | ||
| 72 | else => |e| return e, | ||
| 73 | }; | ||
| 74 | errdefer file.close(); | ||
| 75 | |||
| 76 | const dylib = try allocator.create(Dylib); | ||
| 77 | errdefer allocator.destroy(dylib); | ||
| 78 | |||
| 79 | const name = try allocator.dupe(u8, path); | ||
| 80 | errdefer allocator.free(name); | ||
| 81 | |||
| 82 | dylib.* = .{ | ||
| 83 | .allocator = allocator, | ||
| 84 | .arch = arch, | ||
| 85 | .name = name, | ||
| 86 | .file = file, | ||
| 87 | .syslibroot = syslibroot, | ||
| 88 | }; | ||
| 89 | |||
| 90 | dylib.parse(recurse_libs) catch |err| switch (err) { | ||
| 91 | error.EndOfStream, error.NotDylib => { | ||
| 92 | try file.seekTo(0); | ||
| 93 | |||
| 94 | var lib_stub = LibStub.loadFromFile(allocator, file) catch { | ||
| 95 | dylib.deinit(); | ||
| 96 | allocator.destroy(dylib); | ||
| 97 | return null; | ||
| 98 | }; | ||
| 99 | defer lib_stub.deinit(); | ||
| 100 | |||
| 101 | try dylib.parseFromStub(lib_stub, recurse_libs); | ||
| 102 | }, | ||
| 103 | else => |e| return e, | ||
| 104 | }; | ||
| 105 | |||
| 106 | return dylib; | ||
| 51 | } | 107 | } |
| 52 | 108 | ||
| 53 | pub fn deinit(self: *Dylib) void { | 109 | pub fn deinit(self: *Dylib) void { |
| ... | @@ -60,6 +116,7 @@ pub fn deinit(self: *Dylib) void { | ... | @@ -60,6 +116,7 @@ pub fn deinit(self: *Dylib) void { |
| 60 | self.allocator.free(key); | 116 | self.allocator.free(key); |
| 61 | } | 117 | } |
| 62 | self.symbols.deinit(self.allocator); | 118 | self.symbols.deinit(self.allocator); |
| 119 | self.dylibs.deinit(self.allocator); | ||
| 63 | 120 | ||
| 64 | if (self.name) |name| { | 121 | if (self.name) |name| { |
| 65 | self.allocator.free(name); | 122 | self.allocator.free(name); |
| ... | @@ -76,15 +133,15 @@ pub fn closeFile(self: Dylib) void { | ... | @@ -76,15 +133,15 @@ pub fn closeFile(self: Dylib) void { |
| 76 | } | 133 | } |
| 77 | } | 134 | } |
| 78 | 135 | ||
| 79 | pub fn parse(self: *Dylib) !void { | 136 | pub fn parse(self: *Dylib, recurse_libs: bool) !void { |
| 80 | log.debug("parsing shared library '{s}'", .{self.name.?}); | 137 | log.debug("parsing shared library '{s}'", .{self.name.?}); |
| 81 | 138 | ||
| 82 | var reader = self.file.?.reader(); | 139 | var reader = self.file.?.reader(); |
| 83 | self.header = try reader.readStruct(macho.mach_header_64); | 140 | self.header = try reader.readStruct(macho.mach_header_64); |
| 84 | 141 | ||
| 85 | if (self.header.?.filetype != macho.MH_DYLIB) { | 142 | if (self.header.?.filetype != macho.MH_DYLIB) { |
| 86 | log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); | 143 | log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); |
| 87 | return error.MalformedDylib; | 144 | return error.NotDylib; |
| 88 | } | 145 | } |
| 89 | 146 | ||
| 90 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | 147 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { |
| ... | @@ -190,7 +247,7 @@ fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { | ... | @@ -190,7 +247,7 @@ fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { |
| 190 | } | 247 | } |
| 191 | } | 248 | } |
| 192 | 249 | ||
| 193 | pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { | 250 | pub fn parseFromStub(self: *Dylib, lib_stub: LibStub, recurse_libs: bool) !void { |
| 194 | if (lib_stub.inner.len == 0) return error.EmptyStubFile; | 251 | if (lib_stub.inner.len == 0) return error.EmptyStubFile; |
| 195 | 252 | ||
| 196 | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); | 253 | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); |
| ... | @@ -236,9 +293,17 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { | ... | @@ -236,9 +293,17 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 236 | for (reexports) |reexp| { | 293 | for (reexports) |reexp| { |
| 237 | if (!hasTarget(reexp.targets, target_string)) continue; | 294 | if (!hasTarget(reexp.targets, target_string)) continue; |
| 238 | 295 | ||
| 239 | for (reexp.symbols) |sym_name| { | 296 | if (reexp.symbols) |symbols| { |
| 240 | if (self.symbols.contains(sym_name)) continue; | 297 | for (symbols) |sym_name| { |
| 241 | try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); | 298 | if (self.symbols.contains(sym_name)) continue; |
| 299 | try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if (reexp.objc_classes) |classes| { | ||
| 304 | for (classes) |sym_name| { | ||
| 305 | try self.addObjCClassSymbols(sym_name); | ||
| 306 | } | ||
| 242 | } | 307 | } |
| 243 | } | 308 | } |
| 244 | } | 309 | } |
| ... | @@ -249,6 +314,60 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { | ... | @@ -249,6 +314,60 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 249 | } | 314 | } |
| 250 | } | 315 | } |
| 251 | } | 316 | } |
| 317 | |||
| 318 | for (lib_stub.inner) |stub| { | ||
| 319 | if (!hasTarget(stub.targets, target_string)) continue; | ||
| 320 | |||
| 321 | if (stub.reexported_libraries) |reexports| reexports: { | ||
| 322 | if (!recurse_libs) break :reexports; | ||
| 323 | |||
| 324 | for (reexports) |reexp| { | ||
| 325 | if (!hasTarget(reexp.targets, target_string)) continue; | ||
| 326 | |||
| 327 | outer: for (reexp.libraries) |lib| { | ||
| 328 | const dirname = fs.path.dirname(lib) orelse { | ||
| 329 | log.warn("unable to resolve dependency {s}", .{lib}); | ||
| 330 | continue; | ||
| 331 | }; | ||
| 332 | const filename = fs.path.basename(lib); | ||
| 333 | const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| | ||
| 334 | filename[0..index] | ||
| 335 | else | ||
| 336 | filename; | ||
| 337 | |||
| 338 | for (&[_][]const u8{ "dylib", "tbd" }) |ext| { | ||
| 339 | const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ | ||
| 340 | without_ext, | ||
| 341 | ext, | ||
| 342 | }); | ||
| 343 | defer self.allocator.free(with_ext); | ||
| 344 | |||
| 345 | const lib_path = if (self.syslibroot) |syslibroot| | ||
| 346 | try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) | ||
| 347 | else | ||
| 348 | try fs.path.join(self.allocator, &.{ dirname, with_ext }); | ||
| 349 | |||
| 350 | log.debug("trying dependency at fully resolved path {s}", .{lib_path}); | ||
| 351 | |||
| 352 | const dylib = (try createAndParseFromPath( | ||
| 353 | self.allocator, | ||
| 354 | self.arch.?, | ||
| 355 | lib_path, | ||
| 356 | self.syslibroot, | ||
| 357 | true, | ||
| 358 | )) orelse { | ||
| 359 | continue; | ||
| 360 | }; | ||
| 361 | |||
| 362 | try self.dylibs.append(self.allocator, dylib); | ||
| 363 | continue :outer; | ||
| 364 | } else { | ||
| 365 | log.warn("unable to resolve dependency {s}", .{lib}); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 252 | } | 371 | } |
| 253 | 372 | ||
| 254 | fn hasTarget(targets: []const []const u8, target: []const u8) bool { | 373 | fn hasTarget(targets: []const []const u8, target: []const u8) bool { |
| ... | @@ -258,15 +377,6 @@ fn hasTarget(targets: []const []const u8, target: []const u8) bool { | ... | @@ -258,15 +377,6 @@ fn hasTarget(targets: []const []const u8, target: []const u8) bool { |
| 258 | return false; | 377 | return false; |
| 259 | } | 378 | } |
| 260 | 379 | ||
| 261 | pub fn isDylib(file: fs.File) !bool { | ||
| 262 | const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) { | ||
| 263 | error.EndOfStream => return false, | ||
| 264 | else => |e| return e, | ||
| 265 | }; | ||
| 266 | try file.seekTo(0); | ||
| 267 | return header.filetype == macho.MH_DYLIB; | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { | 380 | pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { |
| 271 | if (!self.symbols.contains(sym_name)) return null; | 381 | if (!self.symbols.contains(sym_name)) return null; |
| 272 | 382 |
src/link/MachO/Object.zig+37-15| ... | @@ -11,6 +11,7 @@ const mem = std.mem; | ... | @@ -11,6 +11,7 @@ const mem = std.mem; |
| 11 | const reloc = @import("reloc.zig"); | 11 | const reloc = @import("reloc.zig"); |
| 12 | 12 | ||
| 13 | const Allocator = mem.Allocator; | 13 | const Allocator = mem.Allocator; |
| 14 | const Arch = std.Target.Cpu.Arch; | ||
| 14 | const Relocation = reloc.Relocation; | 15 | const Relocation = reloc.Relocation; |
| 15 | const Symbol = @import("Symbol.zig"); | 16 | const Symbol = @import("Symbol.zig"); |
| 16 | const parseName = @import("Zld.zig").parseName; | 17 | const parseName = @import("Zld.zig").parseName; |
| ... | @@ -18,7 +19,7 @@ const parseName = @import("Zld.zig").parseName; | ... | @@ -18,7 +19,7 @@ const parseName = @import("Zld.zig").parseName; |
| 18 | usingnamespace @import("commands.zig"); | 19 | usingnamespace @import("commands.zig"); |
| 19 | 20 | ||
| 20 | allocator: *Allocator, | 21 | allocator: *Allocator, |
| 21 | arch: ?std.Target.Cpu.Arch = null, | 22 | arch: ?Arch = null, |
| 22 | header: ?macho.mach_header_64 = null, | 23 | header: ?macho.mach_header_64 = null, |
| 23 | file: ?fs.File = null, | 24 | file: ?fs.File = null, |
| 24 | file_offset: ?u32 = null, | 25 | file_offset: ?u32 = null, |
| ... | @@ -173,10 +174,36 @@ const DebugInfo = struct { | ... | @@ -173,10 +174,36 @@ const DebugInfo = struct { |
| 173 | } | 174 | } |
| 174 | }; | 175 | }; |
| 175 | 176 | ||
| 176 | pub fn init(allocator: *Allocator) Object { | 177 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Object { |
| 177 | return .{ | 178 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 179 | error.FileNotFound => return null, | ||
| 180 | else => |e| return e, | ||
| 181 | }; | ||
| 182 | errdefer file.close(); | ||
| 183 | |||
| 184 | const object = try allocator.create(Object); | ||
| 185 | errdefer allocator.destroy(object); | ||
| 186 | |||
| 187 | const name = try allocator.dupe(u8, path); | ||
| 188 | errdefer allocator.free(name); | ||
| 189 | |||
| 190 | object.* = .{ | ||
| 178 | .allocator = allocator, | 191 | .allocator = allocator, |
| 192 | .arch = arch, | ||
| 193 | .name = name, | ||
| 194 | .file = file, | ||
| 179 | }; | 195 | }; |
| 196 | |||
| 197 | object.parse() catch |err| switch (err) { | ||
| 198 | error.EndOfStream, error.NotObject => { | ||
| 199 | object.deinit(); | ||
| 200 | allocator.destroy(object); | ||
| 201 | return null; | ||
| 202 | }, | ||
| 203 | else => |e| return e, | ||
| 204 | }; | ||
| 205 | |||
| 206 | return object; | ||
| 180 | } | 207 | } |
| 181 | 208 | ||
| 182 | pub fn deinit(self: *Object) void { | 209 | pub fn deinit(self: *Object) void { |
| ... | @@ -223,11 +250,15 @@ pub fn parse(self: *Object) !void { | ... | @@ -223,11 +250,15 @@ pub fn parse(self: *Object) !void { |
| 223 | self.header = try reader.readStruct(macho.mach_header_64); | 250 | self.header = try reader.readStruct(macho.mach_header_64); |
| 224 | 251 | ||
| 225 | if (self.header.?.filetype != macho.MH_OBJECT) { | 252 | if (self.header.?.filetype != macho.MH_OBJECT) { |
| 226 | log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_OBJECT, self.header.?.filetype }); | 253 | log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ |
| 227 | return error.MalformedObject; | 254 | macho.MH_OBJECT, |
| 255 | self.header.?.filetype, | ||
| 256 | }); | ||
| 257 | |||
| 258 | return error.NotObject; | ||
| 228 | } | 259 | } |
| 229 | 260 | ||
| 230 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | 261 | const this_arch: Arch = switch (self.header.?.cputype) { |
| 231 | macho.CPU_TYPE_ARM64 => .aarch64, | 262 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 232 | macho.CPU_TYPE_X86_64 => .x86_64, | 263 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 233 | else => |value| { | 264 | else => |value| { |
| ... | @@ -533,12 +564,3 @@ pub fn parseDataInCode(self: *Object) !void { | ... | @@ -533,12 +564,3 @@ pub fn parseDataInCode(self: *Object) !void { |
| 533 | try self.data_in_code_entries.append(self.allocator, dice); | 564 | try self.data_in_code_entries.append(self.allocator, dice); |
| 534 | } | 565 | } |
| 535 | } | 566 | } |
| 536 | |||
| 537 | pub 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); | ||
| 543 | return header.filetype == macho.MH_OBJECT; | ||
| 544 | } |
src/link/MachO/Zld.zig+53-177| ... | @@ -16,7 +16,6 @@ const Allocator = mem.Allocator; | ... | @@ -16,7 +16,6 @@ const Allocator = mem.Allocator; |
| 16 | const Archive = @import("Archive.zig"); | 16 | const Archive = @import("Archive.zig"); |
| 17 | const CodeSignature = @import("CodeSignature.zig"); | 17 | const CodeSignature = @import("CodeSignature.zig"); |
| 18 | const Dylib = @import("Dylib.zig"); | 18 | const Dylib = @import("Dylib.zig"); |
| 19 | const LibStub = @import("../tapi.zig").LibStub; | ||
| 20 | const Object = @import("Object.zig"); | 19 | const Object = @import("Object.zig"); |
| 21 | const Symbol = @import("Symbol.zig"); | 20 | const Symbol = @import("Symbol.zig"); |
| 22 | const Trie = @import("Trie.zig"); | 21 | const Trie = @import("Trie.zig"); |
| ... | @@ -33,6 +32,7 @@ out_path: ?[]const u8 = null, | ... | @@ -33,6 +32,7 @@ out_path: ?[]const u8 = null, |
| 33 | 32 | ||
| 34 | // TODO these args will become obselete once Zld is coalesced with incremental | 33 | // TODO these args will become obselete once Zld is coalesced with incremental |
| 35 | // linker. | 34 | // linker. |
| 35 | syslibroot: ?[]const u8 = null, | ||
| 36 | stack_size: u64 = 0, | 36 | stack_size: u64 = 0, |
| 37 | 37 | ||
| 38 | objects: std.ArrayListUnmanaged(*Object) = .{}, | 38 | objects: std.ArrayListUnmanaged(*Object) = .{}, |
| ... | @@ -257,214 +257,90 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L | ... | @@ -257,214 +257,90 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | 259 | fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 260 | const Input = struct { | ||
| 261 | kind: union(enum) { | ||
| 262 | object: fs.File, | ||
| 263 | archive: fs.File, | ||
| 264 | dylib: fs.File, | ||
| 265 | stub: LibStub, | ||
| 266 | }, | ||
| 267 | name: []const u8, | ||
| 268 | |||
| 269 | fn deinit(input: *@This()) void { | ||
| 270 | switch (input.kind) { | ||
| 271 | .stub => |*stub| { | ||
| 272 | stub.deinit(); | ||
| 273 | }, | ||
| 274 | else => {}, | ||
| 275 | } | ||
| 276 | } | ||
| 277 | }; | ||
| 278 | var classified = std.ArrayList(Input).init(self.allocator); | ||
| 279 | defer { | ||
| 280 | for (classified.items) |*input| { | ||
| 281 | input.deinit(); | ||
| 282 | } | ||
| 283 | classified.deinit(); | ||
| 284 | } | ||
| 285 | |||
| 286 | // First, classify input files: object, archive, dylib or stub (tbd). | ||
| 287 | for (files) |file_name| { | 260 | for (files) |file_name| { |
| 288 | const file = try fs.cwd().openFile(file_name, .{}); | ||
| 289 | const full_path = full_path: { | 261 | const full_path = full_path: { |
| 290 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 262 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 291 | const path = try std.fs.realpath(file_name, &buffer); | 263 | const path = try std.fs.realpath(file_name, &buffer); |
| 292 | break :full_path try self.allocator.dupe(u8, path); | 264 | break :full_path try self.allocator.dupe(u8, path); |
| 293 | }; | 265 | }; |
| 294 | 266 | ||
| 295 | try_object: { | 267 | if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| { |
| 296 | if (!(try Object.isObject(file))) break :try_object; | 268 | try self.objects.append(self.allocator, object); |
| 297 | try classified.append(.{ | ||
| 298 | .kind = .{ .object = file }, | ||
| 299 | .name = full_path, | ||
| 300 | }); | ||
| 301 | continue; | ||
| 302 | } | ||
| 303 | |||
| 304 | try_archive: { | ||
| 305 | if (!(try Archive.isArchive(file))) break :try_archive; | ||
| 306 | try classified.append(.{ | ||
| 307 | .kind = .{ .archive = file }, | ||
| 308 | .name = full_path, | ||
| 309 | }); | ||
| 310 | continue; | 269 | continue; |
| 311 | } | 270 | } |
| 312 | 271 | ||
| 313 | try_dylib: { | 272 | if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| { |
| 314 | if (!(try Dylib.isDylib(file))) break :try_dylib; | 273 | try self.archives.append(self.allocator, archive); |
| 315 | try classified.append(.{ | ||
| 316 | .kind = .{ .dylib = file }, | ||
| 317 | .name = full_path, | ||
| 318 | }); | ||
| 319 | continue; | 274 | continue; |
| 320 | } | 275 | } |
| 321 | 276 | ||
| 322 | try_stub: { | 277 | if (try Dylib.createAndParseFromPath( |
| 323 | var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { | 278 | self.allocator, |
| 324 | break :try_stub; | 279 | self.arch.?, |
| 325 | }; | 280 | full_path, |
| 326 | try classified.append(.{ | 281 | self.syslibroot, |
| 327 | .kind = .{ .stub = lib_stub }, | 282 | true, |
| 328 | .name = full_path, | 283 | )) |dylib| { |
| 329 | }); | 284 | try self.dylibs.append(self.allocator, dylib); |
| 330 | file.close(); | ||
| 331 | continue; | 285 | continue; |
| 332 | } | 286 | } |
| 333 | 287 | ||
| 334 | file.close(); | ||
| 335 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | 288 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); |
| 336 | } | 289 | } |
| 337 | |||
| 338 | // Based on our classification, proceed with parsing. | ||
| 339 | for (classified.items) |input| { | ||
| 340 | switch (input.kind) { | ||
| 341 | .object => |file| { | ||
| 342 | const object = try self.allocator.create(Object); | ||
| 343 | errdefer self.allocator.destroy(object); | ||
| 344 | |||
| 345 | object.* = Object.init(self.allocator); | ||
| 346 | object.arch = self.arch.?; | ||
| 347 | object.name = input.name; | ||
| 348 | object.file = file; | ||
| 349 | |||
| 350 | try object.parse(); | ||
| 351 | try self.objects.append(self.allocator, object); | ||
| 352 | }, | ||
| 353 | .archive => |file| { | ||
| 354 | const archive = try self.allocator.create(Archive); | ||
| 355 | errdefer self.allocator.destroy(archive); | ||
| 356 | |||
| 357 | archive.* = Archive.init(self.allocator); | ||
| 358 | archive.arch = self.arch.?; | ||
| 359 | archive.name = input.name; | ||
| 360 | archive.file = file; | ||
| 361 | |||
| 362 | try archive.parse(); | ||
| 363 | try self.archives.append(self.allocator, archive); | ||
| 364 | }, | ||
| 365 | .dylib, .stub => { | ||
| 366 | const dylib = try self.allocator.create(Dylib); | ||
| 367 | errdefer self.allocator.destroy(dylib); | ||
| 368 | |||
| 369 | dylib.* = Dylib.init(self.allocator); | ||
| 370 | dylib.arch = self.arch.?; | ||
| 371 | dylib.name = input.name; | ||
| 372 | |||
| 373 | if (input.kind == .dylib) { | ||
| 374 | dylib.file = input.kind.dylib; | ||
| 375 | try dylib.parse(); | ||
| 376 | } else { | ||
| 377 | try dylib.parseFromStub(input.kind.stub); | ||
| 378 | } | ||
| 379 | |||
| 380 | try self.dylibs.append(self.allocator, dylib); | ||
| 381 | }, | ||
| 382 | } | ||
| 383 | } | ||
| 384 | } | 290 | } |
| 385 | 291 | ||
| 386 | fn parseLibs(self: *Zld, libs: []const []const u8) !void { | 292 | fn parseLibs(self: *Zld, libs: []const []const u8) !void { |
| 387 | for (libs) |lib| { | 293 | const DylibDeps = struct { |
| 388 | const file = try fs.cwd().openFile(lib, .{}); | 294 | fn bubbleUp(out: *std.ArrayList(*Dylib), next: *Dylib) error{OutOfMemory}!void { |
| 389 | 295 | try out.ensureUnusedCapacity(next.dylibs.items.len); | |
| 390 | var kind: ?union(enum) { | 296 | for (next.dylibs.items) |dylib| { |
| 391 | archive, | 297 | out.appendAssumeCapacity(dylib); |
| 392 | dylib, | 298 | } |
| 393 | stub: LibStub, | 299 | for (next.dylibs.items) |dylib| { |
| 394 | } = kind: { | 300 | try bubbleUp(out, dylib); |
| 395 | if (try Archive.isArchive(file)) break :kind .archive; | ||
| 396 | if (try Dylib.isDylib(file)) break :kind .dylib; | ||
| 397 | var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { | ||
| 398 | break :kind null; | ||
| 399 | }; | ||
| 400 | break :kind .{ .stub = lib_stub }; | ||
| 401 | }; | ||
| 402 | defer { | ||
| 403 | if (kind) |*kk| { | ||
| 404 | switch (kk.*) { | ||
| 405 | .stub => |*stub| { | ||
| 406 | stub.deinit(); | ||
| 407 | }, | ||
| 408 | else => {}, | ||
| 409 | } | ||
| 410 | } | 301 | } |
| 411 | } | 302 | } |
| 303 | }; | ||
| 412 | 304 | ||
| 413 | const unwrapped = kind orelse { | 305 | for (libs) |lib| { |
| 414 | file.close(); | 306 | if (try Dylib.createAndParseFromPath( |
| 415 | log.warn("unknown filetype for a library: '{s}'", .{lib}); | 307 | self.allocator, |
| 308 | self.arch.?, | ||
| 309 | lib, | ||
| 310 | self.syslibroot, | ||
| 311 | true, | ||
| 312 | )) |dylib| { | ||
| 313 | try self.dylibs.append(self.allocator, dylib); | ||
| 416 | continue; | 314 | continue; |
| 417 | }; | ||
| 418 | switch (unwrapped) { | ||
| 419 | .archive => { | ||
| 420 | const archive = try self.allocator.create(Archive); | ||
| 421 | errdefer self.allocator.destroy(archive); | ||
| 422 | |||
| 423 | archive.* = Archive.init(self.allocator); | ||
| 424 | archive.arch = self.arch.?; | ||
| 425 | archive.name = try self.allocator.dupe(u8, lib); | ||
| 426 | archive.file = file; | ||
| 427 | |||
| 428 | try archive.parse(); | ||
| 429 | try self.archives.append(self.allocator, archive); | ||
| 430 | }, | ||
| 431 | .dylib, .stub => { | ||
| 432 | const dylib = try self.allocator.create(Dylib); | ||
| 433 | errdefer self.allocator.destroy(dylib); | ||
| 434 | |||
| 435 | dylib.* = Dylib.init(self.allocator); | ||
| 436 | dylib.arch = self.arch.?; | ||
| 437 | dylib.name = try self.allocator.dupe(u8, lib); | ||
| 438 | |||
| 439 | if (unwrapped == .dylib) { | ||
| 440 | dylib.file = file; | ||
| 441 | try dylib.parse(); | ||
| 442 | } else { | ||
| 443 | try dylib.parseFromStub(unwrapped.stub); | ||
| 444 | } | ||
| 445 | |||
| 446 | try self.dylibs.append(self.allocator, dylib); | ||
| 447 | }, | ||
| 448 | } | 315 | } |
| 449 | } | ||
| 450 | } | ||
| 451 | 316 | ||
| 452 | fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { | 317 | if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| { |
| 453 | const file = try fs.cwd().openFile(libc_stub_path, .{}); | 318 | try self.archives.append(self.allocator, archive); |
| 454 | defer file.close(); | 319 | continue; |
| 320 | } | ||
| 455 | 321 | ||
| 456 | var lib_stub = try LibStub.loadFromFile(self.allocator, file); | 322 | log.warn("unknown filetype for a library: '{s}'", .{lib}); |
| 457 | defer lib_stub.deinit(); | 323 | } |
| 458 | 324 | ||
| 459 | const dylib = try self.allocator.create(Dylib); | 325 | // Flatten out any parsed dependencies. |
| 460 | errdefer self.allocator.destroy(dylib); | 326 | var deps = std.ArrayList(*Dylib).init(self.allocator); |
| 327 | defer deps.deinit(); | ||
| 461 | 328 | ||
| 462 | dylib.* = Dylib.init(self.allocator); | 329 | for (self.dylibs.items) |dylib| { |
| 463 | dylib.arch = self.arch.?; | 330 | try DylibDeps.bubbleUp(&deps, dylib); |
| 464 | dylib.name = try self.allocator.dupe(u8, libc_stub_path); | 331 | } |
| 465 | 332 | ||
| 466 | try dylib.parseFromStub(lib_stub); | 333 | try self.dylibs.appendSlice(self.allocator, deps.toOwnedSlice()); |
| 334 | } | ||
| 467 | 335 | ||
| 336 | fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { | ||
| 337 | const dylib = (try Dylib.createAndParseFromPath( | ||
| 338 | self.allocator, | ||
| 339 | self.arch.?, | ||
| 340 | libc_stub_path, | ||
| 341 | self.syslibroot, | ||
| 342 | false, | ||
| 343 | )) orelse return error.FailedToParseLibSystem; | ||
| 468 | self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); | 344 | self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); |
| 469 | try self.dylibs.append(self.allocator, dylib); | 345 | try self.dylibs.append(self.allocator, dylib); |
| 470 | 346 |
src/link/tapi.zig+2-1| ... | @@ -41,7 +41,8 @@ pub const LibStub = struct { | ... | @@ -41,7 +41,8 @@ pub const LibStub = struct { |
| 41 | }, | 41 | }, |
| 42 | reexports: ?[]const struct { | 42 | reexports: ?[]const struct { |
| 43 | targets: []const []const u8, | 43 | targets: []const []const u8, |
| 44 | symbols: []const []const u8, | 44 | symbols: ?[]const []const u8, |
| 45 | objc_classes: ?[]const []const u8, | ||
| 45 | }, | 46 | }, |
| 46 | allowable_clients: ?[]const struct { | 47 | allowable_clients: ?[]const struct { |
| 47 | targets: []const []const u8, | 48 | targets: []const []const u8, |