zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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