authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-02 19:33:10+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-04 09:12:07+01:00
log3b9455f0052d4ae648e8eec9685455eb501abcfd
tree793590c4bee2b58126f28c9f397085d44056c638
parenteddf9cc65b0abe8a8cdf8c80d8cd97e56e860515

elf: generate pretty rudimentary archive


3 files changed, 177 insertions(+), 25 deletions(-)

src/link/Elf.zig+174-22
...@@ -189,7 +189,7 @@ strings: StringTable = .{},...@@ -189,7 +189,7 @@ strings: StringTable = .{},
189/// Static archive state.189/// Static archive state.
190/// TODO it may be wise to move it somewhere else, but for the time being, it190/// TODO it may be wise to move it somewhere else, but for the time being, it
191/// is far easier to pollute global state.191/// is far easier to pollute global state.
192ar_symtab: std.ArrayListUnmanaged(struct { u32, File.Index }) = .{},192ar_symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .{},
193ar_strtab: StringTable = .{},193ar_strtab: StringTable = .{},
194194
195/// When allocating, the ideal_capacity is calculated by195/// When allocating, the ideal_capacity is calculated by
...@@ -1532,6 +1532,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1532,6 +1532,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
15321532
1533pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void {1533pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void {
1534 _ = comp;1534 _ = comp;
1535 const gpa = self.base.allocator;
15351536
1536 // First, we flush relocatable object file generated with our backends.1537 // First, we flush relocatable object file generated with our backends.
1537 if (self.zigObjectPtr()) |zig_object| {1538 if (self.zigObjectPtr()) |zig_object| {
...@@ -1553,28 +1554,162 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void...@@ -1553,28 +1554,162 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void
1553 try self.writeShStrtab();1554 try self.writeShStrtab();
1554 try self.writeElfHeader();1555 try self.writeElfHeader();
15551556
1556 // Update ar symbol and string tables.1557 // Update ar symbol table.
1557 try zig_object.asFile().updateArSymtab(self);1558 try zig_object.asFile().updateArSymtab(self);
1558
1559 for (self.ar_symtab.items, 0..) |entry, i| {
1560 std.debug.print("{d}: {s} in {}\n", .{
1561 i,
1562 self.ar_strtab.getAssumeExists(entry[0]),
1563 self.file(entry[1]).?.fmtPath(),
1564 });
1565 }
1566 }1559 }
15671560
1568 // TODO parse positionals that we want to make part of the archive1561 // TODO parse positionals that we want to make part of the archive
15691562
1563 mem.sort(ArSymtabEntry, self.ar_symtab.items, {}, ArSymtabEntry.lessThan);
1564
1570 if (build_options.enable_logging) {1565 if (build_options.enable_logging) {
1571 state_log.debug("{}", .{self.dumpState()});1566 state_log.debug("{}", .{self.dumpState()});
1572 }1567 }
15731568
1574 // try self.writeArHdr();1569 // Save object paths in strtab.
1575 // TODO beyond this point I expect writing out objects parsed from the cmdline1570 var files = std.AutoHashMap(File.Index, struct { u32, u64, u64 }).init(gpa);
1571 defer files.deinit();
1572 try files.ensureUnusedCapacity(@intCast(self.objects.items.len + 1));
1573
1574 if (self.zigObjectPtr()) |zig_object| {
1575 files.putAssumeCapacityNoClobber(zig_object.index, .{ try self.ar_strtab.insert(gpa, zig_object.path), 0, 0 });
1576 }
1577
1578 // Encode ar symtab in 64bit format.
1579 var ar_symtab = std.ArrayList(u8).init(gpa);
1580 defer ar_symtab.deinit();
1581 try ar_symtab.ensureTotalCapacityPrecise(8 * (3 * self.ar_symtab.items.len + 1));
1582
1583 // Number of symbols
1584 ar_symtab.writer().writeInt(u64, @as(u64, @intCast(self.ar_symtab.items.len)), .big) catch unreachable;
1585
1586 // Offsets which we will relocate later.
1587 for (0..self.ar_symtab.items.len) |_| {
1588 ar_symtab.writer().writeInt(u64, 0, .big) catch unreachable;
1589 }
1590
1591 // ASCII offsets into the strtab.
1592 for (self.ar_symtab.items) |entry| {
1593 ar_symtab.writer().print("/{d}", .{entry.off}) catch unreachable;
1594 }
1595
1596 // Align to 8bytes if required
1597 {
1598 const end = ar_symtab.items.len;
1599 const aligned = mem.alignForward(usize, end, 8);
1600 ar_symtab.writer().writeByteNTimes(0, aligned - end) catch unreachable;
1601 }
1602
1603 assert(mem.isAligned(ar_symtab.items.len, 8));
15761604
1577 try self.writeArMagic();1605 // Calculate required size for headers before ZigObject pos in file.
1606 if (self.zigObjectPtr()) |zig_object| {
1607 var file_off: u64 = 0;
1608 // Magic
1609 file_off += Archive.SARMAG;
1610 // Symtab
1611 file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len));
1612 // Strtab
1613 file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(self.ar_strtab.buffer.items.len));
1614 // And because we are nice, we will align to 8 bytes.
1615 file_off = mem.alignForward(u64, file_off, 8);
1616
1617 const files_ptr = files.getPtr(zig_object.index).?;
1618 files_ptr[1] = file_off;
1619
1620 // Move ZigObject into place.
1621 {
1622 var end_pos: u64 = self.shdr_table_offset.?;
1623 for (self.shdrs.items) |shdr| {
1624 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
1625 }
1626 const contents = try gpa.alloc(u8, end_pos);
1627 defer gpa.free(contents);
1628 const amt = try self.base.file.?.preadAll(contents, 0);
1629 if (amt != end_pos) return error.InputOutput;
1630 try self.base.file.?.pwriteAll(contents, file_off + @sizeOf(Archive.ar_hdr));
1631
1632 files_ptr[2] = end_pos;
1633 }
1634 }
1635
1636 // Fixup file offsets in the symtab.
1637 for (self.ar_symtab.items, 1..) |entry, i| {
1638 const file_off = files.get(entry.file_index).?[1];
1639 mem.writeInt(u64, ar_symtab.items[8 * i ..][0..8], file_off, .big);
1640 }
1641
1642 var pos: usize = Archive.SARMAG;
1643
1644 // Write symtab.
1645 {
1646 const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar_symtab.items.len) });
1647 try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos);
1648 pos += @sizeOf(Archive.ar_hdr);
1649 try self.base.file.?.pwriteAll(ar_symtab.items, pos);
1650 pos += ar_symtab.items.len;
1651 }
1652
1653 // Write strtab.
1654 {
1655 const hdr = setArHdr(.{
1656 .kind = .strtab,
1657 .name_off = 0,
1658 .size = @intCast(mem.alignForward(usize, self.ar_strtab.buffer.items.len, 8)),
1659 });
1660 try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos);
1661 pos += @sizeOf(Archive.ar_hdr);
1662 try self.base.file.?.pwriteAll(self.ar_strtab.buffer.items, pos);
1663 pos += self.ar_strtab.buffer.items.len;
1664 }
1665
1666 // Zig object if defined
1667 if (self.zigObjectPtr()) |zig_object| {
1668 const entry = files.get(zig_object.index).?;
1669 const hdr = setArHdr(.{ .kind = .object, .name_off = entry[0], .size = @intCast(entry[2]) });
1670 try self.base.file.?.pwriteAll(mem.asBytes(&hdr), entry[1]);
1671 }
1672
1673 // TODO parsed positionals
1674
1675 // Magic bytes.
1676 {
1677 try self.base.file.?.pwriteAll(Archive.ARMAG, 0);
1678 }
1679}
1680
1681fn setArHdr(opts: struct {
1682 kind: enum { symtab, strtab, object },
1683 name_off: u32,
1684 size: u32,
1685}) Archive.ar_hdr {
1686 var hdr: Archive.ar_hdr = .{
1687 .ar_name = undefined,
1688 .ar_date = undefined,
1689 .ar_uid = undefined,
1690 .ar_gid = undefined,
1691 .ar_mode = undefined,
1692 .ar_size = undefined,
1693 .ar_fmag = undefined,
1694 };
1695 @memset(mem.asBytes(&hdr), 0x20);
1696 @memcpy(&hdr.ar_fmag, Archive.ARFMAG);
1697
1698 {
1699 var stream = std.io.fixedBufferStream(&hdr.ar_name);
1700 const writer = stream.writer();
1701 switch (opts.kind) {
1702 .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable,
1703 .strtab => writer.print("//", .{}) catch unreachable,
1704 .object => writer.print("/{d}", .{opts.name_off}) catch unreachable,
1705 }
1706 }
1707 {
1708 var stream = std.io.fixedBufferStream(&hdr.ar_size);
1709 stream.writer().print("{d}", .{opts.size}) catch unreachable;
1710 }
1711
1712 return hdr;
1578}1713}
15791714
1580pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void {1715pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void {
...@@ -2968,15 +3103,6 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2968,15 +3103,6 @@ fn writeElfHeader(self: *Elf) !void {
2968 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);3103 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
2969}3104}
29703105
2971fn writeArMagic(self: *Elf) !void {
2972 // Magic bytes.
2973 var buffer: [@as(usize, Archive.SARMAG) + 1]u8 = undefined;
2974 var stream = std.io.fixedBufferStream(&buffer);
2975 const writer = stream.writer();
2976 try writer.print("{s}\x00", .{Archive.ARMAG});
2977 try self.base.file.?.pwriteAll(&buffer, 0);
2978}
2979
2980pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {3106pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2981 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);3107 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
2982 return self.zigObjectPtr().?.freeDecl(self, decl_index);3108 return self.zigObjectPtr().?.freeDecl(self, decl_index);
...@@ -5683,6 +5809,19 @@ fn fmtDumpState(...@@ -5683,6 +5809,19 @@ fn fmtDumpState(
5683 }5809 }
5684 try writer.print("{}\n", .{self.got.fmt(self)});5810 try writer.print("{}\n", .{self.got.fmt(self)});
5685 try writer.print("{}\n", .{self.zig_got.fmt(self)});5811 try writer.print("{}\n", .{self.zig_got.fmt(self)});
5812
5813 if (self.isStaticLib()) {
5814 try writer.writeAll("ar symtab\n");
5815 for (self.ar_symtab.items, 0..) |entry, i| {
5816 try writer.print(" {d} : {s} in file({d})\n", .{
5817 i,
5818 self.ar_strtab.getAssumeExists(entry.off),
5819 entry.file_index,
5820 });
5821 }
5822 try writer.writeByte('\n');
5823 }
5824
5686 try writer.writeAll("Output shdrs\n");5825 try writer.writeAll("Output shdrs\n");
5687 for (self.shdrs.items, 0..) |shdr, shndx| {5826 for (self.shdrs.items, 0..) |shdr, shndx| {
5688 try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{5827 try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{
...@@ -5815,6 +5954,19 @@ const LastAtomAndFreeList = struct {...@@ -5815,6 +5954,19 @@ const LastAtomAndFreeList = struct {
58155954
5816const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList);5955const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList);
58175956
5957const ArSymtabEntry = struct {
5958 off: u32,
5959 file_index: File.Index,
5960
5961 pub fn lessThan(ctx: void, lhs: ArSymtabEntry, rhs: ArSymtabEntry) bool {
5962 _ = ctx;
5963 if (lhs.off == rhs.off) {
5964 return lhs.file_index < rhs.file_index;
5965 }
5966 return lhs.off < rhs.off;
5967 }
5968};
5969
5818pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;5970pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
5819pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;5971pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
58205972
src/link/Elf/Archive.zig+2-2
...@@ -10,7 +10,7 @@ strtab: []const u8 = &[0]u8{},...@@ -10,7 +10,7 @@ strtab: []const u8 = &[0]u8{},
10/// String that begins an archive file.10/// String that begins an archive file.
11pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";11pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
12/// Size of that string.12/// Size of that string.
13pub const SARMAG: u4 = 8;13pub const SARMAG = 8;
1414
15/// String in ar_fmag at the end of each header.15/// String in ar_fmag at the end of each header.
16pub const ARFMAG: *const [2:0]u8 = "`\n";16pub const ARFMAG: *const [2:0]u8 = "`\n";
...@@ -58,7 +58,7 @@ pub const ar_hdr = extern struct {...@@ -58,7 +58,7 @@ pub const ar_hdr = extern struct {
58 }58 }
5959
60 fn isSymtab(self: ar_hdr) bool {60 fn isSymtab(self: ar_hdr) bool {
61 return mem.eql(u8, getValue(&self.ar_name), "/");61 return mem.eql(u8, getValue(&self.ar_name), "/") or mem.eql(u8, getValue(&self.ar_name), SYM64NAME);
62 }62 }
63};63};
6464
src/link/Elf/ZigObject.zig+1-1
...@@ -514,7 +514,7 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void {...@@ -514,7 +514,7 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void {
514 if (global.type(elf_file) == elf.SHN_UNDEF) continue;514 if (global.type(elf_file) == elf.SHN_UNDEF) continue;
515515
516 const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file));516 const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file));
517 elf_file.ar_symtab.appendAssumeCapacity(.{ off, self.index });517 elf_file.ar_symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
518 }518 }
519}519}
520520