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 = .{},...@@ -93,7 +93,9 @@ base_relocs: BaseRelocationTable = .{},
93hot_state: HotUpdateState = .{},93hot_state: HotUpdateState = .{},
9494
95const HotUpdateState = struct {95const 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};
98100
99const Entry = struct {101const 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);
788789
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 }
803811
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}
806815
807extern "ntdll" fn NtReadVirtualMemory(816fn 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
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 {
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}
912823
913fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {824fn 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
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;
920}836}
921837
922fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {838fn 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}
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 {
956 const relocs = self.relocs.getPtr(atom_index) orelse return;872 const relocs = self.relocs.getPtr(atom_index) orelse return;
957873
958 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});874 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
959875
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 {
967883
968pub fn ptraceAttach(self: *Coff, handle: std.ChildProcess.Id) !void {884pub 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 };
src/link/Coff/Relocation.zig+2-2
...@@ -74,7 +74,7 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {...@@ -74,7 +74,7 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
7474
75/// Returns `false` if obtaining the target address has been deferred until `flushModule`.75/// Returns `false` if obtaining the target address has been deferred until `flushModule`.
76/// This can happen when trying to resolve address of an import table entry ahead of time.76/// 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 {
78 const atom = coff_file.getAtom(atom_index);78 const atom = coff_file.getAtom(atom_index);
79 const source_sym = atom.getSymbol(coff_file);79 const source_sym = atom.getSymbol(coff_file);
80 const source_vaddr = source_sym.value + self.offset;80 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:...@@ -92,7 +92,7 @@ pub fn resolve(self: Relocation, atom_index: Atom.Index, code: []u8, coff_file:
92 const ctx: Context = .{92 const ctx: Context = .{
93 .source_vaddr = source_vaddr,93 .source_vaddr = source_vaddr,
94 .target_vaddr = target_vaddr_with_addend,94 .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,
96 .code = code,96 .code = code,
97 .ptr_width = coff_file.ptr_width,97 .ptr_width = coff_file.ptr_width,
98 };98 };