| ... | @@ -475,8 +475,8 @@ const UnitHeader = struct { | ... | @@ -475,8 +475,8 @@ const UnitHeader = struct { |
| 475 | header_length: u4, | 475 | header_length: u4, |
| 476 | unit_length: u64, | 476 | unit_length: u64, |
| 477 | }; | 477 | }; |
| 478 | fn readUnitHeader(fbr: *FixedBufferReader) !UnitHeader { | 478 | fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*debug.StackIterator.MemoryAccessor) !UnitHeader { |
| 479 | return switch (try fbr.readInt(u32)) { | 479 | return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) { |
| 480 | 0...0xfffffff0 - 1 => |unit_length| .{ | 480 | 0...0xfffffff0 - 1 => |unit_length| .{ |
| 481 | .format = .@"32", | 481 | .format = .@"32", |
| 482 | .header_length = 4, | 482 | .header_length = 4, |
| ... | @@ -486,7 +486,7 @@ fn readUnitHeader(fbr: *FixedBufferReader) !UnitHeader { | ... | @@ -486,7 +486,7 @@ fn readUnitHeader(fbr: *FixedBufferReader) !UnitHeader { |
| 486 | 0xffffffff => .{ | 486 | 0xffffffff => .{ |
| 487 | .format = .@"64", | 487 | .format = .@"64", |
| 488 | .header_length = 12, | 488 | .header_length = 12, |
| 489 | .unit_length = try fbr.readInt(u64), | 489 | .unit_length = try if (opt_ma) |ma| fbr.readIntChecked(u64, ma) else fbr.readInt(u64), |
| 490 | }, | 490 | }, |
| 491 | }; | 491 | }; |
| 492 | } | 492 | } |
| ... | @@ -663,7 +663,7 @@ pub const DwarfInfo = struct { | ... | @@ -663,7 +663,7 @@ pub const DwarfInfo = struct { |
| 663 | while (this_unit_offset < fbr.buf.len) { | 663 | while (this_unit_offset < fbr.buf.len) { |
| 664 | try fbr.seekTo(this_unit_offset); | 664 | try fbr.seekTo(this_unit_offset); |
| 665 | | 665 | |
| 666 | const unit_header = try readUnitHeader(&fbr); | 666 | const unit_header = try readUnitHeader(&fbr, null); |
| 667 | if (unit_header.unit_length == 0) return; | 667 | if (unit_header.unit_length == 0) return; |
| 668 | const next_offset = unit_header.header_length + unit_header.unit_length; | 668 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 669 | | 669 | |
| ... | @@ -853,7 +853,7 @@ pub const DwarfInfo = struct { | ... | @@ -853,7 +853,7 @@ pub const DwarfInfo = struct { |
| 853 | while (this_unit_offset < fbr.buf.len) { | 853 | while (this_unit_offset < fbr.buf.len) { |
| 854 | try fbr.seekTo(this_unit_offset); | 854 | try fbr.seekTo(this_unit_offset); |
| 855 | | 855 | |
| 856 | const unit_header = try readUnitHeader(&fbr); | 856 | const unit_header = try readUnitHeader(&fbr, null); |
| 857 | if (unit_header.unit_length == 0) return; | 857 | if (unit_header.unit_length == 0) return; |
| 858 | const next_offset = unit_header.header_length + unit_header.unit_length; | 858 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 859 | | 859 | |
| ... | @@ -1200,7 +1200,7 @@ pub const DwarfInfo = struct { | ... | @@ -1200,7 +1200,7 @@ pub const DwarfInfo = struct { |
| 1200 | var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian }; | 1200 | var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian }; |
| 1201 | try fbr.seekTo(line_info_offset); | 1201 | try fbr.seekTo(line_info_offset); |
| 1202 | | 1202 | |
| 1203 | const unit_header = try readUnitHeader(&fbr); | 1203 | const unit_header = try readUnitHeader(&fbr, null); |
| 1204 | if (unit_header.unit_length == 0) return missingDwarf(); | 1204 | if (unit_header.unit_length == 0) return missingDwarf(); |
| 1205 | const next_offset = unit_header.header_length + unit_header.unit_length; | 1205 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 1206 | | 1206 | |
| ... | @@ -1532,7 +1532,7 @@ pub const DwarfInfo = struct { | ... | @@ -1532,7 +1532,7 @@ pub const DwarfInfo = struct { |
| 1532 | if (di.section(frame_section)) |section_data| { | 1532 | if (di.section(frame_section)) |section_data| { |
| 1533 | var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian }; | 1533 | var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian }; |
| 1534 | while (fbr.pos < fbr.buf.len) { | 1534 | while (fbr.pos < fbr.buf.len) { |
| 1535 | const entry_header = try EntryHeader.read(&fbr, frame_section); | 1535 | const entry_header = try EntryHeader.read(&fbr, null, frame_section); |
| 1536 | switch (entry_header.type) { | 1536 | switch (entry_header.type) { |
| 1537 | .cie => { | 1537 | .cie => { |
| 1538 | const cie = try CommonInformationEntry.parse( | 1538 | const cie = try CommonInformationEntry.parse( |
| ... | @@ -1580,7 +1580,7 @@ pub const DwarfInfo = struct { | ... | @@ -1580,7 +1580,7 @@ pub const DwarfInfo = struct { |
| 1580 | /// | 1580 | /// |
| 1581 | /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info | 1581 | /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info |
| 1582 | /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. | 1582 | /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. |
| 1583 | pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, explicit_fde_offset: ?usize) !usize { | 1583 | pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, ma: *debug.StackIterator.MemoryAccessor, explicit_fde_offset: ?usize) !usize { |
| 1584 | if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture; | 1584 | if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture; |
| 1585 | if (context.pc == 0) return 0; | 1585 | if (context.pc == 0) return 0; |
| 1586 | | 1586 | |
| ... | @@ -1599,14 +1599,14 @@ pub const DwarfInfo = struct { | ... | @@ -1599,14 +1599,14 @@ pub const DwarfInfo = struct { |
| 1599 | .endian = di.endian, | 1599 | .endian = di.endian, |
| 1600 | }; | 1600 | }; |
| 1601 | | 1601 | |
| 1602 | const fde_entry_header = try EntryHeader.read(&fbr, dwarf_section); | 1602 | const fde_entry_header = try EntryHeader.read(&fbr, null, dwarf_section); |
| 1603 | if (fde_entry_header.type != .fde) return error.MissingFDE; | 1603 | if (fde_entry_header.type != .fde) return error.MissingFDE; |
| 1604 | | 1604 | |
| 1605 | const cie_offset = fde_entry_header.type.fde; | 1605 | const cie_offset = fde_entry_header.type.fde; |
| 1606 | try fbr.seekTo(cie_offset); | 1606 | try fbr.seekTo(cie_offset); |
| 1607 | | 1607 | |
| 1608 | fbr.endian = native_endian; | 1608 | fbr.endian = native_endian; |
| 1609 | const cie_entry_header = try EntryHeader.read(&fbr, dwarf_section); | 1609 | const cie_entry_header = try EntryHeader.read(&fbr, null, dwarf_section); |
| 1610 | if (cie_entry_header.type != .cie) return badDwarf(); | 1610 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 1611 | | 1611 | |
| 1612 | cie = try CommonInformationEntry.parse( | 1612 | cie = try CommonInformationEntry.parse( |
| ... | @@ -1631,7 +1631,7 @@ pub const DwarfInfo = struct { | ... | @@ -1631,7 +1631,7 @@ pub const DwarfInfo = struct { |
| 1631 | } else if (di.eh_frame_hdr) |header| { | 1631 | } else if (di.eh_frame_hdr) |header| { |
| 1632 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; | 1632 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| 1633 | try header.findEntry( | 1633 | try header.findEntry( |
| 1634 | context.isValidMemory, | 1634 | ma, |
| 1635 | eh_frame_len, | 1635 | eh_frame_len, |
| 1636 | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), | 1636 | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), |
| 1637 | context.pc, | 1637 | context.pc, |
| ... | @@ -1656,7 +1656,7 @@ pub const DwarfInfo = struct { | ... | @@ -1656,7 +1656,7 @@ pub const DwarfInfo = struct { |
| 1656 | | 1656 | |
| 1657 | var expression_context: expressions.ExpressionContext = .{ | 1657 | var expression_context: expressions.ExpressionContext = .{ |
| 1658 | .format = cie.format, | 1658 | .format = cie.format, |
| 1659 | .isValidMemory = context.isValidMemory, | 1659 | .memory_accessor = ma, |
| 1660 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, | 1660 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, |
| 1661 | .thread_context = context.thread_context, | 1661 | .thread_context = context.thread_context, |
| 1662 | .reg_context = context.reg_context, | 1662 | .reg_context = context.reg_context, |
| ... | @@ -1691,7 +1691,7 @@ pub const DwarfInfo = struct { | ... | @@ -1691,7 +1691,7 @@ pub const DwarfInfo = struct { |
| 1691 | else => return error.InvalidCFARule, | 1691 | else => return error.InvalidCFARule, |
| 1692 | }; | 1692 | }; |
| 1693 | | 1693 | |
| 1694 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; | 1694 | if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA; |
| 1695 | expression_context.cfa = context.cfa; | 1695 | expression_context.cfa = context.cfa; |
| 1696 | | 1696 | |
| 1697 | // Buffering the modifications is done because copying the thread context is not portable, | 1697 | // Buffering the modifications is done because copying the thread context is not portable, |
| ... | @@ -1730,6 +1730,7 @@ pub const DwarfInfo = struct { | ... | @@ -1730,6 +1730,7 @@ pub const DwarfInfo = struct { |
| 1730 | try column.resolveValue( | 1730 | try column.resolveValue( |
| 1731 | context, | 1731 | context, |
| 1732 | expression_context, | 1732 | expression_context, |
| | 1733 | ma, |
| 1733 | src, | 1734 | src, |
| 1734 | ); | 1735 | ); |
| 1735 | } | 1736 | } |
| ... | @@ -1788,7 +1789,13 @@ const macho = std.macho; | ... | @@ -1788,7 +1789,13 @@ const macho = std.macho; |
| 1788 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). | 1789 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). |
| 1789 | /// If the compact encoding can't encode a way to unwind a frame, it will | 1790 | /// If the compact encoding can't encode a way to unwind a frame, it will |
| 1790 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. | 1791 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. |
| 1791 | pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_frame: ?[]const u8, module_base_address: usize) !usize { | 1792 | pub fn unwindFrameMachO( |
| | 1793 | context: *UnwindContext, |
| | 1794 | ma: *debug.StackIterator.MemoryAccessor, |
| | 1795 | unwind_info: []const u8, |
| | 1796 | eh_frame: ?[]const u8, |
| | 1797 | module_base_address: usize, |
| | 1798 | ) !usize { |
| 1792 | const header = mem.bytesAsValue( | 1799 | const header = mem.bytesAsValue( |
| 1793 | macho.unwind_info_section_header, | 1800 | macho.unwind_info_section_header, |
| 1794 | unwind_info[0..@sizeOf(macho.unwind_info_section_header)], | 1801 | unwind_info[0..@sizeOf(macho.unwind_info_section_header)], |
| ... | @@ -1950,7 +1957,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -1950,7 +1957,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 1950 | const new_sp = fp + 2 * @sizeOf(usize); | 1957 | const new_sp = fp + 2 * @sizeOf(usize); |
| 1951 | | 1958 | |
| 1952 | // Verify the stack range we're about to read register values from | 1959 | // Verify the stack range we're about to read register values from |
| 1953 | if (!context.isValidMemory(new_sp) or !context.isValidMemory(fp - frame_offset + max_reg * @sizeOf(usize))) return error.InvalidUnwindInfo; | 1960 | if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo; |
| 1954 | | 1961 | |
| 1955 | const ip_ptr = fp + @sizeOf(usize); | 1962 | const ip_ptr = fp + @sizeOf(usize); |
| 1956 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; | 1963 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| ... | @@ -1981,7 +1988,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -1981,7 +1988,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 1981 | module_base_address + | 1988 | module_base_address + |
| 1982 | entry.function_offset + | 1989 | entry.function_offset + |
| 1983 | encoding.value.x86_64.frameless.stack.indirect.sub_offset; | 1990 | encoding.value.x86_64.frameless.stack.indirect.sub_offset; |
| 1984 | if (!context.isValidMemory(sub_offset_addr)) return error.InvalidUnwindInfo; | 1991 | if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; |
| 1985 | | 1992 | |
| 1986 | // `sub_offset_addr` points to the offset of the literal within the instruction | 1993 | // `sub_offset_addr` points to the offset of the literal within the instruction |
| 1987 | const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*; | 1994 | const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*; |
| ... | @@ -2023,7 +2030,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2023,7 +2030,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2023 | } | 2030 | } |
| 2024 | | 2031 | |
| 2025 | var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); | 2032 | var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); |
| 2026 | if (!context.isValidMemory(reg_addr)) return error.InvalidUnwindInfo; | 2033 | if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo; |
| 2027 | for (0..reg_count) |i| { | 2034 | for (0..reg_count) |i| { |
| 2028 | const reg_number = try compactUnwindToDwarfRegNumber(registers[i]); | 2035 | const reg_number = try compactUnwindToDwarfRegNumber(registers[i]); |
| 2029 | (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 2036 | (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| ... | @@ -2035,7 +2042,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2035,7 +2042,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2035 | | 2042 | |
| 2036 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; | 2043 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 2037 | const new_sp = ip_ptr + @sizeOf(usize); | 2044 | const new_sp = ip_ptr + @sizeOf(usize); |
| 2038 | if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo; | 2045 | if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; |
| 2039 | | 2046 | |
| 2040 | (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp; | 2047 | (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp; |
| 2041 | (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip; | 2048 | (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip; |
| ... | @@ -2043,7 +2050,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2043,7 +2050,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2043 | break :blk new_ip; | 2050 | break :blk new_ip; |
| 2044 | }, | 2051 | }, |
| 2045 | .DWARF => { | 2052 | .DWARF => { |
| 2046 | return unwindFrameMachODwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); | 2053 | return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); |
| 2047 | }, | 2054 | }, |
| 2048 | }, | 2055 | }, |
| 2049 | .aarch64 => switch (encoding.mode.arm64) { | 2056 | .aarch64 => switch (encoding.mode.arm64) { |
| ... | @@ -2052,12 +2059,12 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2052,12 +2059,12 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2052 | const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*; | 2059 | const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*; |
| 2053 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; | 2060 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; |
| 2054 | const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*; | 2061 | const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*; |
| 2055 | if (!context.isValidMemory(new_sp)) return error.InvalidUnwindInfo; | 2062 | if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; |
| 2056 | (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp; | 2063 | (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp; |
| 2057 | break :blk new_ip; | 2064 | break :blk new_ip; |
| 2058 | }, | 2065 | }, |
| 2059 | .DWARF => { | 2066 | .DWARF => { |
| 2060 | return unwindFrameMachODwarf(context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); | 2067 | return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); |
| 2061 | }, | 2068 | }, |
| 2062 | .FRAME => blk: { | 2069 | .FRAME => blk: { |
| 2063 | const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*; | 2070 | const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*; |
| ... | @@ -2069,7 +2076,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2069,7 +2076,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2069 | @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs))); | 2076 | @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs))); |
| 2070 | const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize); | 2077 | const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize); |
| 2071 | | 2078 | |
| 2072 | if (!context.isValidMemory(new_sp) or !context.isValidMemory(min_reg_addr)) return error.InvalidUnwindInfo; | 2079 | if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo; |
| 2073 | | 2080 | |
| 2074 | var reg_addr = fp - @sizeOf(usize); | 2081 | var reg_addr = fp - @sizeOf(usize); |
| 2075 | inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| { | 2082 | inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| { |
| ... | @@ -2114,7 +2121,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra | ... | @@ -2114,7 +2121,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2114 | return new_ip; | 2121 | return new_ip; |
| 2115 | } | 2122 | } |
| 2116 | | 2123 | |
| 2117 | fn unwindFrameMachODwarf(context: *UnwindContext, eh_frame: []const u8, fde_offset: usize) !usize { | 2124 | fn unwindFrameMachODwarf(context: *UnwindContext, ma: *debug.StackIterator.MemoryAccessor, eh_frame: []const u8, fde_offset: usize) !usize { |
| 2118 | var di = DwarfInfo{ | 2125 | var di = DwarfInfo{ |
| 2119 | .endian = native_endian, | 2126 | .endian = native_endian, |
| 2120 | .is_macho = true, | 2127 | .is_macho = true, |
| ... | @@ -2126,7 +2133,7 @@ fn unwindFrameMachODwarf(context: *UnwindContext, eh_frame: []const u8, fde_offs | ... | @@ -2126,7 +2133,7 @@ fn unwindFrameMachODwarf(context: *UnwindContext, eh_frame: []const u8, fde_offs |
| 2126 | .owned = false, | 2133 | .owned = false, |
| 2127 | }; | 2134 | }; |
| 2128 | | 2135 | |
| 2129 | return di.unwindFrame(context, fde_offset); | 2136 | return di.unwindFrame(context, ma, fde_offset); |
| 2130 | } | 2137 | } |
| 2131 | | 2138 | |
| 2132 | pub const UnwindContext = struct { | 2139 | pub const UnwindContext = struct { |
| ... | @@ -2135,12 +2142,21 @@ pub const UnwindContext = struct { | ... | @@ -2135,12 +2142,21 @@ pub const UnwindContext = struct { |
| 2135 | pc: usize, | 2142 | pc: usize, |
| 2136 | thread_context: *debug.ThreadContext, | 2143 | thread_context: *debug.ThreadContext, |
| 2137 | reg_context: abi.RegisterContext, | 2144 | reg_context: abi.RegisterContext, |
| 2138 | isValidMemory: *const fn (address: usize) bool, | | |
| 2139 | vm: call_frame.VirtualMachine, | 2145 | vm: call_frame.VirtualMachine, |
| 2140 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }), | 2146 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }), |
| 2141 | | 2147 | |
| 2142 | pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext { | 2148 | pub fn init( |
| 2143 | const pc = abi.stripInstructionPtrAuthCode((try abi.regValueNative(usize, thread_context, abi.ipRegNum(), null)).*); | 2149 | allocator: mem.Allocator, |
| | 2150 | thread_context: *const debug.ThreadContext, |
| | 2151 | ) !UnwindContext { |
| | 2152 | const pc = abi.stripInstructionPtrAuthCode( |
| | 2153 | (try abi.regValueNative( |
| | 2154 | usize, |
| | 2155 | thread_context, |
| | 2156 | abi.ipRegNum(), |
| | 2157 | null, |
| | 2158 | )).*, |
| | 2159 | ); |
| 2144 | | 2160 | |
| 2145 | const context_copy = try allocator.create(debug.ThreadContext); | 2161 | const context_copy = try allocator.create(debug.ThreadContext); |
| 2146 | debug.copyContext(thread_context, context_copy); | 2162 | debug.copyContext(thread_context, context_copy); |
| ... | @@ -2151,7 +2167,6 @@ pub const UnwindContext = struct { | ... | @@ -2151,7 +2167,6 @@ pub const UnwindContext = struct { |
| 2151 | .pc = pc, | 2167 | .pc = pc, |
| 2152 | .thread_context = context_copy, | 2168 | .thread_context = context_copy, |
| 2153 | .reg_context = undefined, | 2169 | .reg_context = undefined, |
| 2154 | .isValidMemory = isValidMemory, | | |
| 2155 | .vm = .{}, | 2170 | .vm = .{}, |
| 2156 | .stack_machine = .{}, | 2171 | .stack_machine = .{}, |
| 2157 | }; | 2172 | }; |
| ... | @@ -2297,25 +2312,26 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -2297,25 +2312,26 @@ pub const ExceptionFrameHeader = struct { |
| 2297 | | 2312 | |
| 2298 | fn isValidPtr( | 2313 | fn isValidPtr( |
| 2299 | self: ExceptionFrameHeader, | 2314 | self: ExceptionFrameHeader, |
| | 2315 | comptime T: type, |
| 2300 | ptr: usize, | 2316 | ptr: usize, |
| 2301 | isValidMemory: *const fn (address: usize) bool, | 2317 | ma: *debug.StackIterator.MemoryAccessor, |
| 2302 | eh_frame_len: ?usize, | 2318 | eh_frame_len: ?usize, |
| 2303 | ) bool { | 2319 | ) bool { |
| 2304 | if (eh_frame_len) |len| { | 2320 | if (eh_frame_len) |len| { |
| 2305 | return ptr >= self.eh_frame_ptr and ptr < self.eh_frame_ptr + len; | 2321 | return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T); |
| 2306 | } else { | 2322 | } else { |
| 2307 | return isValidMemory(ptr); | 2323 | return ma.load(T, ptr) != null; |
| 2308 | } | 2324 | } |
| 2309 | } | 2325 | } |
| 2310 | | 2326 | |
| 2311 | /// Find an entry by binary searching the eh_frame_hdr section. | 2327 | /// Find an entry by binary searching the eh_frame_hdr section. |
| 2312 | /// | 2328 | /// |
| 2313 | /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller, | 2329 | /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller, |
| 2314 | /// `isValidMemory` will be called before accessing any memory referenced by | 2330 | /// MemoryAccessor will be used to verify readability of the header entries. |
| 2315 | /// the header entries. If `eh_frame_len` is provided, then these checks can be skipped. | 2331 | /// If `eh_frame_len` is provided, then these checks can be skipped. |
| 2316 | pub fn findEntry( | 2332 | pub fn findEntry( |
| 2317 | self: ExceptionFrameHeader, | 2333 | self: ExceptionFrameHeader, |
| 2318 | isValidMemory: *const fn (address: usize) bool, | 2334 | ma: *debug.StackIterator.MemoryAccessor, |
| 2319 | eh_frame_len: ?usize, | 2335 | eh_frame_len: ?usize, |
| 2320 | eh_frame_hdr_ptr: usize, | 2336 | eh_frame_hdr_ptr: usize, |
| 2321 | pc: usize, | 2337 | pc: usize, |
| ... | @@ -2364,14 +2380,9 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -2364,14 +2380,9 @@ pub const ExceptionFrameHeader = struct { |
| 2364 | .data_rel_base = eh_frame_hdr_ptr, | 2380 | .data_rel_base = eh_frame_hdr_ptr, |
| 2365 | }) orelse return badDwarf()) orelse return badDwarf(); | 2381 | }) orelse return badDwarf()) orelse return badDwarf(); |
| 2366 | | 2382 | |
| 2367 | // Verify the length fields of the FDE header are readable | 2383 | if (fde_ptr < self.eh_frame_ptr) return badDwarf(); |
| 2368 | if (!self.isValidPtr(fde_ptr, isValidMemory, eh_frame_len) or fde_ptr < self.eh_frame_ptr) return badDwarf(); | | |
| 2369 | | | |
| 2370 | var fde_entry_header_len: usize = 4; | | |
| 2371 | if (!self.isValidPtr(fde_ptr + 3, isValidMemory, eh_frame_len)) return badDwarf(); | | |
| 2372 | if (self.isValidPtr(fde_ptr + 11, isValidMemory, eh_frame_len)) fde_entry_header_len = 12; | | |
| 2373 | | 2384 | |
| 2374 | // Even if eh_frame_len is not specified, all ranges accssed are checked by isValidPtr | 2385 | // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor |
| 2375 | const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse math.maxInt(u32)]; | 2386 | const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse math.maxInt(u32)]; |
| 2376 | | 2387 | |
| 2377 | const fde_offset = fde_ptr - self.eh_frame_ptr; | 2388 | const fde_offset = fde_ptr - self.eh_frame_ptr; |
| ... | @@ -2381,15 +2392,15 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -2381,15 +2392,15 @@ pub const ExceptionFrameHeader = struct { |
| 2381 | .endian = native_endian, | 2392 | .endian = native_endian, |
| 2382 | }; | 2393 | }; |
| 2383 | | 2394 | |
| 2384 | const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); | 2395 | const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame); |
| 2385 | if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); | 2396 | if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf(); |
| 2386 | if (fde_entry_header.type != .fde) return badDwarf(); | 2397 | if (fde_entry_header.type != .fde) return badDwarf(); |
| 2387 | | 2398 | |
| 2388 | // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable | 2399 | // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable |
| 2389 | const cie_offset = fde_entry_header.type.fde; | 2400 | const cie_offset = fde_entry_header.type.fde; |
| 2390 | try eh_frame_fbr.seekTo(cie_offset); | 2401 | try eh_frame_fbr.seekTo(cie_offset); |
| 2391 | const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); | 2402 | const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame); |
| 2392 | if (!self.isValidPtr(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); | 2403 | if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf(); |
| 2393 | if (cie_entry_header.type != .cie) return badDwarf(); | 2404 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 2394 | | 2405 | |
| 2395 | cie.* = try CommonInformationEntry.parse( | 2406 | cie.* = try CommonInformationEntry.parse( |
| ... | @@ -2434,11 +2445,15 @@ pub const EntryHeader = struct { | ... | @@ -2434,11 +2445,15 @@ pub const EntryHeader = struct { |
| 2434 | | 2445 | |
| 2435 | /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure. | 2446 | /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure. |
| 2436 | /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections. | 2447 | /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections. |
| 2437 | pub fn read(fbr: *FixedBufferReader, dwarf_section: DwarfSection) !EntryHeader { | 2448 | pub fn read( |
| | 2449 | fbr: *FixedBufferReader, |
| | 2450 | opt_ma: ?*debug.StackIterator.MemoryAccessor, |
| | 2451 | dwarf_section: DwarfSection, |
| | 2452 | ) !EntryHeader { |
| 2438 | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); | 2453 | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); |
| 2439 | | 2454 | |
| 2440 | const length_offset = fbr.pos; | 2455 | const length_offset = fbr.pos; |
| 2441 | const unit_header = try readUnitHeader(fbr); | 2456 | const unit_header = try readUnitHeader(fbr, opt_ma); |
| 2442 | const unit_length = math.cast(usize, unit_header.unit_length) orelse return badDwarf(); | 2457 | const unit_length = math.cast(usize, unit_header.unit_length) orelse return badDwarf(); |
| 2443 | if (unit_length == 0) return .{ | 2458 | if (unit_length == 0) return .{ |
| 2444 | .length_offset = length_offset, | 2459 | .length_offset = length_offset, |
| ... | @@ -2450,7 +2465,10 @@ pub const EntryHeader = struct { | ... | @@ -2450,7 +2465,10 @@ pub const EntryHeader = struct { |
| 2450 | const end_offset = start_offset + unit_length; | 2465 | const end_offset = start_offset + unit_length; |
| 2451 | defer fbr.pos = end_offset; | 2466 | defer fbr.pos = end_offset; |
| 2452 | | 2467 | |
| 2453 | const id = try fbr.readAddress(unit_header.format); | 2468 | const id = try if (opt_ma) |ma| |
| | 2469 | fbr.readAddressChecked(unit_header.format, ma) |
| | 2470 | else |
| | 2471 | fbr.readAddress(unit_header.format); |
| 2454 | const entry_bytes = fbr.buf[fbr.pos..end_offset]; | 2472 | const entry_bytes = fbr.buf[fbr.pos..end_offset]; |
| 2455 | const cie_id: u64 = switch (dwarf_section) { | 2473 | const cie_id: u64 = switch (dwarf_section) { |
| 2456 | .eh_frame => CommonInformationEntry.eh_id, | 2474 | .eh_frame => CommonInformationEntry.eh_id, |
| ... | @@ -2732,7 +2750,7 @@ pub const FixedBufferReader = struct { | ... | @@ -2732,7 +2750,7 @@ pub const FixedBufferReader = struct { |
| 2732 | pos: usize = 0, | 2750 | pos: usize = 0, |
| 2733 | endian: std.builtin.Endian, | 2751 | endian: std.builtin.Endian, |
| 2734 | | 2752 | |
| 2735 | pub const Error = error{ EndOfBuffer, Overflow }; | 2753 | pub const Error = error{ EndOfBuffer, Overflow, InvalidBuffer }; |
| 2736 | | 2754 | |
| 2737 | fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void { | 2755 | fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void { |
| 2738 | if (pos > fbr.buf.len) return error.EndOfBuffer; | 2756 | if (pos > fbr.buf.len) return error.EndOfBuffer; |
| ... | @@ -2761,6 +2779,17 @@ pub const FixedBufferReader = struct { | ... | @@ -2761,6 +2779,17 @@ pub const FixedBufferReader = struct { |
| 2761 | return mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian); | 2779 | return mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian); |
| 2762 | } | 2780 | } |
| 2763 | | 2781 | |
| | 2782 | fn readIntChecked( |
| | 2783 | fbr: *FixedBufferReader, |
| | 2784 | comptime T: type, |
| | 2785 | ma: *debug.StackIterator.MemoryAccessor, |
| | 2786 | ) Error!T { |
| | 2787 | if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null) |
| | 2788 | return error.InvalidBuffer; |
| | 2789 | |
| | 2790 | return readInt(fbr, T); |
| | 2791 | } |
| | 2792 | |
| 2764 | fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T { | 2793 | fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 2765 | return std.leb.readUleb128(T, fbr); | 2794 | return std.leb.readUleb128(T, fbr); |
| 2766 | } | 2795 | } |
| ... | @@ -2776,6 +2805,17 @@ pub const FixedBufferReader = struct { | ... | @@ -2776,6 +2805,17 @@ pub const FixedBufferReader = struct { |
| 2776 | }; | 2805 | }; |
| 2777 | } | 2806 | } |
| 2778 | | 2807 | |
| | 2808 | fn readAddressChecked( |
| | 2809 | fbr: *FixedBufferReader, |
| | 2810 | format: Format, |
| | 2811 | ma: *debug.StackIterator.MemoryAccessor, |
| | 2812 | ) Error!u64 { |
| | 2813 | return switch (format) { |
| | 2814 | .@"32" => try fbr.readIntChecked(u32, ma), |
| | 2815 | .@"64" => try fbr.readIntChecked(u64, ma), |
| | 2816 | }; |
| | 2817 | } |
| | 2818 | |
| 2779 | fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 { | 2819 | fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 { |
| 2780 | if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer; | 2820 | if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer; |
| 2781 | defer fbr.pos += len; | 2821 | defer fbr.pos += len; |