| ... | ... | @@ -781,24 +781,47 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { |
| 781 | 781 | const sym = atom.getSymbol(self); |
| 782 | 782 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 783 | 783 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; |
| 784 | |
| 784 | 785 | log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ |
| 785 | 786 | atom.getName(self), |
| 786 | 787 | file_offset, |
| 787 | 788 | file_offset + code.len, |
| 788 | 789 | }); |
| 789 | 790 | |
| 791 | const gpa = self.base.allocator; |
| 792 | |
| 793 | // Gather relocs which can be resolved. |
| 794 | // We need to do this as we will be applying different slide values depending |
| 795 | // if we are running in hot-code swapping mode or not. |
| 796 | // TODO: how crazy would it be to try and apply the actual image base of the loaded |
| 797 | // process for the in-file values rather than the Windows defaults? |
| 798 | var relocs = std.ArrayList(*Relocation).init(gpa); |
| 799 | defer relocs.deinit(); |
| 800 | |
| 801 | if (self.relocs.getPtr(atom_index)) |rels| { |
| 802 | try relocs.ensureTotalCapacityPrecise(rels.items.len); |
| 803 | for (rels.items) |*reloc| { |
| 804 | if (reloc.isResolvable(self)) relocs.appendAssumeCapacity(reloc); |
| 805 | } |
| 806 | } |
| 807 | |
| 790 | 808 | if (self.base.child_pid) |handle| { |
| 791 | 809 | const slide = @ptrToInt(self.hot_state.loaded_base_address.?); |
| 792 | 810 | |
| 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); |
| 811 | const mem_code = try gpa.dupe(u8, code); |
| 812 | defer gpa.free(mem_code); |
| 813 | self.resolveRelocs(atom_index, relocs.items, mem_code, slide); |
| 796 | 814 | |
| 797 | 815 | const vaddr = sym.value + slide; |
| 798 | 816 | const pvaddr = @intToPtr(*anyopaque, vaddr); |
| 817 | |
| 799 | 818 | log.debug("writing to memory at address {x}", .{vaddr}); |
| 819 | |
| 820 | if (build_options.enable_logging) { |
| 821 | try debugMem(gpa, handle, pvaddr, mem_code); |
| 822 | } |
| 823 | |
| 800 | 824 | if (section.header.flags.MEM_WRITE == 0) { |
| 801 | | log.debug("page not mapped for write access; re-mapping...", .{}); |
| 802 | 825 | writeMemProtected(handle, pvaddr, mem_code) catch |err| { |
| 803 | 826 | log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); |
| 804 | 827 | }; |
| ... | ... | @@ -809,25 +832,29 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { |
| 809 | 832 | } |
| 810 | 833 | } |
| 811 | 834 | |
| 812 | | self.resolveRelocs(atom_index, code, self.getImageBase()); |
| 835 | self.resolveRelocs(atom_index, relocs.items, code, self.getImageBase()); |
| 813 | 836 | try self.base.file.?.pwriteAll(code, file_offset); |
| 837 | |
| 838 | // Now we can mark the relocs as resolved. |
| 839 | while (relocs.popOrNull()) |reloc| { |
| 840 | reloc.dirty = false; |
| 841 | } |
| 814 | 842 | } |
| 815 | 843 | |
| 816 | 844 | fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void { |
| 817 | 845 | var buffer = try allocator.alloc(u8, code.len); |
| 818 | 846 | defer allocator.free(buffer); |
| 819 | 847 | const memread = try std.os.windows.ReadProcessMemory(handle, pvaddr, buffer); |
| 820 | | log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)}); |
| 821 | 848 | log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)}); |
| 849 | log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)}); |
| 822 | 850 | } |
| 823 | 851 | |
| 824 | 852 | fn 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); |
| 853 | const old_prot = try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, std.os.windows.PAGE_EXECUTE_WRITECOPY); |
| 827 | 854 | try writeMem(handle, pvaddr, code); |
| 828 | 855 | // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes. |
| 829 | 856 | // For that though, we want to track which page has already been modified. |
| 830 | | try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, old_prot, null); |
| 857 | _ = try std.os.windows.VirtualProtectEx(handle, pvaddr, code.len, old_prot); |
| 831 | 858 | } |
| 832 | 859 | |
| 833 | 860 | fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []const u8) !void { |
| ... | ... | @@ -868,16 +895,10 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 868 | 895 | } |
| 869 | 896 | } |
| 870 | 897 | |
| 871 | | fn resolveRelocs(self: *Coff, atom_index: Atom.Index, code: []u8, image_base: u64) void { |
| 872 | | const relocs = self.relocs.getPtr(atom_index) orelse return; |
| 873 | | |
| 898 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void { |
| 874 | 899 | log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)}); |
| 875 | | |
| 876 | | for (relocs.items) |*reloc| { |
| 877 | | if (!reloc.dirty) continue; |
| 878 | | if (reloc.resolve(atom_index, code, image_base, self)) { |
| 879 | | reloc.dirty = false; |
| 880 | | } |
| 900 | for (relocs) |reloc| { |
| 901 | reloc.resolve(atom_index, code, image_base, self); |
| 881 | 902 | } |
| 882 | 903 | } |
| 883 | 904 | |
| ... | ... | @@ -1488,7 +1509,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1488 | 1509 | |
| 1489 | 1510 | for (self.relocs.keys(), self.relocs.values()) |atom_index, relocs| { |
| 1490 | 1511 | const needs_update = for (relocs.items) |reloc| { |
| 1491 | | if (reloc.dirty) break true; |
| 1512 | if (reloc.isResolvable(self)) break true; |
| 1492 | 1513 | } else false; |
| 1493 | 1514 | |
| 1494 | 1515 | if (!needs_update) continue; |