authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-08-12 12:34:06+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-08-13 13:30:24+02:00
logd7c5fbce92b0da1bb302db4c4adab21775f3d98b
tree4258c88b6623a8ab3d366021cc3881c8b1996777
parentd328140858ab7f0f3eeb5d53b50bae32ab331686

elf: emit a jump table in place of offset table for functions


1 files changed, 62 insertions(+), 49 deletions(-)

src/link/Elf/ZigObject.zig+62-49
...@@ -1473,10 +1473,7 @@ fn initOffsetTable(self: *ZigObject, allocator: Allocator, elf_file: *Elf) error...@@ -1473,10 +1473,7 @@ fn initOffsetTable(self: *ZigObject, allocator: Allocator, elf_file: *Elf) error
1473 esym.st_info |= elf.STT_OBJECT;1473 esym.st_info |= elf.STT_OBJECT;
1474 const atom_ptr = sym.atom(elf_file).?;1474 const atom_ptr = sym.atom(elf_file).?;
1475 atom_ptr.alive = true;1475 atom_ptr.alive = true;
1476 atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(switch (elf_file.ptr_width) {1476 atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(OffsetTable.alignment(elf_file.getTarget().cpu.arch));
1477 .p32 => 4,
1478 .p64 => 8,
1479 });
1480 atom_ptr.output_section_index = elf_file.zig_text_section_index.?;1477 atom_ptr.output_section_index = elf_file.zig_text_section_index.?;
1481 self.offset_table = OffsetTable{ .sym_index = sym_index };1478 self.offset_table = OffsetTable{ .sym_index = sym_index };
1482 return &(self.offset_table.?);1479 return &(self.offset_table.?);
...@@ -1754,15 +1751,31 @@ pub const OffsetTable = struct {...@@ -1754,15 +1751,31 @@ pub const OffsetTable = struct {
1754 return sym.atom(elf_file).?.size;1751 return sym.atom(elf_file).?.size;
1755 }1752 }
17561753
1754 pub fn alignment(cpu_arch: std.Target.Cpu.Arch) u64 {
1755 return switch (cpu_arch) {
1756 .x86_64 => 1,
1757 else => @panic("TODO implement alignment for this CPU arch"),
1758 };
1759 }
1760
1757 pub fn entryAddress(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 {1761 pub fn entryAddress(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 {
1758 return ot.address(zo, elf_file) + index * elf_file.archPtrWidthBytes();1762 return ot.address(zo, elf_file) + @as(i64, @intCast(index * entrySize(elf_file.getTarget().cpu.arch)));
1759 }1763 }
17601764
1761 pub fn entryOffset(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) u64 {1765 pub fn entryOffset(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) u64 {
1762 const sym = zo.symbol(ot.sym_index);1766 const sym = zo.symbol(ot.sym_index);
1763 const atom_ptr = sym.atom(elf_file).?;1767 const atom_ptr = sym.atom(elf_file).?;
1764 const shdr = elf_file.shdrs.items[atom_ptr.output_section_index];1768 const shdr = elf_file.shdrs.items[atom_ptr.output_section_index];
1765 return shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)) + index * elf_file.archPtrWidthBytes();1769 return shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)) + index * entrySize(elf_file.getTarget().cpu.arch);
1770 }
1771
1772 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) u64 {
1773 const seq_len = switch (cpu_arch) {
1774 .x86_64 => 5, // jmp rel32
1775 else => @panic("TODO implement entry size for this CPU arch"),
1776 };
1777 comptime assert(seq_len <= max_jump_seq_len);
1778 return seq_len;
1766 }1779 }
17671780
1768 pub fn targetAddress(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 {1781 pub fn targetAddress(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 {
...@@ -1770,56 +1783,43 @@ pub const OffsetTable = struct {...@@ -1770,56 +1783,43 @@ pub const OffsetTable = struct {
1770 return zo.symbol(sym_index).address(.{}, elf_file);1783 return zo.symbol(sym_index).address(.{}, elf_file);
1771 }1784 }
17721785
1786 const max_jump_seq_len = 12;
1787
1773 pub fn writeEntry(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) !void {1788 pub fn writeEntry(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) !void {
1774 const entry_size: u16 = elf_file.archPtrWidthBytes();
1775 const target = elf_file.getTarget();
1776 const endian = target.cpu.arch.endian();
1777 const fileoff = ot.entryOffset(index, zo, elf_file);1789 const fileoff = ot.entryOffset(index, zo, elf_file);
1778 const vaddr: u64 = @intCast(ot.entryAddress(index, zo, elf_file));1790 const source_addr = ot.entryAddress(index, zo, elf_file);
1779 const value = ot.targetAddress(index, zo, elf_file);1791 const target_addr = @as(i64, @intCast(ot.targetAddress(index, zo, elf_file)));
1780 switch (entry_size) {1792 var buf: [max_jump_seq_len]u8 = undefined;
1781 2 => {1793 const out = switch (elf_file.getTarget().cpu.arch) {
1782 var buf: [2]u8 = undefined;1794 .x86_64 => try x86_64.writeEntry(source_addr, target_addr, &buf),
1783 std.mem.writeInt(u16, &buf, @intCast(value), endian);1795 else => @panic("TODO implement write entry for this CPU arch"),
1784 try elf_file.base.file.?.pwriteAll(&buf, fileoff);1796 };
1785 },1797 try elf_file.base.file.?.pwriteAll(out, fileoff);
1786 4 => {1798
1787 var buf: [4]u8 = undefined;1799 if (elf_file.base.child_pid) |pid| {
1788 std.mem.writeInt(u32, &buf, @intCast(value), endian);1800 switch (builtin.os.tag) {
1789 try elf_file.base.file.?.pwriteAll(&buf, fileoff);1801 .linux => {
1790 },1802 var local_vec: [1]std.posix.iovec_const = .{.{
1791 8 => {1803 .base = out.ptr,
1792 var buf: [8]u8 = undefined;1804 .len = out.len,
1793 std.mem.writeInt(u64, &buf, @intCast(value), endian);1805 }};
1794 try elf_file.base.file.?.pwriteAll(&buf, fileoff);1806 var remote_vec: [1]std.posix.iovec_const = .{.{
17951807 .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))),
1796 if (elf_file.base.child_pid) |pid| {1808 .len = out.len,
1797 switch (builtin.os.tag) {1809 }};
1798 .linux => {1810 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
1799 var local_vec: [1]std.posix.iovec_const = .{.{1811 switch (std.os.linux.E.init(rc)) {
1800 .base = &buf,1812 .SUCCESS => assert(rc == out.len),
1801 .len = buf.len,1813 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
1802 }};
1803 var remote_vec: [1]std.posix.iovec_const = .{.{
1804 .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
1805 .len = buf.len,
1806 }};
1807 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
1808 switch (std.os.linux.E.init(rc)) {
1809 .SUCCESS => assert(rc == buf.len),
1810 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
1811 }
1812 },
1813 else => return error.HotSwapUnavailableOnHostOperatingSystem,
1814 }1814 }
1815 }1815 },
1816 },1816 else => return error.HotSwapUnavailableOnHostOperatingSystem,
1817 else => unreachable,1817 }
1818 }1818 }
1819 }1819 }
18201820
1821 pub fn updateSize(ot: OffsetTable, zo: *ZigObject, elf_file: *Elf) !void {1821 pub fn updateSize(ot: OffsetTable, zo: *ZigObject, elf_file: *Elf) !void {
1822 const ot_size: u64 = @intCast(ot.entries.items(.sym_index).len * elf_file.archPtrWidthBytes());1822 const ot_size: u64 = @intCast(ot.entries.items(.sym_index).len * entrySize(elf_file.getTarget().cpu.arch));
1823 const sym = zo.symbol(ot.sym_index);1823 const sym = zo.symbol(ot.sym_index);
1824 const esym = &zo.symtab.items(.elf_sym)[sym.esym_index];1824 const esym = &zo.symtab.items(.elf_sym)[sym.esym_index];
1825 esym.st_size = ot_size;1825 esym.st_size = ot_size;
...@@ -1871,6 +1871,19 @@ pub const OffsetTable = struct {...@@ -1871,6 +1871,19 @@ pub const OffsetTable = struct {
1871 };1871 };
18721872
1873 pub const Index = u32;1873 pub const Index = u32;
1874
1875 const x86_64 = struct {
1876 fn writeEntry(source_addr: i64, target_addr: i64, buf: *[max_jump_seq_len]u8) ![]u8 {
1877 const disp = @as(i64, @intCast(target_addr)) - source_addr - 4;
1878 var bytes = [_]u8{
1879 0xe8, 0x00, 0x00, 0x00, 0x00, // jmp rel32
1880 };
1881 assert(bytes.len == entrySize(.x86_64));
1882 mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
1883 @memcpy(buf[0..bytes.len], &bytes);
1884 return buf[0..bytes.len];
1885 }
1886 };
1874};1887};
18751888
1876const assert = std.debug.assert;1889const assert = std.debug.assert;