Commit 4f4ddf5ef2

Andrew Kelley <andrew@ziglang.org>
2022-01-22 02:47:29
hot code swapping PoC working
- improve fn prototypes of process_vm_writev - make the memory writable in the ELF file - force the linker to always append the function - write updates with process_vm_writev
1 parent 50a2bb5
Changed files (2)
lib
std
src
link
lib/std/os/linux.zig
@@ -1716,26 +1716,26 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u
     );
 }
 
-pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize {
+pub fn process_vm_readv(pid: pid_t, local: []iovec, remote: []const iovec_const, flags: usize) usize {
     return syscall6(
         .process_vm_readv,
         @bitCast(usize, @as(isize, pid)),
-        @ptrToInt(local),
-        local_count,
-        @ptrToInt(remote),
-        remote_count,
+        @ptrToInt(local.ptr),
+        local.len,
+        @ptrToInt(remote.ptr),
+        remote.len,
         flags,
     );
 }
 
-pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize {
+pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const iovec_const, flags: usize) usize {
     return syscall6(
         .process_vm_writev,
         @bitCast(usize, @as(isize, pid)),
-        @ptrToInt(local),
-        local_count,
-        @ptrToInt(remote),
-        remote_count,
+        @ptrToInt(local.ptr),
+        local.len,
+        @ptrToInt(remote.ptr),
+        remote.len,
         flags,
     );
 }
src/link/Elf.zig
@@ -467,7 +467,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
             .p_paddr = entry_addr,
             .p_memsz = file_size,
             .p_align = p_align,
-            .p_flags = elf.PF_X | elf.PF_R,
+            .p_flags = elf.PF_X | elf.PF_R | elf.PF_W,
         });
         self.entry_addr = null;
         self.phdr_table_dirty = true;
@@ -493,7 +493,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
             .p_paddr = got_addr,
             .p_memsz = file_size,
             .p_align = p_align,
-            .p_flags = elf.PF_R,
+            .p_flags = elf.PF_R | elf.PF_W,
         });
         self.phdr_table_dirty = true;
     }
@@ -516,7 +516,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
             .p_paddr = rodata_addr,
             .p_memsz = file_size,
             .p_align = p_align,
-            .p_flags = elf.PF_R,
+            .p_flags = elf.PF_R | elf.PF_W,
         });
         self.phdr_table_dirty = true;
     }
@@ -2451,6 +2451,23 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
     const phdr_index = self.sections.items(.phdr_index)[shdr_index];
     const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
     const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
+
+    if (self.base.child_pid) |pid| {
+        var code_vec: [1]std.os.iovec_const = .{.{
+            .iov_base = code.ptr,
+            .iov_len = code.len,
+        }};
+        var remote_vec: [1]std.os.iovec_const = .{.{
+            .iov_base = @intToPtr([*]u8, local_sym.st_value),
+            .iov_len = code.len,
+        }};
+        const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
+        switch (std.os.errno(rc)) {
+            .SUCCESS => assert(rc == code.len),
+            else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
+        }
+    }
+
     try self.base.file.?.pwriteAll(code, file_offset);
 
     return local_sym;
@@ -2820,6 +2837,8 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
     const endian = self.base.options.target.cpu.arch.endian();
     const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
     const off = shdr.sh_offset + @as(u64, entry_size) * index;
+    const phdr = &self.program_headers.items[self.phdr_got_index.?];
+    const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;
     switch (entry_size) {
         2 => {
             var buf: [2]u8 = undefined;
@@ -2835,6 +2854,22 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
             var buf: [8]u8 = undefined;
             mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
             try self.base.file.?.pwriteAll(&buf, off);
+
+            if (self.base.child_pid) |pid| {
+                var local_vec: [1]std.os.iovec_const = .{.{
+                    .iov_base = &buf,
+                    .iov_len = buf.len,
+                }};
+                var remote_vec: [1]std.os.iovec_const = .{.{
+                    .iov_base = @intToPtr([*]u8, vaddr),
+                    .iov_len = buf.len,
+                }};
+                const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
+                switch (std.os.errno(rc)) {
+                    .SUCCESS => assert(rc == buf.len),
+                    else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
+                }
+            }
         },
         else => unreachable,
     }