authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-30 18:52:56+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-30 21:08:31+02:00
log216badef0bd90c6353bf32bd927e2c6d36b3ebf6
treefc1ec7f502b30d4b0b103d624f2ea33e918df5e6
parentba5302c4f88d2942890a5838e53eb078d61da123

coff: use std.os.windows wrappers; fix relocating in-file


2 files changed, 35 insertions(+), 119 deletions(-)

src/link/Coff.zig+33-117
......@@ -93,7 +93,9 @@ base_relocs: BaseRelocationTable = .{},
9393hot_state: HotUpdateState = .{},
9494
9595const 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,
9799};
98100
99101const Entry = struct {
......@@ -784,139 +786,53 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void {
784786 file_offset,
785787 file_offset + code.len,
786788 });
787 self.resolveRelocs(atom_index, code);
788789
789790 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);
791799 log.debug("writing to memory at address {x}", .{vaddr});
792800 if (section.header.flags.MEM_WRITE == 0) {
793801 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 };
795805 } else {
796 if (WriteProcessMemory(handle, vaddr, code)) |amt| {
797 if (amt != code.len) return error.InputOutput;
798 } else |err| {
799 log.warn("writing to process memory failed with error: {s}", .{@errorName(err)});
800 }
806 writeMem(handle, pvaddr, mem_code) catch |err| {
807 log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
808 };
801809 }
802810 }
803811
812 self.resolveRelocs(atom_index, code, self.getImageBase());
804813 try self.base.file.?.pwriteAll(code, file_offset);
805814}
806815
807extern "ntdll" fn NtReadVirtualMemory(
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
815extern "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
823extern "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
831fn 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
845fn 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
859fn 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
875const 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
884fn 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
905fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {
816fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void {
906817 var buffer = try allocator.alloc(u8, code.len);
907818 defer allocator.free(buffer);
908 const memread = try ReadProcessMemory(handle, vaddr, buffer);
819 const memread = try std.os.windows.ReadProcessMemory(handle, pvaddr, buffer);
909820 log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)});
910821 log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)});
911822}
912823
913fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {
914 const old_prot = try VirtualProtectEx(handle, vaddr, code.len, std.os.windows.PAGE_EXECUTE_WRITECOPY);
915 const amt = try WriteProcessMemory(handle, vaddr, code);
916 if (amt != code.len) return error.InputOutput;
824fn writeMemProtected(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void {
825 var old_prot: std.os.windows.DWORD = undefined;
826 try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, std.os.windows.PAGE_EXECUTE_WRITECOPY, &old_prot);
827 try writeMem(handle, pvaddr, code);
917828 // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes.
918829 // 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
833fn 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;
920836}
921837
922838fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
......@@ -952,14 +868,14 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
952868 }
953869}
954870
955fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void {
871fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8, image_base: u64) void {
956872 const relocs = self.relocs.getPtr(atom_index) orelse return;
957873
958874 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
959875
960876 for (relocs.items) |*reloc| {
961877 if (!reloc.dirty) continue;
962 if (reloc.resolve(atom_index, code, self)) {
878 if (reloc.resolve(atom_index, code, image_base, self)) {
963879 reloc.dirty = false;
964880 }
965881 }
......@@ -967,7 +883,7 @@ fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8) void {
967883
968884pub fn ptraceAttach(self: *Coff, handle: std.ChildProcess.Id) !void {
969885 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| {
971887 log.warn("failed to get base address for the process with error: {s}", .{@errorName(err)});
972888 return;
973889 };
src/link/Coff/Relocation.zig+2-2
......@@ -74,7 +74,7 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
7474
7575/// Returns `false` if obtaining the target address has been deferred until `flushModule`.
7676/// This can happen when trying to resolve address of an import table entry ahead of time.
77pub fn resolve(self: Relocation, atom_index: Atom.Index, code: []u8, coff_file: *Coff) bool {
77pub fn resolve(self: Relocation, atom_index: Atom.Index, code: []u8, image_base: u64, coff_file: *Coff) bool {
7878 const atom = coff_file.getAtom(atom_index);
7979 const source_sym = atom.getSymbol(coff_file);
8080 const source_vaddr = source_sym.value + self.offset;
......@@ -92,7 +92,7 @@ pub fn resolve(self: Relocation, atom_index: Atom.Index, code: []u8, coff_file:
9292 const ctx: Context = .{
9393 .source_vaddr = source_vaddr,
9494 .target_vaddr = target_vaddr_with_addend,
95 .image_base = coff_file.hot_state.loaded_base_address orelse coff_file.getImageBase(),
95 .image_base = image_base,
9696 .code = code,
9797 .ptr_width = coff_file.ptr_width,
9898 };