From 8547c42ba542f77c55ce50253446fd2bf4f3592b Mon Sep 17 00:00:00 2001 From: kcbanner Date: Thu, 6 Jul 2023 20:04:33 -0400 Subject: [PATCH] dwarf: expression fixups for non-64bit arches, check call_frame_context when writing expressions --- lib/std/dwarf.zig | 2 +- lib/std/dwarf/expressions.zig | 71 ++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 3a8ea847bf..2d71885e9b 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1783,7 +1783,7 @@ pub const UnwindContext = struct { reg_ctx: abi.RegisterContext, isValidMemory: *const fn (address: usize) bool, vm: call_frame.VirtualMachine = .{}, - stack_machine: expressions.StackMachine(.{ .call_frame_mode = true }) = .{}, + stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{}, pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); diff --git a/lib/std/dwarf/expressions.zig b/lib/std/dwarf/expressions.zig index cc6b687930..beff1e6754 100644 --- a/lib/std/dwarf/expressions.zig +++ b/lib/std/dwarf/expressions.zig @@ -33,7 +33,7 @@ pub const ExpressionOptions = struct { endian: std.builtin.Endian = .Little, /// Restrict the stack machine to a subset of opcodes used in call frame instructions - call_frame_mode: bool = false, + call_frame_context: bool = false, }; /// A stack machine that can decode and run DWARF expressions. @@ -111,13 +111,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { // TODO: For these two prongs, look up the type and assert it's integral? .regval_type => |regval_type| regval_type.value, .const_type => |const_type| { - return switch (const_type.value_bytes.len) { + const value: u64 = switch (const_type.value_bytes.len) { 1 => mem.readIntSliceNative(u8, const_type.value_bytes), 2 => mem.readIntSliceNative(u16, const_type.value_bytes), 4 => mem.readIntSliceNative(u32, const_type.value_bytes), 8 => mem.readIntSliceNative(u64, const_type.value_bytes), - else => error.InvalidIntegralTypeLength, + else => return error.InvalidIntegralTypeSize, }; + + return std.math.cast(addr_type, value) orelse error.TruncatedIntegralType; }, }; } @@ -278,26 +280,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { @compileError("Execution of non-native address sizees / endianness is not supported"); const opcode = try stream.reader().readByte(); - if (options.call_frame_mode) { - // Certain opcodes are not allowed in a CFA context, see 6.4.2 - switch (opcode) { - OP.addrx, - OP.call2, - OP.call4, - OP.call_ref, - OP.const_type, - OP.constx, - OP.convert, - OP.deref_type, - OP.regval_type, - OP.reinterpret, - OP.push_object_address, - OP.call_frame_cfa, - => return error.InvalidCFAExpression, - else => {}, - } - } - + if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; switch (opcode) { // 2.5.1.1: Literal Encodings @@ -420,7 +403,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { OP.xderef_type, => { if (self.stack.items.len == 0) return error.InvalidExpression; - var addr = try self.stack.pop().asIntegral(); + var addr = try self.stack.items[self.stack.items.len - 1].asIntegral(); const addr_space_identifier: ?usize = switch (opcode) { OP.xderef, OP.xderef_size, @@ -447,24 +430,24 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { else => unreachable, }; - const value: u64 = switch (size) { + const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) { 1 => @as(*const u8, @ptrFromInt(addr)).*, 2 => @as(*const u16, @ptrFromInt(addr)).*, 4 => @as(*const u32, @ptrFromInt(addr)).*, 8 => @as(*const u64, @ptrFromInt(addr)).*, else => return error.InvalidExpression, - }; + })) orelse return error.InvalidExpression; if (opcode == OP.deref_type) { - try self.stack.append(allocator, .{ + self.stack.items[self.stack.items.len - 1] = .{ .regval_type = .{ .type_offset = operand.?.deref_type.type_offset, .type_size = operand.?.deref_type.size, .value = value, }, - }); + }; } else { - try self.stack.append(allocator, .{ .generic = value }); + self.stack.items[self.stack.items.len - 1] = .{ .generic = value }; } }, OP.push_object_address, @@ -738,6 +721,7 @@ pub fn Writer(options: ExpressionOptions) type { return struct { /// Zero-operand instructions pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void { + if (options.call_frame_context and !comptime opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; switch (opcode) { OP.dup, OP.drop, @@ -820,6 +804,7 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeConstType(writer: anytype, die_offset: anytype, size: u8, value_bytes: []const u8) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; if (size != value_bytes.len) return error.InvalidValueSize; try writer.writeByte(OP.const_type); try leb.writeULEB128(writer, die_offset); @@ -833,6 +818,7 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.addrx); try leb.writeULEB128(writer, debug_addr_offset); } @@ -856,6 +842,7 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.bregx); try leb.writeULEB128(writer, register); try leb.writeULEB128(writer, offset); @@ -878,6 +865,7 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeDerefType(writer: anytype, size: u8, die_offset: anytype) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.deref_type); try writer.writeByte(size); try leb.writeULEB128(writer, die_offset); @@ -909,6 +897,7 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeCall(writer: anytype, comptime T: type, offset: T) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; switch (T) { u16 => try writer.writeByte(OP.call2), u32 => try writer.writeByte(OP.call4), @@ -919,16 +908,19 @@ pub fn Writer(options: ExpressionOptions) type { } pub fn writeCallRef(writer: anytype, debug_info_offset: addr_type) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.call_ref); try writer.writeInt(addr_type, debug_info_offset, options.endian); } pub fn writeConvert(writer: anytype, die_offset: anytype) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.convert); try leb.writeULEB128(writer, die_offset); } pub fn writeReinterpret(writer: anytype, die_offset: anytype) !void { + if (options.call_frame_context) return error.InvalidCFAOpcode; try writer.writeByte(OP.reinterpret); try leb.writeULEB128(writer, die_offset); } @@ -942,12 +934,31 @@ pub fn Writer(options: ExpressionOptions) type { } // 2.6: Location Descriptions - // TODO }; } +// Certain opcodes are not allowed in a CFA context, see 6.4.2 +fn opcodeValidInCFA(opcode: u8) bool { + return switch (opcode) { + OP.addrx, + OP.call2, + OP.call4, + OP.call_ref, + OP.const_type, + OP.constx, + OP.convert, + OP.deref_type, + OP.regval_type, + OP.reinterpret, + OP.push_object_address, + OP.call_frame_cfa, + => false, + else => true, + }; +} + test "DWARF expressions" { const allocator = std.testing.allocator;