Commit c5e509595a

Jakub Konka <kubkon@jakubkonka.com>
2024-01-11 17:34:52
macho: parse archives
1 parent d05e9c3
Changed files (3)
src/link/MachO/Archive.zig
@@ -73,14 +73,20 @@ pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
 }
 
 pub fn deinit(self: *Archive, allocator: Allocator) void {
+    allocator.free(self.data);
+    allocator.free(self.path);
     self.objects.deinit(allocator);
 }
 
-pub fn parse(self: *Archive, arena: Allocator, macho_file: *MachO) !void {
-    const gpa = macho_file.base.allocator;
+pub fn parse(self: *Archive, macho_file: *MachO) !void {
+    const gpa = macho_file.base.comp.gpa;
+
+    var arena = std.heap.ArenaAllocator.init(gpa);
+    defer arena.deinit();
 
     var stream = std.io.fixedBufferStream(self.data);
     const reader = stream.reader();
+    _ = try reader.readBytesNoEof(SARMAG);
 
     while (true) {
         if (stream.pos >= self.data.len) break;
@@ -89,18 +95,18 @@ pub fn parse(self: *Archive, arena: Allocator, macho_file: *MachO) !void {
         const hdr = try reader.readStruct(ar_hdr);
 
         if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
-            macho_file.base.fatal("{s}: invalid header delimiter: expected '{s}', found '{s}'", .{
-                self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
+            try macho_file.reportParseError(self.path, "invalid header delimiter: expected '{s}', found '{s}'", .{
+                std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
             });
-            return error.ParseFailed;
+            return error.MalformedArchive;
         }
 
         var size = try hdr.size();
         const name = name: {
-            if (hdr.name()) |n| break :name try arena.dupe(u8, n);
+            if (hdr.name()) |n| break :name n;
             if (try hdr.nameLength()) |len| {
                 size -= len;
-                const buf = try arena.alloc(u8, len);
+                const buf = try arena.allocator().alloc(u8, len);
                 try reader.readNoEof(buf);
                 const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len;
                 break :name buf[0..actual_len];
@@ -114,9 +120,9 @@ pub fn parse(self: *Archive, arena: Allocator, macho_file: *MachO) !void {
         if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
 
         const object = Object{
-            .archive = self.path,
-            .path = name,
-            .data = self.data[stream.pos..][0..size],
+            .archive = try gpa.dupe(u8, self.path),
+            .path = try gpa.dupe(u8, name),
+            .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
             .index = undefined,
             .alive = false,
             .mtime = hdr.date() catch 0,
src/link/MachO/Object.zig
@@ -56,6 +56,7 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
         sf.stabs.deinit(allocator);
     }
     self.stab_files.deinit(allocator);
+    allocator.free(self.data);
 }
 
 pub fn parse(self: *Object, macho_file: *MachO) !void {
src/link/MachO.zig
@@ -845,11 +845,48 @@ fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
 }
 
 fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Arch) ParseError!void {
-    _ = self;
-    _ = lib;
-    _ = must_link;
-    _ = fat_arch;
-    return error.Unhandled;
+    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));
+
+    var archive = Archive{ .path = try gpa.dupe(u8, lib.path), .data = data };
+    defer archive.deinit(gpa);
+    try archive.parse(self);
+
+    var has_parse_error = false;
+    for (archive.objects.items) |extracted| {
+        const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
+        self.files.set(index, .{ .object = extracted });
+        const object = &self.files.items(.data)[index].object;
+        object.index = index;
+        object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
+        object.hidden = lib.hidden;
+        object.parse(self) catch |err| switch (err) {
+            error.MalformedObject,
+            error.InvalidCpuArch,
+            error.InvalidTarget,
+            => has_parse_error = true,
+            else => |e| return e,
+        };
+        try self.objects.append(gpa, index);
+
+        // Finally, we do a post-parse check for -ObjC to see if we need to force load this member
+        // anyhow.
+        // TODO: object.alive = object.alive or (self.options.force_load_objc and object.hasObjc());
+    }
+    if (has_parse_error) return error.MalformedArchive;
 }
 
 fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!void {