authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:57:21-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:16-04:00
logb1d86db7b45c57b2a9d48655738bce8d77327438
tree02294dfdfb662d582546bc36c878a42eba5f5343
parent8e6a62ba10326e48eaefd40f89c9452d92f39c9d

dwarf: move macho unwind code from macho -> dwarf

dwarf: fixup unchecked .eh_frame CIE offset subtraction

3 files changed, 361 insertions(+), 359 deletions(-)

lib/std/debug.zig+1-1
...@@ -648,7 +648,7 @@ pub const StackIterator = struct {...@@ -648,7 +648,7 @@ pub const StackIterator = struct {
648 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding648 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
649 // via DWARF before attempting to use the compact unwind info will produce incorrect results.649 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
650 if (module.unwind_info) |unwind_info| {650 if (module.unwind_info) |unwind_info| {
651 if (macho.unwindFrame(&unwind_state.dwarf_context, unwind_info, module.eh_frame, module.base_address)) |return_address| {651 if (DW.unwindFrameMachO(&unwind_state.dwarf_context, unwind_info, module.eh_frame, module.base_address)) |return_address| {
652 return return_address;652 return return_address;
653 } else |err| {653 } else |err| {
654 if (err != error.RequiresDWARFUnwind) return err;654 if (err != error.RequiresDWARFUnwind) return err;
lib/std/dwarf.zig+360-1
...@@ -1841,6 +1841,365 @@ pub const DwarfInfo = struct {...@@ -1841,6 +1841,365 @@ pub const DwarfInfo = struct {
1841 }1841 }
1842};1842};
18431843
1844/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
1845fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1846 return switch (unwind_reg_number) {
1847 1 => 3, // RBX
1848 2 => 12, // R12
1849 3 => 13, // R13
1850 4 => 14, // R14
1851 5 => 15, // R15
1852 6 => 6, // RBP
1853 else => error.InvalidUnwindRegisterNumber,
1854 };
1855}
1856
1857const macho = std.macho;
1858
1859/// Unwind a frame using MachO compact unwind info (from __unwind_info).
1860/// If the compact encoding can't encode a way to unwind a frame, it will
1861/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
1862pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_frame: ?[]const u8, module_base_address: usize) !usize {
1863 const header = mem.bytesAsValue(
1864 macho.unwind_info_section_header,
1865 unwind_info[0..@sizeOf(macho.unwind_info_section_header)],
1866 );
1867 const indices = mem.bytesAsSlice(
1868 macho.unwind_info_section_header_index_entry,
1869 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry)],
1870 );
1871 if (indices.len == 0) return error.MissingUnwindInfo;
1872
1873 const mapped_pc = context.pc - module_base_address;
1874 const second_level_index = blk: {
1875 var left: usize = 0;
1876 var len: usize = indices.len;
1877
1878 while (len > 1) {
1879 const mid = left + len / 2;
1880 const offset = indices[mid].functionOffset;
1881 if (mapped_pc < offset) {
1882 len /= 2;
1883 } else {
1884 left = mid;
1885 if (mapped_pc == offset) break;
1886 len -= len / 2;
1887 }
1888 }
1889
1890 // Last index is a sentinel containing the highest address as its functionOffset
1891 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
1892 break :blk &indices[left];
1893 };
1894
1895 const common_encodings = mem.bytesAsSlice(
1896 macho.compact_unwind_encoding_t,
1897 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t)],
1898 );
1899
1900 const start_offset = second_level_index.secondLevelPagesSectionOffset;
1901 const kind = mem.bytesAsValue(
1902 macho.UNWIND_SECOND_LEVEL,
1903 unwind_info[start_offset..][0..@sizeOf(macho.UNWIND_SECOND_LEVEL)],
1904 );
1905
1906 const entry: struct {
1907 function_offset: usize,
1908 raw_encoding: u32,
1909 } = switch (kind.*) {
1910 .REGULAR => blk: {
1911 const page_header = mem.bytesAsValue(
1912 macho.unwind_info_regular_second_level_page_header,
1913 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_regular_second_level_page_header)],
1914 );
1915
1916 const entries = mem.bytesAsSlice(
1917 macho.unwind_info_regular_second_level_entry,
1918 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry)],
1919 );
1920 if (entries.len == 0) return error.InvalidUnwindInfo;
1921
1922 var left: usize = 0;
1923 var len: usize = entries.len;
1924 while (len > 1) {
1925 const mid = left + len / 2;
1926 const offset = entries[mid].functionOffset;
1927 if (mapped_pc < offset) {
1928 len /= 2;
1929 } else {
1930 left = mid;
1931 if (mapped_pc == offset) break;
1932 len -= len / 2;
1933 }
1934 }
1935
1936 break :blk .{
1937 .function_offset = entries[left].functionOffset,
1938 .raw_encoding = entries[left].encoding,
1939 };
1940 },
1941 .COMPRESSED => blk: {
1942 const page_header = mem.bytesAsValue(
1943 macho.unwind_info_compressed_second_level_page_header,
1944 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_compressed_second_level_page_header)],
1945 );
1946
1947 const entries = mem.bytesAsSlice(
1948 macho.UnwindInfoCompressedEntry,
1949 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry)],
1950 );
1951 if (entries.len == 0) return error.InvalidUnwindInfo;
1952
1953 var left: usize = 0;
1954 var len: usize = entries.len;
1955 while (len > 1) {
1956 const mid = left + len / 2;
1957 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
1958 if (mapped_pc < offset) {
1959 len /= 2;
1960 } else {
1961 left = mid;
1962 if (mapped_pc == offset) break;
1963 len -= len / 2;
1964 }
1965 }
1966
1967 const entry = entries[left];
1968 const function_offset = second_level_index.functionOffset + entry.funcOffset;
1969 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
1970 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
1971 break :blk .{
1972 .function_offset = function_offset,
1973 .raw_encoding = common_encodings[entry.encodingIndex],
1974 };
1975 } else {
1976 const local_index = try std.math.sub(
1977 u8,
1978 entry.encodingIndex,
1979 std.math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
1980 );
1981 const local_encodings = mem.bytesAsSlice(
1982 macho.compact_unwind_encoding_t,
1983 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t)],
1984 );
1985 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
1986 break :blk .{
1987 .function_offset = function_offset,
1988 .raw_encoding = local_encodings[local_index],
1989 };
1990 }
1991 },
1992 else => return error.InvalidUnwindInfo,
1993 };
1994
1995 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
1996 const reg_context = abi.RegisterContext{
1997 .eh_frame = false,
1998 .is_macho = true,
1999 };
2000
2001 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
2002 const new_ip = switch (builtin.cpu.arch) {
2003 .x86_64 => switch (encoding.mode.x86_64) {
2004 .OLD => return error.UnimplementedUnwindEncoding,
2005 .RBP_FRAME => blk: {
2006 const regs: [5]u3 = .{
2007 encoding.value.x86_64.frame.reg0,
2008 encoding.value.x86_64.frame.reg1,
2009 encoding.value.x86_64.frame.reg2,
2010 encoding.value.x86_64.frame.reg3,
2011 encoding.value.x86_64.frame.reg4,
2012 };
2013
2014 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
2015 var max_reg: usize = 0;
2016 inline for (regs, 0..) |reg, i| {
2017 if (reg > 0) max_reg = i;
2018 }
2019
2020 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2021 const new_sp = fp + 2 * @sizeOf(usize);
2022
2023 // Verify the stack range we're about to read register values from
2024 if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo;
2025
2026 const ip_ptr = fp + @sizeOf(usize);
2027 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2028 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2029
2030 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2031 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2032 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2033
2034 for (regs, 0..) |reg, i| {
2035 if (reg == 0) continue;
2036 const addr = fp - frame_offset + i * @sizeOf(usize);
2037 const reg_number = try compactUnwindToDwarfRegNumber(reg);
2038 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
2039 }
2040
2041 break :blk new_ip;
2042 },
2043 .STACK_IMMD,
2044 .STACK_IND,
2045 => blk: {
2046 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2047 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
2048 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
2049 else stack_size: {
2050 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
2051 const sub_offset_addr =
2052 module_base_address +
2053 entry.function_offset +
2054 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
2055 if (!context.isValidMemory(sub_offset_addr)) return error.InvalidUnwindInfo;
2056
2057 // `sub_offset_addr` points to the offset of the literal within the instruction
2058 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
2059 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
2060 };
2061
2062 // Decode the Lehmer-coded sequence of registers.
2063 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
2064
2065 // Decode the variable-based permutation number into its digits. Each digit represents
2066 // an index into the list of register numbers that weren't yet used in the sequence at
2067 // the time the digit was added.
2068 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2069 const ip_ptr = if (reg_count > 0) reg_blk: {
2070 var digits: [6]u3 = undefined;
2071 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2072 var base: usize = 2;
2073 for (0..reg_count) |i| {
2074 const div = accumulator / base;
2075 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2076 accumulator = div;
2077 base += 1;
2078 }
2079
2080 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2081 var registers: [reg_numbers.len]u3 = undefined;
2082 var used_indices = [_]bool{false} ** reg_numbers.len;
2083 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2084 var unused_count: u8 = 0;
2085 const unused_index = for (used_indices, 0..) |used, index| {
2086 if (!used) {
2087 if (target_unused_index == unused_count) break index;
2088 unused_count += 1;
2089 }
2090 } else unreachable;
2091
2092 registers[i] = reg_numbers[unused_index];
2093 used_indices[unused_index] = true;
2094 }
2095
2096 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2097 if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo;
2098 for (0..reg_count) |i| {
2099 const reg_number = try compactUnwindToDwarfRegNumber(registers[i]);
2100 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2101 reg_addr += @sizeOf(usize);
2102 }
2103
2104 break :reg_blk reg_addr;
2105 } else sp + stack_size - @sizeOf(usize);
2106
2107 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2108 const new_sp = ip_ptr + @sizeOf(usize);
2109 if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo;
2110
2111 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2112 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2113
2114 break :blk new_ip;
2115 },
2116 .DWARF => {
2117 return unwindFrameMachODwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
2118 },
2119 },
2120 .aarch64 => switch (encoding.mode.arm64) {
2121 .OLD => return error.UnimplementedUnwindEncoding,
2122 .FRAMELESS => blk: {
2123 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2124 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
2125 const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*;
2126 if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo;
2127 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2128 break :blk new_ip;
2129 },
2130 .DWARF => {
2131 return unwindFrameMachODwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
2132 },
2133 .FRAME => blk: {
2134 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2135 const new_sp = fp + 16;
2136 const ip_ptr = fp + @sizeOf(usize);
2137
2138 const num_restored_pairs: usize =
2139 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2140 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2141 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2142
2143 if (!context.isValidMemory(new_sp) or !context.isValidMemory(min_reg_addr)) return error.InvalidUnwindInfo;
2144
2145 var reg_addr = fp - @sizeOf(usize);
2146 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2147 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2148 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2149 reg_addr += @sizeOf(usize);
2150 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2151 reg_addr += @sizeOf(usize);
2152 }
2153 }
2154
2155 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2156 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2157 // Only the lower half of the 128-bit V registers are restored during unwinding
2158 @memcpy(
2159 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2160 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2161 );
2162 reg_addr += @sizeOf(usize);
2163 @memcpy(
2164 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2165 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2166 );
2167 reg_addr += @sizeOf(usize);
2168 }
2169 }
2170
2171 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2172 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2173
2174 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2175 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2176
2177 break :blk new_ip;
2178 },
2179 },
2180 else => return error.UnimplementedArch,
2181 };
2182
2183 context.pc = abi.stripInstructionPtrAuthCode(new_ip);
2184 if (context.pc > 0) context.pc -= 1;
2185 return new_ip;
2186}
2187
2188fn unwindFrameMachODwarf(context: *UnwindContext, eh_frame: []const u8, fde_offset: usize) !usize {
2189 var di = DwarfInfo{
2190 .endian = builtin.cpu.arch.endian(),
2191 .is_macho = true,
2192 };
2193 defer di.deinit(context.allocator);
2194
2195 di.sections[@intFromEnum(DwarfSection.eh_frame)] = .{
2196 .data = eh_frame,
2197 .owned = false,
2198 };
2199
2200 return di.unwindFrame(context, fde_offset);
2201}
2202
1844pub const UnwindContext = struct {2203pub const UnwindContext = struct {
1845 allocator: mem.Allocator,2204 allocator: mem.Allocator,
1846 cfa: ?usize,2205 cfa: ?usize,
...@@ -2166,7 +2525,7 @@ pub const EntryHeader = struct {...@@ -2166,7 +2525,7 @@ pub const EntryHeader = struct {
2166 .is_64 = is_64,2525 .is_64 = is_64,
2167 .type = if (id == cie_id) .{ .cie = {} } else .{2526 .type = if (id == cie_id) .{ .cie = {} } else .{
2168 .fde = switch (dwarf_section) {2527 .fde = switch (dwarf_section) {
2169 .eh_frame => stream.pos - id_len - id,2528 .eh_frame => try std.math.sub(u64, stream.pos - id_len, id),
2170 .debug_frame => id,2529 .debug_frame => id,
2171 else => unreachable,2530 else => unreachable,
2172 },2531 },
lib/std/macho.zig-357
...@@ -2125,360 +2125,3 @@ pub const CompactUnwindEncoding = packed struct(u32) {...@@ -2125,360 +2125,3 @@ pub const CompactUnwindEncoding = packed struct(u32) {
2125 has_lsda: u1,2125 has_lsda: u1,
2126 start: u1,2126 start: u1,
2127};2127};
2128
2129/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
2130fn dwarfRegNumber(unwind_reg_number: u3) !u8 {
2131 return switch (unwind_reg_number) {
2132 1 => 3, // RBX
2133 2 => 12, // R12
2134 3 => 13, // R13
2135 4 => 14, // R14
2136 5 => 15, // R15
2137 6 => 6, // RBP
2138 else => error.InvalidUnwindRegisterNumber,
2139 };
2140}
2141
2142const dwarf = std.dwarf;
2143const abi = dwarf.abi;
2144
2145pub fn unwindFrame(context: *dwarf.UnwindContext, unwind_info: []const u8, eh_frame: ?[]const u8, module_base_address: usize) !usize {
2146 const header = mem.bytesAsValue(
2147 unwind_info_section_header,
2148 unwind_info[0..@sizeOf(unwind_info_section_header)],
2149 );
2150 const indices = mem.bytesAsSlice(
2151 unwind_info_section_header_index_entry,
2152 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(unwind_info_section_header_index_entry)],
2153 );
2154 if (indices.len == 0) return error.MissingUnwindInfo;
2155
2156 const mapped_pc = context.pc - module_base_address;
2157 const second_level_index = blk: {
2158 var left: usize = 0;
2159 var len: usize = indices.len;
2160
2161 while (len > 1) {
2162 const mid = left + len / 2;
2163 const offset = indices[mid].functionOffset;
2164 if (mapped_pc < offset) {
2165 len /= 2;
2166 } else {
2167 left = mid;
2168 if (mapped_pc == offset) break;
2169 len -= len / 2;
2170 }
2171 }
2172
2173 // Last index is a sentinel containing the highest address as its functionOffset
2174 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
2175 break :blk &indices[left];
2176 };
2177
2178 const common_encodings = mem.bytesAsSlice(
2179 compact_unwind_encoding_t,
2180 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(compact_unwind_encoding_t)],
2181 );
2182
2183 const start_offset = second_level_index.secondLevelPagesSectionOffset;
2184 const kind = mem.bytesAsValue(
2185 UNWIND_SECOND_LEVEL,
2186 unwind_info[start_offset..][0..@sizeOf(UNWIND_SECOND_LEVEL)],
2187 );
2188
2189 const entry: struct {
2190 function_offset: usize,
2191 raw_encoding: u32,
2192 } = switch (kind.*) {
2193 .REGULAR => blk: {
2194 const page_header = mem.bytesAsValue(
2195 unwind_info_regular_second_level_page_header,
2196 unwind_info[start_offset..][0..@sizeOf(unwind_info_regular_second_level_page_header)],
2197 );
2198
2199 const entries = mem.bytesAsSlice(
2200 unwind_info_regular_second_level_entry,
2201 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(unwind_info_regular_second_level_entry)],
2202 );
2203 if (entries.len == 0) return error.InvalidUnwindInfo;
2204
2205 var left: usize = 0;
2206 var len: usize = entries.len;
2207 while (len > 1) {
2208 const mid = left + len / 2;
2209 const offset = entries[mid].functionOffset;
2210 if (mapped_pc < offset) {
2211 len /= 2;
2212 } else {
2213 left = mid;
2214 if (mapped_pc == offset) break;
2215 len -= len / 2;
2216 }
2217 }
2218
2219 break :blk .{
2220 .function_offset = entries[left].functionOffset,
2221 .raw_encoding = entries[left].encoding,
2222 };
2223 },
2224 .COMPRESSED => blk: {
2225 const page_header = mem.bytesAsValue(
2226 unwind_info_compressed_second_level_page_header,
2227 unwind_info[start_offset..][0..@sizeOf(unwind_info_compressed_second_level_page_header)],
2228 );
2229
2230 const entries = mem.bytesAsSlice(
2231 UnwindInfoCompressedEntry,
2232 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(UnwindInfoCompressedEntry)],
2233 );
2234 if (entries.len == 0) return error.InvalidUnwindInfo;
2235
2236 var left: usize = 0;
2237 var len: usize = entries.len;
2238 while (len > 1) {
2239 const mid = left + len / 2;
2240 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
2241 if (mapped_pc < offset) {
2242 len /= 2;
2243 } else {
2244 left = mid;
2245 if (mapped_pc == offset) break;
2246 len -= len / 2;
2247 }
2248 }
2249
2250 const entry = entries[left];
2251 const function_offset = second_level_index.functionOffset + entry.funcOffset;
2252 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
2253 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
2254 break :blk .{
2255 .function_offset = function_offset,
2256 .raw_encoding = common_encodings[entry.encodingIndex],
2257 };
2258 } else {
2259 const local_index = try std.math.sub(
2260 u8,
2261 entry.encodingIndex,
2262 std.math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
2263 );
2264 const local_encodings = mem.bytesAsSlice(
2265 compact_unwind_encoding_t,
2266 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(compact_unwind_encoding_t)],
2267 );
2268 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
2269 break :blk .{
2270 .function_offset = function_offset,
2271 .raw_encoding = local_encodings[local_index],
2272 };
2273 }
2274 },
2275 else => return error.InvalidUnwindInfo,
2276 };
2277
2278 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
2279 const reg_context = dwarf.abi.RegisterContext{
2280 .eh_frame = false,
2281 .is_macho = true,
2282 };
2283
2284 const encoding: CompactUnwindEncoding = @bitCast(entry.raw_encoding);
2285 const new_ip = switch (builtin.cpu.arch) {
2286 .x86_64 => switch (encoding.mode.x86_64) {
2287 .OLD => return error.UnimplementedUnwindEncoding,
2288 .RBP_FRAME => blk: {
2289 const regs: [5]u3 = .{
2290 encoding.value.x86_64.frame.reg0,
2291 encoding.value.x86_64.frame.reg1,
2292 encoding.value.x86_64.frame.reg2,
2293 encoding.value.x86_64.frame.reg3,
2294 encoding.value.x86_64.frame.reg4,
2295 };
2296
2297 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
2298 var max_reg: usize = 0;
2299 inline for (regs, 0..) |reg, i| {
2300 if (reg > 0) max_reg = i;
2301 }
2302
2303 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2304 const new_sp = fp + 2 * @sizeOf(usize);
2305
2306 // Verify the stack range we're about to read register values from
2307 if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo;
2308
2309 const ip_ptr = fp + @sizeOf(usize);
2310 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2311 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2312
2313 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2314 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2315 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2316
2317 for (regs, 0..) |reg, i| {
2318 if (reg == 0) continue;
2319 const addr = fp - frame_offset + i * @sizeOf(usize);
2320 const reg_number = try dwarfRegNumber(reg);
2321 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
2322 }
2323
2324 break :blk new_ip;
2325 },
2326 .STACK_IMMD,
2327 .STACK_IND,
2328 => blk: {
2329 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2330 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
2331 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
2332 else stack_size: {
2333 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
2334 const sub_offset_addr =
2335 module_base_address +
2336 entry.function_offset +
2337 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
2338 if (!context.isValidMemory(sub_offset_addr)) return error.InvalidUnwindInfo;
2339
2340 // `sub_offset_addr` points to the offset of the literal within the instruction
2341 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
2342 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
2343 };
2344
2345 // Decode the Lehmer-coded sequence of registers.
2346 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
2347
2348 // Decode the variable-based permutation number into its digits. Each digit represents
2349 // an index into the list of register numbers that weren't yet used in the sequence at
2350 // the time the digit was added.
2351 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2352 const ip_ptr = if (reg_count > 0) reg_blk: {
2353 var digits: [6]u3 = undefined;
2354 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2355 var base: usize = 2;
2356 for (0..reg_count) |i| {
2357 const div = accumulator / base;
2358 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2359 accumulator = div;
2360 base += 1;
2361 }
2362
2363 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2364 var registers: [reg_numbers.len]u3 = undefined;
2365 var used_indices = [_]bool{false} ** reg_numbers.len;
2366 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2367 var unused_count: u8 = 0;
2368 const unused_index = for (used_indices, 0..) |used, index| {
2369 if (!used) {
2370 if (target_unused_index == unused_count) break index;
2371 unused_count += 1;
2372 }
2373 } else unreachable;
2374
2375 registers[i] = reg_numbers[unused_index];
2376 used_indices[unused_index] = true;
2377 }
2378
2379 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2380 if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo;
2381 for (0..reg_count) |i| {
2382 const reg_number = try dwarfRegNumber(registers[i]);
2383 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2384 reg_addr += @sizeOf(usize);
2385 }
2386
2387 break :reg_blk reg_addr;
2388 } else sp + stack_size - @sizeOf(usize);
2389
2390 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2391 const new_sp = ip_ptr + @sizeOf(usize);
2392 if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo;
2393
2394 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2395 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2396
2397 break :blk new_ip;
2398 },
2399 .DWARF => {
2400 return unwindFrameDwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
2401 },
2402 },
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 },
2413 .DWARF => {
2414 return unwindFrameDwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
2415 },
2416 .FRAME => blk: {
2417 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2418 const new_sp = fp + 16;
2419 const ip_ptr = fp + @sizeOf(usize);
2420
2421 const num_restored_pairs: usize =
2422 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2423 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2424 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2425
2426 if (!context.isValidMemory(new_sp) or !context.isValidMemory(min_reg_addr)) return error.InvalidUnwindInfo;
2427
2428 var reg_addr = fp - @sizeOf(usize);
2429 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2430 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2431 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2432 reg_addr += @sizeOf(usize);
2433 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2434 reg_addr += @sizeOf(usize);
2435 }
2436 }
2437
2438 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2439 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2440 // Only the lower half of the 128-bit V registers are restored during unwinding
2441 @memcpy(
2442 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2443 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2444 );
2445 reg_addr += @sizeOf(usize);
2446 @memcpy(
2447 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2448 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2449 );
2450 reg_addr += @sizeOf(usize);
2451 }
2452 }
2453
2454 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2455 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2456
2457 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2458 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2459
2460 break :blk new_ip;
2461 },
2462 },
2463 else => return error.UnimplementedArch,
2464 };
2465
2466 context.pc = dwarf.abi.stripInstructionPtrAuthCode(new_ip);
2467 if (context.pc > 0) context.pc -= 1;
2468 return new_ip;
2469}
2470
2471fn unwindFrameDwarf(context: *dwarf.UnwindContext, eh_frame: []const u8, fde_offset: usize) !usize {
2472 var di = dwarf.DwarfInfo{
2473 .endian = builtin.cpu.arch.endian(),
2474 .is_macho = true,
2475 };
2476 defer di.deinit(context.allocator);
2477
2478 di.sections[@intFromEnum(dwarf.DwarfSection.eh_frame)] = .{
2479 .data = eh_frame,
2480 .owned = false,
2481 };
2482
2483 return di.unwindFrame(context, fde_offset);
2484}