| author | |
| committer | |
| log | 0b15ba8334bc03b59a975e579aac01a6b3fc2109 |
| tree | 267ab3306af2b4277b933770de1d3fd696260372 |
| parent | f023cdad7ca676977d9b5abd3d38677779aab211 |
instead, immediately transfer ownership to MachO struct. Also, revert
back to try-ok-fail parsing approach of objects, archives, and dylibs.
It seems easier to try and fail than check if the file *is* of a
certain type given that a dylib may be a stub and parsing yaml
twice in a row seems very wasteful.
Hint for the future: if we optimise yaml/TAPI parsing, this approach
may be rethought!4 files changed, 85 insertions(+), 77 deletions(-)
src/link/MachO.zig+13-34| ... | ... | @@ -63,7 +63,7 @@ entry_addr: ?u64 = null, |
| 63 | 63 | |
| 64 | 64 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 65 | 65 | archives: std.ArrayListUnmanaged(Archive) = .{}, |
| 66 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 66 | dylibs: std.ArrayListUnmanaged(Dylib) = .{}, | |
| 67 | 67 | |
| 68 | 68 | next_dylib_ordinal: u16 = 1, |
| 69 | 69 | |
| ... | ... | @@ -994,25 +994,15 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 994 | 994 | const path = try std.fs.realpath(file_name, &buffer); |
| 995 | 995 | break :full_path try self.base.allocator.dupe(u8, path); |
| 996 | 996 | }; |
| 997 | const file = try fs.cwd().openFile(full_path, .{}); | |
| 997 | defer self.base.allocator.free(full_path); | |
| 998 | 998 | |
| 999 | if (try Object.isObject(file)) { | |
| 1000 | const object = try self.objects.addOne(self.base.allocator); | |
| 1001 | object.* = .{ | |
| 1002 | .name = full_path, | |
| 1003 | .file = file, | |
| 1004 | }; | |
| 1005 | try object.parse(self.base.allocator, arch); | |
| 999 | if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| { | |
| 1000 | try self.objects.append(self.base.allocator, object); | |
| 1006 | 1001 | continue; |
| 1007 | 1002 | } |
| 1008 | 1003 | |
| 1009 | if (try Archive.isArchive(file, arch)) { | |
| 1010 | const archive = try self.archives.addOne(self.base.allocator); | |
| 1011 | archive.* = .{ | |
| 1012 | .name = full_path, | |
| 1013 | .file = file, | |
| 1014 | }; | |
| 1015 | try archive.parse(self.base.allocator, arch); | |
| 1004 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| { | |
| 1005 | try self.archives.append(self.base.allocator, archive); | |
| 1016 | 1006 | continue; |
| 1017 | 1007 | } |
| 1018 | 1008 | |
| ... | ... | @@ -1024,8 +1014,6 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 1024 | 1014 | continue; |
| 1025 | 1015 | } |
| 1026 | 1016 | |
| 1027 | self.base.allocator.free(full_path); | |
| 1028 | file.close(); | |
| 1029 | 1017 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); |
| 1030 | 1018 | } |
| 1031 | 1019 | } |
| ... | ... | @@ -1033,8 +1021,6 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 1033 | 1021 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { |
| 1034 | 1022 | const arch = self.base.options.target.cpu.arch; |
| 1035 | 1023 | for (libs) |lib| { |
| 1036 | const file = try fs.cwd().openFile(lib, .{}); | |
| 1037 | ||
| 1038 | 1024 | if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{ |
| 1039 | 1025 | .syslibroot = syslibroot, |
| 1040 | 1026 | })) |dylibs| { |
| ... | ... | @@ -1043,17 +1029,11 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v |
| 1043 | 1029 | continue; |
| 1044 | 1030 | } |
| 1045 | 1031 | |
| 1046 | if (try Archive.isArchive(file, arch)) { | |
| 1047 | const archive = try self.archives.addOne(self.base.allocator); | |
| 1048 | archive.* = .{ | |
| 1049 | .name = try self.base.allocator.dupe(u8, lib), | |
| 1050 | .file = file, | |
| 1051 | }; | |
| 1052 | try archive.parse(self.base.allocator, arch); | |
| 1032 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| { | |
| 1033 | try self.archives.append(self.base.allocator, archive); | |
| 1053 | 1034 | continue; |
| 1054 | 1035 | } |
| 1055 | 1036 | |
| 1056 | file.close(); | |
| 1057 | 1037 | log.warn("unknown filetype for a library: '{s}'", .{lib}); |
| 1058 | 1038 | } |
| 1059 | 1039 | } |
| ... | ... | @@ -2351,17 +2331,17 @@ fn resolveSymbols(self: *MachO) !void { |
| 2351 | 2331 | }); |
| 2352 | 2332 | } |
| 2353 | 2333 | |
| 2354 | var referenced = std.AutoHashMap(*Dylib, void).init(self.base.allocator); | |
| 2334 | var referenced = std.AutoHashMap(u16, void).init(self.base.allocator); | |
| 2355 | 2335 | defer referenced.deinit(); |
| 2356 | 2336 | |
| 2357 | 2337 | loop: for (self.undefs.items) |sym| { |
| 2358 | 2338 | if (symbolIsNull(sym)) continue; |
| 2359 | 2339 | |
| 2360 | 2340 | const sym_name = self.getString(sym.n_strx); |
| 2361 | for (self.dylibs.items) |dylib| { | |
| 2341 | for (self.dylibs.items) |*dylib, id| { | |
| 2362 | 2342 | if (!dylib.symbols.contains(sym_name)) continue; |
| 2363 | 2343 | |
| 2364 | if (!referenced.contains(dylib)) { | |
| 2344 | if (!referenced.contains(@intCast(u16, id))) { | |
| 2365 | 2345 | // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. |
| 2366 | 2346 | dylib.ordinal = self.next_dylib_ordinal; |
| 2367 | 2347 | const dylib_id = dylib.id orelse unreachable; |
| ... | ... | @@ -2375,7 +2355,7 @@ fn resolveSymbols(self: *MachO) !void { |
| 2375 | 2355 | errdefer dylib_cmd.deinit(self.base.allocator); |
| 2376 | 2356 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 2377 | 2357 | self.next_dylib_ordinal += 1; |
| 2378 | try referenced.putNoClobber(dylib, {}); | |
| 2358 | try referenced.putNoClobber(@intCast(u16, id), {}); | |
| 2379 | 2359 | } |
| 2380 | 2360 | |
| 2381 | 2361 | const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable; |
| ... | ... | @@ -3365,9 +3345,8 @@ pub fn deinit(self: *MachO) void { |
| 3365 | 3345 | } |
| 3366 | 3346 | self.archives.deinit(self.base.allocator); |
| 3367 | 3347 | |
| 3368 | for (self.dylibs.items) |dylib| { | |
| 3348 | for (self.dylibs.items) |*dylib| { | |
| 3369 | 3349 | dylib.deinit(self.base.allocator); |
| 3370 | self.base.allocator.destroy(dylib); | |
| 3371 | 3350 | } |
| 3372 | 3351 | self.dylibs.deinit(self.base.allocator); |
| 3373 | 3352 |
src/link/MachO/Archive.zig+35-21| ... | ... | @@ -104,38 +104,52 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void { |
| 104 | 104 | allocator.free(self.name); |
| 105 | 105 | } |
| 106 | 106 | |
| 107 | pub fn isArchive(file: fs.File, arch: Arch) !bool { | |
| 108 | const Internal = struct { | |
| 109 | fn isArchive(reader: anytype, a: Arch) !bool { | |
| 110 | const offset = try fat.getLibraryOffset(reader, a); | |
| 111 | try reader.context.seekTo(offset); | |
| 112 | const magic = try reader.readBytesNoEof(SARMAG); | |
| 113 | if (!mem.eql(u8, &magic, ARMAG)) return false; | |
| 114 | const header = try reader.readStruct(ar_hdr); | |
| 115 | return mem.eql(u8, &header.ar_fmag, ARFMAG); | |
| 116 | } | |
| 107 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive { | |
| 108 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | |
| 109 | error.FileNotFound => return null, | |
| 110 | else => |e| return e, | |
| 111 | }; | |
| 112 | errdefer file.close(); | |
| 113 | ||
| 114 | const name = try allocator.dupe(u8, path); | |
| 115 | errdefer allocator.free(name); | |
| 116 | ||
| 117 | var archive = Archive{ | |
| 118 | .name = name, | |
| 119 | .file = file, | |
| 117 | 120 | }; |
| 118 | const is_archive = if (Internal.isArchive(file.reader(), arch)) |res| | |
| 119 | res | |
| 120 | else |err| switch (err) { | |
| 121 | error.EndOfStream => false, | |
| 122 | error.MismatchedCpuArchitecture => true, // TODO maybe this check should be done differently? | |
| 121 | ||
| 122 | archive.parse(allocator, arch) catch |err| switch (err) { | |
| 123 | error.EndOfStream, error.NotArchive => { | |
| 124 | archive.deinit(allocator); | |
| 125 | return null; | |
| 126 | }, | |
| 123 | 127 | else => |e| return e, |
| 124 | 128 | }; |
| 125 | try file.seekTo(0); | |
| 126 | return is_archive; | |
| 129 | ||
| 130 | return archive; | |
| 127 | 131 | } |
| 128 | 132 | |
| 129 | 133 | pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void { |
| 130 | self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch); | |
| 131 | try self.file.seekTo(self.library_offset); | |
| 132 | 134 | const reader = self.file.reader(); |
| 135 | self.library_offset = try fat.getLibraryOffset(reader, arch); | |
| 136 | try self.file.seekTo(self.library_offset); | |
| 137 | ||
| 133 | 138 | const magic = try reader.readBytesNoEof(SARMAG); |
| 139 | if (!mem.eql(u8, &magic, ARMAG)) { | |
| 140 | log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); | |
| 141 | return error.NotArchive; | |
| 142 | } | |
| 143 | ||
| 134 | 144 | self.header = try reader.readStruct(ar_hdr); |
| 135 | var embedded_name = try parseName(allocator, self.header.?, reader); | |
| 136 | defer allocator.free(embedded_name); | |
| 145 | if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) { | |
| 146 | log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); | |
| 147 | return error.NotArchive; | |
| 148 | } | |
| 137 | 149 | |
| 150 | var embedded_name = try parseName(allocator, self.header.?, reader); | |
| 138 | 151 | log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name }); |
| 152 | defer allocator.free(embedded_name); | |
| 139 | 153 | |
| 140 | 154 | try self.parseTableOfContents(allocator, reader); |
| 141 | 155 | try reader.context.seekTo(0); |
src/link/MachO/Dylib.zig+5-9| ... | ... | @@ -148,20 +148,17 @@ pub fn createAndParseFromPath( |
| 148 | 148 | arch: Arch, |
| 149 | 149 | path: []const u8, |
| 150 | 150 | opts: CreateOpts, |
| 151 | ) Error!?[]*Dylib { | |
| 151 | ) Error!?[]Dylib { | |
| 152 | 152 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 153 | 153 | error.FileNotFound => return null, |
| 154 | 154 | else => |e| return e, |
| 155 | 155 | }; |
| 156 | 156 | errdefer file.close(); |
| 157 | 157 | |
| 158 | const dylib = try allocator.create(Dylib); | |
| 159 | errdefer allocator.destroy(dylib); | |
| 160 | ||
| 161 | 158 | const name = try allocator.dupe(u8, path); |
| 162 | 159 | errdefer allocator.free(name); |
| 163 | 160 | |
| 164 | dylib.* = .{ | |
| 161 | var dylib = Dylib{ | |
| 165 | 162 | .name = name, |
| 166 | 163 | .file = file, |
| 167 | 164 | }; |
| ... | ... | @@ -172,7 +169,6 @@ pub fn createAndParseFromPath( |
| 172 | 169 | |
| 173 | 170 | var lib_stub = LibStub.loadFromFile(allocator, file) catch { |
| 174 | 171 | dylib.deinit(allocator); |
| 175 | allocator.destroy(dylib); | |
| 176 | 172 | return null; |
| 177 | 173 | }; |
| 178 | 174 | defer lib_stub.deinit(); |
| ... | ... | @@ -191,12 +187,11 @@ pub fn createAndParseFromPath( |
| 191 | 187 | |
| 192 | 188 | // TODO maybe this should be an error and facilitate auto-cleanup? |
| 193 | 189 | dylib.deinit(allocator); |
| 194 | allocator.destroy(dylib); | |
| 195 | 190 | return null; |
| 196 | 191 | } |
| 197 | 192 | } |
| 198 | 193 | |
| 199 | var dylibs = std.ArrayList(*Dylib).init(allocator); | |
| 194 | var dylibs = std.ArrayList(Dylib).init(allocator); | |
| 200 | 195 | defer dylibs.deinit(); |
| 201 | 196 | |
| 202 | 197 | try dylibs.append(dylib); |
| ... | ... | @@ -449,7 +444,7 @@ pub fn parseDependentLibs( |
| 449 | 444 | self: *Dylib, |
| 450 | 445 | allocator: *Allocator, |
| 451 | 446 | arch: Arch, |
| 452 | out: *std.ArrayList(*Dylib), | |
| 447 | out: *std.ArrayList(Dylib), | |
| 453 | 448 | syslibroot: ?[]const u8, |
| 454 | 449 | ) !void { |
| 455 | 450 | outer: for (self.dependent_libs.items) |id| { |
| ... | ... | @@ -489,6 +484,7 @@ pub fn parseDependentLibs( |
| 489 | 484 | )) orelse { |
| 490 | 485 | continue; |
| 491 | 486 | }; |
| 487 | defer allocator.free(dylibs); | |
| 492 | 488 | |
| 493 | 489 | try out.appendSlice(dylibs); |
| 494 | 490 |
src/link/MachO/Object.zig+32-13| ... | ... | @@ -154,29 +154,47 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { |
| 154 | 154 | } |
| 155 | 155 | } |
| 156 | 156 | |
| 157 | pub fn isObject(file: fs.File) !bool { | |
| 158 | const Internal = struct { | |
| 159 | fn isObject(reader: anytype) !bool { | |
| 160 | const header = try reader.readStruct(macho.mach_header_64); | |
| 161 | return header.filetype == macho.MH_OBJECT; | |
| 162 | } | |
| 157 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object { | |
| 158 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | |
| 159 | error.FileNotFound => return null, | |
| 160 | else => |e| return e, | |
| 163 | 161 | }; |
| 164 | const is_object = if (Internal.isObject(file.reader())) |res| | |
| 165 | res | |
| 166 | else |err| switch (err) { | |
| 167 | error.EndOfStream => false, | |
| 162 | errdefer file.close(); | |
| 163 | ||
| 164 | const name = try allocator.dupe(u8, path); | |
| 165 | errdefer allocator.free(name); | |
| 166 | ||
| 167 | var object = Object{ | |
| 168 | .name = name, | |
| 169 | .file = file, | |
| 170 | }; | |
| 171 | ||
| 172 | object.parse(allocator, arch) catch |err| switch (err) { | |
| 173 | error.EndOfStream, error.NotObject => { | |
| 174 | object.deinit(allocator); | |
| 175 | return null; | |
| 176 | }, | |
| 168 | 177 | else => |e| return e, |
| 169 | 178 | }; |
| 170 | try file.seekTo(0); | |
| 171 | return is_object; | |
| 179 | ||
| 180 | return object; | |
| 172 | 181 | } |
| 173 | 182 | |
| 174 | 183 | pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { |
| 175 | var reader = self.file.reader(); | |
| 184 | const reader = self.file.reader(); | |
| 176 | 185 | if (self.file_offset) |offset| { |
| 177 | 186 | try reader.context.seekTo(offset); |
| 178 | 187 | } |
| 188 | ||
| 179 | 189 | const header = try reader.readStruct(macho.mach_header_64); |
| 190 | if (header.filetype != macho.MH_OBJECT) { | |
| 191 | log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ | |
| 192 | macho.MH_OBJECT, | |
| 193 | header.filetype, | |
| 194 | }); | |
| 195 | return error.NotObject; | |
| 196 | } | |
| 197 | ||
| 180 | 198 | const this_arch: Arch = switch (header.cputype) { |
| 181 | 199 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 182 | 200 | macho.CPU_TYPE_X86_64 => .x86_64, |
| ... | ... | @@ -189,6 +207,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { |
| 189 | 207 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); |
| 190 | 208 | return error.MismatchedCpuArchitecture; |
| 191 | 209 | } |
| 210 | ||
| 192 | 211 | self.header = header; |
| 193 | 212 | |
| 194 | 213 | try self.readLoadCommands(allocator, reader); |