blob 0eb2f2d4 (4436B) - Raw
1 path: []const u8, 2 data: []const u8, 3 4 objects: std.ArrayListUnmanaged(Object) = .{}, 5 strtab: []const u8 = &[0]u8{}, 6 7 // Archive files start with the ARMAG identifying string. Then follows a 8 // `struct ar_hdr', and as many bytes of member file data as its `ar_size' 9 // member indicates, for each member file. 10 /// String that begins an archive file. 11 pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; 12 /// Size of that string. 13 pub const SARMAG: u4 = 8; 14 15 /// String in ar_fmag at the end of each header. 16 const ARFMAG: *const [2:0]u8 = "`\n"; 17 18 const SYM64NAME: *const [7:0]u8 = "/SYM64/"; 19 20 const ar_hdr = extern struct { 21 /// Member file name, sometimes / terminated. 22 ar_name: [16]u8, 23 24 /// File date, decimal seconds since Epoch. 25 ar_date: [12]u8, 26 27 /// User ID, in ASCII format. 28 ar_uid: [6]u8, 29 30 /// Group ID, in ASCII format. 31 ar_gid: [6]u8, 32 33 /// File mode, in ASCII octal. 34 ar_mode: [8]u8, 35 36 /// File size, in ASCII decimal. 37 ar_size: [10]u8, 38 39 /// Always contains ARFMAG. 40 ar_fmag: [2]u8, 41 42 fn date(self: ar_hdr) !u64 { 43 const value = getValue(&self.ar_date); 44 return std.fmt.parseInt(u64, value, 10); 45 } 46 47 fn size(self: ar_hdr) !u32 { 48 const value = getValue(&self.ar_size); 49 return std.fmt.parseInt(u32, value, 10); 50 } 51 52 fn getValue(raw: []const u8) []const u8 { 53 return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); 54 } 55 56 fn isStrtab(self: ar_hdr) bool { 57 return mem.eql(u8, getValue(&self.ar_name), "//"); 58 } 59 60 fn isSymtab(self: ar_hdr) bool { 61 return mem.eql(u8, getValue(&self.ar_name), "/"); 62 } 63 }; 64 65 pub fn isArchive(path: []const u8) !bool { 66 const file = try std.fs.cwd().openFile(path, .{}); 67 defer file.close(); 68 const reader = file.reader(); 69 const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false; 70 if (!mem.eql(u8, &magic, ARMAG)) return false; 71 return true; 72 } 73 74 pub fn deinit(self: *Archive, allocator: Allocator) void { 75 allocator.free(self.path); 76 allocator.free(self.data); 77 self.objects.deinit(allocator); 78 } 79 80 pub fn parse(self: *Archive, elf_file: *Elf) !void { 81 const gpa = elf_file.base.allocator; 82 83 var stream = std.io.fixedBufferStream(self.data); 84 const reader = stream.reader(); 85 _ = try reader.readBytesNoEof(SARMAG); 86 87 while (true) { 88 if (stream.pos % 2 != 0) { 89 stream.pos += 1; 90 } 91 92 const hdr = reader.readStruct(ar_hdr) catch break; 93 94 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { 95 // TODO convert into an error 96 log.debug( 97 "{s}: invalid header delimiter: expected '{s}', found '{s}'", 98 .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) }, 99 ); 100 return; 101 } 102 103 const size = try hdr.size(); 104 defer { 105 _ = stream.seekBy(size) catch {}; 106 } 107 108 if (hdr.isSymtab()) continue; 109 if (hdr.isStrtab()) { 110 self.strtab = self.data[stream.pos..][0..size]; 111 continue; 112 } 113 114 const name = ar_hdr.getValue(&hdr.ar_name); 115 116 if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue; 117 118 const object_name = blk: { 119 if (name[0] == '/') { 120 const off = try std.fmt.parseInt(u32, name[1..], 10); 121 break :blk self.getString(off); 122 } 123 break :blk name; 124 }; 125 126 const object = Object{ 127 .archive = try gpa.dupe(u8, self.path), 128 .path = try gpa.dupe(u8, object_name[0 .. object_name.len - 1]), // To account for trailing '/' 129 .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), 130 .index = undefined, 131 .alive = false, 132 }; 133 134 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path }); 135 136 try self.objects.append(gpa, object); 137 } 138 } 139 140 fn getString(self: Archive, off: u32) []const u8 { 141 assert(off < self.strtab.len); 142 return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); 143 } 144 145 const std = @import("std"); 146 const assert = std.debug.assert; 147 const elf = std.elf; 148 const fs = std.fs; 149 const log = std.log.scoped(.link); 150 const mem = std.mem; 151 152 const Allocator = mem.Allocator; 153 const Archive = @This(); 154 const Elf = @import("../Elf.zig"); 155 const Object = @import("Object.zig");