master
1objects: []const Object,
2/// '\n'-delimited
3strtab: []const u8,
4
5pub fn deinit(a: *Archive, gpa: Allocator) void {
6 gpa.free(a.objects);
7 gpa.free(a.strtab);
8 a.* = undefined;
9}
10
11pub fn parse(
12 gpa: Allocator,
13 diags: *Diags,
14 file_handles: *const std.ArrayList(File.Handle),
15 path: Path,
16 handle_index: File.HandleIndex,
17) !Archive {
18 const handle = file_handles.items[handle_index];
19 var pos: usize = 0;
20 {
21 var magic_buffer: [elf.ARMAG.len]u8 = undefined;
22 const n = try handle.preadAll(&magic_buffer, pos);
23 if (n != magic_buffer.len) return error.BadMagic;
24 if (!mem.eql(u8, &magic_buffer, elf.ARMAG)) return error.BadMagic;
25 pos += magic_buffer.len;
26 }
27
28 const size = (try handle.stat()).size;
29
30 var objects: std.ArrayList(Object) = .empty;
31 defer objects.deinit(gpa);
32
33 var strtab: std.ArrayList(u8) = .empty;
34 defer strtab.deinit(gpa);
35
36 while (pos < size) {
37 var hdr: elf.ar_hdr = undefined;
38 {
39 const n = try handle.preadAll(mem.asBytes(&hdr), pos);
40 if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile;
41 }
42 pos += @sizeOf(elf.ar_hdr);
43
44 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
45 return diags.failParse(path, "invalid archive header delimiter: {f}", .{
46 std.ascii.hexEscape(&hdr.ar_fmag, .lower),
47 });
48 }
49
50 const obj_size = try hdr.size();
51 defer pos = std.mem.alignForward(usize, pos + obj_size, 2);
52
53 if (hdr.isSymtab() or hdr.isSymtab64()) continue;
54 if (hdr.isStrtab()) {
55 try strtab.resize(gpa, obj_size);
56 const amt = try handle.preadAll(strtab.items, pos);
57 if (amt != obj_size) return error.InputOutput;
58 continue;
59 }
60 if (hdr.isSymdef() or hdr.isSymdefSorted()) continue;
61
62 const name = if (hdr.name()) |name|
63 name
64 else if (try hdr.nameOffset()) |off|
65 stringTableLookup(strtab.items, off)
66 else
67 unreachable;
68
69 const object: Object = .{
70 .archive = .{
71 .path = .{
72 .root_dir = path.root_dir,
73 .sub_path = try gpa.dupe(u8, path.sub_path),
74 },
75 .offset = pos,
76 .size = obj_size,
77 },
78 .path = Path.initCwd(try gpa.dupe(u8, name)),
79 .file_handle = handle_index,
80 .index = undefined,
81 .alive = false,
82 };
83
84 log.debug("extracting object '{f}' from archive '{f}'", .{
85 @as(Path, object.path), @as(Path, path),
86 });
87
88 try objects.append(gpa, object);
89 }
90
91 return .{
92 .objects = try objects.toOwnedSlice(gpa),
93 .strtab = try strtab.toOwnedSlice(gpa),
94 };
95}
96
97pub fn stringTableLookup(strtab: []const u8, off: u32) [:'\n']const u8 {
98 const slice = strtab[off..];
99 return slice[0..mem.indexOfScalar(u8, slice, '\n').? :'\n'];
100}
101
102pub fn setArHdr(opts: struct {
103 name: union(enum) {
104 symtab: void,
105 strtab: void,
106 name: []const u8,
107 name_off: u32,
108 },
109 size: usize,
110}) elf.ar_hdr {
111 var hdr: elf.ar_hdr = .{
112 .ar_name = undefined,
113 .ar_date = undefined,
114 .ar_uid = undefined,
115 .ar_gid = undefined,
116 .ar_mode = undefined,
117 .ar_size = undefined,
118 .ar_fmag = undefined,
119 };
120 @memset(mem.asBytes(&hdr), 0x20);
121
122 {
123 var writer: std.Io.Writer = .fixed(&hdr.ar_name);
124 switch (opts.name) {
125 .symtab => writer.print("{s}", .{elf.SYM64NAME}) catch unreachable,
126 .strtab => writer.print("//", .{}) catch unreachable,
127 .name => |x| writer.print("{s}/", .{x}) catch unreachable,
128 .name_off => |x| writer.print("/{d}", .{x}) catch unreachable,
129 }
130 }
131 hdr.ar_date[0] = '0';
132 hdr.ar_uid[0] = '0';
133 hdr.ar_gid[0] = '0';
134 hdr.ar_mode[0] = '0';
135 {
136 var writer: std.Io.Writer = .fixed(&hdr.ar_size);
137 writer.print("{d}", .{opts.size}) catch unreachable;
138 }
139 hdr.ar_fmag = elf.ARFMAG.*;
140
141 return hdr;
142}
143
144const strtab_delimiter = '\n';
145pub const max_member_name_len = 15;
146
147pub const ArSymtab = struct {
148 symtab: std.ArrayList(Entry) = .empty,
149 strtab: StringTable = .{},
150
151 pub fn deinit(ar: *ArSymtab, allocator: Allocator) void {
152 ar.symtab.deinit(allocator);
153 ar.strtab.deinit(allocator);
154 }
155
156 pub fn sort(ar: *ArSymtab) void {
157 mem.sort(Entry, ar.symtab.items, {}, Entry.lessThan);
158 }
159
160 pub fn size(ar: ArSymtab, kind: enum { p32, p64 }) usize {
161 const ptr_size: usize = switch (kind) {
162 .p32 => 4,
163 .p64 => 8,
164 };
165 var ss: usize = ptr_size + ar.symtab.items.len * ptr_size;
166 for (ar.symtab.items) |entry| {
167 ss += ar.strtab.getAssumeExists(entry.off).len + 1;
168 }
169 return ss;
170 }
171
172 pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void {
173 assert(kind == .p64); // TODO p32
174 const hdr = setArHdr(.{ .name = .symtab, .size = @intCast(ar.size(.p64)) });
175 try writer.writeAll(mem.asBytes(&hdr));
176
177 const comp = elf_file.base.comp;
178 const gpa = comp.gpa;
179 var offsets = std.AutoHashMap(File.Index, u64).init(gpa);
180 defer offsets.deinit();
181 try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1));
182
183 if (elf_file.zigObjectPtr()) |zig_object| {
184 offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off);
185 }
186 for (elf_file.objects.items) |index| {
187 offsets.putAssumeCapacityNoClobber(index, elf_file.file(index).?.object.output_ar_state.file_off);
188 }
189
190 // Number of symbols
191 try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big);
192
193 // Offsets to files
194 for (ar.symtab.items) |entry| {
195 const off = offsets.get(entry.file_index).?;
196 try writer.writeInt(u64, off, .big);
197 }
198
199 // Strings
200 for (ar.symtab.items) |entry| {
201 try writer.print("{s}\x00", .{ar.strtab.getAssumeExists(entry.off)});
202 }
203 }
204
205 const Format = struct {
206 ar: ArSymtab,
207 elf_file: *Elf,
208
209 fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
210 const ar = f.ar;
211 const elf_file = f.elf_file;
212 for (ar.symtab.items, 0..) |entry, i| {
213 const name = ar.strtab.getAssumeExists(entry.off);
214 const file = elf_file.file(entry.file_index).?;
215 try writer.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file_index, file.fmtPath() });
216 }
217 }
218 };
219
220 pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Alt(Format, Format.default) {
221 return .{ .data = .{
222 .ar = ar,
223 .elf_file = elf_file,
224 } };
225 }
226
227 const Entry = struct {
228 /// Offset into the string table.
229 off: u32,
230 /// Index of the file defining the global.
231 file_index: File.Index,
232
233 pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool {
234 _ = ctx;
235 if (lhs.off == rhs.off) return lhs.file_index < rhs.file_index;
236 return lhs.off < rhs.off;
237 }
238 };
239};
240
241pub const ArStrtab = struct {
242 buffer: std.ArrayList(u8) = .empty,
243
244 pub fn deinit(ar: *ArStrtab, allocator: Allocator) void {
245 ar.buffer.deinit(allocator);
246 }
247
248 pub fn insert(ar: *ArStrtab, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 {
249 const off = @as(u32, @intCast(ar.buffer.items.len));
250 try ar.buffer.print(allocator, "{s}/{c}", .{ name, strtab_delimiter });
251 return off;
252 }
253
254 pub fn size(ar: ArStrtab) usize {
255 return ar.buffer.items.len;
256 }
257
258 pub fn write(ar: ArStrtab, writer: anytype) !void {
259 const hdr = setArHdr(.{ .name = .strtab, .size = @intCast(ar.size()) });
260 try writer.writeAll(mem.asBytes(&hdr));
261 try writer.writeAll(ar.buffer.items);
262 }
263
264 pub fn format(ar: ArStrtab, writer: *std.Io.Writer) std.Io.Writer.Error!void {
265 try writer.print("{f}", .{std.ascii.hexEscape(ar.buffer.items, .lower)});
266 }
267};
268
269pub const ArState = struct {
270 /// Name offset in the string table.
271 name_off: u32 = 0,
272
273 /// File offset of the ar_hdr describing the contributing
274 /// object in the archive.
275 file_off: u64 = 0,
276
277 /// Total size of the contributing object (excludes ar_hdr).
278 size: u64 = 0,
279};
280
281const std = @import("std");
282const assert = std.debug.assert;
283const elf = std.elf;
284const fs = std.fs;
285const log = std.log.scoped(.link);
286const mem = std.mem;
287const Path = std.Build.Cache.Path;
288const Allocator = std.mem.Allocator;
289
290const Diags = @import("../../link.zig").Diags;
291const Archive = @This();
292const Elf = @import("../Elf.zig");
293const File = @import("file.zig").File;
294const Object = @import("Object.zig");
295const StringTable = @import("../StringTable.zig");