Commit d153bc2f0c

Jakub Konka <kubkon@jakubkonka.com>
2024-01-11 17:53:58
macho: parse dylibs
1 parent c5e5095
Changed files (2)
src
src/link/MachO/Dylib.zig
@@ -34,6 +34,8 @@ pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
 }
 
 pub fn deinit(self: *Dylib, allocator: Allocator) void {
+    allocator.free(self.data);
+    allocator.free(self.path);
     self.exports.deinit(allocator);
     self.strtab.deinit(allocator);
     if (self.id) |*id| id.deinit(allocator);
@@ -49,7 +51,7 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const gpa = macho_file.base.allocator;
+    const gpa = macho_file.base.comp.gpa;
     var stream = std.io.fixedBufferStream(self.data);
     const reader = stream.reader();
 
@@ -57,9 +59,22 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
 
     self.header = try reader.readStruct(macho.mach_header_64);
 
+    const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
+        macho.CPU_TYPE_ARM64 => .aarch64,
+        macho.CPU_TYPE_X86_64 => .x86_64,
+        else => |x| {
+            try macho_file.reportParseError2(self.index, "unknown cpu architecture: {d}", .{x});
+            return error.InvalidCpuArch;
+        },
+    };
+    if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
+        try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
+        return error.InvalidCpuArch;
+    }
+
     const lc_id = self.getLoadCommand(.ID_DYLIB) orelse {
-        macho_file.base.fatal("{s}: missing LC_ID_DYLIB load command", .{self.path});
-        return error.ParseFailed;
+        try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{});
+        return error.MalformedDylib;
     };
     self.id = try Id.fromLoadCommand(gpa, lc_id.cast(macho.dylib_command).?, lc_id.getDylibPathName());
 
@@ -90,6 +105,23 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
     };
 
     self.initPlatform();
+
+    if (self.platform) |platform| {
+        if (!macho_file.platform.eqlTarget(platform)) {
+            try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
+                platform.fmtTarget(macho_file.getTarget().cpu.arch),
+            });
+            return error.InvalidTarget;
+        }
+        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;
+        }
+    }
 }
 
 const TrieIterator = struct {
@@ -187,7 +219,7 @@ fn parseTrieNode(
 fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
-    const gpa = macho_file.base.allocator;
+    const gpa = macho_file.base.comp.gpa;
     var arena = std.heap.ArenaAllocator.init(gpa);
     defer arena.deinit();
 
@@ -204,7 +236,7 @@ pub fn parseTbd(
 ) !void {
     const tracy = trace(@src());
     defer tracy.end();
-    const gpa = macho_file.base.allocator;
+    const gpa = macho_file.base.comp.gpa;
 
     log.debug("parsing dylib from stub", .{});
 
@@ -435,7 +467,7 @@ fn addObjCExport(
 }
 
 pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
-    const gpa = macho_file.base.allocator;
+    const gpa = macho_file.base.comp.gpa;
 
     try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);
 
@@ -459,7 +491,7 @@ fn initPlatform(self: *Dylib) void {
             .VERSION_MIN_IPHONEOS,
             .VERSION_MIN_TVOS,
             .VERSION_MIN_WATCHOS,
-            => break MachO.Options.Platform.fromLoadCommand(cmd),
+            => break MachO.Platform.fromLoadCommand(cmd),
             else => {},
         }
     } else null;
src/link/MachO.zig
@@ -382,6 +382,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
         self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
             error.MalformedObject,
             error.MalformedArchive,
+            error.MalformedDylib,
             error.InvalidCpuArch,
             error.InvalidTarget,
             error.UnknownFileType,
@@ -432,8 +433,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
 
     for (system_libs.items) |lib| {
         self.parseLibrary(lib, false) catch |err| switch (err) {
-            error.MalformedDylib,
             error.MalformedArchive,
+            error.MalformedDylib,
             error.InvalidCpuArch,
             error.UnknownFileType,
             => continue, // already reported
@@ -794,7 +795,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
         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);
+            _ = try self.parseDylib(lib, true, fat_arch);
         } else {
             try self.reportParseError(lib.path, "unknown file type for a library", .{});
             return error.UnknownFileType;
@@ -802,7 +803,7 @@ fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
     } 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);
+        _ = try self.parseDylib(lib, true, null);
     } else {
         try self.parseTbd(lib, true);
     }
@@ -889,12 +890,39 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar
     if (has_parse_error) return error.MalformedArchive;
 }
 
-fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!void {
-    _ = self;
-    _ = lib;
-    _ = explicit;
-    _ = fat_arch;
-    return error.Unhandled;
+fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!File.Index {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const gpa = self.base.comp.gpa;
+
+    const file = try std.fs.cwd().openFile(lib.path, .{});
+    defer file.close();
+
+    const data = if (fat_arch) |arch| blk: {
+        try file.seekTo(arch.offset);
+        const data = try gpa.alloc(u8, arch.size);
+        const nread = try file.readAll(data);
+        if (nread != arch.size) return error.InputOutput;
+        break :blk data;
+    } else try file.readToEndAlloc(gpa, std.math.maxInt(u32));
+
+    const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
+    self.files.set(index, .{ .dylib = .{
+        .path = try gpa.dupe(u8, lib.path),
+        .data = data,
+        .index = index,
+        .needed = lib.needed,
+        .weak = lib.weak,
+        .reexport = lib.reexport,
+        .explicit = explicit,
+    } });
+    const dylib = &self.files.items(.data)[index].dylib;
+    try dylib.parse(self);
+
+    try self.dylibs.append(gpa, index);
+
+    return index;
 }
 
 fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!void {