| author | |
| committer | |
| log | acd700ac6b4ec03412e2bac6aaf168f80f83f521 |
| tree | 15c00fc6dfb8d7cd3fd9e7d2f53f460877ca6067 |
| parent | ed2984f335bfaf7cc3cb7841554f4cbb958476dd |
4 files changed, 347 insertions(+), 243 deletions(-)
src/link/Elf.zig+42-177| ... | @@ -186,12 +186,6 @@ comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{} | ... | @@ -186,12 +186,6 @@ comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{} |
| 186 | /// such as `resolver` and `comdat_groups_table`. | 186 | /// such as `resolver` and `comdat_groups_table`. |
| 187 | strings: StringTable = .{}, | 187 | strings: StringTable = .{}, |
| 188 | 188 | ||
| 189 | /// Static archive state. | ||
| 190 | /// TODO it may be wise to move it somewhere else, but for the time being, it | ||
| 191 | /// is far easier to pollute global state. | ||
| 192 | ar_symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .{}, | ||
| 193 | ar_strtab: StringTable = .{}, | ||
| 194 | |||
| 195 | /// When allocating, the ideal_capacity is calculated by | 189 | /// When allocating, the ideal_capacity is calculated by |
| 196 | /// actual_capacity + (actual_capacity / ideal_factor) | 190 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 197 | const ideal_factor = 3; | 191 | const ideal_factor = 3; |
| ... | @@ -398,9 +392,6 @@ pub fn deinit(self: *Elf) void { | ... | @@ -398,9 +392,6 @@ pub fn deinit(self: *Elf) void { |
| 398 | self.copy_rel.deinit(gpa); | 392 | self.copy_rel.deinit(gpa); |
| 399 | self.rela_dyn.deinit(gpa); | 393 | self.rela_dyn.deinit(gpa); |
| 400 | self.rela_plt.deinit(gpa); | 394 | self.rela_plt.deinit(gpa); |
| 401 | |||
| 402 | self.ar_symtab.deinit(gpa); | ||
| 403 | self.ar_strtab.deinit(gpa); | ||
| 404 | } | 395 | } |
| 405 | 396 | ||
| 406 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { | 397 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { |
| ... | @@ -1553,175 +1544,74 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void | ... | @@ -1553,175 +1544,74 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void |
| 1553 | try self.writeSymtab(); | 1544 | try self.writeSymtab(); |
| 1554 | try self.writeShStrtab(); | 1545 | try self.writeShStrtab(); |
| 1555 | try self.writeElfHeader(); | 1546 | try self.writeElfHeader(); |
| 1556 | |||
| 1557 | // Update ar symbol table. | ||
| 1558 | try zig_object.asFile().updateArSymtab(self); | ||
| 1559 | } | 1547 | } |
| 1560 | 1548 | ||
| 1561 | // TODO parse positionals that we want to make part of the archive | 1549 | // TODO parse positionals that we want to make part of the archive |
| 1562 | 1550 | ||
| 1563 | mem.sort(ArSymtabEntry, self.ar_symtab.items, {}, ArSymtabEntry.lessThan); | 1551 | // TODO update ar symtab from parsed positionals |
| 1564 | 1552 | ||
| 1565 | if (build_options.enable_logging) { | 1553 | var ar_symtab: Archive.ArSymtab = .{}; |
| 1566 | state_log.debug("{}", .{self.dumpState()}); | 1554 | defer ar_symtab.deinit(gpa); |
| 1567 | } | ||
| 1568 | |||
| 1569 | // Save object paths in filenames strtab. | ||
| 1570 | var ar_strtab = std.ArrayList(u8).init(gpa); | ||
| 1571 | defer ar_strtab.deinit(); | ||
| 1572 | |||
| 1573 | var files = std.AutoHashMap(File.Index, struct { u32, u64, u64 }).init(gpa); | ||
| 1574 | defer files.deinit(); | ||
| 1575 | try files.ensureUnusedCapacity(@intCast(self.objects.items.len + 1)); | ||
| 1576 | 1555 | ||
| 1577 | if (self.zigObjectPtr()) |zig_object| { | 1556 | if (self.zigObjectPtr()) |zig_object| { |
| 1578 | const off = @as(u32, @intCast(ar_strtab.items.len)); | 1557 | try zig_object.updateArSymtab(&ar_symtab, self); |
| 1579 | try ar_strtab.writer().print("{s}/\n", .{zig_object.path}); | ||
| 1580 | files.putAssumeCapacityNoClobber(zig_object.index, .{ off, 0, 0 }); | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | // Align to even byte boundary | ||
| 1584 | { | ||
| 1585 | const end = ar_strtab.items.len; | ||
| 1586 | const aligned = mem.alignForward(usize, end, 2); | ||
| 1587 | try ar_strtab.writer().writeByteNTimes(0, aligned - end); | ||
| 1588 | } | 1558 | } |
| 1589 | 1559 | ||
| 1590 | // Encode ar symtab in 64bit format. | 1560 | ar_symtab.sort(); |
| 1591 | var ar_symtab = std.ArrayList(u8).init(gpa); | ||
| 1592 | defer ar_symtab.deinit(); | ||
| 1593 | try ar_symtab.ensureUnusedCapacity(8 * (self.ar_symtab.items.len + 1)); | ||
| 1594 | |||
| 1595 | // Number of symbols | ||
| 1596 | ar_symtab.writer().writeInt(u64, @as(u64, @intCast(self.ar_symtab.items.len)), .big) catch unreachable; | ||
| 1597 | 1561 | ||
| 1598 | // Offsets which we will relocate later. | 1562 | // Save object paths in filenames strtab. |
| 1599 | for (0..self.ar_symtab.items.len) |_| { | 1563 | var ar_strtab: Archive.ArStrtab = .{}; |
| 1600 | ar_symtab.writer().writeInt(u64, 0, .big) catch unreachable; | 1564 | defer ar_strtab.deinit(gpa); |
| 1601 | } | ||
| 1602 | 1565 | ||
| 1603 | // ASCII offsets into the strtab. | 1566 | if (self.zigObjectPtr()) |zig_object| { |
| 1604 | for (self.ar_symtab.items) |entry| { | 1567 | try zig_object.updateArStrtab(gpa, &ar_strtab); |
| 1605 | const name = self.ar_strtab.getAssumeExists(entry.off); | 1568 | zig_object.updateArSize(self); |
| 1606 | try ar_symtab.writer().print("{s}\x00", .{name}); | ||
| 1607 | } | 1569 | } |
| 1608 | 1570 | ||
| 1609 | // Align to 8 bytes if required | 1571 | // Update file offsets of contributing objects. |
| 1610 | { | 1572 | const total_size: u64 = blk: { |
| 1611 | const end = ar_symtab.items.len; | 1573 | var pos: u64 = Archive.SARMAG; |
| 1612 | const aligned = mem.alignForward(usize, end, 8); | 1574 | pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64); |
| 1613 | try ar_symtab.writer().writeByteNTimes(0, aligned - end); | 1575 | pos = mem.alignForward(u64, pos, 2); |
| 1614 | } | 1576 | pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); |
| 1615 | 1577 | ||
| 1616 | assert(mem.isAligned(ar_symtab.items.len, 8)); | 1578 | if (self.zigObjectPtr()) |zig_object| { |
| 1579 | pos = mem.alignForward(u64, pos, 2); | ||
| 1580 | zig_object.output_ar_state.file_off = pos; | ||
| 1581 | pos += @sizeOf(Archive.ar_hdr) + zig_object.output_ar_state.size; | ||
| 1582 | } | ||
| 1617 | 1583 | ||
| 1618 | // Calculate required size for headers before ZigObject pos in file. | 1584 | break :blk pos; |
| 1619 | if (self.zigObjectPtr()) |zig_object| { | 1585 | }; |
| 1620 | var file_off: u64 = 0; | ||
| 1621 | // Magic | ||
| 1622 | file_off += Archive.SARMAG; | ||
| 1623 | // Symtab | ||
| 1624 | file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len)); | ||
| 1625 | // Strtab | ||
| 1626 | file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_strtab.items.len)); | ||
| 1627 | |||
| 1628 | const files_ptr = files.getPtr(zig_object.index).?; | ||
| 1629 | files_ptr[1] = file_off; | ||
| 1630 | |||
| 1631 | // Move ZigObject into place. | ||
| 1632 | { | ||
| 1633 | var end_pos: u64 = self.shdr_table_offset.?; | ||
| 1634 | for (self.shdrs.items) |shdr| { | ||
| 1635 | end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); | ||
| 1636 | } | ||
| 1637 | const contents = try gpa.alloc(u8, end_pos); | ||
| 1638 | defer gpa.free(contents); | ||
| 1639 | const amt = try self.base.file.?.preadAll(contents, 0); | ||
| 1640 | if (amt != end_pos) return error.InputOutput; | ||
| 1641 | try self.base.file.?.pwriteAll(contents, file_off + @sizeOf(Archive.ar_hdr)); | ||
| 1642 | 1586 | ||
| 1643 | files_ptr[2] = end_pos; | 1587 | if (build_options.enable_logging) { |
| 1644 | } | 1588 | state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)}); |
| 1589 | state_log.debug("ar_strtab\n{}\n", .{ar_strtab}); | ||
| 1645 | } | 1590 | } |
| 1646 | 1591 | ||
| 1647 | // Fixup file offsets in the symtab. | 1592 | var buffer = std.ArrayList(u8).init(gpa); |
| 1648 | for (self.ar_symtab.items, 1..) |entry, i| { | 1593 | defer buffer.deinit(); |
| 1649 | const file_off = files.get(entry.file_index).?[1]; | 1594 | try buffer.ensureTotalCapacityPrecise(total_size); |
| 1650 | mem.writeInt(u64, ar_symtab.items[8 * i ..][0..8], file_off, .big); | ||
| 1651 | } | ||
| 1652 | 1595 | ||
| 1653 | var pos: usize = Archive.SARMAG; | 1596 | // Write magic |
| 1597 | try buffer.writer().writeAll(Archive.ARMAG); | ||
| 1654 | 1598 | ||
| 1655 | // Write symtab. | 1599 | // Write symtab |
| 1656 | { | 1600 | try ar_symtab.write(.p64, self, buffer.writer()); |
| 1657 | const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar_symtab.items.len) }); | 1601 | if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); |
| 1658 | try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); | ||
| 1659 | pos += @sizeOf(Archive.ar_hdr); | ||
| 1660 | try self.base.file.?.pwriteAll(ar_symtab.items, pos); | ||
| 1661 | pos += ar_symtab.items.len; | ||
| 1662 | } | ||
| 1663 | 1602 | ||
| 1664 | // Write strtab. | 1603 | // Write strtab |
| 1665 | { | 1604 | try ar_strtab.write(buffer.writer()); |
| 1666 | const hdr = setArHdr(.{ | 1605 | if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); |
| 1667 | .kind = .strtab, | ||
| 1668 | .name_off = 0, | ||
| 1669 | .size = @intCast(ar_strtab.items.len), | ||
| 1670 | }); | ||
| 1671 | try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); | ||
| 1672 | pos += @sizeOf(Archive.ar_hdr); | ||
| 1673 | try self.base.file.?.pwriteAll(ar_strtab.items, pos); | ||
| 1674 | pos += ar_strtab.items.len; | ||
| 1675 | } | ||
| 1676 | 1606 | ||
| 1677 | // Zig object if defined | 1607 | // Write object files |
| 1678 | if (self.zigObjectPtr()) |zig_object| { | 1608 | if (self.zigObjectPtr()) |zig_object| { |
| 1679 | const entry = files.get(zig_object.index).?; | 1609 | try zig_object.writeAr(self, buffer.writer()); |
| 1680 | const hdr = setArHdr(.{ .kind = .object, .name_off = entry[0], .size = @intCast(entry[2]) }); | ||
| 1681 | try self.base.file.?.pwriteAll(mem.asBytes(&hdr), entry[1]); | ||
| 1682 | pos += @sizeOf(Archive.ar_hdr) + entry[2]; | ||
| 1683 | } | 1610 | } |
| 1684 | 1611 | ||
| 1685 | // TODO parsed positionals | 1612 | assert(buffer.items.len == total_size); |
| 1686 | 1613 | ||
| 1687 | // Magic bytes. | 1614 | try self.base.file.?.pwriteAll(buffer.items, 0); |
| 1688 | { | ||
| 1689 | try self.base.file.?.pwriteAll(Archive.ARMAG, 0); | ||
| 1690 | } | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | fn setArHdr(opts: struct { | ||
| 1694 | kind: enum { symtab, strtab, object }, | ||
| 1695 | name_off: u32, | ||
| 1696 | size: u32, | ||
| 1697 | }) Archive.ar_hdr { | ||
| 1698 | var hdr: Archive.ar_hdr = .{ | ||
| 1699 | .ar_name = undefined, | ||
| 1700 | .ar_date = undefined, | ||
| 1701 | .ar_uid = undefined, | ||
| 1702 | .ar_gid = undefined, | ||
| 1703 | .ar_mode = undefined, | ||
| 1704 | .ar_size = undefined, | ||
| 1705 | .ar_fmag = undefined, | ||
| 1706 | }; | ||
| 1707 | @memset(mem.asBytes(&hdr), 0x20); | ||
| 1708 | @memcpy(&hdr.ar_fmag, Archive.ARFMAG); | ||
| 1709 | |||
| 1710 | { | ||
| 1711 | var stream = std.io.fixedBufferStream(&hdr.ar_name); | ||
| 1712 | const writer = stream.writer(); | ||
| 1713 | switch (opts.kind) { | ||
| 1714 | .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, | ||
| 1715 | .strtab => writer.print("//", .{}) catch unreachable, | ||
| 1716 | .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | { | ||
| 1720 | var stream = std.io.fixedBufferStream(&hdr.ar_size); | ||
| 1721 | stream.writer().print("{d}", .{opts.size}) catch unreachable; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | return hdr; | ||
| 1725 | } | 1615 | } |
| 1726 | 1616 | ||
| 1727 | pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { | 1617 | pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { |
| ... | @@ -5822,18 +5712,6 @@ fn fmtDumpState( | ... | @@ -5822,18 +5712,6 @@ fn fmtDumpState( |
| 5822 | try writer.print("{}\n", .{self.got.fmt(self)}); | 5712 | try writer.print("{}\n", .{self.got.fmt(self)}); |
| 5823 | try writer.print("{}\n", .{self.zig_got.fmt(self)}); | 5713 | try writer.print("{}\n", .{self.zig_got.fmt(self)}); |
| 5824 | 5714 | ||
| 5825 | if (self.isStaticLib()) { | ||
| 5826 | try writer.writeAll("ar symtab\n"); | ||
| 5827 | for (self.ar_symtab.items, 0..) |entry, i| { | ||
| 5828 | try writer.print(" {d} : {s} in file({d})\n", .{ | ||
| 5829 | i, | ||
| 5830 | self.ar_strtab.getAssumeExists(entry.off), | ||
| 5831 | entry.file_index, | ||
| 5832 | }); | ||
| 5833 | } | ||
| 5834 | try writer.writeByte('\n'); | ||
| 5835 | } | ||
| 5836 | |||
| 5837 | try writer.writeAll("Output shdrs\n"); | 5715 | try writer.writeAll("Output shdrs\n"); |
| 5838 | for (self.shdrs.items, 0..) |shdr, shndx| { | 5716 | for (self.shdrs.items, 0..) |shdr, shndx| { |
| 5839 | try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ | 5717 | try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ |
| ... | @@ -5966,19 +5844,6 @@ const LastAtomAndFreeList = struct { | ... | @@ -5966,19 +5844,6 @@ const LastAtomAndFreeList = struct { |
| 5966 | 5844 | ||
| 5967 | const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); | 5845 | const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); |
| 5968 | 5846 | ||
| 5969 | const ArSymtabEntry = struct { | ||
| 5970 | off: u32, | ||
| 5971 | file_index: File.Index, | ||
| 5972 | |||
| 5973 | pub fn lessThan(ctx: void, lhs: ArSymtabEntry, rhs: ArSymtabEntry) bool { | ||
| 5974 | _ = ctx; | ||
| 5975 | if (lhs.off == rhs.off) { | ||
| 5976 | return lhs.file_index < rhs.file_index; | ||
| 5977 | } | ||
| 5978 | return lhs.off < rhs.off; | ||
| 5979 | } | ||
| 5980 | }; | ||
| 5981 | |||
| 5982 | pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; | 5847 | pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; |
| 5983 | pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; | 5848 | pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; |
| 5984 | 5849 |
src/link/Elf/Archive.zig+262-60| ... | @@ -4,69 +4,11 @@ data: []const u8, | ... | @@ -4,69 +4,11 @@ data: []const u8, |
| 4 | objects: std.ArrayListUnmanaged(Object) = .{}, | 4 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 5 | strtab: []const u8 = &[0]u8{}, | 5 | strtab: []const u8 = &[0]u8{}, |
| 6 | 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 = 8; | ||
| 14 | |||
| 15 | /// String in ar_fmag at the end of each header. | ||
| 16 | pub const ARFMAG: *const [2:0]u8 = "`\n"; | ||
| 17 | |||
| 18 | pub const SYM64NAME: *const [7:0]u8 = "/SYM64/"; | ||
| 19 | |||
| 20 | pub 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), "/") or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | pub fn isArchive(path: []const u8) !bool { | 7 | pub fn isArchive(path: []const u8) !bool { |
| 66 | const file = try std.fs.cwd().openFile(path, .{}); | 8 | const file = try std.fs.cwd().openFile(path, .{}); |
| 67 | defer file.close(); | 9 | defer file.close(); |
| 68 | const reader = file.reader(); | 10 | const reader = file.reader(); |
| 69 | const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false; | 11 | const magic = reader.readBytesNoEof(SARMAG) catch return false; |
| 70 | if (!mem.eql(u8, &magic, ARMAG)) return false; | 12 | if (!mem.eql(u8, &magic, ARMAG)) return false; |
| 71 | return true; | 13 | return true; |
| 72 | } | 14 | } |
| ... | @@ -140,9 +82,267 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { | ... | @@ -140,9 +82,267 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { |
| 140 | 82 | ||
| 141 | fn getString(self: Archive, off: u32) []const u8 { | 83 | fn getString(self: Archive, off: u32) []const u8 { |
| 142 | assert(off < self.strtab.len); | 84 | assert(off < self.strtab.len); |
| 143 | return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); | 85 | return mem.sliceTo(@as([*:strtab_delimiter]const u8, @ptrCast(self.strtab.ptr + off)), 0); |
| 86 | } | ||
| 87 | |||
| 88 | pub fn setArHdr(opts: struct { | ||
| 89 | kind: enum { symtab, strtab, object }, | ||
| 90 | name_off: u32, | ||
| 91 | size: u32, | ||
| 92 | }) ar_hdr { | ||
| 93 | var hdr: ar_hdr = .{ | ||
| 94 | .ar_name = undefined, | ||
| 95 | .ar_date = undefined, | ||
| 96 | .ar_uid = undefined, | ||
| 97 | .ar_gid = undefined, | ||
| 98 | .ar_mode = undefined, | ||
| 99 | .ar_size = undefined, | ||
| 100 | .ar_fmag = undefined, | ||
| 101 | }; | ||
| 102 | @memset(mem.asBytes(&hdr), 0x20); | ||
| 103 | @memcpy(&hdr.ar_fmag, Archive.ARFMAG); | ||
| 104 | |||
| 105 | { | ||
| 106 | var stream = std.io.fixedBufferStream(&hdr.ar_name); | ||
| 107 | const writer = stream.writer(); | ||
| 108 | switch (opts.kind) { | ||
| 109 | .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, | ||
| 110 | .strtab => writer.print("//", .{}) catch unreachable, | ||
| 111 | .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, | ||
| 112 | } | ||
| 113 | } | ||
| 114 | { | ||
| 115 | var stream = std.io.fixedBufferStream(&hdr.ar_size); | ||
| 116 | stream.writer().print("{d}", .{opts.size}) catch unreachable; | ||
| 117 | } | ||
| 118 | |||
| 119 | return hdr; | ||
| 144 | } | 120 | } |
| 145 | 121 | ||
| 122 | // Archive files start with the ARMAG identifying string. Then follows a | ||
| 123 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | ||
| 124 | // member indicates, for each member file. | ||
| 125 | /// String that begins an archive file. | ||
| 126 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | ||
| 127 | /// Size of that string. | ||
| 128 | pub const SARMAG = 8; | ||
| 129 | |||
| 130 | /// String in ar_fmag at the end of each header. | ||
| 131 | const ARFMAG: *const [2:0]u8 = "`\n"; | ||
| 132 | |||
| 133 | /// Strtab identifier | ||
| 134 | const STRNAME: *const [2:0]u8 = "//"; | ||
| 135 | |||
| 136 | /// 32-bit symtab identifier | ||
| 137 | const SYMNAME: *const [1:0]u8 = "/"; | ||
| 138 | |||
| 139 | /// 64-bit symtab identifier | ||
| 140 | const SYM64NAME: *const [7:0]u8 = "/SYM64/"; | ||
| 141 | |||
| 142 | const strtab_delimiter = '\n'; | ||
| 143 | |||
| 144 | pub const ar_hdr = extern struct { | ||
| 145 | /// Member file name, sometimes / terminated. | ||
| 146 | ar_name: [16]u8, | ||
| 147 | |||
| 148 | /// File date, decimal seconds since Epoch. | ||
| 149 | ar_date: [12]u8, | ||
| 150 | |||
| 151 | /// User ID, in ASCII format. | ||
| 152 | ar_uid: [6]u8, | ||
| 153 | |||
| 154 | /// Group ID, in ASCII format. | ||
| 155 | ar_gid: [6]u8, | ||
| 156 | |||
| 157 | /// File mode, in ASCII octal. | ||
| 158 | ar_mode: [8]u8, | ||
| 159 | |||
| 160 | /// File size, in ASCII decimal. | ||
| 161 | ar_size: [10]u8, | ||
| 162 | |||
| 163 | /// Always contains ARFMAG. | ||
| 164 | ar_fmag: [2]u8, | ||
| 165 | |||
| 166 | fn date(self: ar_hdr) !u64 { | ||
| 167 | const value = getValue(&self.ar_date); | ||
| 168 | return std.fmt.parseInt(u64, value, 10); | ||
| 169 | } | ||
| 170 | |||
| 171 | fn size(self: ar_hdr) !u32 { | ||
| 172 | const value = getValue(&self.ar_size); | ||
| 173 | return std.fmt.parseInt(u32, value, 10); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn getValue(raw: []const u8) []const u8 { | ||
| 177 | return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn isStrtab(self: ar_hdr) bool { | ||
| 181 | return mem.eql(u8, getValue(&self.ar_name), STRNAME); | ||
| 182 | } | ||
| 183 | |||
| 184 | fn isSymtab(self: ar_hdr) bool { | ||
| 185 | return mem.eql(u8, getValue(&self.ar_name), SYMNAME) or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | |||
| 189 | pub const ArSymtab = struct { | ||
| 190 | symtab: std.ArrayListUnmanaged(Entry) = .{}, | ||
| 191 | strtab: StringTable = .{}, | ||
| 192 | |||
| 193 | pub fn deinit(ar: *ArSymtab, allocator: Allocator) void { | ||
| 194 | ar.symtab.deinit(allocator); | ||
| 195 | ar.strtab.deinit(allocator); | ||
| 196 | } | ||
| 197 | |||
| 198 | pub fn sort(ar: *ArSymtab) void { | ||
| 199 | mem.sort(Entry, ar.symtab.items, {}, Entry.lessThan); | ||
| 200 | } | ||
| 201 | |||
| 202 | pub fn size(ar: ArSymtab, kind: enum { p32, p64 }) usize { | ||
| 203 | const ptr_size: usize = switch (kind) { | ||
| 204 | .p32 => 4, | ||
| 205 | .p64 => 8, | ||
| 206 | }; | ||
| 207 | var ss: usize = ptr_size + ar.symtab.items.len * ptr_size; | ||
| 208 | for (ar.symtab.items) |entry| { | ||
| 209 | ss += ar.strtab.getAssumeExists(entry.off).len + 1; | ||
| 210 | } | ||
| 211 | return ss; | ||
| 212 | } | ||
| 213 | |||
| 214 | pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void { | ||
| 215 | assert(kind == .p64); // TODO p32 | ||
| 216 | const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar.size(.p64)) }); | ||
| 217 | try writer.writeAll(mem.asBytes(&hdr)); | ||
| 218 | |||
| 219 | const gpa = elf_file.base.allocator; | ||
| 220 | var offsets = std.AutoHashMap(File.Index, u64).init(gpa); | ||
| 221 | defer offsets.deinit(); | ||
| 222 | try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1)); | ||
| 223 | |||
| 224 | if (elf_file.zigObjectPtr()) |zig_object| { | ||
| 225 | offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off); | ||
| 226 | } | ||
| 227 | |||
| 228 | // Number of symbols | ||
| 229 | try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big); | ||
| 230 | |||
| 231 | // Offsets to files | ||
| 232 | for (ar.symtab.items) |entry| { | ||
| 233 | const off = offsets.get(entry.file_index).?; | ||
| 234 | try writer.writeInt(u64, off, .big); | ||
| 235 | } | ||
| 236 | |||
| 237 | // Strings | ||
| 238 | for (ar.symtab.items) |entry| { | ||
| 239 | try writer.print("{s}\x00", .{ar.strtab.getAssumeExists(entry.off)}); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | pub fn format( | ||
| 244 | ar: ArSymtab, | ||
| 245 | comptime unused_fmt_string: []const u8, | ||
| 246 | options: std.fmt.FormatOptions, | ||
| 247 | writer: anytype, | ||
| 248 | ) !void { | ||
| 249 | _ = ar; | ||
| 250 | _ = unused_fmt_string; | ||
| 251 | _ = options; | ||
| 252 | _ = writer; | ||
| 253 | @compileError("do not format ar symtab directly; use fmt instead"); | ||
| 254 | } | ||
| 255 | |||
| 256 | const FormatContext = struct { | ||
| 257 | ar: ArSymtab, | ||
| 258 | elf_file: *Elf, | ||
| 259 | }; | ||
| 260 | |||
| 261 | pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) { | ||
| 262 | return .{ .data = .{ | ||
| 263 | .ar = ar, | ||
| 264 | .elf_file = elf_file, | ||
| 265 | } }; | ||
| 266 | } | ||
| 267 | |||
| 268 | fn format2( | ||
| 269 | ctx: FormatContext, | ||
| 270 | comptime unused_fmt_string: []const u8, | ||
| 271 | options: std.fmt.FormatOptions, | ||
| 272 | writer: anytype, | ||
| 273 | ) !void { | ||
| 274 | _ = unused_fmt_string; | ||
| 275 | _ = options; | ||
| 276 | const ar = ctx.ar; | ||
| 277 | const elf_file = ctx.elf_file; | ||
| 278 | for (ar.symtab.items, 0..) |entry, i| { | ||
| 279 | const name = ar.strtab.getAssumeExists(entry.off); | ||
| 280 | const file = elf_file.file(entry.file_index).?; | ||
| 281 | try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file_index, file.fmtPath() }); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | const Entry = struct { | ||
| 286 | /// Offset into the string table. | ||
| 287 | off: u32, | ||
| 288 | /// Index of the file defining the global. | ||
| 289 | file_index: File.Index, | ||
| 290 | |||
| 291 | pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool { | ||
| 292 | _ = ctx; | ||
| 293 | if (lhs.off == rhs.off) return lhs.file_index < rhs.file_index; | ||
| 294 | return lhs.off < rhs.off; | ||
| 295 | } | ||
| 296 | }; | ||
| 297 | }; | ||
| 298 | |||
| 299 | pub const ArStrtab = struct { | ||
| 300 | buffer: std.ArrayListUnmanaged(u8) = .{}, | ||
| 301 | |||
| 302 | pub fn deinit(ar: *ArStrtab, allocator: Allocator) void { | ||
| 303 | ar.buffer.deinit(allocator); | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn insert(ar: *ArStrtab, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 { | ||
| 307 | const off = @as(u32, @intCast(ar.buffer.items.len)); | ||
| 308 | try ar.buffer.writer(allocator).print("{s}/{c}", .{ name, strtab_delimiter }); | ||
| 309 | return off; | ||
| 310 | } | ||
| 311 | |||
| 312 | pub fn size(ar: ArStrtab) usize { | ||
| 313 | return ar.buffer.items.len; | ||
| 314 | } | ||
| 315 | |||
| 316 | pub fn write(ar: ArStrtab, writer: anytype) !void { | ||
| 317 | const hdr = setArHdr(.{ .kind = .strtab, .name_off = 0, .size = @intCast(ar.size()) }); | ||
| 318 | try writer.writeAll(mem.asBytes(&hdr)); | ||
| 319 | try writer.writeAll(ar.buffer.items); | ||
| 320 | } | ||
| 321 | |||
| 322 | pub fn format( | ||
| 323 | ar: ArStrtab, | ||
| 324 | comptime unused_fmt_string: []const u8, | ||
| 325 | options: std.fmt.FormatOptions, | ||
| 326 | writer: anytype, | ||
| 327 | ) !void { | ||
| 328 | _ = unused_fmt_string; | ||
| 329 | _ = options; | ||
| 330 | try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)}); | ||
| 331 | } | ||
| 332 | }; | ||
| 333 | |||
| 334 | pub const ArState = struct { | ||
| 335 | /// Name offset in the string table. | ||
| 336 | name_off: u32 = 0, | ||
| 337 | |||
| 338 | /// File offset of the ar_hdr describing the contributing | ||
| 339 | /// object in the archive. | ||
| 340 | file_off: u64 = 0, | ||
| 341 | |||
| 342 | /// Total size of the contributing object (excludes ar_hdr). | ||
| 343 | size: u64 = 0, | ||
| 344 | }; | ||
| 345 | |||
| 146 | const std = @import("std"); | 346 | const std = @import("std"); |
| 147 | const assert = std.debug.assert; | 347 | const assert = std.debug.assert; |
| 148 | const elf = std.elf; | 348 | const elf = std.elf; |
| ... | @@ -153,4 +353,6 @@ const mem = std.mem; | ... | @@ -153,4 +353,6 @@ const mem = std.mem; |
| 153 | const Allocator = mem.Allocator; | 353 | const Allocator = mem.Allocator; |
| 154 | const Archive = @This(); | 354 | const Archive = @This(); |
| 155 | const Elf = @import("../Elf.zig"); | 355 | const Elf = @import("../Elf.zig"); |
| 356 | const File = @import("file.zig").File; | ||
| 156 | const Object = @import("Object.zig"); | 357 | const Object = @import("Object.zig"); |
| 358 | const StringTable = @import("../StringTable.zig"); |
src/link/Elf/ZigObject.zig+40-4| ... | @@ -20,6 +20,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, | ... | @@ -20,6 +20,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, |
| 20 | num_dynrelocs: u32 = 0, | 20 | num_dynrelocs: u32 = 0, |
| 21 | 21 | ||
| 22 | output_symtab_size: Elf.SymtabSize = .{}, | 22 | output_symtab_size: Elf.SymtabSize = .{}, |
| 23 | output_ar_state: Archive.ArState = .{}, | ||
| 23 | 24 | ||
| 24 | dwarf: ?Dwarf = null, | 25 | dwarf: ?Dwarf = null, |
| 25 | 26 | ||
| ... | @@ -502,10 +503,10 @@ fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { | ... | @@ -502,10 +503,10 @@ fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { |
| 502 | // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); | 503 | // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); |
| 503 | } | 504 | } |
| 504 | 505 | ||
| 505 | pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { | 506 | pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { |
| 506 | const gpa = elf_file.base.allocator; | 507 | const gpa = elf_file.base.allocator; |
| 507 | 508 | ||
| 508 | try elf_file.ar_symtab.ensureUnusedCapacity(gpa, self.globals().len); | 509 | try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len); |
| 509 | 510 | ||
| 510 | for (self.globals()) |global_index| { | 511 | for (self.globals()) |global_index| { |
| 511 | const global = elf_file.symbol(global_index); | 512 | const global = elf_file.symbol(global_index); |
| ... | @@ -513,11 +514,45 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { | ... | @@ -513,11 +514,45 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { |
| 513 | assert(file_ptr.index() == self.index); | 514 | assert(file_ptr.index() == self.index); |
| 514 | if (global.type(elf_file) == elf.SHN_UNDEF) continue; | 515 | if (global.type(elf_file) == elf.SHN_UNDEF) continue; |
| 515 | 516 | ||
| 516 | const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file)); | 517 | const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); |
| 517 | elf_file.ar_symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); | 518 | ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); |
| 518 | } | 519 | } |
| 519 | } | 520 | } |
| 520 | 521 | ||
| 522 | pub fn updateArStrtab( | ||
| 523 | self: *ZigObject, | ||
| 524 | allocator: Allocator, | ||
| 525 | ar_strtab: *Archive.ArStrtab, | ||
| 526 | ) error{OutOfMemory}!void { | ||
| 527 | const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)}); | ||
| 528 | defer allocator.free(name); | ||
| 529 | const name_off = try ar_strtab.insert(allocator, name); | ||
| 530 | self.output_ar_state.name_off = name_off; | ||
| 531 | } | ||
| 532 | |||
| 533 | pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { | ||
| 534 | var end_pos: u64 = elf_file.shdr_table_offset.?; | ||
| 535 | for (elf_file.shdrs.items) |shdr| { | ||
| 536 | end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); | ||
| 537 | } | ||
| 538 | self.output_ar_state.size = end_pos; | ||
| 539 | } | ||
| 540 | |||
| 541 | pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { | ||
| 542 | const gpa = elf_file.base.allocator; | ||
| 543 | const contents = try gpa.alloc(u8, self.output_ar_state.size); | ||
| 544 | defer gpa.free(contents); | ||
| 545 | const amt = try elf_file.base.file.?.preadAll(contents, 0); | ||
| 546 | if (amt != self.output_ar_state.size) return error.InputOutput; | ||
| 547 | const hdr = Archive.setArHdr(.{ | ||
| 548 | .kind = .object, | ||
| 549 | .name_off = self.output_ar_state.name_off, | ||
| 550 | .size = @intCast(self.output_ar_state.size), | ||
| 551 | }); | ||
| 552 | try writer.writeAll(mem.asBytes(&hdr)); | ||
| 553 | try writer.writeAll(contents); | ||
| 554 | } | ||
| 555 | |||
| 521 | pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { | 556 | pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { |
| 522 | _ = self; | 557 | _ = self; |
| 523 | 558 | ||
| ... | @@ -1533,6 +1568,7 @@ const std = @import("std"); | ... | @@ -1533,6 +1568,7 @@ const std = @import("std"); |
| 1533 | 1568 | ||
| 1534 | const Air = @import("../../Air.zig"); | 1569 | const Air = @import("../../Air.zig"); |
| 1535 | const Allocator = std.mem.Allocator; | 1570 | const Allocator = std.mem.Allocator; |
| 1571 | const Archive = @import("Archive.zig"); | ||
| 1536 | const Atom = @import("Atom.zig"); | 1572 | const Atom = @import("Atom.zig"); |
| 1537 | const Dwarf = @import("../Dwarf.zig"); | 1573 | const Dwarf = @import("../Dwarf.zig"); |
| 1538 | const Elf = @import("../Elf.zig"); | 1574 | const Elf = @import("../Elf.zig"); |
src/link/Elf/file.zig+3-2| ... | @@ -196,9 +196,9 @@ pub const File = union(enum) { | ... | @@ -196,9 +196,9 @@ pub const File = union(enum) { |
| 196 | } | 196 | } |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | pub fn updateArSymtab(file: File, elf_file: *Elf) !void { | 199 | pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { |
| 200 | return switch (file) { | 200 | return switch (file) { |
| 201 | .zig_object => |x| x.updateArSymtab(elf_file), | 201 | .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), |
| 202 | .object => @panic("TODO"), | 202 | .object => @panic("TODO"), |
| 203 | inline else => unreachable, | 203 | inline else => unreachable, |
| 204 | }; | 204 | }; |
| ... | @@ -219,6 +219,7 @@ const std = @import("std"); | ... | @@ -219,6 +219,7 @@ const std = @import("std"); |
| 219 | const elf = std.elf; | 219 | const elf = std.elf; |
| 220 | 220 | ||
| 221 | const Allocator = std.mem.Allocator; | 221 | const Allocator = std.mem.Allocator; |
| 222 | const Archive = @import("Archive.zig"); | ||
| 222 | const Atom = @import("Atom.zig"); | 223 | const Atom = @import("Atom.zig"); |
| 223 | const Cie = @import("eh_frame.zig").Cie; | 224 | const Cie = @import("eh_frame.zig").Cie; |
| 224 | const Elf = @import("../Elf.zig"); | 225 | const Elf = @import("../Elf.zig"); |