authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-09 19:32:29-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log5dfb159e15dc7c66118d47a06536d61f65522bb9
tree02927b4636ce1c5a300cc4ae93d2bf4477caeab5
parent203d96ae97682703f39d7d7ea4e45f971a3a6043

macho: add aarch64 implementation to unwindFrame

dwarf: map the V registers in abi.regBytes test: add test case that exercises the stack-indirect __unwind_info mode in x86_64

4 files changed, 120 insertions(+), 25 deletions(-)

lib/std/debug.zig+1-3
...@@ -517,9 +517,7 @@ pub const StackIterator = struct {...@@ -517,9 +517,7 @@ pub const StackIterator = struct {
517 }517 }
518518
519 pub fn deinit(self: *StackIterator) void {519 pub fn deinit(self: *StackIterator) void {
520 if (have_ucontext and self.debug_info != null) {520 if (have_ucontext and self.debug_info != null) self.dwarf_context.deinit();
521 self.dwarf_context.deinit();
522 }
523 }521 }
524522
525 pub fn getLastError(self: *StackIterator) ?struct {523 pub fn getLastError(self: *StackIterator) ?struct {
lib/std/dwarf/abi.zig+2
...@@ -312,6 +312,8 @@ pub fn regBytes(...@@ -312,6 +312,8 @@ pub fn regBytes(
312 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr),312 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr),
313 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp),313 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp),
314 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc),314 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc),
315 // V0-V31
316 64...95 => mem.asBytes(&ucontext_ptr.mcontext.ns.q[reg_number - 64]),
315 else => error.InvalidRegister,317 else => error.InvalidRegister,
316 },318 },
317 .netbsd => switch (reg_number) {319 .netbsd => switch (reg_number) {
lib/std/macho.zig+111-22
...@@ -2080,30 +2080,38 @@ pub const CompactUnwindEncoding = packed struct(u32) {...@@ -2080,30 +2080,38 @@ pub const CompactUnwindEncoding = packed struct(u32) {
2080 frameless: packed struct(u24) {2080 frameless: packed struct(u24) {
2081 stack_reg_permutation: u10,2081 stack_reg_permutation: u10,
2082 stack_reg_count: u3,2082 stack_reg_count: u3,
2083 stack_adjust: u3,2083 stack: packed union {
2084 stack_size: u8,2084 direct: packed struct(u11) {
2085 _: u3,
2086 stack_size: u8,
2087 },
2088 indirect: packed struct(u11) {
2089 stack_adjust: u3,
2090 sub_offset: u8,
2091 },
2092 },
2085 },2093 },
2086 dwarf: u24,2094 dwarf: u24,
2087 },2095 },
2088 arm64: packed union {2096 arm64: packed union {
2089 frame: packed struct(u24) {2097 frame: packed struct(u24) {
2090 x_reg_pairs: packed struct {2098 x_reg_pairs: packed struct(u5) {
2091 x19_x20: u1,2099 x19_x20: u1,
2092 x21_x22: u1,2100 x21_x22: u1,
2093 x23_x24: u1,2101 x23_x24: u1,
2094 x25_x26: u1,2102 x25_x26: u1,
2095 x27_x28: u1,2103 x27_x28: u1,
2096 },2104 },
2097 d_reg_pairs: packed struct {2105 d_reg_pairs: packed struct(u4) {
2098 d8_d9: u1,2106 d8_d9: u1,
2099 d10_d11: u1,2107 d10_d11: u1,
2100 d12_d13: u1,2108 d12_d13: u1,
2101 d14_d15: u1,2109 d14_d15: u1,
2102 },2110 },
2103 unused: u15,2111 _: u15,
2104 },2112 },
2105 frameless: packed struct(u24) {2113 frameless: packed struct(u24) {
2106 unused: u12 = 0,2114 _: u12 = 0,
2107 stack_size: u12,2115 stack_size: u12,
2108 },2116 },
2109 dwarf: u24,2117 dwarf: u24,
...@@ -2177,7 +2185,11 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2177,7 +2185,11 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2177 UNWIND_SECOND_LEVEL,2185 UNWIND_SECOND_LEVEL,
2178 unwind_info[start_offset..][0..@sizeOf(UNWIND_SECOND_LEVEL)],2186 unwind_info[start_offset..][0..@sizeOf(UNWIND_SECOND_LEVEL)],
2179 );2187 );
2180 const raw_encoding = switch (kind.*) {2188
2189 const entry: struct {
2190 function_offset: usize,
2191 raw_encoding: u32,
2192 } = switch (kind.*) {
2181 .REGULAR => blk: {2193 .REGULAR => blk: {
2182 const page_header = mem.bytesAsValue(2194 const page_header = mem.bytesAsValue(
2183 unwind_info_regular_second_level_page_header,2195 unwind_info_regular_second_level_page_header,
...@@ -2205,7 +2217,10 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2205,7 +2217,10 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2205 }2217 }
22062218
2207 if (len == 0) return error.InvalidUnwindInfo;2219 if (len == 0) return error.InvalidUnwindInfo;
2208 break :blk entries[left].encoding;2220 break :blk .{
2221 .function_offset = entries[left].functionOffset,
2222 .raw_encoding = entries[left].encoding,
2223 };
2209 },2224 },
2210 .COMPRESSED => blk: {2225 .COMPRESSED => blk: {
2211 const page_header = mem.bytesAsValue(2226 const page_header = mem.bytesAsValue(
...@@ -2235,9 +2250,13 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2235,9 +2250,13 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
22352250
2236 if (len == 0) return error.InvalidUnwindInfo;2251 if (len == 0) return error.InvalidUnwindInfo;
2237 const entry = entries[left];2252 const entry = entries[left];
2253 const function_offset = second_level_index.functionOffset + entry.funcOffset;
2238 if (entry.encodingIndex < header.commonEncodingsArrayCount) {2254 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
2239 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;2255 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
2240 break :blk common_encodings[entry.encodingIndex];2256 break :blk .{
2257 .function_offset = function_offset,
2258 .raw_encoding = common_encodings[entry.encodingIndex],
2259 };
2241 } else {2260 } else {
2242 const local_index = try std.math.sub(2261 const local_index = try std.math.sub(
2243 u8,2262 u8,
...@@ -2249,19 +2268,22 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2249,19 +2268,22 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2249 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(compact_unwind_encoding_t)],2268 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(compact_unwind_encoding_t)],
2250 );2269 );
2251 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;2270 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
2252 break :blk local_encodings[local_index];2271 break :blk .{
2272 .function_offset = function_offset,
2273 .raw_encoding = local_encodings[local_index],
2274 };
2253 }2275 }
2254 },2276 },
2255 else => return error.InvalidUnwindInfo,2277 else => return error.InvalidUnwindInfo,
2256 };2278 };
22572279
2258 if (raw_encoding == 0) return error.NoUnwindInfo;2280 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
2259 const reg_context = dwarf.abi.RegisterContext{2281 const reg_context = dwarf.abi.RegisterContext{
2260 .eh_frame = false,2282 .eh_frame = false,
2261 .is_macho = true,2283 .is_macho = true,
2262 };2284 };
22632285
2264 const encoding: CompactUnwindEncoding = @bitCast(raw_encoding);2286 const encoding: CompactUnwindEncoding = @bitCast(entry.raw_encoding);
2265 const new_ip = switch (builtin.cpu.arch) {2287 const new_ip = switch (builtin.cpu.arch) {
2266 .x86_64 => switch (encoding.mode.x86_64) {2288 .x86_64 => switch (encoding.mode.x86_64) {
2267 .OLD => return error.UnimplementedUnwindEncoding,2289 .OLD => return error.UnimplementedUnwindEncoding,
...@@ -2283,7 +2305,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2283,7 +2305,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2283 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;2305 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2284 const new_sp = fp + 2 * @sizeOf(usize);2306 const new_sp = fp + 2 * @sizeOf(usize);
22852307
2286 // Verify the stack range we're about to read register values from is valid2308 // Verify the stack range we're about to read register values from
2287 if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo;2309 if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo;
22882310
2289 const ip_ptr = fp + @sizeOf(usize);2311 const ip_ptr = fp + @sizeOf(usize);
...@@ -2303,10 +2325,26 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2303,10 +2325,26 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
23032325
2304 break :blk new_ip;2326 break :blk new_ip;
2305 },2327 },
2306 .STACK_IMMD => blk: {2328 .STACK_IMMD,
2329 .STACK_IND,
2330 => blk: {
2307 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;2331 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2332 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
2333 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
2334 else stack_size: {
2335 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
2336 const sub_offset_addr =
2337 module_base_address +
2338 entry.function_offset +
2339 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
2340 if (!context.isValidMemory(sub_offset_addr)) return error.InvalidUnwindInfo;
2341
2342 // `sub_offset_addr` points to the offset of the literal within the instruction
2343 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
2344 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
2345 };
23082346
2309 // Decode Lehmer-coded sequence of registers.2347 // Decode the Lehmer-coded sequence of registers.
2310 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h2348 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
23112349
2312 // Decode the variable-based permutation number into its digits. Each digit represents2350 // Decode the variable-based permutation number into its digits. Each digit represents
...@@ -2340,7 +2378,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2340,7 +2378,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2340 used_indices[unused_index] = true;2378 used_indices[unused_index] = true;
2341 }2379 }
23422380
2343 var reg_addr = sp + @as(usize, (encoding.value.x86_64.frameless.stack_size - reg_count - 1)) * @sizeOf(usize);2381 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2344 if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo;2382 if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo;
2345 for (0..reg_count) |i| {2383 for (0..reg_count) |i| {
2346 const reg_number = try dwarfRegNumber(registers[i]);2384 const reg_number = try dwarfRegNumber(registers[i]);
...@@ -2349,7 +2387,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2349,7 +2387,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
2349 }2387 }
23502388
2351 break :reg_blk reg_addr;2389 break :reg_blk reg_addr;
2352 } else sp + @as(usize, (encoding.value.x86_64.frameless.stack_size - 1)) * @sizeOf(usize);2390 } else sp + stack_size - @sizeOf(usize);
23532391
2354 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;2392 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2355 const new_sp = ip_ptr + @sizeOf(usize);2393 const new_sp = ip_ptr + @sizeOf(usize);
...@@ -2360,14 +2398,65 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul...@@ -2360,14 +2398,65 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul
23602398
2361 break :blk new_ip;2399 break :blk new_ip;
2362 },2400 },
2363 .STACK_IND => {
2364 return error.UnimplementedUnwindEncoding; // TODO
2365 },
2366 .DWARF => return error.RequiresDWARFUnwind,2401 .DWARF => return error.RequiresDWARFUnwind,
2367 },2402 },
2368 .aarch64 => switch (encoding.mode.x86_64) {2403 .aarch64 => switch (encoding.mode.arm64) {
2404 .OLD => return error.UnimplementedUnwindEncoding,
2405 .FRAMELESS => blk: {
2406 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2407 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
2408 const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*;
2409 if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo;
2410 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2411 break :blk new_ip;
2412 },
2369 .DWARF => return error.RequiresDWARFUnwind,2413 .DWARF => return error.RequiresDWARFUnwind,
2370 else => return error.UnimplementedUnwindEncoding,2414 .FRAME => {
2415 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2416 const new_sp = fp + 16;
2417 const ip_ptr = fp + @sizeOf(usize);
2418
2419 const num_restored_pairs: usize =
2420 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2421 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2422 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2423
2424 if (!context.isValidMemory(new_sp) or !context.isValidMemory(min_reg_addr)) return error.InvalidUnwindInfo;
2425
2426 var reg_addr = fp - @sizeOf(usize);
2427 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2428 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2429 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2430 reg_addr += @sizeOf(usize);
2431 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2432 reg_addr += @sizeOf(usize);
2433 }
2434 }
2435
2436 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2437 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2438 // Only the lower half of the 128-bit V registers are restored during unwinding
2439 @memcpy(
2440 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2441 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2442 );
2443 reg_addr += @sizeOf(usize);
2444 @memcpy(
2445 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2446 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2447 );
2448 reg_addr += @sizeOf(usize);
2449 }
2450 }
2451
2452 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2453 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2454
2455 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2456 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2457
2458 return error.UnimplementedUnwindEncoding;
2459 },
2371 },2460 },
2372 else => return error.UnimplementedArch,2461 else => return error.UnimplementedArch,
2373 };2462 };
test/standalone/dwarf_unwinding/zig_unwind.zig+6
...@@ -52,6 +52,12 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {...@@ -52,6 +52,12 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
5252
53noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {53noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
54 expected[2] = @returnAddress();54 expected[2] = @returnAddress();
55
56 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
57 // to exercise the stack-indirect encoding path
58 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
59 _ = pad;
60
55 frame2(expected, unwound);61 frame2(expected, unwound);
56}62}
5763