| author | |
| committer | |
| log | f1e25cf43ec60075a4fc6f3eceb5a3af1f9f0712 |
| tree | 29533cb639a0c3fff0889f8d1233ef1a5cbafc8c |
| parent | 266c81322e4e7b6c0b7f0a7fe9873b092aef7f54 |
7 files changed, 165 insertions(+), 110 deletions(-)
build.zig+2| ... | @@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void { |
| 152 | if (only_install_lib_files) | 152 | if (only_install_lib_files) |
| 153 | return; | 153 | return; |
| 154 | 154 | ||
| 155 | const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS"); | ||
| 155 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); | 156 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); |
| 156 | const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null); | 157 | const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null); |
| 157 | const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null); | 158 | const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null); |
| ... | @@ -173,6 +174,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -173,6 +174,7 @@ pub fn build(b: *std.Build) !void { |
| 173 | exe.pie = pie; | 174 | exe.pie = pie; |
| 174 | exe.sanitize_thread = sanitize_thread; | 175 | exe.sanitize_thread = sanitize_thread; |
| 175 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; | 176 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; |
| 177 | exe.entitlements = entitlements; | ||
| 176 | exe.install(); | 178 | exe.install(); |
| 177 | 179 | ||
| 178 | const compile_step = b.step("compile", "Build the self-hosted compiler"); | 180 | const compile_step = b.step("compile", "Build the self-hosted compiler"); |
lib/std/macho.zig+4| ... | @@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct { | ... | @@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct { |
| 656 | pub fn segName(seg: *const segment_command_64) []const u8 { | 656 | pub fn segName(seg: *const segment_command_64) []const u8 { |
| 657 | return parseName(&seg.segname); | 657 | return parseName(&seg.segname); |
| 658 | } | 658 | } |
| 659 | |||
| 660 | pub fn isWriteable(seg: segment_command_64) bool { | ||
| 661 | return seg.initprot & PROT.WRITE != 0; | ||
| 662 | } | ||
| 659 | }; | 663 | }; |
| 660 | 664 | ||
| 661 | pub const PROT = struct { | 665 | pub const PROT = struct { |
src/link.zig+16| ... | @@ -392,6 +392,19 @@ pub const File = struct { | ... | @@ -392,6 +392,19 @@ pub const File = struct { |
| 392 | .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { | 392 | .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { |
| 393 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | 393 | log.warn("ptrace failure: {s}", .{@errorName(err)}); |
| 394 | }, | 394 | }, |
| 395 | .macos => { | ||
| 396 | const macho = base.cast(MachO).?; | ||
| 397 | if (macho.mach_task == null) { | ||
| 398 | if (std.os.darwin.machTaskForPid(pid)) |task| { | ||
| 399 | macho.mach_task = task; | ||
| 400 | std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0) catch |err| { | ||
| 401 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | ||
| 402 | }; | ||
| 403 | } else |err| { | ||
| 404 | log.warn("failed to acquire Mach task for child process: {s}", .{@errorName(err)}); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | }, | ||
| 395 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | 408 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| 396 | } | 409 | } |
| 397 | } | 410 | } |
| ... | @@ -430,6 +443,9 @@ pub const File = struct { | ... | @@ -430,6 +443,9 @@ pub const File = struct { |
| 430 | .linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| { | 443 | .linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| { |
| 431 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | 444 | log.warn("ptrace failure: {s}", .{@errorName(err)}); |
| 432 | }, | 445 | }, |
| 446 | .macos => std.os.ptrace(std.os.darwin.PT.KILL, pid, 0, 0) catch |err| { | ||
| 447 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | ||
| 448 | }, | ||
| 433 | else => return error.HotSwapUnavailableOnHostOperatingSystem, | 449 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| 434 | } | 450 | } |
| 435 | } | 451 | } |
src/link/MachO.zig+48-7| ... | @@ -221,6 +221,9 @@ lazy_bindings: BindingTable = .{}, | ... | @@ -221,6 +221,9 @@ lazy_bindings: BindingTable = .{}, |
| 221 | /// Table of tracked Decls. | 221 | /// Table of tracked Decls. |
| 222 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, | 222 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 223 | 223 | ||
| 224 | /// Mach task used when the compiler is in hot-code swapping mode. | ||
| 225 | mach_task: ?std.os.darwin.MachTask = null, | ||
| 226 | |||
| 224 | const DeclMetadata = struct { | 227 | const DeclMetadata = struct { |
| 225 | atom: Atom.Index, | 228 | atom: Atom.Index, |
| 226 | section: u8, | 229 | section: u8, |
| ... | @@ -584,7 +587,21 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -584,7 +587,21 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 584 | try self.allocateSpecialSymbols(); | 587 | try self.allocateSpecialSymbols(); |
| 585 | 588 | ||
| 586 | for (self.relocs.keys()) |atom_index| { | 589 | for (self.relocs.keys()) |atom_index| { |
| 587 | try Atom.resolveRelocations(self, atom_index); | 590 | if (self.relocs.get(atom_index) == null) continue; |
| 591 | |||
| 592 | const atom = self.getAtom(atom_index); | ||
| 593 | const sym = atom.getSymbol(self); | ||
| 594 | const section = self.sections.get(sym.n_sect - 1).header; | ||
| 595 | const file_offset = section.offset + sym.n_value - section.addr; | ||
| 596 | |||
| 597 | var code = std.ArrayList(u8).init(self.base.allocator); | ||
| 598 | defer code.deinit(); | ||
| 599 | try code.resize(atom.size); | ||
| 600 | |||
| 601 | const amt = try self.base.file.?.preadAll(code.items, file_offset); | ||
| 602 | if (amt != code.items.len) return error.InputOutput; | ||
| 603 | |||
| 604 | try self.writeAtom(atom_index, code.items); | ||
| 588 | } | 605 | } |
| 589 | 606 | ||
| 590 | if (build_options.enable_logging) { | 607 | if (build_options.enable_logging) { |
| ... | @@ -1052,14 +1069,38 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: | ... | @@ -1052,14 +1069,38 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: |
| 1052 | } | 1069 | } |
| 1053 | } | 1070 | } |
| 1054 | 1071 | ||
| 1055 | pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void { | 1072 | pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { |
| 1056 | const atom = self.getAtom(atom_index); | 1073 | const atom = self.getAtom(atom_index); |
| 1057 | const sym = atom.getSymbol(self); | 1074 | const sym = atom.getSymbol(self); |
| 1058 | const section = self.sections.get(sym.n_sect - 1); | 1075 | const section = self.sections.get(sym.n_sect - 1); |
| 1059 | const file_offset = section.header.offset + sym.n_value - section.header.addr; | 1076 | const file_offset = section.header.offset + sym.n_value - section.header.addr; |
| 1060 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); | 1077 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); |
| 1078 | |||
| 1079 | if (self.relocs.get(atom_index)) |relocs| { | ||
| 1080 | try Atom.resolveRelocations(self, atom_index, relocs.items, code); | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | if (self.base.child_pid) |pid| blk: { | ||
| 1084 | const task = self.mach_task orelse { | ||
| 1085 | log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid}); | ||
| 1086 | break :blk; | ||
| 1087 | }; | ||
| 1088 | self.writeAtomToMemory(task, section.segment_index, sym.n_value, code) catch |err| { | ||
| 1089 | log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); | ||
| 1090 | }; | ||
| 1091 | } | ||
| 1092 | |||
| 1061 | try self.base.file.?.pwriteAll(code, file_offset); | 1093 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1062 | try Atom.resolveRelocations(self, atom_index); | 1094 | } |
| 1095 | |||
| 1096 | fn writeAtomToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { | ||
| 1097 | const segment = self.segments.items[segment_index]; | ||
| 1098 | if (!segment.isWriteable()) { | ||
| 1099 | try task.setCurrProtection(addr, code.len, macho.PROT.READ | macho.PROT.WRITE | macho.PROT.COPY); | ||
| 1100 | } | ||
| 1101 | defer if (!segment.isWriteable()) task.setCurrProtection(addr, code.len, segment.initprot) catch {}; | ||
| 1102 | const nwritten = try task.writeMem(addr, code, self.base.options.target.cpu.arch); | ||
| 1103 | if (nwritten != code.len) return error.InputOutput; | ||
| 1063 | } | 1104 | } |
| 1064 | 1105 | ||
| 1065 | fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { | 1106 | fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { |
| ... | @@ -2063,7 +2104,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv | ... | @@ -2063,7 +2104,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 2063 | else | 2104 | else |
| 2064 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | 2105 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2065 | 2106 | ||
| 2066 | const code = switch (res) { | 2107 | var code = switch (res) { |
| 2067 | .ok => code_buffer.items, | 2108 | .ok => code_buffer.items, |
| 2068 | .fail => |em| { | 2109 | .fail => |em| { |
| 2069 | decl.analysis = .codegen_failure; | 2110 | decl.analysis = .codegen_failure; |
| ... | @@ -2115,7 +2156,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu | ... | @@ -2115,7 +2156,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2115 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ | 2156 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ |
| 2116 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, | 2157 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, |
| 2117 | }); | 2158 | }); |
| 2118 | const code = switch (res) { | 2159 | var code = switch (res) { |
| 2119 | .ok => code_buffer.items, | 2160 | .ok => code_buffer.items, |
| 2120 | .fail => |em| { | 2161 | .fail => |em| { |
| 2121 | decl.analysis = .codegen_failure; | 2162 | decl.analysis = .codegen_failure; |
| ... | @@ -2202,7 +2243,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) | ... | @@ -2202,7 +2243,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2202 | .parent_atom_index = atom.getSymbolIndex().?, | 2243 | .parent_atom_index = atom.getSymbolIndex().?, |
| 2203 | }); | 2244 | }); |
| 2204 | 2245 | ||
| 2205 | const code = switch (res) { | 2246 | var code = switch (res) { |
| 2206 | .ok => code_buffer.items, | 2247 | .ok => code_buffer.items, |
| 2207 | .fail => |em| { | 2248 | .fail => |em| { |
| 2208 | decl.analysis = .codegen_failure; | 2249 | decl.analysis = .codegen_failure; |
| ... | @@ -2375,7 +2416,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 { | ... | @@ -2375,7 +2416,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 { |
| 2375 | return sect_id; | 2416 | return sect_id; |
| 2376 | } | 2417 | } |
| 2377 | 2418 | ||
| 2378 | fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 { | 2419 | fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 { |
| 2379 | const gpa = self.base.allocator; | 2420 | const gpa = self.base.allocator; |
| 2380 | const mod = self.base.options.module.?; | 2421 | const mod = self.base.options.module.?; |
| 2381 | const decl = mod.declPtr(decl_index); | 2422 | const decl = mod.declPtr(decl_index); |
src/link/MachO/Atom.zig+4-12| ... | @@ -183,19 +183,11 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) ! | ... | @@ -183,19 +183,11 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) ! |
| 183 | try gop.value_ptr.append(gpa, binding); | 183 | try gop.value_ptr.append(gpa, binding); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void { | 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation, code: []u8) !void { |
| 187 | const atom = macho_file.getAtom(atom_index); | 187 | log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)}); |
| 188 | const relocs = macho_file.relocs.get(atom_index) orelse return; | 188 | for (relocs) |*reloc| { |
| 189 | const source_sym = atom.getSymbol(macho_file); | ||
| 190 | const source_section = macho_file.sections.get(source_sym.n_sect - 1).header; | ||
| 191 | const file_offset = source_section.offset + source_sym.n_value - source_section.addr; | ||
| 192 | |||
| 193 | log.debug("relocating '{s}'", .{atom.getName(macho_file)}); | ||
| 194 | |||
| 195 | for (relocs.items) |*reloc| { | ||
| 196 | if (!reloc.dirty) continue; | 189 | if (!reloc.dirty) continue; |
| 197 | 190 | try reloc.resolve(macho_file, atom_index, code); | |
| 198 | try reloc.resolve(macho_file, atom_index, file_offset); | ||
| 199 | reloc.dirty = false; | 191 | reloc.dirty = false; |
| 200 | } | 192 | } |
| 201 | } | 193 | } |
src/link/MachO/Relocation.zig+61-85| ... | @@ -50,7 +50,7 @@ pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index { | ... | @@ -50,7 +50,7 @@ pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index { |
| 50 | return macho_file.getAtomIndexForSymbol(self.target); | 50 | return macho_file.getAtomIndexForSymbol(self.target); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void { | 53 | pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) !void { |
| 54 | const arch = macho_file.base.options.target.cpu.arch; | 54 | const arch = macho_file.base.options.target.cpu.arch; |
| 55 | const atom = macho_file.getAtom(atom_index); | 55 | const atom = macho_file.getAtom(atom_index); |
| 56 | const source_sym = atom.getSymbol(macho_file); | 56 | const source_sym = atom.getSymbol(macho_file); |
| ... | @@ -68,42 +68,28 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, bas | ... | @@ -68,42 +68,28 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, bas |
| 68 | }); | 68 | }); |
| 69 | 69 | ||
| 70 | switch (arch) { | 70 | switch (arch) { |
| 71 | .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset), | 71 | .aarch64 => return self.resolveAarch64(source_addr, target_addr, code), |
| 72 | .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset), | 72 | .x86_64 => return self.resolveX8664(source_addr, target_addr, code), |
| 73 | else => unreachable, | 73 | else => unreachable, |
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | fn resolveAarch64( | 77 | fn resolveAarch64( |
| 78 | self: Relocation, | 78 | self: Relocation, |
| 79 | macho_file: *MachO, | ||
| 80 | source_addr: u64, | 79 | source_addr: u64, |
| 81 | target_addr: i64, | 80 | target_addr: i64, |
| 82 | base_offset: u64, | 81 | code: []u8, |
| 83 | ) !void { | 82 | ) !void { |
| 84 | const rel_type = @intToEnum(macho.reloc_type_arm64, self.type); | 83 | const rel_type = @intToEnum(macho.reloc_type_arm64, self.type); |
| 85 | if (rel_type == .ARM64_RELOC_UNSIGNED) { | 84 | if (rel_type == .ARM64_RELOC_UNSIGNED) { |
| 86 | var buffer: [@sizeOf(u64)]u8 = undefined; | 85 | return switch (self.length) { |
| 87 | const code = blk: { | 86 | 2 => mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))), |
| 88 | switch (self.length) { | 87 | 3 => mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)), |
| 89 | 2 => { | 88 | else => unreachable, |
| 90 | mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr))); | ||
| 91 | break :blk buffer[0..4]; | ||
| 92 | }, | ||
| 93 | 3 => { | ||
| 94 | mem.writeIntLittle(u64, &buffer, @bitCast(u64, target_addr)); | ||
| 95 | break :blk &buffer; | ||
| 96 | }, | ||
| 97 | else => unreachable, | ||
| 98 | } | ||
| 99 | }; | 89 | }; |
| 100 | return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset); | ||
| 101 | } | 90 | } |
| 102 | 91 | ||
| 103 | var buffer: [@sizeOf(u32)]u8 = undefined; | 92 | var buffer = code[self.offset..][0..4]; |
| 104 | const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset); | ||
| 105 | if (amt != buffer.len) return error.InputOutput; | ||
| 106 | |||
| 107 | switch (rel_type) { | 93 | switch (rel_type) { |
| 108 | .ARM64_RELOC_BRANCH26 => { | 94 | .ARM64_RELOC_BRANCH26 => { |
| 109 | const displacement = math.cast( | 95 | const displacement = math.cast( |
| ... | @@ -114,10 +100,10 @@ fn resolveAarch64( | ... | @@ -114,10 +100,10 @@ fn resolveAarch64( |
| 114 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( | 100 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( |
| 115 | aarch64.Instruction, | 101 | aarch64.Instruction, |
| 116 | aarch64.Instruction.unconditional_branch_immediate, | 102 | aarch64.Instruction.unconditional_branch_immediate, |
| 117 | ), &buffer), | 103 | ), buffer), |
| 118 | }; | 104 | }; |
| 119 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); | 105 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); |
| 120 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | 106 | mem.writeIntLittle(u32, buffer, inst.toU32()); |
| 121 | }, | 107 | }, |
| 122 | .ARM64_RELOC_PAGE21, | 108 | .ARM64_RELOC_PAGE21, |
| 123 | .ARM64_RELOC_GOT_LOAD_PAGE21, | 109 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| ... | @@ -130,31 +116,31 @@ fn resolveAarch64( | ... | @@ -130,31 +116,31 @@ fn resolveAarch64( |
| 130 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( | 116 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( |
| 131 | aarch64.Instruction, | 117 | aarch64.Instruction, |
| 132 | aarch64.Instruction.pc_relative_address, | 118 | aarch64.Instruction.pc_relative_address, |
| 133 | ), &buffer), | 119 | ), buffer), |
| 134 | }; | 120 | }; |
| 135 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); | 121 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); |
| 136 | inst.pc_relative_address.immlo = @truncate(u2, pages); | 122 | inst.pc_relative_address.immlo = @truncate(u2, pages); |
| 137 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | 123 | mem.writeIntLittle(u32, buffer, inst.toU32()); |
| 138 | }, | 124 | }, |
| 139 | .ARM64_RELOC_PAGEOFF12, | 125 | .ARM64_RELOC_PAGEOFF12, |
| 140 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | 126 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 141 | => { | 127 | => { |
| 142 | const narrowed = @truncate(u12, @intCast(u64, target_addr)); | 128 | const narrowed = @truncate(u12, @intCast(u64, target_addr)); |
| 143 | if (isArithmeticOp(&buffer)) { | 129 | if (isArithmeticOp(buffer)) { |
| 144 | var inst = aarch64.Instruction{ | 130 | var inst = aarch64.Instruction{ |
| 145 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( | 131 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( |
| 146 | aarch64.Instruction, | 132 | aarch64.Instruction, |
| 147 | aarch64.Instruction.add_subtract_immediate, | 133 | aarch64.Instruction.add_subtract_immediate, |
| 148 | ), &buffer), | 134 | ), buffer), |
| 149 | }; | 135 | }; |
| 150 | inst.add_subtract_immediate.imm12 = narrowed; | 136 | inst.add_subtract_immediate.imm12 = narrowed; |
| 151 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | 137 | mem.writeIntLittle(u32, buffer, inst.toU32()); |
| 152 | } else { | 138 | } else { |
| 153 | var inst = aarch64.Instruction{ | 139 | var inst = aarch64.Instruction{ |
| 154 | .load_store_register = mem.bytesToValue(meta.TagPayload( | 140 | .load_store_register = mem.bytesToValue(meta.TagPayload( |
| 155 | aarch64.Instruction, | 141 | aarch64.Instruction, |
| 156 | aarch64.Instruction.load_store_register, | 142 | aarch64.Instruction.load_store_register, |
| 157 | ), &buffer), | 143 | ), buffer), |
| 158 | }; | 144 | }; |
| 159 | const offset: u12 = blk: { | 145 | const offset: u12 = blk: { |
| 160 | if (inst.load_store_register.size == 0) { | 146 | if (inst.load_store_register.size == 0) { |
| ... | @@ -170,7 +156,7 @@ fn resolveAarch64( | ... | @@ -170,7 +156,7 @@ fn resolveAarch64( |
| 170 | } | 156 | } |
| 171 | }; | 157 | }; |
| 172 | inst.load_store_register.offset = offset; | 158 | inst.load_store_register.offset = offset; |
| 173 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | 159 | mem.writeIntLittle(u32, buffer, inst.toU32()); |
| 174 | } | 160 | } |
| 175 | }, | 161 | }, |
| 176 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { | 162 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { |
| ... | @@ -180,11 +166,11 @@ fn resolveAarch64( | ... | @@ -180,11 +166,11 @@ fn resolveAarch64( |
| 180 | size: u2, | 166 | size: u2, |
| 181 | }; | 167 | }; |
| 182 | const reg_info: RegInfo = blk: { | 168 | const reg_info: RegInfo = blk: { |
| 183 | if (isArithmeticOp(&buffer)) { | 169 | if (isArithmeticOp(buffer)) { |
| 184 | const inst = mem.bytesToValue(meta.TagPayload( | 170 | const inst = mem.bytesToValue(meta.TagPayload( |
| 185 | aarch64.Instruction, | 171 | aarch64.Instruction, |
| 186 | aarch64.Instruction.add_subtract_immediate, | 172 | aarch64.Instruction.add_subtract_immediate, |
| 187 | ), &buffer); | 173 | ), buffer); |
| 188 | break :blk .{ | 174 | break :blk .{ |
| 189 | .rd = inst.rd, | 175 | .rd = inst.rd, |
| 190 | .rn = inst.rn, | 176 | .rn = inst.rn, |
| ... | @@ -194,7 +180,7 @@ fn resolveAarch64( | ... | @@ -194,7 +180,7 @@ fn resolveAarch64( |
| 194 | const inst = mem.bytesToValue(meta.TagPayload( | 180 | const inst = mem.bytesToValue(meta.TagPayload( |
| 195 | aarch64.Instruction, | 181 | aarch64.Instruction, |
| 196 | aarch64.Instruction.load_store_register, | 182 | aarch64.Instruction.load_store_register, |
| 197 | ), &buffer); | 183 | ), buffer); |
| 198 | break :blk .{ | 184 | break :blk .{ |
| 199 | .rd = inst.rt, | 185 | .rd = inst.rt, |
| 200 | .rn = inst.rn, | 186 | .rn = inst.rn, |
| ... | @@ -214,72 +200,62 @@ fn resolveAarch64( | ... | @@ -214,72 +200,62 @@ fn resolveAarch64( |
| 214 | .sf = @truncate(u1, reg_info.size), | 200 | .sf = @truncate(u1, reg_info.size), |
| 215 | }, | 201 | }, |
| 216 | }; | 202 | }; |
| 217 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | 203 | mem.writeIntLittle(u32, buffer, inst.toU32()); |
| 218 | }, | 204 | }, |
| 219 | .ARM64_RELOC_POINTER_TO_GOT => { | 205 | .ARM64_RELOC_POINTER_TO_GOT => { |
| 220 | const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)); | 206 | const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)); |
| 221 | mem.writeIntLittle(i32, &buffer, result); | 207 | mem.writeIntLittle(i32, buffer, result); |
| 222 | }, | 208 | }, |
| 223 | .ARM64_RELOC_SUBTRACTOR => unreachable, | 209 | .ARM64_RELOC_SUBTRACTOR => unreachable, |
| 224 | .ARM64_RELOC_ADDEND => unreachable, | 210 | .ARM64_RELOC_ADDEND => unreachable, |
| 225 | .ARM64_RELOC_UNSIGNED => unreachable, | 211 | .ARM64_RELOC_UNSIGNED => unreachable, |
| 226 | } | 212 | } |
| 227 | try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset); | ||
| 228 | } | 213 | } |
| 229 | 214 | ||
| 230 | fn resolveX8664( | 215 | fn resolveX8664( |
| 231 | self: Relocation, | 216 | self: Relocation, |
| 232 | macho_file: *MachO, | ||
| 233 | source_addr: u64, | 217 | source_addr: u64, |
| 234 | target_addr: i64, | 218 | target_addr: i64, |
| 235 | base_offset: u64, | 219 | code: []u8, |
| 236 | ) !void { | 220 | ) !void { |
| 237 | const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type); | 221 | const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type); |
| 238 | var buffer: [@sizeOf(u64)]u8 = undefined; | 222 | switch (rel_type) { |
| 239 | const code = blk: { | 223 | .X86_64_RELOC_BRANCH, |
| 240 | switch (rel_type) { | 224 | .X86_64_RELOC_GOT, |
| 241 | .X86_64_RELOC_BRANCH, | 225 | .X86_64_RELOC_GOT_LOAD, |
| 242 | .X86_64_RELOC_GOT, | 226 | .X86_64_RELOC_TLV, |
| 243 | .X86_64_RELOC_GOT_LOAD, | 227 | => { |
| 244 | .X86_64_RELOC_TLV, | 228 | const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); |
| 245 | => { | 229 | mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement)); |
| 246 | const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); | 230 | }, |
| 247 | mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement)); | 231 | .X86_64_RELOC_SIGNED, |
| 248 | break :blk buffer[0..4]; | 232 | .X86_64_RELOC_SIGNED_1, |
| 249 | }, | 233 | .X86_64_RELOC_SIGNED_2, |
| 250 | .X86_64_RELOC_SIGNED, | 234 | .X86_64_RELOC_SIGNED_4, |
| 251 | .X86_64_RELOC_SIGNED_1, | 235 | => { |
| 252 | .X86_64_RELOC_SIGNED_2, | 236 | const correction: u3 = switch (rel_type) { |
| 253 | .X86_64_RELOC_SIGNED_4, | 237 | .X86_64_RELOC_SIGNED => 0, |
| 254 | => { | 238 | .X86_64_RELOC_SIGNED_1 => 1, |
| 255 | const correction: u3 = switch (rel_type) { | 239 | .X86_64_RELOC_SIGNED_2 => 2, |
| 256 | .X86_64_RELOC_SIGNED => 0, | 240 | .X86_64_RELOC_SIGNED_4 => 4, |
| 257 | .X86_64_RELOC_SIGNED_1 => 1, | 241 | else => unreachable, |
| 258 | .X86_64_RELOC_SIGNED_2 => 2, | 242 | }; |
| 259 | .X86_64_RELOC_SIGNED_4 => 4, | 243 | const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4)); |
| 260 | else => unreachable, | 244 | mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement)); |
| 261 | }; | 245 | }, |
| 262 | const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4)); | 246 | .X86_64_RELOC_UNSIGNED => { |
| 263 | mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement)); | 247 | switch (self.length) { |
| 264 | break :blk buffer[0..4]; | 248 | 2 => { |
| 265 | }, | 249 | mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))); |
| 266 | .X86_64_RELOC_UNSIGNED => { | 250 | }, |
| 267 | switch (self.length) { | 251 | 3 => { |
| 268 | 2 => { | 252 | mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)); |
| 269 | mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr))); | 253 | }, |
| 270 | break :blk buffer[0..4]; | 254 | else => unreachable, |
| 271 | }, | 255 | } |
| 272 | 3 => { | 256 | }, |
| 273 | mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr)); | 257 | .X86_64_RELOC_SUBTRACTOR => unreachable, |
| 274 | break :blk &buffer; | 258 | } |
| 275 | }, | ||
| 276 | else => unreachable, | ||
| 277 | } | ||
| 278 | }, | ||
| 279 | .X86_64_RELOC_SUBTRACTOR => unreachable, | ||
| 280 | } | ||
| 281 | }; | ||
| 282 | try macho_file.base.file.?.pwriteAll(code, base_offset + self.offset); | ||
| 283 | } | 259 | } |
| 284 | 260 | ||
| 285 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | 261 | inline fn isArithmeticOp(inst: *const [4]u8) bool { |
src/main.zig+30-6| ... | @@ -3851,15 +3851,39 @@ fn runOrTestHotSwap( | ... | @@ -3851,15 +3851,39 @@ fn runOrTestHotSwap( |
| 3851 | if (runtime_args_start) |i| { | 3851 | if (runtime_args_start) |i| { |
| 3852 | try argv.appendSlice(all_args[i..]); | 3852 | try argv.appendSlice(all_args[i..]); |
| 3853 | } | 3853 | } |
| 3854 | var child = std.ChildProcess.init(argv.items, gpa); | ||
| 3855 | 3854 | ||
| 3856 | child.stdin_behavior = .Inherit; | 3855 | switch (builtin.target.os.tag) { |
| 3857 | child.stdout_behavior = .Inherit; | 3856 | .macos, .ios, .tvos, .watchos => { |
| 3858 | child.stderr_behavior = .Inherit; | 3857 | const PosixSpawn = std.os.darwin.PosixSpawn; |
| 3858 | var attr = try PosixSpawn.Attr.init(); | ||
| 3859 | defer attr.deinit(); | ||
| 3860 | const flags: u16 = std.os.darwin.POSIX_SPAWN_SETSIGDEF | | ||
| 3861 | std.os.darwin.POSIX_SPAWN_SETSIGMASK | | ||
| 3862 | std.os.darwin._POSIX_SPAWN_DISABLE_ASLR; | ||
| 3863 | try attr.set(flags); | ||
| 3864 | |||
| 3865 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | ||
| 3866 | defer arena_allocator.deinit(); | ||
| 3867 | const arena = arena_allocator.allocator(); | ||
| 3868 | |||
| 3869 | const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null); | ||
| 3870 | for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 3871 | |||
| 3872 | const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ); | ||
| 3873 | return pid; | ||
| 3874 | }, | ||
| 3875 | else => { | ||
| 3876 | var child = std.ChildProcess.init(argv.items, gpa); | ||
| 3877 | |||
| 3878 | child.stdin_behavior = .Inherit; | ||
| 3879 | child.stdout_behavior = .Inherit; | ||
| 3880 | child.stderr_behavior = .Inherit; | ||
| 3859 | 3881 | ||
| 3860 | try child.spawn(); | 3882 | try child.spawn(); |
| 3861 | 3883 | ||
| 3862 | return child.id; | 3884 | return child.id; |
| 3885 | }, | ||
| 3886 | } | ||
| 3863 | } | 3887 | } |
| 3864 | 3888 | ||
| 3865 | const AfterUpdateHook = union(enum) { | 3889 | const AfterUpdateHook = union(enum) { |