| ... | ... | @@ -1473,10 +1473,7 @@ fn initOffsetTable(self: *ZigObject, allocator: Allocator, elf_file: *Elf) error |
| 1473 | 1473 | esym.st_info |= elf.STT_OBJECT; |
| 1474 | 1474 | const atom_ptr = sym.atom(elf_file).?; |
| 1475 | 1475 | atom_ptr.alive = true; |
| 1476 | | atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(switch (elf_file.ptr_width) { |
| 1477 | | .p32 => 4, |
| 1478 | | .p64 => 8, |
| 1479 | | }); |
| 1476 | atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(OffsetTable.alignment(elf_file.getTarget().cpu.arch)); |
| 1480 | 1477 | atom_ptr.output_section_index = elf_file.zig_text_section_index.?; |
| 1481 | 1478 | self.offset_table = OffsetTable{ .sym_index = sym_index }; |
| 1482 | 1479 | return &(self.offset_table.?); |
| ... | ... | @@ -1754,15 +1751,31 @@ pub const OffsetTable = struct { |
| 1754 | 1751 | return sym.atom(elf_file).?.size; |
| 1755 | 1752 | } |
| 1756 | 1753 | |
| 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 | 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 | } |
| 1760 | 1764 | |
| 1761 | 1765 | pub fn entryOffset(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) u64 { |
| 1762 | 1766 | const sym = zo.symbol(ot.sym_index); |
| 1763 | 1767 | const atom_ptr = sym.atom(elf_file).?; |
| 1764 | 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 | } |
| 1767 | 1780 | |
| 1768 | 1781 | pub fn targetAddress(ot: OffsetTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 { |
| ... | ... | @@ -1770,56 +1783,43 @@ pub const OffsetTable = struct { |
| 1770 | 1783 | return zo.symbol(sym_index).address(.{}, elf_file); |
| 1771 | 1784 | } |
| 1772 | 1785 | |
| 1786 | const max_jump_seq_len = 12; |
| 1787 | |
| 1773 | 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 | 1789 | const fileoff = ot.entryOffset(index, zo, elf_file); |
| 1778 | | const vaddr: u64 = @intCast(ot.entryAddress(index, zo, elf_file)); |
| 1779 | | const value = ot.targetAddress(index, zo, elf_file); |
| 1780 | | switch (entry_size) { |
| 1781 | | 2 => { |
| 1782 | | var buf: [2]u8 = undefined; |
| 1783 | | std.mem.writeInt(u16, &buf, @intCast(value), endian); |
| 1784 | | try elf_file.base.file.?.pwriteAll(&buf, fileoff); |
| 1785 | | }, |
| 1786 | | 4 => { |
| 1787 | | var buf: [4]u8 = undefined; |
| 1788 | | std.mem.writeInt(u32, &buf, @intCast(value), endian); |
| 1789 | | try elf_file.base.file.?.pwriteAll(&buf, fileoff); |
| 1790 | | }, |
| 1791 | | 8 => { |
| 1792 | | var buf: [8]u8 = undefined; |
| 1793 | | std.mem.writeInt(u64, &buf, @intCast(value), endian); |
| 1794 | | try elf_file.base.file.?.pwriteAll(&buf, fileoff); |
| 1795 | | |
| 1796 | | if (elf_file.base.child_pid) |pid| { |
| 1797 | | switch (builtin.os.tag) { |
| 1798 | | .linux => { |
| 1799 | | var local_vec: [1]std.posix.iovec_const = .{.{ |
| 1800 | | .base = &buf, |
| 1801 | | .len = buf.len, |
| 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, |
| 1790 | const source_addr = ot.entryAddress(index, zo, elf_file); |
| 1791 | const target_addr = @as(i64, @intCast(ot.targetAddress(index, zo, elf_file))); |
| 1792 | var buf: [max_jump_seq_len]u8 = undefined; |
| 1793 | const out = switch (elf_file.getTarget().cpu.arch) { |
| 1794 | .x86_64 => try x86_64.writeEntry(source_addr, target_addr, &buf), |
| 1795 | else => @panic("TODO implement write entry for this CPU arch"), |
| 1796 | }; |
| 1797 | try elf_file.base.file.?.pwriteAll(out, fileoff); |
| 1798 | |
| 1799 | if (elf_file.base.child_pid) |pid| { |
| 1800 | switch (builtin.os.tag) { |
| 1801 | .linux => { |
| 1802 | var local_vec: [1]std.posix.iovec_const = .{.{ |
| 1803 | .base = out.ptr, |
| 1804 | .len = out.len, |
| 1805 | }}; |
| 1806 | var remote_vec: [1]std.posix.iovec_const = .{.{ |
| 1807 | .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))), |
| 1808 | .len = out.len, |
| 1809 | }}; |
| 1810 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); |
| 1811 | switch (std.os.linux.E.init(rc)) { |
| 1812 | .SUCCESS => assert(rc == out.len), |
| 1813 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), |
| 1814 | 1814 | } |
| 1815 | | } |
| 1816 | | }, |
| 1817 | | else => unreachable, |
| 1815 | }, |
| 1816 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| 1817 | } |
| 1818 | 1818 | } |
| 1819 | 1819 | } |
| 1820 | 1820 | |
| 1821 | 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 | 1823 | const sym = zo.symbol(ot.sym_index); |
| 1824 | 1824 | const esym = &zo.symtab.items(.elf_sym)[sym.esym_index]; |
| 1825 | 1825 | esym.st_size = ot_size; |
| ... | ... | @@ -1871,6 +1871,19 @@ pub const OffsetTable = struct { |
| 1871 | 1871 | }; |
| 1872 | 1872 | |
| 1873 | 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 | }; |
| 1875 | 1888 | |
| 1876 | 1889 | const assert = std.debug.assert; |