authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-08 16:39:38-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log94354aa6aa16274070e49cc261778f1924432ecc
treee9178fe11a37e36a4c1756e989ed8aaf309ca0ca
parentd226b74ae8a408ca6d363295e00fdc2876d77fb0

macho: add unwindFrame which can unwind stack frames using the __unwind_info section

dwarf: fixup missing error

6 files changed, 409 insertions(+), 40 deletions(-)

lib/std/debug.zig+32-21
......@@ -623,11 +623,15 @@ pub const StackIterator = struct {
623623 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
624624 switch (native_os) {
625625 .macos, .ios, .watchos, .tvos => {
626 const o_file_info = try module.getOFileInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc);
627 if (o_file_info.unwind_info == null) return error.MissingUnwindInfo;
628
629 // TODO: Unwind using __unwind_info,
630 unreachable;
626 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
627 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
628 if (module.unwind_info) |unwind_info| {
629 if (macho.unwindFrame(&self.dwarf_context, unwind_info, module.base_address)) |return_address| {
630 return return_address;
631 } else |err| {
632 if (err != error.RequiresDWARFUnwind) return err;
633 }
634 } else return error.MissingUnwindInfo;
631635 },
632636 else => {},
633637 }
......@@ -1236,7 +1240,16 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
12361240 .ncmds = hdr.ncmds,
12371241 .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
12381242 };
1243 var unwind_info: ?[]const u8 = null;
12391244 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
1245 .SEGMENT_64 => {
1246 for (cmd.getSections()) |sect| {
1247 if (std.mem.eql(u8, "__TEXT", sect.segName()) and mem.eql(u8, "__unwind_info", sect.sectName())) {
1248 unwind_info = try chopSlice(mapped_mem, sect.offset, sect.size);
1249 break;
1250 }
1251 }
1252 },
12401253 .SYMTAB => break cmd.cast(macho.symtab_command).?,
12411254 else => {},
12421255 } else return error.MissingDebugInfo;
......@@ -1346,6 +1359,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
13461359 .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
13471360 .symbols = symbols,
13481361 .strings = strings,
1362 .unwind_info = unwind_info,
13491363 };
13501364}
13511365
......@@ -1886,12 +1900,13 @@ pub const ModuleDebugInfo = switch (native_os) {
18861900 symbols: []const MachoSymbol,
18871901 strings: [:0]const u8,
18881902 ofiles: OFileTable,
1903 // Backed by mapped_memory
1904 unwind_info: ?[]const u8,
18891905
18901906 const OFileTable = std.StringHashMap(OFileInfo);
18911907 const OFileInfo = struct {
18921908 di: DW.DwarfInfo,
18931909 addr_table: std.StringHashMap(u64),
1894 unwind_info: ?[]const u8,
18951910 };
18961911
18971912 fn deinit(self: *@This(), allocator: mem.Allocator) void {
......@@ -1949,24 +1964,21 @@ pub const ModuleDebugInfo = switch (native_os) {
19491964 addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value);
19501965 }
19511966
1952 var unwind_info: ?[]const u8 = null;
19531967 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
19541968 for (segcmd.?.getSections()) |sect| {
1955 if (std.mem.eql(u8, "__TEXT", sect.segName()) and mem.eql(u8, "__unwind_info", sect.sectName())) {
1956 unwind_info = try chopSlice(mapped_mem, sect.offset, sect.size);
1957 } else if (std.mem.eql(u8, "__DWARF", sect.segName())) {
1958 var section_index: ?usize = null;
1959 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
1960 if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
1961 }
1962 if (section_index == null) continue;
1969 if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue;
19631970
1964 const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);
1965 sections[section_index.?] = .{
1966 .data = section_bytes,
1967 .owned = false,
1968 };
1971 var section_index: ?usize = null;
1972 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
1973 if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
19691974 }
1975 if (section_index == null) continue;
1976
1977 const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);
1978 sections[section_index.?] = .{
1979 .data = section_bytes,
1980 .owned = false,
1981 };
19701982 }
19711983
19721984 const missing_debug_info =
......@@ -1986,7 +1998,6 @@ pub const ModuleDebugInfo = switch (native_os) {
19861998 var info = OFileInfo{
19871999 .di = di,
19882000 .addr_table = addr_table,
1989 .unwind_info = unwind_info,
19902001 };
19912002
19922003 // Add the debug info to the cache
lib/std/dwarf.zig+6-10
......@@ -1641,7 +1641,6 @@ pub const DwarfInfo = struct {
16411641 // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly
16421642 // as pointers will be decoded relative to the alreayd-mapped .eh_frame.
16431643 var mapped_pc: usize = undefined;
1644
16451644 if (di.eh_frame_hdr) |header| {
16461645 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
16471646 mapped_pc = context.pc;
......@@ -1657,16 +1656,12 @@ pub const DwarfInfo = struct {
16571656 mapped_pc = context.pc - module_base_address;
16581657 const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {
16591658 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
1660 if (pc < mid_item.pc_begin) {
1661 return .lt;
1662 } else {
1663 const range_end = mid_item.pc_begin + mid_item.pc_range;
1664 if (pc < range_end) {
1665 return .eq;
1666 }
1659 if (pc < mid_item.pc_begin) return .lt;
16671660
1668 return .gt;
1669 }
1661 const range_end = mid_item.pc_begin + mid_item.pc_range;
1662 if (pc < range_end) return .eq;
1663
1664 return .gt;
16701665 }
16711666 }.compareFn);
16721667
......@@ -2000,6 +1995,7 @@ pub const ExceptionFrameHeader = struct {
20001995 }
20011996 }
20021997
1998 if (len == 0) return badDwarf();
20031999 try stream.seekTo(left * entry_size);
20042000
20052001 // Read past the pc_begin field of the entry
lib/std/dwarf/abi.zig+38-9
......@@ -45,15 +45,6 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
4545 };
4646}
4747
48fn RegBytesReturnType(comptime ContextPtrType: type) type {
49 const info = @typeInfo(ContextPtrType);
50 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
51 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
52 }
53
54 return if (info.Pointer.is_const) return []const u8 else []u8;
55}
56
5748pub const RegisterContext = struct {
5849 eh_frame: bool,
5950 is_macho: bool,
......@@ -63,9 +54,47 @@ pub const AbiError = error{
6354 InvalidRegister,
6455 UnimplementedArch,
6556 UnimplementedOs,
57 RegisterContextRequired,
6658 ThreadContextNotSupported,
6759};
6860
61fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
62 const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
63 const info = @typeInfo(reg_bytes_type).Pointer;
64 return @Type(.{
65 .Pointer = .{
66 .size = .One,
67 .is_const = info.is_const,
68 .is_volatile = info.is_volatile,
69 .is_allowzero = info.is_allowzero,
70 .alignment = info.alignment,
71 .address_space = info.address_space,
72 .child = T,
73 .sentinel = null,
74 },
75 });
76}
77
78pub fn regValueNative(
79 comptime T: type,
80 thread_context_ptr: anytype,
81 reg_number: u8,
82 reg_context: ?RegisterContext,
83) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
84 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
85 if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
86 return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
87}
88
89fn RegBytesReturnType(comptime ContextPtrType: type) type {
90 const info = @typeInfo(ContextPtrType);
91 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
92 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
93 }
94
95 return if (info.Pointer.is_const) return []const u8 else []u8;
96}
97
6998/// Returns a slice containing the backing storage for `reg_number`.
7099///
71100/// `reg_context` describes in what context the register number is used, as it can have different
lib/std/macho.zig+312
......@@ -2064,3 +2064,315 @@ pub const UNWIND_ARM64_FRAME_D14_D15_PAIR: u32 = 0x00000800;
20642064
20652065pub const UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK: u32 = 0x00FFF000;
20662066pub const UNWIND_ARM64_DWARF_SECTION_OFFSET: u32 = 0x00FFFFFF;
2067
2068pub const CompactUnwindEncoding = packed struct(u32) {
2069 value: packed union {
2070 x86_64: packed union {
2071 frame: packed struct(u24) {
2072 reg4: u3,
2073 reg3: u3,
2074 reg2: u3,
2075 reg1: u3,
2076 reg0: u3,
2077 unused: u1 = 0,
2078 frame_offset: u8,
2079 },
2080 frameless: packed struct(u24) {
2081 stack_reg_permutation: u10,
2082 stack_reg_count: u3,
2083 stack_adjust: u3,
2084 stack_size: u8,
2085 },
2086 dwarf: u24,
2087 },
2088 arm64: packed union {
2089 frame: packed struct(u24) {
2090 x_reg_pairs: packed struct {
2091 x19_x20: u1,
2092 x21_x22: u1,
2093 x23_x24: u1,
2094 x25_x26: u1,
2095 x27_x28: u1,
2096 },
2097 d_reg_pairs: packed struct {
2098 d8_d9: u1,
2099 d10_d11: u1,
2100 d12_d13: u1,
2101 d14_d15: u1,
2102 },
2103 unused: u15,
2104 },
2105 frameless: packed struct(u24) {
2106 unused: u12 = 0,
2107 stack_size: u12,
2108 },
2109 dwarf: u24,
2110 },
2111 },
2112 mode: packed union {
2113 x86_64: UNWIND_X86_64_MODE,
2114 arm64: UNWIND_ARM64_MODE,
2115 },
2116 personality_index: u2,
2117 has_lsda: u1,
2118 start: u1,
2119};
2120
2121/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
2122fn dwarfRegNumber(unwind_reg_number: u3) !u8 {
2123 return switch (unwind_reg_number) {
2124 1 => 3, // RBX
2125 2 => 12, // R12
2126 3 => 13, // R13
2127 4 => 14, // R14
2128 5 => 15, // R15
2129 6 => 6, // RBP
2130 else => error.InvalidUnwindRegisterNumber,
2131 };
2132}
2133
2134const dwarf = std.dwarf;
2135const abi = dwarf.abi;
2136
2137pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, module_base_address: usize) !usize {
2138 const header = mem.bytesAsValue(
2139 unwind_info_section_header,
2140 unwind_info[0..@sizeOf(unwind_info_section_header)],
2141 );
2142 const indices = mem.bytesAsSlice(
2143 unwind_info_section_header_index_entry,
2144 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(unwind_info_section_header_index_entry)],
2145 );
2146 if (indices.len == 0) return error.MissingUnwindInfo;
2147
2148 const mapped_pc = context.pc - module_base_address;
2149 const second_level_index = blk: {
2150 var left: usize = 0;
2151 var len: usize = indices.len;
2152
2153 while (len > 1) {
2154 const mid = left + len / 2;
2155 const offset = indices[mid].functionOffset;
2156 if (mapped_pc < offset) {
2157 len /= 2;
2158 } else {
2159 left = mid;
2160 if (mapped_pc == offset) break;
2161 len -= len / 2;
2162 }
2163 }
2164
2165 // Last index is a sentinel containing the highest address as its functionOffset
2166 if (len == 0 or indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
2167 break :blk &indices[left];
2168 };
2169
2170 const common_encodings = mem.bytesAsSlice(
2171 compact_unwind_encoding_t,
2172 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(compact_unwind_encoding_t)],
2173 );
2174
2175 const start_offset = second_level_index.secondLevelPagesSectionOffset;
2176 const kind = mem.bytesAsValue(
2177 UNWIND_SECOND_LEVEL,
2178 unwind_info[start_offset..][0..@sizeOf(UNWIND_SECOND_LEVEL)],
2179 );
2180 const raw_encoding = switch (kind.*) {
2181 .REGULAR => blk: {
2182 const page_header = mem.bytesAsValue(
2183 unwind_info_regular_second_level_page_header,
2184 unwind_info[start_offset..][0..@sizeOf(unwind_info_regular_second_level_page_header)],
2185 );
2186
2187 const entries = mem.bytesAsSlice(
2188 unwind_info_regular_second_level_entry,
2189 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(unwind_info_regular_second_level_entry)],
2190 );
2191 if (entries.len == 0) return error.InvalidUnwindInfo;
2192
2193 var left: usize = 0;
2194 var len: usize = entries.len;
2195 while (len > 1) {
2196 const mid = left + len / 2;
2197 const offset = entries[mid].functionOffset;
2198 if (mapped_pc < offset) {
2199 len /= 2;
2200 } else {
2201 left = mid;
2202 if (mapped_pc == offset) break;
2203 len -= len / 2;
2204 }
2205 }
2206
2207 if (len == 0) return error.InvalidUnwindInfo;
2208 break :blk entries[left].encoding;
2209 },
2210 .COMPRESSED => blk: {
2211 const page_header = mem.bytesAsValue(
2212 unwind_info_compressed_second_level_page_header,
2213 unwind_info[start_offset..][0..@sizeOf(unwind_info_compressed_second_level_page_header)],
2214 );
2215
2216 const entries = mem.bytesAsSlice(
2217 UnwindInfoCompressedEntry,
2218 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(UnwindInfoCompressedEntry)],
2219 );
2220 if (entries.len == 0) return error.InvalidUnwindInfo;
2221
2222 var left: usize = 0;
2223 var len: usize = entries.len;
2224 while (len > 1) {
2225 const mid = left + len / 2;
2226 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
2227 if (mapped_pc < offset) {
2228 len /= 2;
2229 } else {
2230 left = mid;
2231 if (mapped_pc == offset) break;
2232 len -= len / 2;
2233 }
2234 }
2235
2236 if (len == 0) return error.InvalidUnwindInfo;
2237 const entry = entries[left];
2238 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
2239 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
2240 break :blk common_encodings[entry.encodingIndex];
2241 } else {
2242 const local_index = try std.math.sub(
2243 u8,
2244 entry.encodingIndex,
2245 std.math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
2246 );
2247 const local_encodings = mem.bytesAsSlice(
2248 compact_unwind_encoding_t,
2249 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(compact_unwind_encoding_t)],
2250 );
2251 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
2252 break :blk local_encodings[local_index];
2253 }
2254 },
2255 else => return error.InvalidUnwindInfo,
2256 };
2257
2258 if (raw_encoding == 0) return error.NoUnwindInfo;
2259 const reg_context = dwarf.abi.RegisterContext{
2260 .eh_frame = false,
2261 .is_macho = true,
2262 };
2263
2264 const encoding: CompactUnwindEncoding = @bitCast(raw_encoding);
2265 const new_ip = switch (builtin.cpu.arch) {
2266 .x86_64 => switch (encoding.mode.x86_64) {
2267 .OLD => return error.UnimplementedUnwindEncoding,
2268 .RBP_FRAME => blk: {
2269 const regs: [5]u3 = .{
2270 encoding.value.x86_64.frame.reg0,
2271 encoding.value.x86_64.frame.reg1,
2272 encoding.value.x86_64.frame.reg2,
2273 encoding.value.x86_64.frame.reg3,
2274 encoding.value.x86_64.frame.reg4,
2275 };
2276
2277 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
2278 var max_reg: usize = 0;
2279 inline for (regs, 0..) |reg, i| {
2280 if (reg > 0) max_reg = i;
2281 }
2282
2283 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2284 const new_sp = fp + 2 * @sizeOf(usize);
2285
2286 // Verify the stack range we're about to read register values from is valid
2287 if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo;
2288
2289 const ip_ptr = fp + @sizeOf(usize);
2290 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2291 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2292
2293 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2294 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2295 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2296
2297 for (regs, 0..) |reg, i| {
2298 if (reg == 0) continue;
2299 const addr = fp - frame_offset + i * @sizeOf(usize);
2300 const reg_number = try dwarfRegNumber(reg);
2301 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
2302 }
2303
2304 break :blk new_ip;
2305 },
2306 .STACK_IMMD => blk: {
2307 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2308
2309 // Decode 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.h
2311
2312 // Decode the variable-based permutation number into its digits. Each digit represents
2313 // an index into the list of register numbers that weren't yet used in the sequence at
2314 // the time the digit was added.
2315 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2316 const ip_ptr = if (reg_count > 0) reg_blk: {
2317 var digits: [6]u3 = undefined;
2318 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2319 var base: usize = 2;
2320 for (0..reg_count) |i| {
2321 const div = accumulator / base;
2322 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2323 accumulator = div;
2324 base += 1;
2325 }
2326
2327 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2328 var registers: [reg_numbers.len]u3 = undefined;
2329 var used_indices = [_]bool{false} ** reg_numbers.len;
2330 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2331 var unused_count: u8 = 0;
2332 const unused_index = for (used_indices, 0..) |used, index| {
2333 if (!used) {
2334 if (target_unused_index == unused_count) break index;
2335 unused_count += 1;
2336 }
2337 } else unreachable;
2338
2339 registers[i] = reg_numbers[unused_index];
2340 used_indices[unused_index] = true;
2341 }
2342
2343 var reg_addr = sp + @as(usize, (encoding.value.x86_64.frameless.stack_size - reg_count - 1)) * @sizeOf(usize);
2344 if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo;
2345 for (0..reg_count) |i| {
2346 const reg_number = try dwarfRegNumber(registers[i]);
2347 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2348 reg_addr += @sizeOf(usize);
2349 }
2350
2351 break :reg_blk reg_addr;
2352 } else sp + @as(usize, (encoding.value.x86_64.frameless.stack_size - 1)) * @sizeOf(usize);
2353
2354 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2355 const new_sp = ip_ptr + @sizeOf(usize);
2356 if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo;
2357
2358 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2359 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2360
2361 break :blk new_ip;
2362 },
2363 .STACK_IND => {
2364 return error.UnimplementedUnwindEncoding; // TODO
2365 },
2366 .DWARF => return error.RequiresDWARFUnwind,
2367 },
2368 .aarch64 => switch (encoding.mode.x86_64) {
2369 .DWARF => return error.RequiresDWARFUnwind,
2370 else => return error.UnimplementedUnwindEncoding,
2371 },
2372 else => return error.UnimplementedArch,
2373 };
2374
2375 context.pc = new_ip;
2376 if (context.pc > 0) context.pc -= 1;
2377 return new_ip;
2378}
test/standalone/dwarf_unwinding/build.zig+2
......@@ -16,6 +16,7 @@ pub fn build(b: *std.Build) void {
1616 .optimize = optimize,
1717 });
1818
19 if (target.isDarwin()) exe.unwind_tables = true;
1920 exe.omit_frame_pointer = true;
2021
2122 const run_cmd = b.addRunArtifact(exe);
......@@ -43,6 +44,7 @@ pub fn build(b: *std.Build) void {
4344 .optimize = optimize,
4445 });
4546
47 if (target.isDarwin()) exe.unwind_tables = true;
4648 exe.omit_frame_pointer = true;
4749 exe.linkLibrary(c_shared_lib);
4850
test/standalone/dwarf_unwinding/zig_unwind.zig+19
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const debug = std.debug;
34const testing = std.testing;
45
......@@ -18,6 +19,24 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
1819}
1920
2021noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
22 if (builtin.os.tag == .macos) {
23 // Excercise different __unwind_info encodings by forcing some registers to be restored
24 switch (builtin.cpu.arch) {
25 .x86_64 => {
26 asm volatile (
27 \\movq $3, %%rbx
28 \\movq $12, %%r12
29 \\movq $13, %%r13
30 \\movq $14, %%r14
31 \\movq $15, %%r15
32 \\movq $6, %%rbp
33 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
34 },
35 .aarch64 => {},
36 else => {},
37 }
38 }
39
2140 expected[1] = @returnAddress();
2241 frame3(expected, unwound);
2342}