| author | |
| committer | |
| log | 94354aa6aa16274070e49cc261778f1924432ecc |
| tree | e9178fe11a37e36a4c1756e989ed8aaf309ca0ca |
| parent | d226b74ae8a408ca6d363295e00fdc2876d77fb0 |
dwarf: fixup missing error6 files changed, 409 insertions(+), 40 deletions(-)
lib/std/debug.zig+32-21| ... | @@ -623,11 +623,15 @@ pub const StackIterator = struct { | ... | @@ -623,11 +623,15 @@ pub const StackIterator = struct { |
| 623 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); | 623 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); |
| 624 | switch (native_os) { | 624 | switch (native_os) { |
| 625 | .macos, .ios, .watchos, .tvos => { | 625 | .macos, .ios, .watchos, .tvos => { |
| 626 | const o_file_info = try module.getOFileInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc); | 626 | // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding |
| 627 | if (o_file_info.unwind_info == null) return error.MissingUnwindInfo; | 627 | // via DWARF before attempting to use the compact unwind info will produce incorrect results. |
| 628 | 628 | if (module.unwind_info) |unwind_info| { | |
| 629 | // TODO: Unwind using __unwind_info, | 629 | if (macho.unwindFrame(&self.dwarf_context, unwind_info, module.base_address)) |return_address| { |
| 630 | unreachable; | 630 | return return_address; |
| 631 | } else |err| { | ||
| 632 | if (err != error.RequiresDWARFUnwind) return err; | ||
| 633 | } | ||
| 634 | } else return error.MissingUnwindInfo; | ||
| 631 | }, | 635 | }, |
| 632 | else => {}, | 636 | else => {}, |
| 633 | } | 637 | } |
| ... | @@ -1236,7 +1240,16 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn | ... | @@ -1236,7 +1240,16 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn |
| 1236 | .ncmds = hdr.ncmds, | 1240 | .ncmds = hdr.ncmds, |
| 1237 | .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], | 1241 | .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], |
| 1238 | }; | 1242 | }; |
| 1243 | var unwind_info: ?[]const u8 = null; | ||
| 1239 | const symtab = while (it.next()) |cmd| switch (cmd.cmd()) { | 1244 | 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 | }, | ||
| 1240 | .SYMTAB => break cmd.cast(macho.symtab_command).?, | 1253 | .SYMTAB => break cmd.cast(macho.symtab_command).?, |
| 1241 | else => {}, | 1254 | else => {}, |
| 1242 | } else return error.MissingDebugInfo; | 1255 | } else return error.MissingDebugInfo; |
| ... | @@ -1346,6 +1359,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn | ... | @@ -1346,6 +1359,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn |
| 1346 | .ofiles = ModuleDebugInfo.OFileTable.init(allocator), | 1359 | .ofiles = ModuleDebugInfo.OFileTable.init(allocator), |
| 1347 | .symbols = symbols, | 1360 | .symbols = symbols, |
| 1348 | .strings = strings, | 1361 | .strings = strings, |
| 1362 | .unwind_info = unwind_info, | ||
| 1349 | }; | 1363 | }; |
| 1350 | } | 1364 | } |
| 1351 | 1365 | ||
| ... | @@ -1886,12 +1900,13 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1886,12 +1900,13 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1886 | symbols: []const MachoSymbol, | 1900 | symbols: []const MachoSymbol, |
| 1887 | strings: [:0]const u8, | 1901 | strings: [:0]const u8, |
| 1888 | ofiles: OFileTable, | 1902 | ofiles: OFileTable, |
| 1903 | // Backed by mapped_memory | ||
| 1904 | unwind_info: ?[]const u8, | ||
| 1889 | 1905 | ||
| 1890 | const OFileTable = std.StringHashMap(OFileInfo); | 1906 | const OFileTable = std.StringHashMap(OFileInfo); |
| 1891 | const OFileInfo = struct { | 1907 | const OFileInfo = struct { |
| 1892 | di: DW.DwarfInfo, | 1908 | di: DW.DwarfInfo, |
| 1893 | addr_table: std.StringHashMap(u64), | 1909 | addr_table: std.StringHashMap(u64), |
| 1894 | unwind_info: ?[]const u8, | ||
| 1895 | }; | 1910 | }; |
| 1896 | 1911 | ||
| 1897 | fn deinit(self: *@This(), allocator: mem.Allocator) void { | 1912 | fn deinit(self: *@This(), allocator: mem.Allocator) void { |
| ... | @@ -1949,24 +1964,21 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1949,24 +1964,21 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1949 | addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); | 1964 | addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); |
| 1950 | } | 1965 | } |
| 1951 | 1966 | ||
| 1952 | var unwind_info: ?[]const u8 = null; | ||
| 1953 | var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; | 1967 | var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; |
| 1954 | for (segcmd.?.getSections()) |sect| { | 1968 | for (segcmd.?.getSections()) |sect| { |
| 1955 | if (std.mem.eql(u8, "__TEXT", sect.segName()) and mem.eql(u8, "__unwind_info", sect.sectName())) { | 1969 | if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue; |
| 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; | ||
| 1963 | 1970 | ||
| 1964 | const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size); | 1971 | var section_index: ?usize = null; |
| 1965 | sections[section_index.?] = .{ | 1972 | inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| { |
| 1966 | .data = section_bytes, | 1973 | if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i; |
| 1967 | .owned = false, | ||
| 1968 | }; | ||
| 1969 | } | 1974 | } |
| 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 | }; | ||
| 1970 | } | 1982 | } |
| 1971 | 1983 | ||
| 1972 | const missing_debug_info = | 1984 | const missing_debug_info = |
| ... | @@ -1986,7 +1998,6 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1986,7 +1998,6 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1986 | var info = OFileInfo{ | 1998 | var info = OFileInfo{ |
| 1987 | .di = di, | 1999 | .di = di, |
| 1988 | .addr_table = addr_table, | 2000 | .addr_table = addr_table, |
| 1989 | .unwind_info = unwind_info, | ||
| 1990 | }; | 2001 | }; |
| 1991 | 2002 | ||
| 1992 | // Add the debug info to the cache | 2003 | // Add the debug info to the cache |
lib/std/dwarf.zig+6-10| ... | @@ -1641,7 +1641,6 @@ pub const DwarfInfo = struct { | ... | @@ -1641,7 +1641,6 @@ pub const DwarfInfo = struct { |
| 1641 | // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly | 1641 | // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly |
| 1642 | // as pointers will be decoded relative to the alreayd-mapped .eh_frame. | 1642 | // as pointers will be decoded relative to the alreayd-mapped .eh_frame. |
| 1643 | var mapped_pc: usize = undefined; | 1643 | var mapped_pc: usize = undefined; |
| 1644 | |||
| 1645 | if (di.eh_frame_hdr) |header| { | 1644 | if (di.eh_frame_hdr) |header| { |
| 1646 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; | 1645 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| 1647 | mapped_pc = context.pc; | 1646 | mapped_pc = context.pc; |
| ... | @@ -1657,16 +1656,12 @@ pub const DwarfInfo = struct { | ... | @@ -1657,16 +1656,12 @@ pub const DwarfInfo = struct { |
| 1657 | mapped_pc = context.pc - module_base_address; | 1656 | mapped_pc = context.pc - module_base_address; |
| 1658 | const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct { | 1657 | const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct { |
| 1659 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { | 1658 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1660 | if (pc < mid_item.pc_begin) { | 1659 | if (pc < mid_item.pc_begin) return .lt; |
| 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 | } | ||
| 1667 | 1660 | ||
| 1668 | return .gt; | 1661 | const range_end = mid_item.pc_begin + mid_item.pc_range; |
| 1669 | } | 1662 | if (pc < range_end) return .eq; |
| 1663 | |||
| 1664 | return .gt; | ||
| 1670 | } | 1665 | } |
| 1671 | }.compareFn); | 1666 | }.compareFn); |
| 1672 | 1667 | ||
| ... | @@ -2000,6 +1995,7 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -2000,6 +1995,7 @@ pub const ExceptionFrameHeader = struct { |
| 2000 | } | 1995 | } |
| 2001 | } | 1996 | } |
| 2002 | 1997 | ||
| 1998 | if (len == 0) return badDwarf(); | ||
| 2003 | try stream.seekTo(left * entry_size); | 1999 | try stream.seekTo(left * entry_size); |
| 2004 | 2000 | ||
| 2005 | // Read past the pc_begin field of the entry | 2001 | // 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 { | ... | @@ -45,15 +45,6 @@ pub fn spRegNum(reg_context: RegisterContext) u8 { |
| 45 | }; | 45 | }; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | fn 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 | |||
| 57 | pub const RegisterContext = struct { | 48 | pub const RegisterContext = struct { |
| 58 | eh_frame: bool, | 49 | eh_frame: bool, |
| 59 | is_macho: bool, | 50 | is_macho: bool, |
| ... | @@ -63,9 +54,47 @@ pub const AbiError = error{ | ... | @@ -63,9 +54,47 @@ pub const AbiError = error{ |
| 63 | InvalidRegister, | 54 | InvalidRegister, |
| 64 | UnimplementedArch, | 55 | UnimplementedArch, |
| 65 | UnimplementedOs, | 56 | UnimplementedOs, |
| 57 | RegisterContextRequired, | ||
| 66 | ThreadContextNotSupported, | 58 | ThreadContextNotSupported, |
| 67 | }; | 59 | }; |
| 68 | 60 | ||
| 61 | fn 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 | |||
| 78 | pub 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 | |||
| 89 | fn 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 | |||
| 69 | /// Returns a slice containing the backing storage for `reg_number`. | 98 | /// Returns a slice containing the backing storage for `reg_number`. |
| 70 | /// | 99 | /// |
| 71 | /// `reg_context` describes in what context the register number is used, as it can have different | 100 | /// `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; | ... | @@ -2064,3 +2064,315 @@ pub const UNWIND_ARM64_FRAME_D14_D15_PAIR: u32 = 0x00000800; |
| 2064 | 2064 | ||
| 2065 | pub const UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK: u32 = 0x00FFF000; | 2065 | pub const UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK: u32 = 0x00FFF000; |
| 2066 | pub const UNWIND_ARM64_DWARF_SECTION_OFFSET: u32 = 0x00FFFFFF; | 2066 | pub const UNWIND_ARM64_DWARF_SECTION_OFFSET: u32 = 0x00FFFFFF; |
| 2067 | |||
| 2068 | pub 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 | ||
| 2122 | fn 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 | |||
| 2134 | const dwarf = std.dwarf; | ||
| 2135 | const abi = dwarf.abi; | ||
| 2136 | |||
| 2137 | pub 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 { | ... | @@ -16,6 +16,7 @@ pub fn build(b: *std.Build) void { |
| 16 | .optimize = optimize, | 16 | .optimize = optimize, |
| 17 | }); | 17 | }); |
| 18 | 18 | ||
| 19 | if (target.isDarwin()) exe.unwind_tables = true; | ||
| 19 | exe.omit_frame_pointer = true; | 20 | exe.omit_frame_pointer = true; |
| 20 | 21 | ||
| 21 | const run_cmd = b.addRunArtifact(exe); | 22 | const run_cmd = b.addRunArtifact(exe); |
| ... | @@ -43,6 +44,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -43,6 +44,7 @@ pub fn build(b: *std.Build) void { |
| 43 | .optimize = optimize, | 44 | .optimize = optimize, |
| 44 | }); | 45 | }); |
| 45 | 46 | ||
| 47 | if (target.isDarwin()) exe.unwind_tables = true; | ||
| 46 | exe.omit_frame_pointer = true; | 48 | exe.omit_frame_pointer = true; |
| 47 | exe.linkLibrary(c_shared_lib); | 49 | exe.linkLibrary(c_shared_lib); |
| 48 | 50 |
test/standalone/dwarf_unwinding/zig_unwind.zig+19| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const debug = std.debug; | 3 | const debug = std.debug; |
| 3 | const testing = std.testing; | 4 | const testing = std.testing; |
| 4 | 5 | ||
| ... | @@ -18,6 +19,24 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | ... | @@ -18,6 +19,24 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { |
| 18 | } | 19 | } |
| 19 | 20 | ||
| 20 | noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void { | 21 | noinline 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 | |||
| 21 | expected[1] = @returnAddress(); | 40 | expected[1] = @returnAddress(); |
| 22 | frame3(expected, unwound); | 41 | frame3(expected, unwound); |
| 23 | } | 42 | } |