| ... | ... | @@ -1,63 +1,5 @@ |
| 1 | 1 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 2 | 2 | |
| 3 | | // Archive files start with the ARMAG identifying string. Then follows a |
| 4 | | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 5 | | // member indicates, for each member file. |
| 6 | | /// String that begins an archive file. |
| 7 | | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; |
| 8 | | /// Size of that string. |
| 9 | | pub const SARMAG: u4 = 8; |
| 10 | | |
| 11 | | /// String in ar_fmag at the end of each header. |
| 12 | | const ARFMAG: *const [2:0]u8 = "`\n"; |
| 13 | | |
| 14 | | const ar_hdr = extern struct { |
| 15 | | /// Member file name, sometimes / terminated. |
| 16 | | ar_name: [16]u8, |
| 17 | | |
| 18 | | /// File date, decimal seconds since Epoch. |
| 19 | | ar_date: [12]u8, |
| 20 | | |
| 21 | | /// User ID, in ASCII format. |
| 22 | | ar_uid: [6]u8, |
| 23 | | |
| 24 | | /// Group ID, in ASCII format. |
| 25 | | ar_gid: [6]u8, |
| 26 | | |
| 27 | | /// File mode, in ASCII octal. |
| 28 | | ar_mode: [8]u8, |
| 29 | | |
| 30 | | /// File size, in ASCII decimal. |
| 31 | | ar_size: [10]u8, |
| 32 | | |
| 33 | | /// Always contains ARFMAG. |
| 34 | | ar_fmag: [2]u8, |
| 35 | | |
| 36 | | fn date(self: ar_hdr) !u64 { |
| 37 | | const value = mem.trimRight(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)}); |
| 38 | | return std.fmt.parseInt(u64, value, 10); |
| 39 | | } |
| 40 | | |
| 41 | | fn size(self: ar_hdr) !u32 { |
| 42 | | const value = mem.trimRight(u8, &self.ar_size, &[_]u8{@as(u8, 0x20)}); |
| 43 | | return std.fmt.parseInt(u32, value, 10); |
| 44 | | } |
| 45 | | |
| 46 | | fn name(self: *const ar_hdr) ?[]const u8 { |
| 47 | | const value = &self.ar_name; |
| 48 | | if (mem.startsWith(u8, value, "#1/")) return null; |
| 49 | | const sentinel = mem.indexOfScalar(u8, value, '/') orelse value.len; |
| 50 | | return value[0..sentinel]; |
| 51 | | } |
| 52 | | |
| 53 | | fn nameLength(self: ar_hdr) !?u32 { |
| 54 | | const value = &self.ar_name; |
| 55 | | if (!mem.startsWith(u8, value, "#1/")) return null; |
| 56 | | const trimmed = mem.trimRight(u8, self.ar_name["#1/".len..], &[_]u8{0x20}); |
| 57 | | return try std.fmt.parseInt(u32, trimmed, 10); |
| 58 | | } |
| 59 | | }; |
| 60 | | |
| 61 | 3 | pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool { |
| 62 | 4 | const file = try std.fs.cwd().openFile(path, .{}); |
| 63 | 5 | defer file.close(); |
| ... | ... | @@ -85,9 +27,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: |
| 85 | 27 | try handle.seekTo(offset); |
| 86 | 28 | |
| 87 | 29 | const reader = handle.reader(); |
| 88 | | _ = try reader.readBytesNoEof(Archive.SARMAG); |
| 30 | _ = try reader.readBytesNoEof(SARMAG); |
| 89 | 31 | |
| 90 | | var pos: usize = Archive.SARMAG; |
| 32 | var pos: usize = SARMAG; |
| 91 | 33 | while (true) { |
| 92 | 34 | if (pos >= size) break; |
| 93 | 35 | if (!mem.isAligned(pos, 2)) { |
| ... | ... | @@ -123,7 +65,10 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: |
| 123 | 65 | pos += hdr_size; |
| 124 | 66 | } |
| 125 | 67 | |
| 126 | | if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue; |
| 68 | if (mem.eql(u8, name, SYMDEF) or |
| 69 | mem.eql(u8, name, SYMDEF64) or |
| 70 | mem.eql(u8, name, SYMDEF_SORTED) or |
| 71 | mem.eql(u8, name, SYMDEF64_SORTED)) continue; |
| 127 | 72 | |
| 128 | 73 | const object = Object{ |
| 129 | 74 | .archive = .{ |
| ... | ... | @@ -143,6 +88,227 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: |
| 143 | 88 | } |
| 144 | 89 | } |
| 145 | 90 | |
| 91 | pub fn writeHeader( |
| 92 | object_name: []const u8, |
| 93 | object_size: u32, |
| 94 | format: Format, |
| 95 | writer: anytype, |
| 96 | ) !void { |
| 97 | var hdr: ar_hdr = .{ |
| 98 | .ar_name = undefined, |
| 99 | .ar_date = undefined, |
| 100 | .ar_uid = undefined, |
| 101 | .ar_gid = undefined, |
| 102 | .ar_mode = undefined, |
| 103 | .ar_size = undefined, |
| 104 | .ar_fmag = undefined, |
| 105 | }; |
| 106 | @memset(mem.asBytes(&hdr), 0x20); |
| 107 | inline for (@typeInfo(ar_hdr).Struct.fields) |field| { |
| 108 | var stream = std.io.fixedBufferStream(&@field(hdr, field.name)); |
| 109 | stream.writer().print("0", .{}) catch unreachable; |
| 110 | } |
| 111 | @memcpy(&hdr.ar_fmag, ARFMAG); |
| 112 | |
| 113 | const object_name_len = mem.alignForward(u32, object_name.len + 1, format.ptrWidth()); |
| 114 | const total_object_size = object_size + object_name_len; |
| 115 | |
| 116 | { |
| 117 | var stream = std.io.fixedBufferStream(&hdr.ar_name); |
| 118 | stream.writer().print("#1/{d}", .{object_name_len}) catch unreachable; |
| 119 | } |
| 120 | { |
| 121 | var stream = std.io.fixedBufferStream(&hdr.ar_size); |
| 122 | stream.writer().print("{d}", .{total_object_size}) catch unreachable; |
| 123 | } |
| 124 | |
| 125 | try writer.writeAll(mem.asBytes(&hdr)); |
| 126 | try writer.print("{s}\x00", .{object_name}); |
| 127 | |
| 128 | const padding = object_name_len - object_name.len - 1; |
| 129 | if (padding > 0) { |
| 130 | try writer.writeByteNTimes(0, padding); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | // Archive files start with the ARMAG identifying string. Then follows a |
| 135 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 136 | // member indicates, for each member file. |
| 137 | /// String that begins an archive file. |
| 138 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; |
| 139 | /// Size of that string. |
| 140 | pub const SARMAG: u4 = 8; |
| 141 | |
| 142 | /// String in ar_fmag at the end of each header. |
| 143 | const ARFMAG: *const [2:0]u8 = "`\n"; |
| 144 | |
| 145 | const SYMDEF = "__.SYMDEF"; |
| 146 | const SYMDEF64 = "__.SYMDEF_64"; |
| 147 | const SYMDEF_SORTED = "__.SYMDEF SORTED"; |
| 148 | const SYMDEF64_SORTED = "__.SYMDEF_64 SORTED"; |
| 149 | |
| 150 | const ar_hdr = extern struct { |
| 151 | /// Member file name, sometimes / terminated. |
| 152 | ar_name: [16]u8, |
| 153 | |
| 154 | /// File date, decimal seconds since Epoch. |
| 155 | ar_date: [12]u8, |
| 156 | |
| 157 | /// User ID, in ASCII format. |
| 158 | ar_uid: [6]u8, |
| 159 | |
| 160 | /// Group ID, in ASCII format. |
| 161 | ar_gid: [6]u8, |
| 162 | |
| 163 | /// File mode, in ASCII octal. |
| 164 | ar_mode: [8]u8, |
| 165 | |
| 166 | /// File size, in ASCII decimal. |
| 167 | ar_size: [10]u8, |
| 168 | |
| 169 | /// Always contains ARFMAG. |
| 170 | ar_fmag: [2]u8, |
| 171 | |
| 172 | fn date(self: ar_hdr) !u64 { |
| 173 | const value = mem.trimRight(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)}); |
| 174 | return std.fmt.parseInt(u64, value, 10); |
| 175 | } |
| 176 | |
| 177 | fn size(self: ar_hdr) !u32 { |
| 178 | const value = mem.trimRight(u8, &self.ar_size, &[_]u8{@as(u8, 0x20)}); |
| 179 | return std.fmt.parseInt(u32, value, 10); |
| 180 | } |
| 181 | |
| 182 | fn name(self: *const ar_hdr) ?[]const u8 { |
| 183 | const value = &self.ar_name; |
| 184 | if (mem.startsWith(u8, value, "#1/")) return null; |
| 185 | const sentinel = mem.indexOfScalar(u8, value, '/') orelse value.len; |
| 186 | return value[0..sentinel]; |
| 187 | } |
| 188 | |
| 189 | fn nameLength(self: ar_hdr) !?u32 { |
| 190 | const value = &self.ar_name; |
| 191 | if (!mem.startsWith(u8, value, "#1/")) return null; |
| 192 | const trimmed = mem.trimRight(u8, self.ar_name["#1/".len..], &[_]u8{0x20}); |
| 193 | return try std.fmt.parseInt(u32, trimmed, 10); |
| 194 | } |
| 195 | }; |
| 196 | |
| 197 | pub const ArSymtab = struct { |
| 198 | entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 199 | strtab: StringTable = .{}, |
| 200 | format: Format = .p32, |
| 201 | |
| 202 | pub fn deinit(ar: *ArSymtab, allocator: Allocator) void { |
| 203 | ar.entries.deinit(allocator); |
| 204 | ar.strtab.deinit(allocator); |
| 205 | } |
| 206 | |
| 207 | pub fn sort(ar: *ArSymtab) void { |
| 208 | mem.sort(Entry, ar.entries.items, {}, Entry.lessThan); |
| 209 | } |
| 210 | |
| 211 | pub fn size(ar: ArSymtab) usize { |
| 212 | const ptr_width = ar.format.ptrWidth(); |
| 213 | return ptr_width + ar.entries.items.len * 2 * ptr_width + ptr_width + mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width); |
| 214 | } |
| 215 | |
| 216 | pub fn write(ar: ArSymtab, macho_file: *MachO, writer: anytype) !void { |
| 217 | // Header |
| 218 | try writeHeader(SYMDEF, ar.size()); |
| 219 | // Symtab size |
| 220 | try ar.writeInt(ar.entries.items.len * 2); |
| 221 | // Symtab entries |
| 222 | for (ar.entries.items) |entry| { |
| 223 | const file_off = switch (macho_file.getFile(entry.file).?) { |
| 224 | .zig_object => |x| x.output_ar_state.file_off, |
| 225 | .object => |x| x.output_ar_state.file_off, |
| 226 | else => unreachable, |
| 227 | }; |
| 228 | // Name offset |
| 229 | try ar.writeInt(entry.off); |
| 230 | // File offset |
| 231 | try ar.writeInt(file_off); |
| 232 | } |
| 233 | // Strtab size |
| 234 | const strtab_size = mem.alignForward(u64, ar.strtab.buffer.items.len, ar.format.ptrWidth()); |
| 235 | const padding = strtab_size - ar.strtab.buffer.items.len; |
| 236 | try ar.writeInt(strtab_size); |
| 237 | // Strtab |
| 238 | try writer.writeAll(ar.strtab.buffer.items); |
| 239 | if (padding > 0) { |
| 240 | try writer.writeByteNTimes(0, padding); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | fn writeInt(ar: ArSymtab, value: u64, writer: anytype) !void { |
| 245 | switch (ar.format) { |
| 246 | .p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), |
| 247 | .p64 => try writer.writeInt(u64, value, .little), |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | const FormatContext = struct { |
| 252 | ar: ArSymtab, |
| 253 | macho_file: *MachO, |
| 254 | }; |
| 255 | |
| 256 | pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(format2) { |
| 257 | return .{ .data = .{ .ar = ar, .macho_file = macho_file } }; |
| 258 | } |
| 259 | |
| 260 | fn format2( |
| 261 | ctx: FormatContext, |
| 262 | comptime unused_fmt_string: []const u8, |
| 263 | options: std.fmt.FormatOptions, |
| 264 | writer: anytype, |
| 265 | ) !void { |
| 266 | _ = unused_fmt_string; |
| 267 | _ = options; |
| 268 | const ar = ctx.ar; |
| 269 | const macho_file = ctx.macho_file; |
| 270 | for (ar.entries.items, 0..) |entry, i| { |
| 271 | const name = ar.strtab.getAssumeExists(entry.off); |
| 272 | const file = macho_file.getFile(entry.file).?; |
| 273 | try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file, file.fmtPath() }); |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | const Entry = struct { |
| 278 | /// Symbol name offset |
| 279 | off: u32, |
| 280 | /// Exporting file |
| 281 | file: File.Index, |
| 282 | |
| 283 | pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool { |
| 284 | _ = ctx; |
| 285 | if (lhs.off == rhs.off) return lhs.file < rhs.file; |
| 286 | return lhs.off < rhs.off; |
| 287 | } |
| 288 | }; |
| 289 | }; |
| 290 | |
| 291 | const Format = enum { |
| 292 | p32, |
| 293 | p64, |
| 294 | |
| 295 | fn ptrWidth(self: Format) usize { |
| 296 | return switch (self) { |
| 297 | .p32 => @as(usize, 4), |
| 298 | .p64 => 8, |
| 299 | }; |
| 300 | } |
| 301 | }; |
| 302 | |
| 303 | pub const ArState = struct { |
| 304 | /// File offset of the ar_hdr describing the contributing |
| 305 | /// object in the archive. |
| 306 | file_off: u64 = 0, |
| 307 | |
| 308 | /// Total size of the contributing object (excludes ar_hdr and long name with padding). |
| 309 | size: u64 = 0, |
| 310 | }; |
| 311 | |
| 146 | 312 | const fat = @import("fat.zig"); |
| 147 | 313 | const link = @import("../../link.zig"); |
| 148 | 314 | const log = std.log.scoped(.link); |
| ... | ... | @@ -155,3 +321,4 @@ const Archive = @This(); |
| 155 | 321 | const File = @import("file.zig").File; |
| 156 | 322 | const MachO = @import("../MachO.zig"); |
| 157 | 323 | const Object = @import("Object.zig"); |
| 324 | const StringTable = @import("../StringTable.zig"); |