authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-11 17:53:58+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:38+01:00
logd153bc2f0c01af7027f09dd6d42786c4b4d076ed
tree5cc115407b6456f07e6f8e09929d11a05e63fc5f
parentc5e509595a267ddec7d49a9d89ab2bc4f86a9d35

macho: parse dylibs


2 files changed, 76 insertions(+), 16 deletions(-)

src/link/MachO.zig+37-9
...@@ -382,6 +382,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -382,6 +382,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
382 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {382 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
383 error.MalformedObject,383 error.MalformedObject,
384 error.MalformedArchive,384 error.MalformedArchive,
385 error.MalformedDylib,
385 error.InvalidCpuArch,386 error.InvalidCpuArch,
386 error.InvalidTarget,387 error.InvalidTarget,
387 error.UnknownFileType,388 error.UnknownFileType,
...@@ -432,8 +433,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -432,8 +433,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
432433
433 for (system_libs.items) |lib| {434 for (system_libs.items) |lib| {
434 self.parseLibrary(lib, false) catch |err| switch (err) {435 self.parseLibrary(lib, false) catch |err| switch (err) {
435 error.MalformedDylib,
436 error.MalformedArchive,436 error.MalformedArchive,
437 error.MalformedDylib,
437 error.InvalidCpuArch,438 error.InvalidCpuArch,
438 error.UnknownFileType,439 error.UnknownFileType,
439 => continue, // already reported440 => continue, // already reported
...@@ -794,7 +795,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {...@@ -794,7 +795,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
794 if (try Archive.isArchive(lib.path, fat_arch)) {795 if (try Archive.isArchive(lib.path, fat_arch)) {
795 try self.parseArchive(lib, must_link, fat_arch);796 try self.parseArchive(lib, must_link, fat_arch);
796 } else if (try Dylib.isDylib(lib.path, fat_arch)) {797 } else if (try Dylib.isDylib(lib.path, fat_arch)) {
797 try self.parseDylib(lib, true, fat_arch);798 _ = try self.parseDylib(lib, true, fat_arch);
798 } else {799 } else {
799 try self.reportParseError(lib.path, "unknown file type for a library", .{});800 try self.reportParseError(lib.path, "unknown file type for a library", .{});
800 return error.UnknownFileType;801 return error.UnknownFileType;
...@@ -802,7 +803,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {...@@ -802,7 +803,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
802 } else if (try Archive.isArchive(lib.path, null)) {803 } else if (try Archive.isArchive(lib.path, null)) {
803 try self.parseArchive(lib, must_link, null);804 try self.parseArchive(lib, must_link, null);
804 } else if (try Dylib.isDylib(lib.path, null)) {805 } else if (try Dylib.isDylib(lib.path, null)) {
805 try self.parseDylib(lib, true, null);806 _ = try self.parseDylib(lib, true, null);
806 } else {807 } else {
807 try self.parseTbd(lib, true);808 try self.parseTbd(lib, true);
808 }809 }
...@@ -889,12 +890,39 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar...@@ -889,12 +890,39 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar
889 if (has_parse_error) return error.MalformedArchive;890 if (has_parse_error) return error.MalformedArchive;
890}891}
891892
892fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!void {893fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!File.Index {
893 _ = self;894 const tracy = trace(@src());
894 _ = lib;895 defer tracy.end();
895 _ = explicit;896
896 _ = fat_arch;897 const gpa = self.base.comp.gpa;
897 return error.Unhandled;898
899 const file = try std.fs.cwd().openFile(lib.path, .{});
900 defer file.close();
901
902 const data = if (fat_arch) |arch| blk: {
903 try file.seekTo(arch.offset);
904 const data = try gpa.alloc(u8, arch.size);
905 const nread = try file.readAll(data);
906 if (nread != arch.size) return error.InputOutput;
907 break :blk data;
908 } else try file.readToEndAlloc(gpa, std.math.maxInt(u32));
909
910 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
911 self.files.set(index, .{ .dylib = .{
912 .path = try gpa.dupe(u8, lib.path),
913 .data = data,
914 .index = index,
915 .needed = lib.needed,
916 .weak = lib.weak,
917 .reexport = lib.reexport,
918 .explicit = explicit,
919 } });
920 const dylib = &self.files.items(.data)[index].dylib;
921 try dylib.parse(self);
922
923 try self.dylibs.append(gpa, index);
924
925 return index;
898}926}
899927
900fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!void {928fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!void {
src/link/MachO/Dylib.zig+39-7
...@@ -34,6 +34,8 @@ pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {...@@ -34,6 +34,8 @@ pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
34}34}
3535
36pub fn deinit(self: *Dylib, allocator: Allocator) void {36pub fn deinit(self: *Dylib, allocator: Allocator) void {
37 allocator.free(self.data);
38 allocator.free(self.path);
37 self.exports.deinit(allocator);39 self.exports.deinit(allocator);
38 self.strtab.deinit(allocator);40 self.strtab.deinit(allocator);
39 if (self.id) |*id| id.deinit(allocator);41 if (self.id) |*id| id.deinit(allocator);
...@@ -49,7 +51,7 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {...@@ -49,7 +51,7 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
49 const tracy = trace(@src());51 const tracy = trace(@src());
50 defer tracy.end();52 defer tracy.end();
5153
52 const gpa = macho_file.base.allocator;54 const gpa = macho_file.base.comp.gpa;
53 var stream = std.io.fixedBufferStream(self.data);55 var stream = std.io.fixedBufferStream(self.data);
54 const reader = stream.reader();56 const reader = stream.reader();
5557
...@@ -57,9 +59,22 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {...@@ -57,9 +59,22 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
5759
58 self.header = try reader.readStruct(macho.mach_header_64);60 self.header = try reader.readStruct(macho.mach_header_64);
5961
62 const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
63 macho.CPU_TYPE_ARM64 => .aarch64,
64 macho.CPU_TYPE_X86_64 => .x86_64,
65 else => |x| {
66 try macho_file.reportParseError2(self.index, "unknown cpu architecture: {d}", .{x});
67 return error.InvalidCpuArch;
68 },
69 };
70 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
71 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
72 return error.InvalidCpuArch;
73 }
74
60 const lc_id = self.getLoadCommand(.ID_DYLIB) orelse {75 const lc_id = self.getLoadCommand(.ID_DYLIB) orelse {
61 macho_file.base.fatal("{s}: missing LC_ID_DYLIB load command", .{self.path});76 try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{});
62 return error.ParseFailed;77 return error.MalformedDylib;
63 };78 };
64 self.id = try Id.fromLoadCommand(gpa, lc_id.cast(macho.dylib_command).?, lc_id.getDylibPathName());79 self.id = try Id.fromLoadCommand(gpa, lc_id.cast(macho.dylib_command).?, lc_id.getDylibPathName());
6580
...@@ -90,6 +105,23 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {...@@ -90,6 +105,23 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
90 };105 };
91106
92 self.initPlatform();107 self.initPlatform();
108
109 if (self.platform) |platform| {
110 if (!macho_file.platform.eqlTarget(platform)) {
111 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
112 platform.fmtTarget(macho_file.getTarget().cpu.arch),
113 });
114 return error.InvalidTarget;
115 }
116 if (macho_file.platform.version.order(platform.version) == .lt) {
117 try macho_file.reportParseError2(self.index, "object file built for newer platform: {}: {} < {}", .{
118 macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch),
119 macho_file.platform.version,
120 platform.version,
121 });
122 return error.InvalidTarget;
123 }
124 }
93}125}
94126
95const TrieIterator = struct {127const TrieIterator = struct {
...@@ -187,7 +219,7 @@ fn parseTrieNode(...@@ -187,7 +219,7 @@ fn parseTrieNode(
187fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {219fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
188 const tracy = trace(@src());220 const tracy = trace(@src());
189 defer tracy.end();221 defer tracy.end();
190 const gpa = macho_file.base.allocator;222 const gpa = macho_file.base.comp.gpa;
191 var arena = std.heap.ArenaAllocator.init(gpa);223 var arena = std.heap.ArenaAllocator.init(gpa);
192 defer arena.deinit();224 defer arena.deinit();
193225
...@@ -204,7 +236,7 @@ pub fn parseTbd(...@@ -204,7 +236,7 @@ pub fn parseTbd(
204) !void {236) !void {
205 const tracy = trace(@src());237 const tracy = trace(@src());
206 defer tracy.end();238 defer tracy.end();
207 const gpa = macho_file.base.allocator;239 const gpa = macho_file.base.comp.gpa;
208240
209 log.debug("parsing dylib from stub", .{});241 log.debug("parsing dylib from stub", .{});
210242
...@@ -435,7 +467,7 @@ fn addObjCExport(...@@ -435,7 +467,7 @@ fn addObjCExport(
435}467}
436468
437pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {469pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
438 const gpa = macho_file.base.allocator;470 const gpa = macho_file.base.comp.gpa;
439471
440 try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);472 try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);
441473
...@@ -459,7 +491,7 @@ fn initPlatform(self: *Dylib) void {...@@ -459,7 +491,7 @@ fn initPlatform(self: *Dylib) void {
459 .VERSION_MIN_IPHONEOS,491 .VERSION_MIN_IPHONEOS,
460 .VERSION_MIN_TVOS,492 .VERSION_MIN_TVOS,
461 .VERSION_MIN_WATCHOS,493 .VERSION_MIN_WATCHOS,
462 => break MachO.Options.Platform.fromLoadCommand(cmd),494 => break MachO.Platform.fromLoadCommand(cmd),
463 else => {},495 else => {},
464 }496 }
465 } else null;497 } else null;