| ... | @@ -93,7 +93,9 @@ base_relocs: BaseRelocationTable = .{}, | ... | @@ -93,7 +93,9 @@ base_relocs: BaseRelocationTable = .{}, |
| 93 | hot_state: HotUpdateState = .{}, | 93 | hot_state: HotUpdateState = .{}, |
| 94 | | 94 | |
| 95 | const HotUpdateState = struct { | 95 | const HotUpdateState = struct { |
| 96 | loaded_base_address: ?u64 = null, | 96 | /// Base address at which the process (image) got loaded. |
| | 97 | /// We need this info to correctly slide pointers when relocating. |
| | 98 | loaded_base_address: ?std.os.windows.HMODULE = null, |
| 97 | }; | 99 | }; |
| 98 | | 100 | |
| 99 | const Entry = struct { | 101 | const Entry = struct { |
| ... | @@ -784,139 +786,53 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { | ... | @@ -784,139 +786,53 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { |
| 784 | file_offset, | 786 | file_offset, |
| 785 | file_offset + code.len, | 787 | file_offset + code.len, |
| 786 | }); | 788 | }); |
| 787 | self.resolveRelocs(atom_index, code); | | |
| 788 | | 789 | |
| 789 | if (self.base.child_pid) |handle| { | 790 | if (self.base.child_pid) |handle| { |
| 790 | const vaddr = sym.value + (self.hot_state.loaded_base_address orelse self.getImageBase()); | 791 | const slide = @ptrToInt(self.hot_state.loaded_base_address.?); |
| | 792 | |
| | 793 | const mem_code = try self.base.allocator.dupe(u8, code); |
| | 794 | defer self.base.allocator.free(mem_code); |
| | 795 | self.resolveRelocs(atom_index, mem_code, slide); |
| | 796 | |
| | 797 | const vaddr = sym.value + slide; |
| | 798 | const pvaddr = @intToPtr(*anyopaque, vaddr); |
| 791 | log.debug("writing to memory at address {x}", .{vaddr}); | 799 | log.debug("writing to memory at address {x}", .{vaddr}); |
| 792 | if (section.header.flags.MEM_WRITE == 0) { | 800 | if (section.header.flags.MEM_WRITE == 0) { |
| 793 | log.debug("page not mapped for write access; re-mapping...", .{}); | 801 | log.debug("page not mapped for write access; re-mapping...", .{}); |
| 794 | try writeMemProtected(handle, vaddr, code); | 802 | writeMemProtected(handle, pvaddr, mem_code) catch |err| { |
| | 803 | log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); |
| | 804 | }; |
| 795 | } else { | 805 | } else { |
| 796 | if (WriteProcessMemory(handle, vaddr, code)) |amt| { | 806 | writeMem(handle, pvaddr, mem_code) catch |err| { |
| 797 | if (amt != code.len) return error.InputOutput; | 807 | log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); |
| 798 | } else |err| { | 808 | }; |
| 799 | log.warn("writing to process memory failed with error: {s}", .{@errorName(err)}); | | |
| 800 | } | | |
| 801 | } | 809 | } |
| 802 | } | 810 | } |
| 803 | | 811 | |
| | 812 | self.resolveRelocs(atom_index, code, self.getImageBase()); |
| 804 | try self.base.file.?.pwriteAll(code, file_offset); | 813 | try self.base.file.?.pwriteAll(code, file_offset); |
| 805 | } | 814 | } |
| 806 | | 815 | |
| 807 | extern "ntdll" fn NtReadVirtualMemory( | 816 | fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void { |
| 808 | ProcessHandle: std.os.windows.HANDLE, | | |
| 809 | BaseAddress: std.os.windows.PVOID, | | |
| 810 | Buffer: std.os.windows.LPVOID, | | |
| 811 | NumberOfBytesToRead: std.os.windows.SIZE_T, | | |
| 812 | NumberOfBytesRead: ?*std.os.windows.SIZE_T, | | |
| 813 | ) std.os.windows.NTSTATUS; | | |
| 814 | | | |
| 815 | extern "ntdll" fn NtWriteVirtualMemory( | | |
| 816 | ProcessHandle: std.os.windows.HANDLE, | | |
| 817 | BaseAddress: std.os.windows.PVOID, | | |
| 818 | Buffer: std.os.windows.LPCVOID, | | |
| 819 | NumberOfBytesToWrite: std.os.windows.SIZE_T, | | |
| 820 | NumberOfBytesWritten: ?*std.os.windows.SIZE_T, | | |
| 821 | ) std.os.windows.NTSTATUS; | | |
| 822 | | | |
| 823 | extern "ntdll" fn NtProtectVirtualMemory( | | |
| 824 | ProcessHandle: std.os.windows.HANDLE, | | |
| 825 | BaseAddress: *std.os.windows.PVOID, | | |
| 826 | NumberOfBytesToProtect: *std.os.windows.SIZE_T, | | |
| 827 | NewAccessProtection: std.os.windows.ULONG, | | |
| 828 | OldAccessProtection: *std.os.windows.ULONG, | | |
| 829 | ) std.os.windows.NTSTATUS; | | |
| 830 | | | |
| 831 | fn ReadProcessMemory(handle: std.os.windows.HANDLE, base_addr: usize, buffer: []u8) ![]u8 { | | |
| 832 | var nread: usize = 0; | | |
| 833 | switch (NtReadVirtualMemory( | | |
| 834 | handle, | | |
| 835 | @intToPtr(*anyopaque, base_addr), | | |
| 836 | buffer.ptr, | | |
| 837 | buffer.len, | | |
| 838 | &nread, | | |
| 839 | )) { | | |
| 840 | .SUCCESS => return buffer[0..nread], | | |
| 841 | else => |rc| return std.os.windows.unexpectedStatus(rc), | | |
| 842 | } | | |
| 843 | } | | |
| 844 | | | |
| 845 | fn WriteProcessMemory(handle: std.os.windows.HANDLE, base_addr: usize, buffer: []const u8) !usize { | | |
| 846 | var nwritten: usize = 0; | | |
| 847 | switch (NtWriteVirtualMemory( | | |
| 848 | handle, | | |
| 849 | @intToPtr(*anyopaque, base_addr), | | |
| 850 | @ptrCast(*const anyopaque, buffer.ptr), | | |
| 851 | buffer.len, | | |
| 852 | &nwritten, | | |
| 853 | )) { | | |
| 854 | .SUCCESS => return nwritten, | | |
| 855 | else => |rc| return std.os.windows.unexpectedStatus(rc), | | |
| 856 | } | | |
| 857 | } | | |
| 858 | | | |
| 859 | fn VirtualProtectEx(handle: std.os.windows.HANDLE, base_addr: usize, size: usize, new_prot: u32) !u32 { | | |
| 860 | var out_paddr = @intToPtr(*anyopaque, base_addr); | | |
| 861 | var out_size = size; | | |
| 862 | var old_prot: u32 = undefined; | | |
| 863 | switch (NtProtectVirtualMemory( | | |
| 864 | handle, | | |
| 865 | &out_paddr, | | |
| 866 | &out_size, | | |
| 867 | new_prot, | | |
| 868 | &old_prot, | | |
| 869 | )) { | | |
| 870 | .SUCCESS => return old_prot, | | |
| 871 | else => |rc| return std.os.windows.unexpectedStatus(rc), | | |
| 872 | } | | |
| 873 | } | | |
| 874 | | | |
| 875 | const PROCESS_BASIC_INFORMATION = extern struct { | | |
| 876 | ExitStatus: std.os.windows.NTSTATUS, | | |
| 877 | PebBaseAddress: *std.os.windows.PEB, | | |
| 878 | AffinityMask: std.os.windows.ULONG_PTR, | | |
| 879 | BasePriority: std.os.windows.KPRIORITY, | | |
| 880 | UniqueProcessId: std.os.windows.ULONG_PTR, | | |
| 881 | InheritedFromUniqueProcessId: std.os.windows.ULONG_PTR, | | |
| 882 | }; | | |
| 883 | | | |
| 884 | fn getProcessBaseAddress(handle: std.ChildProcess.Id) !u64 { | | |
| 885 | var info: PROCESS_BASIC_INFORMATION = undefined; | | |
| 886 | var nread: std.os.windows.DWORD = 0; | | |
| 887 | const rc = std.os.windows.ntdll.NtQueryInformationProcess( | | |
| 888 | handle, | | |
| 889 | .ProcessBasicInformation, | | |
| 890 | &info, | | |
| 891 | @sizeOf(PROCESS_BASIC_INFORMATION), | | |
| 892 | &nread, | | |
| 893 | ); | | |
| 894 | switch (rc) { | | |
| 895 | .SUCCESS => {}, | | |
| 896 | else => return std.os.windows.unexpectedStatus(rc), | | |
| 897 | } | | |
| 898 | | | |
| 899 | var peb_buf: [@sizeOf(std.os.windows.PEB)]u8 align(@alignOf(std.os.windows.PEB)) = undefined; | | |
| 900 | const pebout = try ReadProcessMemory(handle, @ptrToInt(info.PebBaseAddress), &peb_buf); | | |
| 901 | const peb = @ptrCast(*const std.os.windows.PEB, @alignCast(@alignOf(std.os.windows.PEB), pebout.ptr)); | | |
| 902 | return @ptrToInt(peb.ImageBaseAddress); | | |
| 903 | } | | |
| 904 | | | |
| 905 | fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void { | | |
| 906 | var buffer = try allocator.alloc(u8, code.len); | 817 | var buffer = try allocator.alloc(u8, code.len); |
| 907 | defer allocator.free(buffer); | 818 | defer allocator.free(buffer); |
| 908 | const memread = try ReadProcessMemory(handle, vaddr, buffer); | 819 | const memread = try std.os.windows.ReadProcessMemory(handle, pvaddr, buffer); |
| 909 | log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)}); | 820 | log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)}); |
| 910 | log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)}); | 821 | log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)}); |
| 911 | } | 822 | } |
| 912 | | 823 | |
| 913 | fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void { | 824 | fn writeMemProtected(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void { |
| 914 | const old_prot = try VirtualProtectEx(handle, vaddr, code.len, std.os.windows.PAGE_EXECUTE_WRITECOPY); | 825 | var old_prot: std.os.windows.DWORD = undefined; |
| 915 | const amt = try WriteProcessMemory(handle, vaddr, code); | 826 | try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, std.os.windows.PAGE_EXECUTE_WRITECOPY, &old_prot); |
| 916 | if (amt != code.len) return error.InputOutput; | 827 | try writeMem(handle, pvaddr, code); |
| 917 | // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes. | 828 | // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes. |
| 918 | // For that though, we want to track which page has already been modified. | 829 | // For that though, we want to track which page has already been modified. |
| 919 | _ = try VirtualProtectEx(handle, vaddr, code.len, old_prot); | 830 | try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, old_prot, null); |
| | 831 | } |
| | 832 | |
| | 833 | fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void { |
| | 834 | const amt = try std.os.windows.WriteProcessMemory(handle, pvaddr, code); |
| | 835 | if (amt != code.len) return error.InputOutput; |
| 920 | } | 836 | } |
| 921 | | 837 | |
| 922 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { | 838 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { |
| ... | @@ -952,14 +868,14 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { | ... | @@ -952,14 +868,14 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 952 | } | 868 | } |
| 953 | } | 869 | } |
| 954 | | 870 | |
| 955 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void { | 871 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8, image_base: u64) void { |
| 956 | const relocs = self.relocs.getPtr(atom_index) orelse return; | 872 | const relocs = self.relocs.getPtr(atom_index) orelse return; |
| 957 | | 873 | |
| 958 | log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)}); | 874 | log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)}); |
| 959 | | 875 | |
| 960 | for (relocs.items) |*reloc| { | 876 | for (relocs.items) |*reloc| { |
| 961 | if (!reloc.dirty) continue; | 877 | if (!reloc.dirty) continue; |
| 962 | if (reloc.resolve(atom_index, code, self)) { | 878 | if (reloc.resolve(atom_index, code, image_base, self)) { |
| 963 | reloc.dirty = false; | 879 | reloc.dirty = false; |
| 964 | } | 880 | } |
| 965 | } | 881 | } |
| ... | @@ -967,7 +883,7 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void { | ... | @@ -967,7 +883,7 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void { |
| 967 | | 883 | |
| 968 | pub fn ptraceAttach(self: *Coff, handle: std.ChildProcess.Id) !void { | 884 | pub fn ptraceAttach(self: *Coff, handle: std.ChildProcess.Id) !void { |
| 969 | log.debug("attaching to process with handle {*}", .{handle}); | 885 | log.debug("attaching to process with handle {*}", .{handle}); |
| 970 | self.hot_state.loaded_base_address = getProcessBaseAddress(handle) catch |err| { | 886 | self.hot_state.loaded_base_address = std.os.windows.ProcessBaseAddress(handle) catch |err| { |
| 971 | log.warn("failed to get base address for the process with error: {s}", .{@errorName(err)}); | 887 | log.warn("failed to get base address for the process with error: {s}", .{@errorName(err)}); |
| 972 | return; | 888 | return; |
| 973 | }; | 889 | }; |