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");