authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-16 20:41:46+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-18 21:53:26+01:00
logf1e25cf43ec60075a4fc6f3eceb5a3af1f9f0712
tree29533cb639a0c3fff0889f8d1233ef1a5cbafc8c
parent266c81322e4e7b6c0b7f0a7fe9873b092aef7f54

macho: add hot-code swapping poc


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;
154154
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();
177179
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};
660664
661pub const PROT = struct {665pub 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.
222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
223223
224/// Mach task used when the compiler is in hot-code swapping mode.
225mach_task: ?std.os.darwin.MachTask = null,
226
224const DeclMetadata = struct {227const 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();
585588
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 }
589606
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}
10541071
1055pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void {1072pub 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
1096fn 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}
10641105
1065fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {1106fn 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 else2104 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);
20652106
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 });
22042245
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}
23772418
2378fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {2419fn 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}
185185
186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {186pub 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;
197190 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}
5252
53pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void {53pub 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 });
6969
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}
7676
77fn resolveAarch64(77fn 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 }
10291
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}
229214
230fn resolveX8664(215fn 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}
284260
285inline fn isArithmeticOp(inst: *const [4]u8) bool {261inline 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);
38553854
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;
38593881
3860 try child.spawn();3882 try child.spawn();
38613883
3862 return child.id;3884 return child.id;
3885 },
3886 }
3863}3887}
38643888
3865const AfterUpdateHook = union(enum) {3889const AfterUpdateHook = union(enum) {