| ... | ... | @@ -707,7 +707,7 @@ pub const Module = switch (native_os) { |
| 707 | 707 | } |
| 708 | 708 | } |
| 709 | 709 | |
| 710 | | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf { |
| 710 | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { |
| 711 | 711 | return if ((try self.getOFileInfoForAddress(allocator, address)).o_file_info) |o_file_info| &o_file_info.di else null; |
| 712 | 712 | } |
| 713 | 713 | }, |
| ... | ... | @@ -784,7 +784,7 @@ pub const Module = switch (native_os) { |
| 784 | 784 | return .{}; |
| 785 | 785 | } |
| 786 | 786 | |
| 787 | | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf { |
| 787 | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { |
| 788 | 788 | _ = allocator; |
| 789 | 789 | _ = address; |
| 790 | 790 | |
| ... | ... | @@ -808,7 +808,7 @@ pub const Module = switch (native_os) { |
| 808 | 808 | return .{}; |
| 809 | 809 | } |
| 810 | 810 | |
| 811 | | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf { |
| 811 | pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { |
| 812 | 812 | _ = self; |
| 813 | 813 | _ = allocator; |
| 814 | 814 | _ = address; |
| ... | ... | @@ -1156,11 +1156,12 @@ test machoSearchSymbols { |
| 1156 | 1156 | /// If the compact encoding can't encode a way to unwind a frame, it will |
| 1157 | 1157 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. |
| 1158 | 1158 | pub fn unwindFrameMachO( |
| 1159 | allocator: Allocator, |
| 1160 | base_address: usize, |
| 1159 | 1161 | context: *UnwindContext, |
| 1160 | 1162 | ma: *std.debug.MemoryAccessor, |
| 1161 | 1163 | unwind_info: []const u8, |
| 1162 | 1164 | eh_frame: ?[]const u8, |
| 1163 | | module_base_address: usize, |
| 1164 | 1165 | ) !usize { |
| 1165 | 1166 | const header = std.mem.bytesAsValue( |
| 1166 | 1167 | macho.unwind_info_section_header, |
| ... | ... | @@ -1172,7 +1173,7 @@ pub fn unwindFrameMachO( |
| 1172 | 1173 | ); |
| 1173 | 1174 | if (indices.len == 0) return error.MissingUnwindInfo; |
| 1174 | 1175 | |
| 1175 | | const mapped_pc = context.pc - module_base_address; |
| 1176 | const mapped_pc = context.pc - base_address; |
| 1176 | 1177 | const second_level_index = blk: { |
| 1177 | 1178 | var left: usize = 0; |
| 1178 | 1179 | var len: usize = indices.len; |
| ... | ... | @@ -1351,7 +1352,7 @@ pub fn unwindFrameMachO( |
| 1351 | 1352 | else stack_size: { |
| 1352 | 1353 | // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function. |
| 1353 | 1354 | const sub_offset_addr = |
| 1354 | | module_base_address + |
| 1355 | base_address + |
| 1355 | 1356 | entry.function_offset + |
| 1356 | 1357 | encoding.value.x86_64.frameless.stack.indirect.sub_offset; |
| 1357 | 1358 | if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; |
| ... | ... | @@ -1416,7 +1417,7 @@ pub fn unwindFrameMachO( |
| 1416 | 1417 | break :blk new_ip; |
| 1417 | 1418 | }, |
| 1418 | 1419 | .DWARF => { |
| 1419 | | return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); |
| 1420 | return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); |
| 1420 | 1421 | }, |
| 1421 | 1422 | }, |
| 1422 | 1423 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { |
| ... | ... | @@ -1430,7 +1431,7 @@ pub fn unwindFrameMachO( |
| 1430 | 1431 | break :blk new_ip; |
| 1431 | 1432 | }, |
| 1432 | 1433 | .DWARF => { |
| 1433 | | return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); |
| 1434 | return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); |
| 1434 | 1435 | }, |
| 1435 | 1436 | .FRAME => blk: { |
| 1436 | 1437 | const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; |
| ... | ... | @@ -1555,13 +1556,16 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { |
| 1555 | 1556 | |
| 1556 | 1557 | /// Unwind a stack frame using DWARF unwinding info, updating the register context. |
| 1557 | 1558 | /// |
| 1558 | | /// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE. |
| 1559 | | /// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE. |
| 1559 | /// If `.eh_frame_hdr` is available and complete, it will be used to binary search for the FDE. |
| 1560 | /// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE. The latter |
| 1561 | /// may require lazily loading the data in those sections. |
| 1560 | 1562 | /// |
| 1561 | 1563 | /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info |
| 1562 | 1564 | /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. |
| 1563 | 1565 | pub fn unwindFrameDwarf( |
| 1564 | | di: *const Dwarf, |
| 1566 | allocator: Allocator, |
| 1567 | di: *Dwarf, |
| 1568 | base_address: usize, |
| 1565 | 1569 | context: *UnwindContext, |
| 1566 | 1570 | ma: *std.debug.MemoryAccessor, |
| 1567 | 1571 | explicit_fde_offset: ?usize, |
| ... | ... | @@ -1570,10 +1574,7 @@ pub fn unwindFrameDwarf( |
| 1570 | 1574 | if (context.pc == 0) return 0; |
| 1571 | 1575 | |
| 1572 | 1576 | // Find the FDE and CIE |
| 1573 | | var cie: Dwarf.CommonInformationEntry = undefined; |
| 1574 | | var fde: Dwarf.FrameDescriptionEntry = undefined; |
| 1575 | | |
| 1576 | | if (explicit_fde_offset) |fde_offset| { |
| 1577 | const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: { |
| 1577 | 1578 | const dwarf_section: Dwarf.Section.Id = .eh_frame; |
| 1578 | 1579 | const frame_section = di.section(dwarf_section) orelse return error.MissingFDE; |
| 1579 | 1580 | if (fde_offset >= frame_section.len) return error.MissingFDE; |
| ... | ... | @@ -1594,7 +1595,7 @@ pub fn unwindFrameDwarf( |
| 1594 | 1595 | const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section); |
| 1595 | 1596 | if (cie_entry_header.type != .cie) return Dwarf.bad(); |
| 1596 | 1597 | |
| 1597 | | cie = try Dwarf.CommonInformationEntry.parse( |
| 1598 | const cie = try Dwarf.CommonInformationEntry.parse( |
| 1598 | 1599 | cie_entry_header.entry_bytes, |
| 1599 | 1600 | 0, |
| 1600 | 1601 | true, |
| ... | ... | @@ -1604,8 +1605,7 @@ pub fn unwindFrameDwarf( |
| 1604 | 1605 | @sizeOf(usize), |
| 1605 | 1606 | native_endian, |
| 1606 | 1607 | ); |
| 1607 | | |
| 1608 | | fde = try Dwarf.FrameDescriptionEntry.parse( |
| 1608 | const fde = try Dwarf.FrameDescriptionEntry.parse( |
| 1609 | 1609 | fde_entry_header.entry_bytes, |
| 1610 | 1610 | 0, |
| 1611 | 1611 | true, |
| ... | ... | @@ -1613,17 +1613,44 @@ pub fn unwindFrameDwarf( |
| 1613 | 1613 | @sizeOf(usize), |
| 1614 | 1614 | native_endian, |
| 1615 | 1615 | ); |
| 1616 | | } else if (di.eh_frame_hdr) |header| { |
| 1617 | | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| 1618 | | try header.findEntry( |
| 1619 | | ma, |
| 1620 | | eh_frame_len, |
| 1621 | | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), |
| 1622 | | context.pc, |
| 1623 | | &cie, |
| 1624 | | &fde, |
| 1625 | | ); |
| 1626 | | } else { |
| 1616 | |
| 1617 | break :blk .{ cie, fde }; |
| 1618 | } else blk: { |
| 1619 | // `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall |
| 1620 | // back to loading `.eh_frame`/`.debug_frame` and using those from that point on. |
| 1621 | |
| 1622 | if (di.eh_frame_hdr) |header| hdr: { |
| 1623 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| 1624 | |
| 1625 | var cie: Dwarf.CommonInformationEntry = undefined; |
| 1626 | var fde: Dwarf.FrameDescriptionEntry = undefined; |
| 1627 | |
| 1628 | header.findEntry( |
| 1629 | ma, |
| 1630 | eh_frame_len, |
| 1631 | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), |
| 1632 | context.pc, |
| 1633 | &cie, |
| 1634 | &fde, |
| 1635 | ) catch |err| switch (err) { |
| 1636 | error.InvalidDebugInfo => { |
| 1637 | // `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map` |
| 1638 | // and `fde_list`, and fall back to the binary search logic below. |
| 1639 | try di.scanCieFdeInfo(allocator, base_address); |
| 1640 | |
| 1641 | // Since `.eh_frame_hdr` is incomplete, we're very likely to get more lookup |
| 1642 | // failures using it, and we've just built a complete, sorted list of FDEs |
| 1643 | // anyway, so just stop using `.eh_frame_hdr` altogether. |
| 1644 | di.eh_frame_hdr = null; |
| 1645 | |
| 1646 | break :hdr; |
| 1647 | }, |
| 1648 | else => return err, |
| 1649 | }; |
| 1650 | |
| 1651 | break :blk .{ cie, fde }; |
| 1652 | } |
| 1653 | |
| 1627 | 1654 | const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, di.fde_list.items, context.pc, struct { |
| 1628 | 1655 | pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order { |
| 1629 | 1656 | if (pc < item.pc_begin) return .lt; |
| ... | ... | @@ -1635,9 +1662,11 @@ pub fn unwindFrameDwarf( |
| 1635 | 1662 | } |
| 1636 | 1663 | }.compareFn); |
| 1637 | 1664 | |
| 1638 | | fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE; |
| 1639 | | cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1640 | | } |
| 1665 | const fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE; |
| 1666 | const cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1667 | |
| 1668 | break :blk .{ cie, fde }; |
| 1669 | }; |
| 1641 | 1670 | |
| 1642 | 1671 | var expression_context: Dwarf.expression.Context = .{ |
| 1643 | 1672 | .format = cie.format, |
| ... | ... | @@ -1802,6 +1831,8 @@ pub fn supportsUnwinding(target: std.Target) bool { |
| 1802 | 1831 | } |
| 1803 | 1832 | |
| 1804 | 1833 | fn unwindFrameMachODwarf( |
| 1834 | allocator: Allocator, |
| 1835 | base_address: usize, |
| 1805 | 1836 | context: *UnwindContext, |
| 1806 | 1837 | ma: *std.debug.MemoryAccessor, |
| 1807 | 1838 | eh_frame: []const u8, |
| ... | ... | @@ -1818,7 +1849,7 @@ fn unwindFrameMachODwarf( |
| 1818 | 1849 | .owned = false, |
| 1819 | 1850 | }; |
| 1820 | 1851 | |
| 1821 | | return unwindFrameDwarf(&di, context, ma, fde_offset); |
| 1852 | return unwindFrameDwarf(allocator, &di, base_address, context, ma, fde_offset); |
| 1822 | 1853 | } |
| 1823 | 1854 | |
| 1824 | 1855 | /// This is a virtual machine that runs DWARF call frame instructions. |