| ... | @@ -55,11 +55,6 @@ debug_str_section_zig_size: u64 = 0, | ... | @@ -55,11 +55,6 @@ debug_str_section_zig_size: u64 = 0, |
| 55 | debug_aranges_section_zig_size: u64 = 0, | 55 | debug_aranges_section_zig_size: u64 = 0, |
| 56 | debug_line_section_zig_size: u64 = 0, | 56 | debug_line_section_zig_size: u64 = 0, |
| 57 | | 57 | |
| 58 | /// Function jump table containing trampolines to Zcu functions. | | |
| 59 | /// The table is used for Zig's incremental compilation and is embedded with | | |
| 60 | /// the machine code section. | | |
| 61 | jump_table: ?JumpTable = null, | | |
| 62 | | | |
| 63 | pub const global_symbol_bit: u32 = 0x80000000; | 58 | pub const global_symbol_bit: u32 = 0x80000000; |
| 64 | pub const symbol_mask: u32 = 0x7fffffff; | 59 | pub const symbol_mask: u32 = 0x7fffffff; |
| 65 | pub const SHN_ATOM: u16 = 0x100; | 60 | pub const SHN_ATOM: u16 = 0x100; |
| ... | @@ -127,10 +122,6 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { | ... | @@ -127,10 +122,6 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 127 | if (self.dwarf) |*dw| { | 122 | if (self.dwarf) |*dw| { |
| 128 | dw.deinit(); | 123 | dw.deinit(); |
| 129 | } | 124 | } |
| 130 | | | |
| 131 | if (self.jump_table) |*jt| { | | |
| 132 | jt.deinit(allocator); | | |
| 133 | } | | |
| 134 | } | 125 | } |
| 135 | | 126 | |
| 136 | pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { | 127 | pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { |
| ... | @@ -665,7 +656,7 @@ pub fn getNavVAddr( | ... | @@ -665,7 +656,7 @@ pub fn getNavVAddr( |
| 665 | else => try self.getOrCreateMetadataForNav(elf_file, nav_index), | 656 | else => try self.getOrCreateMetadataForNav(elf_file, nav_index), |
| 666 | }; | 657 | }; |
| 667 | const this_sym = self.symbol(this_sym_index); | 658 | const this_sym = self.symbol(this_sym_index); |
| 668 | const vaddr = this_sym.address(.{ .zjt = true }, elf_file); | 659 | const vaddr = this_sym.address(.{}, elf_file); |
| 669 | const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; | 660 | const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; |
| 670 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); | 661 | const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); |
| 671 | try parent_atom.addReloc(elf_file, .{ | 662 | try parent_atom.addReloc(elf_file, .{ |
| ... | @@ -914,12 +905,6 @@ fn updateNavCode( | ... | @@ -914,12 +905,6 @@ fn updateNavCode( |
| 914 | if (old_vaddr != atom_ptr.value) { | 905 | if (old_vaddr != atom_ptr.value) { |
| 915 | sym.value = 0; | 906 | sym.value = 0; |
| 916 | esym.st_value = 0; | 907 | esym.st_value = 0; |
| 917 | | | |
| 918 | if (stt_bits == elf.STT_FUNC) { | | |
| 919 | const extra = sym.extra(elf_file); | | |
| 920 | const jump_table = self.jumpTablePtr().?; | | |
| 921 | jump_table.entries.items(.dirty)[extra.zjt] = true; | | |
| 922 | } | | |
| 923 | } | 908 | } |
| 924 | } else if (code.len < old_size) { | 909 | } else if (code.len < old_size) { |
| 925 | atom_ptr.shrink(elf_file); | 910 | atom_ptr.shrink(elf_file); |
| ... | @@ -942,7 +927,7 @@ fn updateNavCode( | ... | @@ -942,7 +927,7 @@ fn updateNavCode( |
| 942 | .len = code.len, | 927 | .len = code.len, |
| 943 | }}; | 928 | }}; |
| 944 | var remote_vec: [1]std.posix.iovec_const = .{.{ | 929 | var remote_vec: [1]std.posix.iovec_const = .{.{ |
| 945 | .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{ .zjt = true }, elf_file))))), | 930 | .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))), |
| 946 | .len = code.len, | 931 | .len = code.len, |
| 947 | }}; | 932 | }}; |
| 948 | const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); | 933 | const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); |
| ... | @@ -1036,31 +1021,12 @@ pub fn updateFunc( | ... | @@ -1036,31 +1021,12 @@ pub fn updateFunc( |
| 1036 | const ip = &zcu.intern_pool; | 1021 | const ip = &zcu.intern_pool; |
| 1037 | const gpa = elf_file.base.comp.gpa; | 1022 | const gpa = elf_file.base.comp.gpa; |
| 1038 | const func = zcu.funcInfo(func_index); | 1023 | const func = zcu.funcInfo(func_index); |
| 1039 | if (!elf_file.base.isRelocatable() and self.jumpTablePtr() == null) { | | |
| 1040 | try self.initJumpTable(gpa, elf_file); | | |
| 1041 | } | | |
| 1042 | | 1024 | |
| 1043 | log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav }); | 1025 | log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav }); |
| 1044 | | 1026 | |
| 1045 | const sym_index = try self.getOrCreateMetadataForNav(elf_file, func.owner_nav); | 1027 | const sym_index = try self.getOrCreateMetadataForNav(elf_file, func.owner_nav); |
| 1046 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); | 1028 | self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); |
| 1047 | | 1029 | |
| 1048 | if (self.jumpTablePtr()) |jump_table| { | | |
| 1049 | const sym = self.symbol(sym_index); | | |
| 1050 | if (!sym.flags.has_zjt) { | | |
| 1051 | const index = try jump_table.addSymbol(gpa, sym_index); | | |
| 1052 | sym.flags.has_zjt = true; | | |
| 1053 | sym.addExtra(.{ .zjt = index }, elf_file); | | |
| 1054 | try jump_table.updateSize(self, elf_file); | | |
| 1055 | const old_vaddr = jump_table.address(self, elf_file); | | |
| 1056 | try self.symbol(jump_table.sym_index).atom(elf_file).?.allocate(elf_file); | | |
| 1057 | const new_vaddr = jump_table.address(self, elf_file); | | |
| 1058 | if (old_vaddr != new_vaddr) { | | |
| 1059 | jump_table.dirty = true; | | |
| 1060 | } | | |
| 1061 | } | | |
| 1062 | } | | |
| 1063 | | | |
| 1064 | var code_buffer = std.ArrayList(u8).init(gpa); | 1030 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 1065 | defer code_buffer.deinit(); | 1031 | defer code_buffer.deinit(); |
| 1066 | | 1032 | |
| ... | @@ -1087,14 +1053,22 @@ pub fn updateFunc( | ... | @@ -1087,14 +1053,22 @@ pub fn updateFunc( |
| 1087 | }; | 1053 | }; |
| 1088 | | 1054 | |
| 1089 | const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, code); | 1055 | const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, code); |
| | 1056 | const old_rva, const old_alignment = blk: { |
| | 1057 | const atom_ptr = self.symbol(sym_index).atom(elf_file).?; |
| | 1058 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; |
| | 1059 | }; |
| 1090 | try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC); | 1060 | try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC); |
| | 1061 | const new_rva, const new_alignment = blk: { |
| | 1062 | const atom_ptr = self.symbol(sym_index).atom(elf_file).?; |
| | 1063 | break :blk .{ atom_ptr.value, atom_ptr.alignment }; |
| | 1064 | }; |
| 1091 | | 1065 | |
| 1092 | if (dwarf_state) |*ds| { | 1066 | if (dwarf_state) |*ds| { |
| 1093 | const sym = self.symbol(sym_index); | 1067 | const sym = self.symbol(sym_index); |
| 1094 | try self.dwarf.?.commitNavState( | 1068 | try self.dwarf.?.commitNavState( |
| 1095 | pt, | 1069 | pt, |
| 1096 | func.owner_nav, | 1070 | func.owner_nav, |
| 1097 | @intCast(sym.address(.{ .zjt = true }, elf_file)), | 1071 | @intCast(sym.address(.{}, elf_file)), |
| 1098 | sym.atom(elf_file).?.size, | 1072 | sym.atom(elf_file).?.size, |
| 1099 | ds, | 1073 | ds, |
| 1100 | ); | 1074 | ); |
| ... | @@ -1102,23 +1076,39 @@ pub fn updateFunc( | ... | @@ -1102,23 +1076,39 @@ pub fn updateFunc( |
| 1102 | | 1076 | |
| 1103 | // Exports will be updated by `Zcu.processExports` after the update. | 1077 | // Exports will be updated by `Zcu.processExports` after the update. |
| 1104 | | 1078 | |
| 1105 | if (self.jumpTablePtr()) |jump_table| { | 1079 | if (old_rva != new_rva and old_rva > 0) { |
| 1106 | if (jump_table.dirty) { | 1080 | // If we had to reallocate the function, we re-use the existing slot for a trampoline. |
| 1107 | // TODO write in bulk | 1081 | // In the rare case that the function has been further overaligned we skip creating a |
| 1108 | for (jump_table.entries.items(.dirty), 0..) |*dirty, i| { | 1082 | // trampoline and update all symbols referring this function. |
| 1109 | try jump_table.writeEntry(@intCast(i), self, elf_file); | 1083 | if (old_alignment.order(new_alignment) == .lt) { |
| 1110 | dirty.* = false; | 1084 | @panic("TODO update all symbols referring this function"); |
| 1111 | } | 1085 | } |
| 1112 | } else { | 1086 | |
| 1113 | const sym = self.symbol(sym_index); | 1087 | // Create a trampoline to the new location at `old_rva`. |
| 1114 | const jt_index = sym.extra(elf_file).zjt; | 1088 | if (!self.symbol(sym_index).flags.has_trampoline) { |
| 1115 | var jt_entry = jump_table.entries.get(jt_index); | 1089 | const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{ |
| 1116 | if (jt_entry.dirty) { | 1090 | self.symbol(sym_index).name(elf_file), |
| 1117 | try jump_table.writeEntry(jt_index, self, elf_file); | 1091 | }); |
| 1118 | jt_entry.dirty = false; | 1092 | defer gpa.free(name); |
| 1119 | } | 1093 | const name_off = try self.addString(gpa, name); |
| 1120 | jump_table.entries.set(jt_index, jt_entry); | 1094 | const tr_size = trampolineSize(elf_file.getTarget().cpu.arch); |
| | 1095 | const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off); |
| | 1096 | const tr_sym = self.symbol(tr_sym_index); |
| | 1097 | const tr_esym = &self.symtab.items(.elf_sym)[tr_sym.esym_index]; |
| | 1098 | tr_esym.st_info |= elf.STT_OBJECT; |
| | 1099 | tr_esym.st_size = tr_size; |
| | 1100 | const tr_atom_ptr = tr_sym.atom(elf_file).?; |
| | 1101 | tr_atom_ptr.value = old_rva; |
| | 1102 | tr_atom_ptr.alive = true; |
| | 1103 | tr_atom_ptr.alignment = old_alignment; |
| | 1104 | tr_atom_ptr.output_section_index = elf_file.zig_text_section_index.?; |
| | 1105 | tr_atom_ptr.size = tr_size; |
| | 1106 | const target_sym = self.symbol(sym_index); |
| | 1107 | target_sym.addExtra(.{ .trampoline = tr_sym_index }, elf_file); |
| | 1108 | target_sym.flags.has_trampoline = true; |
| 1121 | } | 1109 | } |
| | 1110 | const target_sym = self.symbol(sym_index); |
| | 1111 | try writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file); |
| 1122 | } | 1112 | } |
| 1123 | } | 1113 | } |
| 1124 | | 1114 | |
| ... | @@ -1193,7 +1183,7 @@ pub fn updateNav( | ... | @@ -1193,7 +1183,7 @@ pub fn updateNav( |
| 1193 | try self.dwarf.?.commitNavState( | 1183 | try self.dwarf.?.commitNavState( |
| 1194 | pt, | 1184 | pt, |
| 1195 | nav_index, | 1185 | nav_index, |
| 1196 | @intCast(sym.address(.{ .zjt = true }, elf_file)), | 1186 | @intCast(sym.address(.{}, elf_file)), |
| 1197 | sym.atom(elf_file).?.size, | 1187 | sym.atom(elf_file).?.size, |
| 1198 | ns, | 1188 | ns, |
| 1199 | ); | 1189 | ); |
| ... | @@ -1474,21 +1464,50 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n | ... | @@ -1474,21 +1464,50 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n |
| 1474 | return lookup_gop.value_ptr.*; | 1464 | return lookup_gop.value_ptr.*; |
| 1475 | } | 1465 | } |
| 1476 | | 1466 | |
| 1477 | pub fn jumpTablePtr(self: *ZigObject) ?*JumpTable { | 1467 | const max_trampoline_len = 12; |
| 1478 | return if (self.jump_table) |*jt| jt else null; | | |
| 1479 | } | | |
| 1480 | | 1468 | |
| 1481 | fn initJumpTable(self: *ZigObject, allocator: Allocator, elf_file: *Elf) error{OutOfMemory}!void { | 1469 | fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 { |
| 1482 | const name_off = try self.addString(allocator, "__zig_jump_table"); | 1470 | const len = switch (cpu_arch) { |
| 1483 | const sym_index = try self.newSymbolWithAtom(allocator, name_off); | 1471 | .x86_64 => 5, // jmp rel32 |
| 1484 | const sym = self.symbol(sym_index); | 1472 | else => @panic("TODO implement trampoline size for this CPU arch"), |
| 1485 | const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; | 1473 | }; |
| 1486 | esym.st_info |= elf.STT_OBJECT; | 1474 | comptime assert(len <= max_trampoline_len); |
| 1487 | const atom_ptr = sym.atom(elf_file).?; | 1475 | return len; |
| 1488 | atom_ptr.alive = true; | 1476 | } |
| 1489 | atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(JumpTable.alignment(elf_file.getTarget().cpu.arch)); | 1477 | |
| 1490 | atom_ptr.output_section_index = elf_file.zig_text_section_index.?; | 1478 | fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void { |
| 1491 | self.jump_table = JumpTable{ .sym_index = sym_index }; | 1479 | const atom_ptr = tr_sym.atom(elf_file).?; |
| | 1480 | const shdr = elf_file.shdrs.items[atom_ptr.output_section_index]; |
| | 1481 | const fileoff = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); |
| | 1482 | const source_addr = tr_sym.address(.{}, elf_file); |
| | 1483 | const target_addr = target.address(.{ .trampoline = false }, elf_file); |
| | 1484 | var buf: [max_trampoline_len]u8 = undefined; |
| | 1485 | const out = switch (elf_file.getTarget().cpu.arch) { |
| | 1486 | .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf), |
| | 1487 | else => @panic("TODO implement write trampoline for this CPU arch"), |
| | 1488 | }; |
| | 1489 | try elf_file.base.file.?.pwriteAll(out, fileoff); |
| | 1490 | |
| | 1491 | if (elf_file.base.child_pid) |pid| { |
| | 1492 | switch (builtin.os.tag) { |
| | 1493 | .linux => { |
| | 1494 | var local_vec: [1]std.posix.iovec_const = .{.{ |
| | 1495 | .base = out.ptr, |
| | 1496 | .len = out.len, |
| | 1497 | }}; |
| | 1498 | var remote_vec: [1]std.posix.iovec_const = .{.{ |
| | 1499 | .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))), |
| | 1500 | .len = out.len, |
| | 1501 | }}; |
| | 1502 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); |
| | 1503 | switch (std.os.linux.E.init(rc)) { |
| | 1504 | .SUCCESS => assert(rc == out.len), |
| | 1505 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), |
| | 1506 | } |
| | 1507 | }, |
| | 1508 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| | 1509 | } |
| | 1510 | } |
| 1492 | } | 1511 | } |
| 1493 | | 1512 | |
| 1494 | pub fn asFile(self: *ZigObject) File { | 1513 | pub fn asFile(self: *ZigObject) File { |
| ... | @@ -1766,169 +1785,17 @@ const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata); | ... | @@ -1766,169 +1785,17 @@ const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata); |
| 1766 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata); | 1785 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata); |
| 1767 | const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); | 1786 | const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); |
| 1768 | | 1787 | |
| 1769 | pub const JumpTable = struct { | 1788 | const x86_64 = struct { |
| 1770 | sym_index: Symbol.Index, | 1789 | fn writeTrampolineCode(source_addr: i64, target_addr: i64, buf: *[max_trampoline_len]u8) ![]u8 { |
| 1771 | entries: std.MultiArrayList(Entry) = .{}, | 1790 | const disp = @as(i64, @intCast(target_addr)) - source_addr - 5; |
| 1772 | dirty: bool = false, | 1791 | var bytes = [_]u8{ |
| 1773 | | 1792 | 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32 |
| 1774 | pub fn deinit(jt: *JumpTable, allocator: Allocator) void { | | |
| 1775 | jt.entries.deinit(allocator); | | |
| 1776 | } | | |
| 1777 | | | |
| 1778 | pub fn addSymbol(jt: *JumpTable, allocator: Allocator, sym_index: Symbol.Index) !Index { | | |
| 1779 | const index: Index = @intCast(try jt.entries.addOne(allocator)); | | |
| 1780 | jt.entries.set(index, .{ .sym_index = sym_index }); | | |
| 1781 | return index; | | |
| 1782 | } | | |
| 1783 | | | |
| 1784 | pub fn address(jt: JumpTable, zo: *ZigObject, elf_file: *Elf) i64 { | | |
| 1785 | const sym = zo.symbol(jt.sym_index); | | |
| 1786 | return sym.address(.{}, elf_file); | | |
| 1787 | } | | |
| 1788 | | | |
| 1789 | pub fn size(jt: JumpTable, zo: *ZigObject, elf_file: *Elf) u64 { | | |
| 1790 | const sym = zo.symbol(jt.sym_index); | | |
| 1791 | return sym.atom(elf_file).?.size; | | |
| 1792 | } | | |
| 1793 | | | |
| 1794 | pub fn alignment(cpu_arch: std.Target.Cpu.Arch) u64 { | | |
| 1795 | return switch (cpu_arch) { | | |
| 1796 | .x86_64 => 1, | | |
| 1797 | else => @panic("TODO implement alignment for this CPU arch"), | | |
| 1798 | }; | | |
| 1799 | } | | |
| 1800 | | | |
| 1801 | pub fn entryAddress(jt: JumpTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 { | | |
| 1802 | return jt.address(zo, elf_file) + @as(i64, @intCast(index * entrySize(elf_file.getTarget().cpu.arch))); | | |
| 1803 | } | | |
| 1804 | | | |
| 1805 | pub fn entryOffset(jt: JumpTable, index: Index, zo: *ZigObject, elf_file: *Elf) u64 { | | |
| 1806 | const sym = zo.symbol(jt.sym_index); | | |
| 1807 | const atom_ptr = sym.atom(elf_file).?; | | |
| 1808 | const shdr = elf_file.shdrs.items[atom_ptr.output_section_index]; | | |
| 1809 | return shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)) + index * entrySize(elf_file.getTarget().cpu.arch); | | |
| 1810 | } | | |
| 1811 | | | |
| 1812 | pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) u64 { | | |
| 1813 | const seq_len = switch (cpu_arch) { | | |
| 1814 | .x86_64 => 5, // jmp rel32 | | |
| 1815 | else => @panic("TODO implement entry size for this CPU arch"), | | |
| 1816 | }; | 1793 | }; |
| 1817 | comptime assert(seq_len <= max_jump_seq_len); | 1794 | assert(bytes.len == trampolineSize(.x86_64)); |
| 1818 | return seq_len; | 1795 | mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little); |
| | 1796 | @memcpy(buf[0..bytes.len], &bytes); |
| | 1797 | return buf[0..bytes.len]; |
| 1819 | } | 1798 | } |
| 1820 | | | |
| 1821 | pub fn targetAddress(jt: JumpTable, index: Index, zo: *ZigObject, elf_file: *Elf) i64 { | | |
| 1822 | const sym_index = jt.entries.items(.sym_index)[index]; | | |
| 1823 | return zo.symbol(sym_index).address(.{}, elf_file); | | |
| 1824 | } | | |
| 1825 | | | |
| 1826 | const max_jump_seq_len = 12; | | |
| 1827 | | | |
| 1828 | pub fn writeEntry(jt: JumpTable, index: Index, zo: *ZigObject, elf_file: *Elf) !void { | | |
| 1829 | const fileoff = jt.entryOffset(index, zo, elf_file); | | |
| 1830 | const source_addr = jt.entryAddress(index, zo, elf_file); | | |
| 1831 | const target_addr = @as(i64, @intCast(jt.targetAddress(index, zo, elf_file))); | | |
| 1832 | var buf: [max_jump_seq_len]u8 = undefined; | | |
| 1833 | const out = switch (elf_file.getTarget().cpu.arch) { | | |
| 1834 | .x86_64 => try x86_64.writeEntry(source_addr, target_addr, &buf), | | |
| 1835 | else => @panic("TODO implement write entry for this CPU arch"), | | |
| 1836 | }; | | |
| 1837 | try elf_file.base.file.?.pwriteAll(out, fileoff); | | |
| 1838 | | | |
| 1839 | if (elf_file.base.child_pid) |pid| { | | |
| 1840 | switch (builtin.os.tag) { | | |
| 1841 | .linux => { | | |
| 1842 | var local_vec: [1]std.posix.iovec_const = .{.{ | | |
| 1843 | .base = out.ptr, | | |
| 1844 | .len = out.len, | | |
| 1845 | }}; | | |
| 1846 | var remote_vec: [1]std.posix.iovec_const = .{.{ | | |
| 1847 | .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))), | | |
| 1848 | .len = out.len, | | |
| 1849 | }}; | | |
| 1850 | const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); | | |
| 1851 | switch (std.os.linux.E.init(rc)) { | | |
| 1852 | .SUCCESS => assert(rc == out.len), | | |
| 1853 | else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), | | |
| 1854 | } | | |
| 1855 | }, | | |
| 1856 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | | |
| 1857 | } | | |
| 1858 | } | | |
| 1859 | } | | |
| 1860 | | | |
| 1861 | pub fn updateSize(jt: JumpTable, zo: *ZigObject, elf_file: *Elf) !void { | | |
| 1862 | const jt_size: u64 = @intCast(jt.entries.items(.sym_index).len * entrySize(elf_file.getTarget().cpu.arch)); | | |
| 1863 | const sym = zo.symbol(jt.sym_index); | | |
| 1864 | const esym = &zo.symtab.items(.elf_sym)[sym.esym_index]; | | |
| 1865 | esym.st_size = jt_size; | | |
| 1866 | const atom_ptr = sym.atom(elf_file).?; | | |
| 1867 | atom_ptr.size = jt_size; | | |
| 1868 | } | | |
| 1869 | | | |
| 1870 | const JumpTableFormatContext = struct { JumpTable, *ZigObject, *Elf }; | | |
| 1871 | | | |
| 1872 | pub fn format( | | |
| 1873 | jt: JumpTable, | | |
| 1874 | comptime unused_fmt_string: []const u8, | | |
| 1875 | options: std.fmt.FormatOptions, | | |
| 1876 | writer: anytype, | | |
| 1877 | ) !void { | | |
| 1878 | _ = jt; | | |
| 1879 | _ = unused_fmt_string; | | |
| 1880 | _ = options; | | |
| 1881 | _ = writer; | | |
| 1882 | @compileError("do not format JumpTable directly"); | | |
| 1883 | } | | |
| 1884 | | | |
| 1885 | pub fn fmt(jt: JumpTable, zo: *ZigObject, elf_file: *Elf) std.fmt.Formatter(format2) { | | |
| 1886 | return .{ .data = .{ jt, zo, elf_file } }; | | |
| 1887 | } | | |
| 1888 | | | |
| 1889 | fn format2( | | |
| 1890 | ctx: JumpTableFormatContext, | | |
| 1891 | comptime unused_fmt_string: []const u8, | | |
| 1892 | options: std.fmt.FormatOptions, | | |
| 1893 | writer: anytype, | | |
| 1894 | ) !void { | | |
| 1895 | _ = options; | | |
| 1896 | _ = unused_fmt_string; | | |
| 1897 | const jt, const zo, const ef = ctx; | | |
| 1898 | try writer.writeAll("__zig_jump_table\n"); | | |
| 1899 | try writer.print(" @{x} : size({x})\n", .{ jt.address(zo, ef), jt.size(zo, ef) }); | | |
| 1900 | for (jt.entries.items(.sym_index), jt.entries.items(.dirty)) |sym_index, dirty| { | | |
| 1901 | const sym = zo.symbol(sym_index); | | |
| 1902 | try writer.print(" {x} => {x} : %{d} : {s}", .{ | | |
| 1903 | sym.address(.{ .zjt = true }, ef), | | |
| 1904 | sym.address(.{}, ef), | | |
| 1905 | sym_index, | | |
| 1906 | sym.name(ef), | | |
| 1907 | }); | | |
| 1908 | if (dirty) try writer.writeAll(" : [!]"); | | |
| 1909 | try writer.writeByte('\n'); | | |
| 1910 | } | | |
| 1911 | } | | |
| 1912 | | | |
| 1913 | const Entry = struct { | | |
| 1914 | sym_index: Symbol.Index, | | |
| 1915 | dirty: bool = true, | | |
| 1916 | }; | | |
| 1917 | | | |
| 1918 | pub const Index = u32; | | |
| 1919 | | | |
| 1920 | const x86_64 = struct { | | |
| 1921 | fn writeEntry(source_addr: i64, target_addr: i64, buf: *[max_jump_seq_len]u8) ![]u8 { | | |
| 1922 | const disp = @as(i64, @intCast(target_addr)) - source_addr - 5; | | |
| 1923 | var bytes = [_]u8{ | | |
| 1924 | 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32 | | |
| 1925 | }; | | |
| 1926 | assert(bytes.len == entrySize(.x86_64)); | | |
| 1927 | mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little); | | |
| 1928 | @memcpy(buf[0..bytes.len], &bytes); | | |
| 1929 | return buf[0..bytes.len]; | | |
| 1930 | } | | |
| 1931 | }; | | |
| 1932 | }; | 1799 | }; |
| 1933 | | 1800 | |
| 1934 | const assert = std.debug.assert; | 1801 | const assert = std.debug.assert; |