| ... | @@ -11,6 +11,9 @@ const assert = std.debug.assert; | ... | @@ -11,6 +11,9 @@ const assert = std.debug.assert; |
| 11 | /// Callers should specify all the fields relevant to their context. If a field is required | 11 | /// Callers should specify all the fields relevant to their context. If a field is required |
| 12 | /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned. | 12 | /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned. |
| 13 | pub const ExpressionContext = struct { | 13 | pub const ExpressionContext = struct { |
| | 14 | /// This expression is from a DWARF64 section |
| | 15 | is_64: bool = false, |
| | 16 | |
| 14 | /// If specified, any addresses will pass through this function before being | 17 | /// If specified, any addresses will pass through this function before being |
| 15 | isValidMemory: ?*const fn (address: usize) bool = null, | 18 | isValidMemory: ?*const fn (address: usize) bool = null, |
| 16 | | 19 | |
| ... | @@ -29,6 +32,9 @@ pub const ExpressionContext = struct { | ... | @@ -29,6 +32,9 @@ pub const ExpressionContext = struct { |
| 29 | | 32 | |
| 30 | /// Call frame address, if in a CFI context | 33 | /// Call frame address, if in a CFI context |
| 31 | cfa: ?usize = null, | 34 | cfa: ?usize = null, |
| | 35 | |
| | 36 | /// This expression is a sub-expression from an OP.entry_value instruction |
| | 37 | entry_value_context: bool = false, |
| 32 | }; | 38 | }; |
| 33 | | 39 | |
| 34 | pub const ExpressionOptions = struct { | 40 | pub const ExpressionOptions = struct { |
| ... | @@ -42,6 +48,28 @@ pub const ExpressionOptions = struct { | ... | @@ -42,6 +48,28 @@ pub const ExpressionOptions = struct { |
| 42 | call_frame_context: bool = false, | 48 | call_frame_context: bool = false, |
| 43 | }; | 49 | }; |
| 44 | | 50 | |
| | 51 | pub const ExpressionError = error{ |
| | 52 | UnimplementedExpressionCall, |
| | 53 | UnimplementedOpcode, |
| | 54 | UnimplementedUserOpcode, |
| | 55 | UnimplementedTypedComparison, |
| | 56 | UnimplementedTypeConversion, |
| | 57 | |
| | 58 | UnknownExpressionOpcode, |
| | 59 | |
| | 60 | IncompleteExpressionContext, |
| | 61 | |
| | 62 | InvalidCFAOpcode, |
| | 63 | InvalidExpression, |
| | 64 | InvalidFrameBase, |
| | 65 | InvalidIntegralTypeSize, |
| | 66 | InvalidRegister, |
| | 67 | InvalidSubExpression, |
| | 68 | InvalidTypeLength, |
| | 69 | |
| | 70 | TruncatedIntegralType, |
| | 71 | } || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero }; |
| | 72 | |
| 45 | /// A stack machine that can decode and run DWARF expressions. | 73 | /// A stack machine that can decode and run DWARF expressions. |
| 46 | /// Expressions can be decoded for non-native address size and endianness, | 74 | /// Expressions can be decoded for non-native address size and endianness, |
| 47 | /// but can only be executed if the current target matches the configuration. | 75 | /// but can only be executed if the current target matches the configuration. |
| ... | @@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 156 | } | 184 | } |
| 157 | } | 185 | } |
| 158 | | 186 | |
| 159 | pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand { | 187 | pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8, context: ExpressionContext) !?Operand { |
| 160 | const reader = stream.reader(); | 188 | const reader = stream.reader(); |
| 161 | return switch (opcode) { | 189 | return switch (opcode) { |
| 162 | OP.addr, | 190 | OP.addr => generic(try reader.readInt(addr_type, options.endian)), |
| 163 | OP.call_ref, | 191 | OP.call_ref => if (context.is_64) |
| 164 | => generic(try reader.readInt(addr_type, options.endian)), | 192 | generic(try reader.readInt(u64, options.endian)) |
| | 193 | else |
| | 194 | generic(try reader.readInt(u32, options.endian)), |
| 165 | OP.const1u, | 195 | OP.const1u, |
| 166 | OP.pick, | 196 | OP.pick, |
| 167 | => generic(try reader.readByte()), | 197 | => generic(try reader.readByte()), |
| ... | @@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 267 | allocator: std.mem.Allocator, | 297 | allocator: std.mem.Allocator, |
| 268 | context: ExpressionContext, | 298 | context: ExpressionContext, |
| 269 | initial_value: ?usize, | 299 | initial_value: ?usize, |
| 270 | ) !?Value { | 300 | ) ExpressionError!?Value { |
| 271 | if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i }); | 301 | if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i }); |
| 272 | var stream = std.io.fixedBufferStream(expression); | 302 | var stream = std.io.fixedBufferStream(expression); |
| 273 | while (try self.step(&stream, allocator, context)) {} | 303 | while (try self.step(&stream, allocator, context)) {} |
| ... | @@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 281 | stream: *std.io.FixedBufferStream([]const u8), | 311 | stream: *std.io.FixedBufferStream([]const u8), |
| 282 | allocator: std.mem.Allocator, | 312 | allocator: std.mem.Allocator, |
| 283 | context: ExpressionContext, | 313 | context: ExpressionContext, |
| 284 | ) !bool { | 314 | ) ExpressionError!bool { |
| 285 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) | 315 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) |
| 286 | @compileError("Execution of non-native address sizes / endianness is not supported"); | 316 | @compileError("Execution of non-native address sizes / endianness is not supported"); |
| 287 | | 317 | |
| 288 | const opcode = try stream.reader().readByte(); | 318 | const opcode = try stream.reader().readByte(); |
| 289 | if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; | 319 | if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; |
| 290 | switch (opcode) { | 320 | switch (opcode) { |
| 291 | | 321 | |
| 292 | // 2.5.1.1: Literal Encodings | 322 | // 2.5.1.1: Literal Encodings |
| ... | @@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 302 | OP.const8s, | 332 | OP.const8s, |
| 303 | OP.constu, | 333 | OP.constu, |
| 304 | OP.consts, | 334 | OP.consts, |
| 305 | => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }), | 335 | => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode, context)).?.generic }), |
| 306 | | 336 | |
| 307 | OP.const_type => { | 337 | OP.const_type => { |
| 308 | const const_type = (try readOperand(stream, opcode)).?.const_type; | 338 | const const_type = (try readOperand(stream, opcode, context)).?.const_type; |
| 309 | try self.stack.append(allocator, .{ .const_type = .{ | 339 | try self.stack.append(allocator, .{ .const_type = .{ |
| 310 | .type_offset = const_type.type_offset, | 340 | .type_offset = const_type.type_offset, |
| 311 | .value_bytes = const_type.value_bytes, | 341 | .value_bytes = const_type.value_bytes, |
| ... | @@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 315 | OP.addrx, | 345 | OP.addrx, |
| 316 | OP.constx, | 346 | OP.constx, |
| 317 | => { | 347 | => { |
| 318 | if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit; | 348 | if (context.compile_unit == null) return error.IncompleteExpressionContext; |
| 319 | if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr; | 349 | if (context.debug_addr == null) return error.IncompleteExpressionContext; |
| 320 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; | 350 | const debug_addr_index = (try readOperand(stream, opcode, context)).?.generic; |
| 321 | const offset = context.compile_unit.?.addr_base + debug_addr_index; | 351 | const offset = context.compile_unit.?.addr_base + debug_addr_index; |
| 322 | if (offset >= context.debug_addr.?.len) return error.InvalidExpression; | 352 | if (offset >= context.debug_addr.?.len) return error.InvalidExpression; |
| 323 | const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]); | 353 | const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]); |
| ... | @@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 326 | | 356 | |
| 327 | // 2.5.1.2: Register Values | 357 | // 2.5.1.2: Register Values |
| 328 | OP.fbreg => { | 358 | OP.fbreg => { |
| 329 | if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit; | 359 | if (context.compile_unit == null) return error.IncompleteExpressionContext; |
| 330 | if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase; | 360 | if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext; |
| 331 | | 361 | |
| 332 | const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic); | 362 | const offset: i64 = @intCast((try readOperand(stream, opcode, context)).?.generic); |
| 333 | _ = offset; | 363 | _ = offset; |
| 334 | | 364 | |
| 335 | switch (context.compile_unit.?.frame_base.?.*) { | 365 | switch (context.compile_unit.?.frame_base.?.*) { |
| ... | @@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 353 | => { | 383 | => { |
| 354 | if (context.thread_context == null) return error.IncompleteExpressionContext; | 384 | if (context.thread_context == null) return error.IncompleteExpressionContext; |
| 355 | | 385 | |
| 356 | const base_register = (try readOperand(stream, opcode)).?.base_register; | 386 | const base_register = (try readOperand(stream, opcode, context)).?.base_register; |
| 357 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes( | 387 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes( |
| 358 | context.thread_context.?, | 388 | context.thread_context.?, |
| 359 | base_register.base_register, | 389 | base_register.base_register, |
| ... | @@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 363 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); | 393 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); |
| 364 | }, | 394 | }, |
| 365 | OP.regval_type => { | 395 | OP.regval_type => { |
| 366 | const register_type = (try readOperand(stream, opcode)).?.register_type; | 396 | const register_type = (try readOperand(stream, opcode, context)).?.register_type; |
| 367 | const value = mem.readIntSliceNative(usize, try abi.regBytes( | 397 | const value = mem.readIntSliceNative(usize, try abi.regBytes( |
| 368 | context.thread_context.?, | 398 | context.thread_context.?, |
| 369 | register_type.register, | 399 | register_type.register, |
| ... | @@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 387 | _ = self.stack.pop(); | 417 | _ = self.stack.pop(); |
| 388 | }, | 418 | }, |
| 389 | OP.pick, OP.over => { | 419 | OP.pick, OP.over => { |
| 390 | const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode)).?.generic; | 420 | const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode, context)).?.generic; |
| 391 | if (stack_index >= self.stack.items.len) return error.InvalidExpression; | 421 | if (stack_index >= self.stack.items.len) return error.InvalidExpression; |
| 392 | try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]); | 422 | try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]); |
| 393 | }, | 423 | }, |
| ... | @@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 429 | | 459 | |
| 430 | if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression; | 460 | if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression; |
| 431 | | 461 | |
| 432 | const operand = try readOperand(stream, opcode); | 462 | const operand = try readOperand(stream, opcode, context); |
| 433 | const size = switch (opcode) { | 463 | const size = switch (opcode) { |
| 434 | OP.deref, | 464 | OP.deref, |
| 435 | OP.xderef, | 465 | OP.xderef, |
| ... | @@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 469 | } | 499 | } |
| 470 | }, | 500 | }, |
| 471 | OP.push_object_address => { | 501 | OP.push_object_address => { |
| 472 | if (context.object_address == null) return error.IncompleteExpressionContext; | 502 | // In sub-expressions, `push_object_address` is not meaningful (as per the |
| 473 | try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) }); | 503 | // spec), so treat it like a nop |
| | 504 | if (!context.entry_value_context) { |
| | 505 | if (context.object_address == null) return error.IncompleteExpressionContext; |
| | 506 | try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) }); |
| | 507 | } |
| 474 | }, | 508 | }, |
| 475 | OP.form_tls_address => { | 509 | OP.form_tls_address => { |
| 476 | return error.UnimplementedExpressionOpcode; | 510 | return error.UnimplementedOpcode; |
| 477 | }, | 511 | }, |
| 478 | OP.call_frame_cfa => { | 512 | OP.call_frame_cfa => { |
| 479 | if (context.cfa) |cfa| { | 513 | if (context.cfa) |cfa| { |
| ... | @@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 559 | }, | 593 | }, |
| 560 | OP.plus_uconst => { | 594 | OP.plus_uconst => { |
| 561 | if (self.stack.items.len == 0) return error.InvalidExpression; | 595 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 562 | const constant = (try readOperand(stream, opcode)).?.generic; | 596 | const constant = (try readOperand(stream, opcode, context)).?.generic; |
| 563 | self.stack.items[self.stack.items.len - 1] = .{ | 597 | self.stack.items[self.stack.items.len - 1] = .{ |
| 564 | .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant), | 598 | .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant), |
| 565 | }; | 599 | }; |
| ... | @@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 628 | } | 662 | } |
| 629 | }, | 663 | }, |
| 630 | OP.skip, OP.bra => { | 664 | OP.skip, OP.bra => { |
| 631 | const branch_offset = (try readOperand(stream, opcode)).?.branch_offset; | 665 | const branch_offset = (try readOperand(stream, opcode, context)).?.branch_offset; |
| 632 | const condition = if (opcode == OP.bra) blk: { | 666 | const condition = if (opcode == OP.bra) blk: { |
| 633 | if (self.stack.items.len == 0) return error.InvalidExpression; | 667 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 634 | break :blk try self.stack.pop().asIntegral() != 0; | 668 | break :blk try self.stack.pop().asIntegral() != 0; |
| ... | @@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 648 | OP.call4, | 682 | OP.call4, |
| 649 | OP.call_ref, | 683 | OP.call_ref, |
| 650 | => { | 684 | => { |
| 651 | const debug_info_offset = (try readOperand(stream, opcode)).?.generic; | 685 | const debug_info_offset = (try readOperand(stream, opcode, context)).?.generic; |
| 652 | _ = debug_info_offset; | 686 | _ = debug_info_offset; |
| 653 | | 687 | |
| 654 | // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it | 688 | // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it |
| ... | @@ -661,7 +695,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -661,7 +695,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 661 | // 2.5.1.6: Type Conversions | 695 | // 2.5.1.6: Type Conversions |
| 662 | OP.convert => { | 696 | OP.convert => { |
| 663 | if (self.stack.items.len == 0) return error.InvalidExpression; | 697 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 664 | const type_offset = (try readOperand(stream, opcode)).?.generic; | 698 | const type_offset = (try readOperand(stream, opcode, context)).?.generic; |
| 665 | | 699 | |
| 666 | // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size | 700 | // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size |
| 667 | const value = self.stack.items[self.stack.items.len - 1]; | 701 | const value = self.stack.items[self.stack.items.len - 1]; |
| ... | @@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 675 | }, | 709 | }, |
| 676 | OP.reinterpret => { | 710 | OP.reinterpret => { |
| 677 | if (self.stack.items.len == 0) return error.InvalidExpression; | 711 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 678 | const type_offset = (try readOperand(stream, opcode)).?.generic; | 712 | const type_offset = (try readOperand(stream, opcode, context)).?.generic; |
| 679 | | 713 | |
| 680 | // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size | 714 | // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size |
| 681 | const value = self.stack.items[self.stack.items.len - 1]; | 715 | const value = self.stack.items[self.stack.items.len - 1]; |
| ... | @@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 710 | // 2.5.1.7: Special Operations | 744 | // 2.5.1.7: Special Operations |
| 711 | OP.nop => {}, | 745 | OP.nop => {}, |
| 712 | OP.entry_value => { | 746 | OP.entry_value => { |
| 713 | const block = (try readOperand(stream, opcode)).?.block; | 747 | const block = (try readOperand(stream, opcode, context)).?.block; |
| 714 | _ = block; | 748 | if (block.len == 0) return error.InvalidSubExpression; |
| | 749 | |
| | 750 | // TODO: The spec states that this sub-expression needs to observe the state (ie. registers) |
| | 751 | // as it was upon entering the current subprogram. If this isn't being called at the |
| | 752 | // end of a frame unwind operation, an additional ThreadContext with this state will be needed. |
| 715 | | 753 | |
| 716 | // TODO: If block is an expression, run it on a new stack. Push the resulting value onto this stack. | 754 | if (isOpcodeRegisterLocation(block[0])) { |
| 717 | // TODO: If block is a register location, push the value that location had before running this program onto this stack. | 755 | if (context.thread_context == null) return error.IncompleteExpressionContext; |
| 718 | // This implies capturing all register values before executing this block, in case this program modifies them. | | |
| 719 | // TODO: If the block contains, OP.push_object_address, treat it as OP.nop | | |
| 720 | | 756 | |
| 721 | return error.UnimplementedSubExpression; | 757 | var block_stream = std.io.fixedBufferStream(block); |
| | 758 | const register = (try readOperand(&block_stream, block[0], context)).?.register; |
| | 759 | const value = mem.readIntSliceNative(usize, try abi.regBytes(context.thread_context.?, register, context.reg_context)); |
| | 760 | try self.stack.append(allocator, .{ .generic = value }); |
| | 761 | } else { |
| | 762 | var stack_machine: Self = .{}; |
| | 763 | defer stack_machine.deinit(allocator); |
| | 764 | |
| | 765 | var sub_context = context; |
| | 766 | sub_context.entry_value_context = true; |
| | 767 | const result = try stack_machine.run(block, allocator, sub_context, null); |
| | 768 | try self.stack.append(allocator, result orelse return error.InvalidSubExpression); |
| | 769 | } |
| 722 | }, | 770 | }, |
| 723 | | 771 | |
| 724 | // These have already been handled by readOperand | 772 | // These have already been handled by readOperand |
| ... | @@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 745 | return struct { | 793 | return struct { |
| 746 | /// Zero-operand instructions | 794 | /// Zero-operand instructions |
| 747 | pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void { | 795 | pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void { |
| 748 | if (options.call_frame_context and !comptime opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; | 796 | if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; |
| 749 | switch (opcode) { | 797 | switch (opcode) { |
| 750 | OP.dup, | 798 | OP.dup, |
| 751 | OP.drop, | 799 | OP.drop, |
| ... | @@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 778 | OP.gt, | 826 | OP.gt, |
| 779 | OP.ne, | 827 | OP.ne, |
| 780 | OP.nop, | 828 | OP.nop, |
| | 829 | OP.stack_value, |
| 781 | => try writer.writeByte(opcode), | 830 | => try writer.writeByte(opcode), |
| 782 | else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"), | 831 | else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"), |
| 783 | } | 832 | } |
| ... | @@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 828 | try leb.writeULEB128(writer, debug_addr_offset); | 877 | try leb.writeULEB128(writer, debug_addr_offset); |
| 829 | } | 878 | } |
| 830 | | 879 | |
| 831 | pub fn writeConstType(writer: anytype, die_offset: anytype, size: u8, value_bytes: []const u8) !void { | 880 | pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void { |
| 832 | if (options.call_frame_context) return error.InvalidCFAOpcode; | 881 | if (options.call_frame_context) return error.InvalidCFAOpcode; |
| 833 | if (size != value_bytes.len) return error.InvalidValueSize; | 882 | if (value_bytes.len > 0xff) return error.InvalidTypeLength; |
| 834 | try writer.writeByte(OP.const_type); | 883 | try writer.writeByte(OP.const_type); |
| 835 | try leb.writeULEB128(writer, die_offset); | 884 | try leb.writeULEB128(writer, die_offset); |
| 836 | try writer.writeByte(size); | 885 | try writer.writeByte(@intCast(value_bytes.len)); |
| 837 | try writer.writeAll(value_bytes); | 886 | try writer.writeAll(value_bytes); |
| 838 | } | 887 | } |
| 839 | | 888 | |
| ... | @@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 932 | try writer.writeInt(T, offset, options.endian); | 981 | try writer.writeInt(T, offset, options.endian); |
| 933 | } | 982 | } |
| 934 | | 983 | |
| 935 | pub fn writeCallRef(writer: anytype, debug_info_offset: addr_type) !void { | 984 | pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void { |
| 936 | if (options.call_frame_context) return error.InvalidCFAOpcode; | 985 | if (options.call_frame_context) return error.InvalidCFAOpcode; |
| 937 | try writer.writeByte(OP.call_ref); | 986 | try writer.writeByte(OP.call_ref); |
| 938 | try writer.writeInt(addr_type, debug_info_offset, options.endian); | 987 | try writer.writeInt(if (is_64) u64 else u32, value, options.endian); |
| 939 | } | 988 | } |
| 940 | | 989 | |
| 941 | pub fn writeConvert(writer: anytype, die_offset: anytype) !void { | 990 | pub fn writeConvert(writer: anytype, die_offset: anytype) !void { |
| ... | @@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 959 | } | 1008 | } |
| 960 | | 1009 | |
| 961 | // 2.6: Location Descriptions | 1010 | // 2.6: Location Descriptions |
| 962 | // TODO | 1011 | pub fn writeReg(writer: anytype, register: u8) !void { |
| | 1012 | try writer.writeByte(OP.reg0 + register); |
| | 1013 | } |
| | 1014 | |
| | 1015 | pub fn writeRegx(writer: anytype, register: anytype) !void { |
| | 1016 | try writer.writeByte(OP.regx); |
| | 1017 | try leb.writeULEB128(writer, register); |
| | 1018 | } |
| | 1019 | |
| | 1020 | pub fn writeImplicitValue(writer: anytype, value_bytes: []const u8) !void { |
| | 1021 | try writer.writeByte(OP.implicit_value); |
| | 1022 | try leb.writeULEB128(writer, value_bytes.len); |
| | 1023 | try writer.writeAll(value_bytes); |
| | 1024 | } |
| | 1025 | |
| | 1026 | // pub fn writeImplicitPointer(writer: anytype, ) void { |
| | 1027 | // } |
| 963 | | 1028 | |
| 964 | }; | 1029 | }; |
| 965 | } | 1030 | } |
| 966 | | 1031 | |
| 967 | // Certain opcodes are not allowed in a CFA context, see 6.4.2 | 1032 | // Certain opcodes are not allowed in a CFA context, see 6.4.2 |
| 968 | fn opcodeValidInCFA(opcode: u8) bool { | 1033 | fn isOpcodeValidInCFA(opcode: u8) bool { |
| 969 | return switch (opcode) { | 1034 | return switch (opcode) { |
| 970 | OP.addrx, | 1035 | OP.addrx, |
| 971 | OP.call2, | 1036 | OP.call2, |
| ... | @@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool { | ... | @@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool { |
| 984 | }; | 1049 | }; |
| 985 | } | 1050 | } |
| 986 | | 1051 | |
| | 1052 | fn isOpcodeRegisterLocation(opcode: u8) bool { |
| | 1053 | return switch (opcode) { |
| | 1054 | OP.reg0...OP.reg31, OP.regx => true, |
| | 1055 | else => false, |
| | 1056 | }; |
| | 1057 | } |
| | 1058 | |
| 987 | const testing = std.testing; | 1059 | const testing = std.testing; |
| 988 | test "DWARF expressions" { | 1060 | test "DWARF expressions" { |
| 989 | const allocator = std.testing.allocator; | 1061 | const allocator = std.testing.allocator; |
| ... | @@ -1067,7 +1139,7 @@ test "DWARF expressions" { | ... | @@ -1067,7 +1139,7 @@ test "DWARF expressions" { |
| 1067 | | 1139 | |
| 1068 | const die_offset: usize = @truncate(0xaabbccdd); | 1140 | const die_offset: usize = @truncate(0xaabbccdd); |
| 1069 | const type_bytes: []const u8 = &.{ 1, 2, 3, 4 }; | 1141 | const type_bytes: []const u8 = &.{ 1, 2, 3, 4 }; |
| 1070 | try b.writeConstType(writer, die_offset, type_bytes.len, type_bytes); | 1142 | try b.writeConstType(writer, die_offset, type_bytes); |
| 1071 | | 1143 | |
| 1072 | _ = try stack_machine.run(program.items, allocator, context, 0); | 1144 | _ = try stack_machine.run(program.items, allocator, context, 0); |
| 1073 | | 1145 | |
| ... | @@ -1137,7 +1209,13 @@ test "DWARF expressions" { | ... | @@ -1137,7 +1209,13 @@ test "DWARF expressions" { |
| 1137 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic); | 1209 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic); |
| 1138 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic); | 1210 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic); |
| 1139 | } else |err| { | 1211 | } else |err| { |
| 1140 | if (err != error.UnimplementedArch and err != error.UnimplementedOs) return err; | 1212 | switch (err) { |
| | 1213 | error.UnimplementedArch, |
| | 1214 | error.UnimplementedOs, |
| | 1215 | error.ThreadContextNotSupported, |
| | 1216 | => {}, |
| | 1217 | else => return err, |
| | 1218 | } |
| 1141 | } | 1219 | } |
| 1142 | } | 1220 | } |
| 1143 | | 1221 | |
| ... | @@ -1396,7 +1474,6 @@ test "DWARF expressions" { | ... | @@ -1396,7 +1474,6 @@ test "DWARF expressions" { |
| 1396 | try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic); | 1474 | try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic); |
| 1397 | } | 1475 | } |
| 1398 | | 1476 | |
| 1399 | | | |
| 1400 | // Control Flow Operations | 1477 | // Control Flow Operations |
| 1401 | { | 1478 | { |
| 1402 | var context = ExpressionContext{}; | 1479 | var context = ExpressionContext{}; |
| ... | @@ -1436,7 +1513,6 @@ test "DWARF expressions" { | ... | @@ -1436,7 +1513,6 @@ test "DWARF expressions" { |
| 1436 | _ = try stack_machine.run(program.items, allocator, context, null); | 1513 | _ = try stack_machine.run(program.items, allocator, context, null); |
| 1437 | try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic); | 1514 | try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic); |
| 1438 | | 1515 | |
| 1439 | | | |
| 1440 | stack_machine.reset(); | 1516 | stack_machine.reset(); |
| 1441 | program.clearRetainingCapacity(); | 1517 | program.clearRetainingCapacity(); |
| 1442 | try b.writeLiteral(writer, 2); | 1518 | try b.writeLiteral(writer, 2); |
| ... | @@ -1470,7 +1546,7 @@ test "DWARF expressions" { | ... | @@ -1470,7 +1546,7 @@ test "DWARF expressions" { |
| 1470 | // Convert to generic type | 1546 | // Convert to generic type |
| 1471 | stack_machine.reset(); | 1547 | stack_machine.reset(); |
| 1472 | program.clearRetainingCapacity(); | 1548 | program.clearRetainingCapacity(); |
| 1473 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | 1549 | try b.writeConstType(writer, @as(usize, 0), &value_bytes); |
| 1474 | try b.writeConvert(writer, @as(usize, 0)); | 1550 | try b.writeConvert(writer, @as(usize, 0)); |
| 1475 | _ = try stack_machine.run(program.items, allocator, context, null); | 1551 | _ = try stack_machine.run(program.items, allocator, context, null); |
| 1476 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); | 1552 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); |
| ... | @@ -1478,7 +1554,7 @@ test "DWARF expressions" { | ... | @@ -1478,7 +1554,7 @@ test "DWARF expressions" { |
| 1478 | // Reinterpret to generic type | 1554 | // Reinterpret to generic type |
| 1479 | stack_machine.reset(); | 1555 | stack_machine.reset(); |
| 1480 | program.clearRetainingCapacity(); | 1556 | program.clearRetainingCapacity(); |
| 1481 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | 1557 | try b.writeConstType(writer, @as(usize, 0), &value_bytes); |
| 1482 | try b.writeReinterpret(writer, @as(usize, 0)); | 1558 | try b.writeReinterpret(writer, @as(usize, 0)); |
| 1483 | _ = try stack_machine.run(program.items, allocator, context, null); | 1559 | _ = try stack_machine.run(program.items, allocator, context, null); |
| 1484 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); | 1560 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); |
| ... | @@ -1488,7 +1564,7 @@ test "DWARF expressions" { | ... | @@ -1488,7 +1564,7 @@ test "DWARF expressions" { |
| 1488 | | 1564 | |
| 1489 | stack_machine.reset(); | 1565 | stack_machine.reset(); |
| 1490 | program.clearRetainingCapacity(); | 1566 | program.clearRetainingCapacity(); |
| 1491 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | 1567 | try b.writeConstType(writer, @as(usize, 0), &value_bytes); |
| 1492 | try b.writeReinterpret(writer, die_offset); | 1568 | try b.writeReinterpret(writer, die_offset); |
| 1493 | _ = try stack_machine.run(program.items, allocator, context, null); | 1569 | _ = try stack_machine.run(program.items, allocator, context, null); |
| 1494 | const const_type = stack_machine.stack.popOrNull().?.const_type; | 1570 | const const_type = stack_machine.stack.popOrNull().?.const_type; |
| ... | @@ -1506,18 +1582,59 @@ test "DWARF expressions" { | ... | @@ -1506,18 +1582,59 @@ test "DWARF expressions" { |
| 1506 | // Special operations | 1582 | // Special operations |
| 1507 | { | 1583 | { |
| 1508 | var context = ExpressionContext{}; | 1584 | var context = ExpressionContext{}; |
| | 1585 | |
| 1509 | stack_machine.reset(); | 1586 | stack_machine.reset(); |
| 1510 | program.clearRetainingCapacity(); | 1587 | program.clearRetainingCapacity(); |
| 1511 | | | |
| 1512 | try b.writeOpcode(writer, OP.nop); | 1588 | try b.writeOpcode(writer, OP.nop); |
| 1513 | _ = try stack_machine.run(program.items, allocator, context, null); | 1589 | _ = try stack_machine.run(program.items, allocator, context, null); |
| 1514 | try testing.expect(stack_machine.stack.popOrNull() == null); | 1590 | try testing.expect(stack_machine.stack.popOrNull() == null); |
| 1515 | | 1591 | |
| | 1592 | // Sub-expression |
| | 1593 | { |
| | 1594 | var sub_program = std.ArrayList(u8).init(allocator); |
| | 1595 | defer sub_program.deinit(); |
| | 1596 | const sub_writer = sub_program.writer(); |
| | 1597 | try b.writeLiteral(sub_writer, 3); |
| 1516 | | 1598 | |
| | 1599 | stack_machine.reset(); |
| | 1600 | program.clearRetainingCapacity(); |
| | 1601 | try b.writeEntryValue(writer, sub_program.items); |
| | 1602 | _ = try stack_machine.run(program.items, allocator, context, null); |
| | 1603 | try testing.expectEqual(@as(usize, 3), stack_machine.stack.popOrNull().?.generic); |
| | 1604 | } |
| 1517 | | 1605 | |
| | 1606 | // Register location description |
| | 1607 | const reg_context = abi.RegisterContext{ |
| | 1608 | .eh_frame = true, |
| | 1609 | .is_macho = builtin.os.tag == .macos, |
| | 1610 | }; |
| | 1611 | var thread_context: std.debug.ThreadContext = undefined; |
| | 1612 | context = ExpressionContext{ |
| | 1613 | .thread_context = &thread_context, |
| | 1614 | .reg_context = reg_context, |
| | 1615 | }; |
| 1518 | | 1616 | |
| 1519 | } | 1617 | if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { |
| | 1618 | mem.writeIntSliceNative(usize, reg_bytes, 0xee); |
| 1520 | | 1619 | |
| | 1620 | var sub_program = std.ArrayList(u8).init(allocator); |
| | 1621 | defer sub_program.deinit(); |
| | 1622 | const sub_writer = sub_program.writer(); |
| | 1623 | try b.writeReg(sub_writer, 0); |
| 1521 | | 1624 | |
| | 1625 | stack_machine.reset(); |
| | 1626 | program.clearRetainingCapacity(); |
| | 1627 | try b.writeEntryValue(writer, sub_program.items); |
| | 1628 | _ = try stack_machine.run(program.items, allocator, context, null); |
| | 1629 | try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.popOrNull().?.generic); |
| | 1630 | } else |err| { |
| | 1631 | switch (err) { |
| | 1632 | error.UnimplementedArch, |
| | 1633 | error.UnimplementedOs, |
| | 1634 | error.ThreadContextNotSupported, |
| | 1635 | => {}, |
| | 1636 | else => return err, |
| | 1637 | } |
| | 1638 | } |
| | 1639 | } |
| 1522 | } | 1640 | } |
| 1523 | | | |