| ... | @@ -17,6 +17,9 @@ pub const ExpressionContext = struct { | ... | @@ -17,6 +17,9 @@ pub const ExpressionContext = struct { |
| 17 | /// The compilation unit this expression relates to, if any | 17 | /// The compilation unit this expression relates to, if any |
| 18 | compile_unit: ?*const dwarf.CompileUnit = null, | 18 | compile_unit: ?*const dwarf.CompileUnit = null, |
| 19 | | 19 | |
| | 20 | // .debug_addr section |
| | 21 | debug_addr: ?[]const u8 = null, |
| | 22 | |
| 20 | /// Thread context | 23 | /// Thread context |
| 21 | thread_context: ?*std.debug.ThreadContext = null, | 24 | thread_context: ?*std.debug.ThreadContext = null, |
| 22 | reg_context: ?abi.RegisterContext = null, | 25 | reg_context: ?abi.RegisterContext = null, |
| ... | @@ -73,15 +76,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -73,15 +76,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 73 | block: []const u8, | 76 | block: []const u8, |
| 74 | register_type: struct { | 77 | register_type: struct { |
| 75 | register: u8, | 78 | register: u8, |
| 76 | type_offset: u64, | 79 | type_offset: addr_type, |
| 77 | }, | 80 | }, |
| 78 | const_type: struct { | 81 | const_type: struct { |
| 79 | type_offset: u64, | 82 | type_offset: addr_type, |
| 80 | value_bytes: []const u8, | 83 | value_bytes: []const u8, |
| 81 | }, | 84 | }, |
| 82 | deref_type: struct { | 85 | deref_type: struct { |
| 83 | size: u8, | 86 | size: u8, |
| 84 | type_offset: u64, | 87 | type_offset: addr_type, |
| 85 | }, | 88 | }, |
| 86 | }; | 89 | }; |
| 87 | | 90 | |
| ... | @@ -91,7 +94,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -91,7 +94,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 91 | // Typed value with a maximum size of a register | 94 | // Typed value with a maximum size of a register |
| 92 | regval_type: struct { | 95 | regval_type: struct { |
| 93 | // Offset of DW_TAG_base_type DIE | 96 | // Offset of DW_TAG_base_type DIE |
| 94 | type_offset: u64, | 97 | type_offset: addr_type, |
| 95 | type_size: u8, | 98 | type_size: u8, |
| 96 | value: addr_type, | 99 | value: addr_type, |
| 97 | }, | 100 | }, |
| ... | @@ -99,7 +102,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -99,7 +102,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 99 | // Typed value specified directly in the instruction stream | 102 | // Typed value specified directly in the instruction stream |
| 100 | const_type: struct { | 103 | const_type: struct { |
| 101 | // Offset of DW_TAG_base_type DIE | 104 | // Offset of DW_TAG_base_type DIE |
| 102 | type_offset: u64, | 105 | type_offset: addr_type, |
| 103 | // Backed by the instruction stream | 106 | // Backed by the instruction stream |
| 104 | value_bytes: []const u8, | 107 | value_bytes: []const u8, |
| 105 | }, | 108 | }, |
| ... | @@ -202,7 +205,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -202,7 +205,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 202 | }, | 205 | }, |
| 203 | OP.regval_type => blk: { | 206 | OP.regval_type => blk: { |
| 204 | const register = try leb.readULEB128(u8, reader); | 207 | const register = try leb.readULEB128(u8, reader); |
| 205 | const type_offset = try leb.readULEB128(u64, reader); | 208 | const type_offset = try leb.readULEB128(addr_type, reader); |
| 206 | break :blk .{ .register_type = .{ | 209 | break :blk .{ .register_type = .{ |
| 207 | .register = register, | 210 | .register = register, |
| 208 | .type_offset = type_offset, | 211 | .type_offset = type_offset, |
| ... | @@ -232,7 +235,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -232,7 +235,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 232 | }; | 235 | }; |
| 233 | }, | 236 | }, |
| 234 | OP.const_type => blk: { | 237 | OP.const_type => blk: { |
| 235 | const type_offset = try leb.readULEB128(u8, reader); | 238 | const type_offset = try leb.readULEB128(addr_type, reader); |
| 236 | const size = try reader.readByte(); | 239 | const size = try reader.readByte(); |
| 237 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; | 240 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; |
| 238 | const value_bytes = stream.buffer[stream.pos..][0..size]; | 241 | const value_bytes = stream.buffer[stream.pos..][0..size]; |
| ... | @@ -247,7 +250,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -247,7 +250,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 247 | => .{ | 250 | => .{ |
| 248 | .deref_type = .{ | 251 | .deref_type = .{ |
| 249 | .size = try reader.readByte(), | 252 | .size = try reader.readByte(), |
| 250 | .type_offset = try leb.readULEB128(u64, reader), | 253 | .type_offset = try leb.readULEB128(addr_type, reader), |
| 251 | }, | 254 | }, |
| 252 | }, | 255 | }, |
| 253 | OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode, | 256 | OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode, |
| ... | @@ -277,7 +280,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -277,7 +280,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 277 | context: ExpressionContext, | 280 | context: ExpressionContext, |
| 278 | ) !bool { | 281 | ) !bool { |
| 279 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) | 282 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) |
| 280 | @compileError("Execution of non-native address sizees / endianness is not supported"); | 283 | @compileError("Execution of non-native address sizes / endianness is not supported"); |
| 281 | | 284 | |
| 282 | const opcode = try stream.reader().readByte(); | 285 | const opcode = try stream.reader().readByte(); |
| 283 | if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; | 286 | if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; |
| ... | @@ -309,12 +312,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -309,12 +312,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 309 | OP.addrx, | 312 | OP.addrx, |
| 310 | OP.constx, | 313 | OP.constx, |
| 311 | => { | 314 | => { |
| | 315 | if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit; |
| | 316 | if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr; |
| 312 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; | 317 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; |
| 313 | | 318 | const offset = context.compile_unit.?.addr_base + debug_addr_index; |
| 314 | // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic | 319 | if (offset >= context.debug_addr.?.len) return error.InvalidExpression; |
| 315 | | 320 | const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]); |
| 316 | _ = debug_addr_index; | 321 | try self.stack.append(allocator, .{ .generic = value }); |
| 317 | unreachable; | | |
| 318 | }, | 322 | }, |
| 319 | | 323 | |
| 320 | // 2.5.1.2: Register Values | 324 | // 2.5.1.2: Register Values |
| ... | @@ -464,7 +468,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -464,7 +468,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 464 | // 2.5.1.4: Arithmetic and Logical Operations | 468 | // 2.5.1.4: Arithmetic and Logical Operations |
| 465 | OP.abs => { | 469 | OP.abs => { |
| 466 | if (self.stack.items.len == 0) return error.InvalidExpression; | 470 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 467 | const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 471 | const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 468 | self.stack.items[self.stack.items.len - 1] = .{ | 472 | self.stack.items[self.stack.items.len - 1] = .{ |
| 469 | .generic = std.math.absCast(value), | 473 | .generic = std.math.absCast(value), |
| 470 | }; | 474 | }; |
| ... | @@ -478,10 +482,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -478,10 +482,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 478 | }, | 482 | }, |
| 479 | OP.div => { | 483 | OP.div => { |
| 480 | if (self.stack.items.len < 2) return error.InvalidExpression; | 484 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 481 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | 485 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 482 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 486 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 483 | self.stack.items[self.stack.items.len - 1] = .{ | 487 | self.stack.items[self.stack.items.len - 1] = .{ |
| 484 | .generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)), | 488 | .generic = @bitCast(try std.math.divTrunc(isize, b, a)), |
| 485 | }; | 489 | }; |
| 486 | }, | 490 | }, |
| 487 | OP.minus => { | 491 | OP.minus => { |
| ... | @@ -493,16 +497,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -493,16 +497,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 493 | }, | 497 | }, |
| 494 | OP.mod => { | 498 | OP.mod => { |
| 495 | if (self.stack.items.len < 2) return error.InvalidExpression; | 499 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 496 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | 500 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 497 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 501 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 498 | self.stack.items[self.stack.items.len - 1] = .{ | 502 | self.stack.items[self.stack.items.len - 1] = .{ |
| 499 | .generic = @bitCast(@mod(b, a)), | 503 | .generic = @bitCast(@mod(b, a)), |
| 500 | }; | 504 | }; |
| 501 | }, | 505 | }, |
| 502 | OP.mul => { | 506 | OP.mul => { |
| 503 | if (self.stack.items.len < 2) return error.InvalidExpression; | 507 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 504 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | 508 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 505 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 509 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 506 | self.stack.items[self.stack.items.len - 1] = .{ | 510 | self.stack.items[self.stack.items.len - 1] = .{ |
| 507 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), | 511 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), |
| 508 | }; | 512 | }; |
| ... | @@ -512,7 +516,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -512,7 +516,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 512 | self.stack.items[self.stack.items.len - 1] = .{ | 516 | self.stack.items[self.stack.items.len - 1] = .{ |
| 513 | .generic = @bitCast( | 517 | .generic = @bitCast( |
| 514 | try std.math.negate( | 518 | try std.math.negate( |
| 515 | @as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())), | 519 | @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())), |
| 516 | ), | 520 | ), |
| 517 | ), | 521 | ), |
| 518 | }; | 522 | }; |
| ... | @@ -563,9 +567,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -563,9 +567,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 563 | OP.shra => { | 567 | OP.shra => { |
| 564 | if (self.stack.items.len < 2) return error.InvalidExpression; | 568 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 565 | const a = try self.stack.pop().asIntegral(); | 569 | const a = try self.stack.pop().asIntegral(); |
| 566 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 570 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 567 | self.stack.items[self.stack.items.len - 1] = .{ | 571 | self.stack.items[self.stack.items.len - 1] = .{ |
| 568 | .generic = @bitCast(std.math.shr(addr_type_signed, b, a)), | 572 | .generic = @bitCast(std.math.shr(isize, b, a)), |
| 569 | }; | 573 | }; |
| 570 | }, | 574 | }, |
| 571 | OP.xor => { | 575 | OP.xor => { |
| ... | @@ -589,8 +593,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -589,8 +593,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 589 | const b = self.stack.items[self.stack.items.len - 1]; | 593 | const b = self.stack.items[self.stack.items.len - 1]; |
| 590 | | 594 | |
| 591 | if (a == .generic and b == .generic) { | 595 | if (a == .generic and b == .generic) { |
| 592 | const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable); | 596 | const a_int: isize = @bitCast(a.asIntegral() catch unreachable); |
| 593 | const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable); | 597 | const b_int: isize = @bitCast(b.asIntegral() catch unreachable); |
| 594 | const result = @intFromBool(switch (opcode) { | 598 | const result = @intFromBool(switch (opcode) { |
| 595 | OP.le => b_int < a_int, | 599 | OP.le => b_int < a_int, |
| 596 | OP.ge => b_int >= a_int, | 600 | OP.ge => b_int >= a_int, |
| ... | @@ -617,7 +621,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -617,7 +621,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 617 | if (condition) { | 621 | if (condition) { |
| 618 | const new_pos = std.math.cast( | 622 | const new_pos = std.math.cast( |
| 619 | usize, | 623 | usize, |
| 620 | try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset), | 624 | try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset), |
| 621 | ) orelse return error.InvalidExpression; | 625 | ) orelse return error.InvalidExpression; |
| 622 | | 626 | |
| 623 | if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression; | 627 | if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression; |
| ... | @@ -781,6 +785,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { | ... | @@ -781,6 +785,7 @@ pub fn Builder(comptime options: ExpressionOptions) type { |
| 781 | i32 => OP.const4s, | 785 | i32 => OP.const4s, |
| 782 | u64 => OP.const8u, | 786 | u64 => OP.const8u, |
| 783 | i64 => OP.const8s, | 787 | i64 => OP.const8s, |
| | 788 | else => unreachable, |
| 784 | }); | 789 | }); |
| 785 | | 790 | |
| 786 | try writer.writeInt(T, value, options.endian); | 791 | try writer.writeInt(T, value, options.endian); |
| ... | @@ -988,4 +993,85 @@ test "DWARF expressions" { | ... | @@ -988,4 +993,85 @@ test "DWARF expressions" { |
| 988 | try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic); | 993 | try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic); |
| 989 | } | 994 | } |
| 990 | } | 995 | } |
| | 996 | |
| | 997 | // Constants |
| | 998 | { |
| | 999 | program.clearRetainingCapacity(); |
| | 1000 | |
| | 1001 | const expected = [_]comptime_int{ |
| | 1002 | 1, |
| | 1003 | -1, |
| | 1004 | 0x0fff, |
| | 1005 | -0x0fff, |
| | 1006 | 0x0fffffff, |
| | 1007 | -0x0fffffff, |
| | 1008 | 0x0fffffffffffffff, |
| | 1009 | -0x0fffffffffffffff, |
| | 1010 | 0x8000000, |
| | 1011 | -0x8000000, |
| | 1012 | @as(usize, @truncate(0x12345678_12345678)), |
| | 1013 | @as(usize, @truncate(0xffffffff_ffffffff)), |
| | 1014 | @as(usize, @truncate(0xeeeeeeee_eeeeeeee)), |
| | 1015 | }; |
| | 1016 | |
| | 1017 | try b.writeConst(writer, u8, expected[0]); |
| | 1018 | try b.writeConst(writer, i8, expected[1]); |
| | 1019 | try b.writeConst(writer, u16, expected[2]); |
| | 1020 | try b.writeConst(writer, i16, expected[3]); |
| | 1021 | try b.writeConst(writer, u32, expected[4]); |
| | 1022 | try b.writeConst(writer, i32, expected[5]); |
| | 1023 | try b.writeConst(writer, u64, expected[6]); |
| | 1024 | try b.writeConst(writer, i64, expected[7]); |
| | 1025 | try b.writeConst(writer, u28, expected[8]); |
| | 1026 | try b.writeConst(writer, i28, expected[9]); |
| | 1027 | try b.writeAddr(writer, expected[10]); |
| | 1028 | |
| | 1029 | var mock_compile_unit: dwarf.CompileUnit = undefined; |
| | 1030 | mock_compile_unit.addr_base = 1; |
| | 1031 | |
| | 1032 | var mock_debug_addr = std.ArrayList(u8).init(allocator); |
| | 1033 | defer mock_debug_addr.deinit(); |
| | 1034 | |
| | 1035 | try mock_debug_addr.writer().writeIntNative(u16, 0); |
| | 1036 | try mock_debug_addr.writer().writeIntNative(usize, expected[11]); |
| | 1037 | try mock_debug_addr.writer().writeIntNative(usize, expected[12]); |
| | 1038 | |
| | 1039 | const context = ExpressionContext{ |
| | 1040 | .compile_unit = &mock_compile_unit, |
| | 1041 | .debug_addr = mock_debug_addr.items, |
| | 1042 | }; |
| | 1043 | |
| | 1044 | try b.writeConstx(writer, @as(usize, 1)); |
| | 1045 | try b.writeAddrx(writer, @as(usize, 1 + @sizeOf(usize))); |
| | 1046 | |
| | 1047 | const die_offset: usize = @truncate(0xaabbccdd); |
| | 1048 | const type_bytes: []const u8 = &.{ 1, 2, 3, 4 }; |
| | 1049 | try b.writeConstType(writer, die_offset, type_bytes.len, type_bytes); |
| | 1050 | |
| | 1051 | _ = try stack_machine.run(program.items, allocator, context, 0); |
| | 1052 | |
| | 1053 | const const_type = stack_machine.stack.popOrNull().?.const_type; |
| | 1054 | try testing.expectEqual(die_offset, const_type.type_offset); |
| | 1055 | try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes); |
| | 1056 | |
| | 1057 | try testing.expectEqual(@as(usize, expected[12]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1058 | try testing.expectEqual(@as(usize, expected[11]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1059 | try testing.expectEqual(@as(usize, expected[10]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1060 | try testing.expectEqual(@as(isize, @truncate(expected[9])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1061 | try testing.expectEqual(@as(usize, @truncate(expected[8])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1062 | try testing.expectEqual(@as(isize, @truncate(expected[7])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1063 | try testing.expectEqual(@as(usize, @truncate(expected[6])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1064 | try testing.expectEqual(@as(isize, @truncate(expected[5])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1065 | try testing.expectEqual(@as(usize, @truncate(expected[4])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1066 | try testing.expectEqual(@as(isize, @truncate(expected[3])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1067 | try testing.expectEqual(@as(usize, @truncate(expected[2])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1068 | try testing.expectEqual(@as(isize, @truncate(expected[1])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1069 | try testing.expectEqual(@as(usize, @truncate(expected[0])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic))); |
| | 1070 | } |
| | 1071 | |
| | 1072 | // Register values |
| | 1073 | var thread_context: std.debug.ThreadContext = undefined; |
| | 1074 | if (std.debug.getContext(&thread_context)) { |
| | 1075 | // TODO: Test fbreg, breg0..31, bregx, regval_type |
| | 1076 | } |
| 991 | } | 1077 | } |