| ... | @@ -89,6 +89,13 @@ relocs: RelocTable = .{}, | ... | @@ -89,6 +89,13 @@ relocs: RelocTable = .{}, |
| 89 | /// this will be a table indexed by index into the list of Atoms. | 89 | /// this will be a table indexed by index into the list of Atoms. |
| 90 | base_relocs: BaseRelocationTable = .{}, | 90 | base_relocs: BaseRelocationTable = .{}, |
| 91 | | 91 | |
| | 92 | /// Hot-code swapping state. |
| | 93 | hot_state: HotUpdateState = .{}, |
| | 94 | |
| | 95 | const HotUpdateState = struct { |
| | 96 | loaded_base_address: ?u64 = null, |
| | 97 | }; |
| | 98 | |
| 92 | const Entry = struct { | 99 | const Entry = struct { |
| 93 | target: SymbolWithLoc, | 100 | target: SymbolWithLoc, |
| 94 | // Index into the synthetic symbol table (i.e., file == null). | 101 | // Index into the synthetic symbol table (i.e., file == null). |
| ... | @@ -778,9 +785,147 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { | ... | @@ -778,9 +785,147 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { |
| 778 | file_offset + code.len, | 785 | file_offset + code.len, |
| 779 | }); | 786 | }); |
| 780 | self.resolveRelocs(atom_index, code); | 787 | self.resolveRelocs(atom_index, code); |
| | 788 | |
| | 789 | if (self.base.child_pid) |handle| { |
| | 790 | const vaddr = sym.value + (self.hot_state.loaded_base_address orelse self.getImageBase()); |
| | 791 | log.warn("hcs: writing to memory at address {x}", .{vaddr}); |
| | 792 | try debugMem(self.base.allocator, handle, vaddr, code); |
| | 793 | if (section.header.flags.MEM_WRITE == 0) { |
| | 794 | log.warn(" page not mapped for write access; re-mapping...", .{}); |
| | 795 | try writeMemProtected(handle, vaddr, code); |
| | 796 | } else { |
| | 797 | try writeMem(handle, vaddr, code); |
| | 798 | } |
| | 799 | } |
| | 800 | |
| 781 | try self.base.file.?.pwriteAll(code, file_offset); | 801 | try self.base.file.?.pwriteAll(code, file_offset); |
| 782 | } | 802 | } |
| 783 | | 803 | |
| | 804 | extern "kernel32" fn ReadProcessMemory( |
| | 805 | hProcess: std.os.windows.HANDLE, |
| | 806 | lpBaseAddress: std.os.windows.LPCVOID, |
| | 807 | lpBuffer: std.os.windows.LPVOID, |
| | 808 | nSize: std.os.windows.SIZE_T, |
| | 809 | lpNumberOfBytesRead: *std.os.windows.SIZE_T, |
| | 810 | ) std.os.windows.BOOL; |
| | 811 | |
| | 812 | extern "kernel32" fn WriteProcessMemory( |
| | 813 | hProcess: std.os.windows.HANDLE, |
| | 814 | lpBaseAddress: std.os.windows.LPVOID, |
| | 815 | lpBuffer: std.os.windows.LPCVOID, |
| | 816 | nSize: std.os.windows.SIZE_T, |
| | 817 | lpNumberOfBytesWritten: *std.os.windows.SIZE_T, |
| | 818 | ) std.os.windows.BOOL; |
| | 819 | |
| | 820 | extern "kernel32" fn VirtualProtectEx( |
| | 821 | hProcess: std.os.windows.HANDLE, |
| | 822 | lpAddress: std.os.windows.LPVOID, |
| | 823 | dwSize: std.os.windows.SIZE_T, |
| | 824 | flNewProtect: std.os.windows.DWORD, |
| | 825 | lpflOldProtect: *std.os.windows.DWORD, |
| | 826 | ) std.os.windows.BOOL; |
| | 827 | |
| | 828 | const PROCESS_BASIC_INFORMATION = extern struct { |
| | 829 | ExitStatus: std.os.windows.NTSTATUS, |
| | 830 | PebBaseAddress: *std.os.windows.PEB, |
| | 831 | AffinityMask: std.os.windows.ULONG_PTR, |
| | 832 | BasePriority: std.os.windows.KPRIORITY, |
| | 833 | UniqueProcessId: std.os.windows.ULONG_PTR, |
| | 834 | InheritedFromUniqueProcessId: std.os.windows.ULONG_PTR, |
| | 835 | }; |
| | 836 | |
| | 837 | fn getProcessBaseAddress(handle: std.ChildProcess.Id) !u64 { |
| | 838 | var info: PROCESS_BASIC_INFORMATION = undefined; |
| | 839 | var nread: std.os.windows.DWORD = 0; |
| | 840 | const rc = std.os.windows.ntdll.NtQueryInformationProcess( |
| | 841 | handle, |
| | 842 | .ProcessBasicInformation, |
| | 843 | &info, |
| | 844 | @sizeOf(PROCESS_BASIC_INFORMATION), |
| | 845 | &nread, |
| | 846 | ); |
| | 847 | switch (rc) { |
| | 848 | .SUCCESS => {}, |
| | 849 | else => return std.os.windows.unexpectedStatus(rc), |
| | 850 | } |
| | 851 | |
| | 852 | var peb_buf: [@sizeOf(std.os.windows.PEB)]u8 align(@alignOf(std.os.windows.PEB)) = undefined; |
| | 853 | var peb_nread: usize = 0; |
| | 854 | if (ReadProcessMemory( |
| | 855 | handle, |
| | 856 | info.PebBaseAddress, |
| | 857 | &peb_buf, |
| | 858 | @sizeOf(std.os.windows.PEB), |
| | 859 | &peb_nread, |
| | 860 | ) == 0) { |
| | 861 | const err = std.os.windows.kernel32.GetLastError(); |
| | 862 | log.warn("hcs: reading from process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) }); |
| | 863 | return error.FailedToReadPebForProcess; |
| | 864 | } |
| | 865 | if (peb_nread != @sizeOf(std.os.windows.PEB)) return error.InputOutput; |
| | 866 | |
| | 867 | const peb = @ptrCast(*const std.os.windows.PEB, &peb_buf); |
| | 868 | return @ptrToInt(peb.ImageBaseAddress); |
| | 869 | } |
| | 870 | |
| | 871 | fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void { |
| | 872 | var buffer = try allocator.alloc(u8, code.len); |
| | 873 | defer allocator.free(buffer); |
| | 874 | var nread: usize = 0; |
| | 875 | if (ReadProcessMemory( |
| | 876 | handle, |
| | 877 | @intToPtr(*anyopaque, vaddr), |
| | 878 | buffer.ptr, |
| | 879 | code.len, |
| | 880 | &nread, |
| | 881 | ) == 0) { |
| | 882 | const err = std.os.windows.kernel32.GetLastError(); |
| | 883 | log.warn("hcs: reading from process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) }); |
| | 884 | } |
| | 885 | if (nread != code.len) { |
| | 886 | log.warn("hcs: reading from process memory InputOutput error: read != requested: {x} != {x}", .{ nread, code.len }); |
| | 887 | } |
| | 888 | |
| | 889 | log.warn("in memory: {x}", .{std.fmt.fmtSliceHexLower(buffer)}); |
| | 890 | log.warn("to write: {x}", .{std.fmt.fmtSliceHexLower(code)}); |
| | 891 | } |
| | 892 | |
| | 893 | fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void { |
| | 894 | const pvaddr = @intToPtr(*anyopaque, vaddr); |
| | 895 | var new_prot: std.os.windows.DWORD = std.os.windows.PAGE_EXECUTE_WRITECOPY; |
| | 896 | var old_prot: std.os.windows.DWORD = undefined; |
| | 897 | if (VirtualProtectEx(handle, pvaddr, code.len, new_prot, &old_prot) == 0) { |
| | 898 | const err = std.os.windows.kernel32.GetLastError(); |
| | 899 | log.warn("hcs: making page(s) writeable failed with error: {s}({x})", .{ @tagName(err), @enumToInt(err) }); |
| | 900 | return; |
| | 901 | } |
| | 902 | log.warn("old = {x}, new = {x}", .{ old_prot, new_prot }); |
| | 903 | try writeMem(handle, vaddr, code); |
| | 904 | // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes. |
| | 905 | // For that though, we want to track which page has already been modified. |
| | 906 | if (VirtualProtectEx(handle, pvaddr, code.len, old_prot, &new_prot) == 0) { |
| | 907 | const err = std.os.windows.kernel32.GetLastError(); |
| | 908 | log.warn("hcs: restoring page(s) attributes failed with error: {s}({x})", .{ @tagName(err), @enumToInt(err) }); |
| | 909 | } |
| | 910 | } |
| | 911 | |
| | 912 | fn writeMem(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void { |
| | 913 | var nwritten: usize = 0; |
| | 914 | if (WriteProcessMemory( |
| | 915 | handle, |
| | 916 | @intToPtr(*anyopaque, vaddr), |
| | 917 | code.ptr, |
| | 918 | code.len, |
| | 919 | &nwritten, |
| | 920 | ) == 0) { |
| | 921 | const err = std.os.windows.kernel32.GetLastError(); |
| | 922 | log.warn("hcs: writing to process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) }); |
| | 923 | } |
| | 924 | if (nwritten != code.len) { |
| | 925 | log.warn("hcs: writing to process memory InputOutput error: written != requested: {x} != {x}", .{ nwritten, code.len }); |
| | 926 | } |
| | 927 | } |
| | 928 | |
| 784 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { | 929 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { |
| 785 | switch (self.ptr_width) { | 930 | switch (self.ptr_width) { |
| 786 | .p32 => { | 931 | .p32 => { |
| ... | @@ -827,9 +972,17 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void { | ... | @@ -827,9 +972,17 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void { |
| 827 | } | 972 | } |
| 828 | } | 973 | } |
| 829 | | 974 | |
| 830 | pub fn ptraceAttach(self: *Coff, handle: std.os.pid_t) !void { | 975 | pub fn ptraceAttach(self: *Coff, handle: std.ChildProcess.Id) !void { |
| 831 | _ = self; | 976 | log.warn("hcs: attaching to process with handle {*}", .{handle}); |
| 832 | log.warn("attaching to process with handle {*}", .{handle}); | 977 | self.hot_state.loaded_base_address = getProcessBaseAddress(handle) catch |err| { |
| | 978 | log.warn("hcs: failed to get base address for the process with error: {s}", .{@errorName(err)}); |
| | 979 | return; |
| | 980 | }; |
| | 981 | } |
| | 982 | |
| | 983 | pub fn ptraceDetach(self: *Coff, handle: std.ChildProcess.Id) void { |
| | 984 | log.warn("hcs: detaching from process with handle {*}", .{handle}); |
| | 985 | self.hot_state.loaded_base_address = null; |
| 833 | } | 986 | } |
| 834 | | 987 | |
| 835 | fn freeAtom(self: *Coff, atom_index: Atom.Index) void { | 988 | fn freeAtom(self: *Coff, atom_index: Atom.Index) void { |