| ... | ... | @@ -1562,8 +1562,7 @@ pub const DwarfInfo = struct { |
| 1562 | 1562 | /// If .eh_frame_hdr is present, then only the header needs to be parsed. |
| 1563 | 1563 | /// |
| 1564 | 1564 | /// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list |
| 1565 | | /// of FDEs is built. In this case, the decoded PC ranges in the FDEs |
| 1566 | | /// are all normalized to be relative to the module's base. |
| 1565 | /// of FDEs is built for binary searching during unwinding. |
| 1567 | 1566 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void { |
| 1568 | 1567 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: { |
| 1569 | 1568 | var stream = io.fixedBufferStream(eh_frame_hdr); |
| ... | ... | @@ -1650,7 +1649,14 @@ pub const DwarfInfo = struct { |
| 1650 | 1649 | } |
| 1651 | 1650 | } |
| 1652 | 1651 | |
| 1653 | | pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, module_base_address: usize) !usize { |
| 1652 | /// Unwind a stack frame using DWARF unwinding info, updating the register context. |
| 1653 | /// |
| 1654 | /// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE. |
| 1655 | /// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE. |
| 1656 | /// |
| 1657 | /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info |
| 1658 | /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. |
| 1659 | pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, explicit_fde_offset: ?usize) !usize { |
| 1654 | 1660 | if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture; |
| 1655 | 1661 | if (context.pc == 0) return 0; |
| 1656 | 1662 | |
| ... | ... | @@ -1660,26 +1666,54 @@ pub const DwarfInfo = struct { |
| 1660 | 1666 | var cie: CommonInformationEntry = undefined; |
| 1661 | 1667 | var fde: FrameDescriptionEntry = undefined; |
| 1662 | 1668 | |
| 1663 | | // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section), |
| 1664 | | // scanAllUnwindInfo has already mapped any pc-relative offsets such that they will be relative to zero |
| 1665 | | // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly |
| 1666 | | // as pointers will be decoded relative to the already-mapped .eh_frame. |
| 1667 | | var mapped_pc: usize = undefined; |
| 1668 | | if (di.eh_frame_hdr) |header| { |
| 1669 | if (explicit_fde_offset) |fde_offset| { |
| 1670 | const dwarf_section: DwarfSection = .eh_frame; |
| 1671 | const frame_section = di.section(dwarf_section) orelse return error.MissingFDE; |
| 1672 | if (fde_offset >= frame_section.len) return error.MissingFDE; |
| 1673 | |
| 1674 | var stream = io.fixedBufferStream(frame_section); |
| 1675 | const fde_entry_header = try EntryHeader.read(&stream, dwarf_section, di.endian); |
| 1676 | if (fde_entry_header.type != .fde) return error.MissingFDE; |
| 1677 | |
| 1678 | const cie_offset = fde_entry_header.type.fde; |
| 1679 | try stream.seekTo(cie_offset); |
| 1680 | |
| 1681 | const cie_entry_header = try EntryHeader.read(&stream, dwarf_section, builtin.cpu.arch.endian()); |
| 1682 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 1683 | |
| 1684 | cie = try CommonInformationEntry.parse( |
| 1685 | cie_entry_header.entry_bytes, |
| 1686 | 0, |
| 1687 | true, |
| 1688 | cie_entry_header.is_64, |
| 1689 | dwarf_section, |
| 1690 | cie_entry_header.length_offset, |
| 1691 | @sizeOf(usize), |
| 1692 | builtin.cpu.arch.endian(), |
| 1693 | ); |
| 1694 | |
| 1695 | fde = try FrameDescriptionEntry.parse( |
| 1696 | fde_entry_header.entry_bytes, |
| 1697 | 0, |
| 1698 | true, |
| 1699 | cie, |
| 1700 | @sizeOf(usize), |
| 1701 | builtin.cpu.arch.endian(), |
| 1702 | ); |
| 1703 | } else if (di.eh_frame_hdr) |header| { |
| 1704 | std.debug.print("EH_FRAME_HDR\n", .{}); |
| 1705 | |
| 1669 | 1706 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| 1670 | | mapped_pc = context.pc; |
| 1671 | 1707 | try header.findEntry( |
| 1672 | 1708 | context.isValidMemory, |
| 1673 | 1709 | eh_frame_len, |
| 1674 | 1710 | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), |
| 1675 | | mapped_pc, |
| 1711 | context.pc, |
| 1676 | 1712 | &cie, |
| 1677 | 1713 | &fde, |
| 1678 | 1714 | ); |
| 1679 | 1715 | } else { |
| 1680 | | //mapped_pc = context.pc - module_base_address; |
| 1681 | | mapped_pc = context.pc; |
| 1682 | | const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct { |
| 1716 | const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct { |
| 1683 | 1717 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1684 | 1718 | if (pc < mid_item.pc_begin) return .lt; |
| 1685 | 1719 | |
| ... | ... | @@ -1707,7 +1741,7 @@ pub const DwarfInfo = struct { |
| 1707 | 1741 | context.reg_context.eh_frame = cie.version != 4; |
| 1708 | 1742 | context.reg_context.is_macho = di.is_macho; |
| 1709 | 1743 | |
| 1710 | | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); |
| 1744 | _ = try context.vm.runToNative(context.allocator, context.pc, cie, fde); |
| 1711 | 1745 | const row = &context.vm.current_row; |
| 1712 | 1746 | |
| 1713 | 1747 | context.cfa = switch (row.cfa.rule) { |
| ... | ... | @@ -2056,7 +2090,7 @@ pub const ExceptionFrameHeader = struct { |
| 2056 | 2090 | if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); |
| 2057 | 2091 | if (fde_entry_header.type != .fde) return badDwarf(); |
| 2058 | 2092 | |
| 2059 | | // CIEs always come before FDEs (the offset is a subtration), so we can assume this memory is readable |
| 2093 | // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable |
| 2060 | 2094 | const cie_offset = fde_entry_header.type.fde; |
| 2061 | 2095 | try eh_frame_stream.seekTo(cie_offset); |
| 2062 | 2096 | const cie_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian()); |