| ... | ... | @@ -2080,30 +2080,38 @@ pub const CompactUnwindEncoding = packed struct(u32) { |
| 2080 | 2080 | frameless: packed struct(u24) { |
| 2081 | 2081 | stack_reg_permutation: u10, |
| 2082 | 2082 | stack_reg_count: u3, |
| 2083 | | stack_adjust: u3, |
| 2084 | | stack_size: u8, |
| 2083 | stack: packed union { |
| 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 | 2094 | dwarf: u24, |
| 2087 | 2095 | }, |
| 2088 | 2096 | arm64: packed union { |
| 2089 | 2097 | frame: packed struct(u24) { |
| 2090 | | x_reg_pairs: packed struct { |
| 2098 | x_reg_pairs: packed struct(u5) { |
| 2091 | 2099 | x19_x20: u1, |
| 2092 | 2100 | x21_x22: u1, |
| 2093 | 2101 | x23_x24: u1, |
| 2094 | 2102 | x25_x26: u1, |
| 2095 | 2103 | x27_x28: u1, |
| 2096 | 2104 | }, |
| 2097 | | d_reg_pairs: packed struct { |
| 2105 | d_reg_pairs: packed struct(u4) { |
| 2098 | 2106 | d8_d9: u1, |
| 2099 | 2107 | d10_d11: u1, |
| 2100 | 2108 | d12_d13: u1, |
| 2101 | 2109 | d14_d15: u1, |
| 2102 | 2110 | }, |
| 2103 | | unused: u15, |
| 2111 | _: u15, |
| 2104 | 2112 | }, |
| 2105 | 2113 | frameless: packed struct(u24) { |
| 2106 | | unused: u12 = 0, |
| 2114 | _: u12 = 0, |
| 2107 | 2115 | stack_size: u12, |
| 2108 | 2116 | }, |
| 2109 | 2117 | dwarf: u24, |
| ... | ... | @@ -2177,7 +2185,11 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2177 | 2185 | UNWIND_SECOND_LEVEL, |
| 2178 | 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 | 2193 | .REGULAR => blk: { |
| 2182 | 2194 | const page_header = mem.bytesAsValue( |
| 2183 | 2195 | unwind_info_regular_second_level_page_header, |
| ... | ... | @@ -2205,7 +2217,10 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2205 | 2217 | } |
| 2206 | 2218 | |
| 2207 | 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 | 2225 | .COMPRESSED => blk: { |
| 2211 | 2226 | const page_header = mem.bytesAsValue( |
| ... | ... | @@ -2235,9 +2250,13 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2235 | 2250 | |
| 2236 | 2251 | if (len == 0) return error.InvalidUnwindInfo; |
| 2237 | 2252 | const entry = entries[left]; |
| 2253 | const function_offset = second_level_index.functionOffset + entry.funcOffset; |
| 2238 | 2254 | if (entry.encodingIndex < header.commonEncodingsArrayCount) { |
| 2239 | 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 | 2260 | } else { |
| 2242 | 2261 | const local_index = try std.math.sub( |
| 2243 | 2262 | u8, |
| ... | ... | @@ -2249,19 +2268,22 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2249 | 2268 | unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(compact_unwind_encoding_t)], |
| 2250 | 2269 | ); |
| 2251 | 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 | 2277 | else => return error.InvalidUnwindInfo, |
| 2256 | 2278 | }; |
| 2257 | 2279 | |
| 2258 | | if (raw_encoding == 0) return error.NoUnwindInfo; |
| 2280 | if (entry.raw_encoding == 0) return error.NoUnwindInfo; |
| 2259 | 2281 | const reg_context = dwarf.abi.RegisterContext{ |
| 2260 | 2282 | .eh_frame = false, |
| 2261 | 2283 | .is_macho = true, |
| 2262 | 2284 | }; |
| 2263 | 2285 | |
| 2264 | | const encoding: CompactUnwindEncoding = @bitCast(raw_encoding); |
| 2286 | const encoding: CompactUnwindEncoding = @bitCast(entry.raw_encoding); |
| 2265 | 2287 | const new_ip = switch (builtin.cpu.arch) { |
| 2266 | 2288 | .x86_64 => switch (encoding.mode.x86_64) { |
| 2267 | 2289 | .OLD => return error.UnimplementedUnwindEncoding, |
| ... | ... | @@ -2283,7 +2305,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2283 | 2305 | const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*; |
| 2284 | 2306 | const new_sp = fp + 2 * @sizeOf(usize); |
| 2285 | 2307 | |
| 2286 | | // Verify the stack range we're about to read register values from is valid |
| 2308 | // Verify the stack range we're about to read register values from |
| 2287 | 2309 | if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo; |
| 2288 | 2310 | |
| 2289 | 2311 | const ip_ptr = fp + @sizeOf(usize); |
| ... | ... | @@ -2303,10 +2325,26 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2303 | 2325 | |
| 2304 | 2326 | break :blk new_ip; |
| 2305 | 2327 | }, |
| 2306 | | .STACK_IMMD => blk: { |
| 2328 | .STACK_IMMD, |
| 2329 | .STACK_IND, |
| 2330 | => blk: { |
| 2307 | 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 | }; |
| 2308 | 2346 | |
| 2309 | | // Decode Lehmer-coded sequence of registers. |
| 2347 | // Decode the Lehmer-coded sequence of registers. |
| 2310 | 2348 | // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h |
| 2311 | 2349 | |
| 2312 | 2350 | // 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 | 2378 | used_indices[unused_index] = true; |
| 2341 | 2379 | } |
| 2342 | 2380 | |
| 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 | 2382 | if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo; |
| 2345 | 2383 | for (0..reg_count) |i| { |
| 2346 | 2384 | const reg_number = try dwarfRegNumber(registers[i]); |
| ... | ... | @@ -2349,7 +2387,7 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2349 | 2387 | } |
| 2350 | 2388 | |
| 2351 | 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); |
| 2353 | 2391 | |
| 2354 | 2392 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 2355 | 2393 | const new_sp = ip_ptr + @sizeOf(usize); |
| ... | ... | @@ -2360,14 +2398,65 @@ pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, modul |
| 2360 | 2398 | |
| 2361 | 2399 | break :blk new_ip; |
| 2362 | 2400 | }, |
| 2363 | | .STACK_IND => { |
| 2364 | | return error.UnimplementedUnwindEncoding; // TODO |
| 2365 | | }, |
| 2366 | 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 | 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 | 2461 | else => return error.UnimplementedArch, |
| 2373 | 2462 | }; |