diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-02-07 12:20:38 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-02-07 19:27:26 +0100 |
| commit | efa1c6124d167b3144c4d4b15ebf384130d35abd (patch) | |
| tree | eb0c4757fa06819b748249b2fee5d598212ddf06 | |
| parent | 897a554109baa3288d575cac0833e10edd1a316c (diff) | |
| download | zig-efa1c6124d167b3144c4d4b15ebf384130d35abd.tar.gz zig-efa1c6124d167b3144c4d4b15ebf384130d35abd.zip | |
macho: emit an archive
| -rw-r--r-- | src/link/MachO.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/Archive.zig | 61 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 7 | ||||
| -rw-r--r-- | src/link/MachO/ZigObject.zig | 5 | ||||
| -rw-r--r-- | src/link/MachO/file.zig | 16 | ||||
| -rw-r--r-- | src/link/MachO/relocatable.zig | 66 |
6 files changed, 118 insertions, 41 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7789c563d1..f19e1c9ea7 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -380,7 +380,7 @@ pub fn deinit(self: *MachO) void { pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { // TODO: I think this is just a temp and can be removed once we can emit static archives - if (self.base.isStaticLib() and build_options.have_llvm) { + if (self.base.isStaticLib() and build_options.have_llvm and self.base.comp.config.use_llvm) { return self.base.linkAsArchive(arena, prog_node); } try self.flushModule(arena, prog_node); @@ -396,7 +396,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node if (self.llvm_object) |llvm_object| { try self.base.emitLlvmObject(arena, llvm_object, prog_node); // TODO: I think this is just a temp and can be removed once we can emit static archives - if (self.base.isStaticLib() and build_options.have_llvm) return; + if (self.base.isStaticLib() and build_options.have_llvm and self.base.comp.config.use_llvm) return; } var sub_prog_node = prog_node.start("MachO Flush", 0); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 2777eb8587..83cf80f05e 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -90,7 +90,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: pub fn writeHeader( object_name: []const u8, - object_size: u32, + object_size: usize, format: Format, writer: anytype, ) !void { @@ -110,7 +110,7 @@ pub fn writeHeader( } @memcpy(&hdr.ar_fmag, ARFMAG); - const object_name_len = mem.alignForward(u32, object_name.len + 1, format.ptrWidth()); + const object_name_len = mem.alignForward(usize, object_name.len + 1, ptrWidth(format)); const total_object_size = object_size + object_name_len; { @@ -142,12 +142,12 @@ pub const SARMAG: u4 = 8; /// String in ar_fmag at the end of each header. const ARFMAG: *const [2:0]u8 = "`\n"; -const SYMDEF = "__.SYMDEF"; -const SYMDEF64 = "__.SYMDEF_64"; -const SYMDEF_SORTED = "__.SYMDEF SORTED"; -const SYMDEF64_SORTED = "__.SYMDEF_64 SORTED"; +pub const SYMDEF = "__.SYMDEF"; +pub const SYMDEF64 = "__.SYMDEF_64"; +pub const SYMDEF_SORTED = "__.SYMDEF SORTED"; +pub const SYMDEF64_SORTED = "__.SYMDEF_64 SORTED"; -const ar_hdr = extern struct { +pub const ar_hdr = extern struct { /// Member file name, sometimes / terminated. ar_name: [16]u8, @@ -197,7 +197,6 @@ const ar_hdr = extern struct { pub const ArSymtab = struct { entries: std.ArrayListUnmanaged(Entry) = .{}, strtab: StringTable = .{}, - format: Format = .p32, pub fn deinit(ar: *ArSymtab, allocator: Allocator) void { ar.entries.deinit(allocator); @@ -208,16 +207,16 @@ pub const ArSymtab = struct { mem.sort(Entry, ar.entries.items, {}, Entry.lessThan); } - pub fn size(ar: ArSymtab) usize { - const ptr_width = ar.format.ptrWidth(); + pub fn size(ar: ArSymtab, format: Format) usize { + const ptr_width = ptrWidth(format); return ptr_width + ar.entries.items.len * 2 * ptr_width + ptr_width + mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width); } - pub fn write(ar: ArSymtab, macho_file: *MachO, writer: anytype) !void { + pub fn write(ar: ArSymtab, format: Format, macho_file: *MachO, writer: anytype) !void { // Header - try writeHeader(SYMDEF, ar.size()); + try writeHeader(SYMDEF, ar.size(format), format, writer); // Symtab size - try ar.writeInt(ar.entries.items.len * 2); + try writeInt(format, ar.entries.items.len * 2, writer); // Symtab entries for (ar.entries.items) |entry| { const file_off = switch (macho_file.getFile(entry.file).?) { @@ -226,14 +225,14 @@ pub const ArSymtab = struct { else => unreachable, }; // Name offset - try ar.writeInt(entry.off); + try writeInt(format, entry.off, writer); // File offset - try ar.writeInt(file_off); + try writeInt(format, file_off, writer); } // Strtab size - const strtab_size = mem.alignForward(u64, ar.strtab.buffer.items.len, ar.format.ptrWidth()); + const strtab_size = mem.alignForward(u64, ar.strtab.buffer.items.len, ptrWidth(format)); const padding = strtab_size - ar.strtab.buffer.items.len; - try ar.writeInt(strtab_size); + try writeInt(format, strtab_size, writer); // Strtab try writer.writeAll(ar.strtab.buffer.items); if (padding > 0) { @@ -241,13 +240,6 @@ pub const ArSymtab = struct { } } - fn writeInt(ar: ArSymtab, value: u64, writer: anytype) !void { - switch (ar.format) { - .p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), - .p64 => try writer.writeInt(u64, value, .little), - } - } - const FormatContext = struct { ar: ArSymtab, macho_file: *MachO, @@ -288,17 +280,24 @@ pub const ArSymtab = struct { }; }; -const Format = enum { +pub const Format = enum { p32, p64, +}; - fn ptrWidth(self: Format) usize { - return switch (self) { - .p32 => @as(usize, 4), - .p64 => 8, - }; +pub fn ptrWidth(format: Format) usize { + return switch (format) { + .p32 => @as(usize, 4), + .p64 => 8, + }; +} + +pub fn writeInt(format: Format, value: u64, writer: anytype) !void { + switch (format) { + .p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), + .p64 => try writer.writeInt(u64, value, .little), } -}; +} pub const ArState = struct { /// File offset of the ar_hdr describing the contributing diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 7e91e04dd2..9d436d0197 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1248,14 +1248,15 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { self.output_ar_state.size = size; } -pub fn writeAr(self: Object, macho_file: *MachO, writer: anytype) !void { +pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { // Header - try Archive.writeHeader(self.path, self.output_ar_state.size, writer); + const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; + try Archive.writeHeader(self.path, size, ar_format, writer); // Data const file = macho_file.getFileHandle(self.file_handle); // TODO try using copyRangeAll const gpa = macho_file.base.comp.gpa; - const data = try file.readToEndAlloc(gpa, self.output_ar_state.size); + const data = try file.readToEndAlloc(gpa, size); defer gpa.free(data); try writer.writeAll(data); } diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 65ae3788ad..1017203bbe 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -314,9 +314,10 @@ pub fn updateArSize(self: *ZigObject) void { self.output_ar_state.size = self.data.items.len; } -pub fn writeAr(self: ZigObject, writer: anytype) !void { +pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void { // Header - try Archive.writeHeader(self.path, self.output_ar_state.size, writer); + const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; + try Archive.writeHeader(self.path, size, ar_format, writer); // Data try writer.writeAll(self.data.items); } diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index d6e21c2f38..60c7a70f26 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -182,6 +182,22 @@ pub const File = union(enum) { }; } + pub fn updateArSize(file: File, macho_file: *MachO) !void { + return switch (file) { + .dylib, .internal => unreachable, + .zig_object => |x| x.updateArSize(), + .object => |x| x.updateArSize(macho_file), + }; + } + + pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { + return switch (file) { + .dylib, .internal => unreachable, + .zig_object => |x| x.writeAr(ar_format, writer), + .object => |x| x.writeAr(ar_format, macho_file, writer), + }; + } + pub fn calcSymtabSize(file: File, macho_file: *MachO) !void { return switch (file) { inline else => |x| x.calcSymtabSize(macho_file), diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index f90988c32e..d7d4469f7f 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -161,6 +161,9 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index); for (macho_file.objects.items) |index| files.appendAssumeCapacity(index); + const format: Archive.Format = .p32; + const ptr_width = Archive.ptrWidth(format); + // Update ar symtab from parsed objects var ar_symtab: Archive.ArSymtab = .{}; defer ar_symtab.deinit(gpa); @@ -171,14 +174,71 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? ar_symtab.sort(); + // Update sizes of contributing objects + for (files.items) |index| { + try macho_file.getFile(index).?.updateArSize(macho_file); + } + + // Update file offsets of contributing objects + const total_size: usize = blk: { + var pos: usize = Archive.SARMAG; + pos += @sizeOf(Archive.ar_hdr) + Archive.SYMDEF.len + 1; + pos = mem.alignForward(usize, pos, ptr_width); + pos += ar_symtab.size(format); + + for (files.items) |index| { + const file = macho_file.getFile(index).?; + const state = switch (file) { + .zig_object => |x| &x.output_ar_state, + .object => |x| &x.output_ar_state, + else => unreachable, + }; + const path = switch (file) { + .zig_object => |x| x.path, + .object => |x| x.path, + else => unreachable, + }; + pos = mem.alignForward(usize, pos, ptr_width); + state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr) + path.len + 1; + pos = mem.alignForward(usize, pos, ptr_width); + pos += math.cast(usize, state.size) orelse return error.Overflow; + } + + break :blk pos; + }; + if (build_options.enable_logging) { state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(macho_file)}); } - var err = try macho_file.addErrorWithNotes(0); - try err.addMsg(macho_file, "TODO implement flushStaticLib", .{}); + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + try buffer.ensureTotalCapacityPrecise(total_size); + const writer = buffer.writer(); + + // Write magic + try writer.writeAll(Archive.ARMAG); - return error.FlushFailure; + // Write symtab + try ar_symtab.write(format, macho_file, writer); + + // Write object files + for (files.items) |index| { + const aligned = mem.alignForward(usize, buffer.items.len, ptr_width); + const padding = aligned - buffer.items.len; + if (padding > 0) { + try writer.writeByteNTimes(0, padding); + } + try macho_file.getFile(index).?.writeAr(format, macho_file, writer); + } + + assert(buffer.items.len == total_size); + + try macho_file.base.file.?.setEndPos(total_size); + try macho_file.base.file.?.pwriteAll(buffer.items, 0); + + if (comp.link_errors.items.len > 0) return error.FlushFailure; } fn markExports(macho_file: *MachO) void { |
