| ... | ... | @@ -17,6 +17,9 @@ pub const ExpressionContext = struct { |
| 17 | 17 | /// The compilation unit this expression relates to, if any |
| 18 | 18 | compile_unit: ?*const dwarf.CompileUnit = null, |
| 19 | 19 | |
| 20 | // .debug_addr section |
| 21 | debug_addr: ?[]const u8 = null, |
| 22 | |
| 20 | 23 | /// Thread context |
| 21 | 24 | thread_context: ?*std.debug.ThreadContext = null, |
| 22 | 25 | reg_context: ?abi.RegisterContext = null, |
| ... | ... | @@ -73,15 +76,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 73 | 76 | block: []const u8, |
| 74 | 77 | register_type: struct { |
| 75 | 78 | register: u8, |
| 76 | | type_offset: u64, |
| 79 | type_offset: addr_type, |
| 77 | 80 | }, |
| 78 | 81 | const_type: struct { |
| 79 | | type_offset: u64, |
| 82 | type_offset: addr_type, |
| 80 | 83 | value_bytes: []const u8, |
| 81 | 84 | }, |
| 82 | 85 | deref_type: struct { |
| 83 | 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 | 94 | // Typed value with a maximum size of a register |
| 92 | 95 | regval_type: struct { |
| 93 | 96 | // Offset of DW_TAG_base_type DIE |
| 94 | | type_offset: u64, |
| 97 | type_offset: addr_type, |
| 95 | 98 | type_size: u8, |
| 96 | 99 | value: addr_type, |
| 97 | 100 | }, |
| ... | ... | @@ -99,7 +102,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 99 | 102 | // Typed value specified directly in the instruction stream |
| 100 | 103 | const_type: struct { |
| 101 | 104 | // Offset of DW_TAG_base_type DIE |
| 102 | | type_offset: u64, |
| 105 | type_offset: addr_type, |
| 103 | 106 | // Backed by the instruction stream |
| 104 | 107 | value_bytes: []const u8, |
| 105 | 108 | }, |
| ... | ... | @@ -202,7 +205,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 202 | 205 | }, |
| 203 | 206 | OP.regval_type => blk: { |
| 204 | 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 | 209 | break :blk .{ .register_type = .{ |
| 207 | 210 | .register = register, |
| 208 | 211 | .type_offset = type_offset, |
| ... | ... | @@ -232,7 +235,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 232 | 235 | }; |
| 233 | 236 | }, |
| 234 | 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 | 239 | const size = try reader.readByte(); |
| 237 | 240 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; |
| 238 | 241 | const value_bytes = stream.buffer[stream.pos..][0..size]; |
| ... | ... | @@ -247,7 +250,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 247 | 250 | => .{ |
| 248 | 251 | .deref_type = .{ |
| 249 | 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 | 256 | OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode, |
| ... | ... | @@ -277,7 +280,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 277 | 280 | context: ExpressionContext, |
| 278 | 281 | ) !bool { |
| 279 | 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 | 285 | const opcode = try stream.reader().readByte(); |
| 283 | 286 | if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; |
| ... | ... | @@ -309,12 +312,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 309 | 312 | OP.addrx, |
| 310 | 313 | OP.constx, |
| 311 | 314 | => { |
| 315 | if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit; |
| 316 | if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr; |
| 312 | 317 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; |
| 313 | | |
| 314 | | // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic |
| 315 | | |
| 316 | | _ = debug_addr_index; |
| 317 | | unreachable; |
| 318 | const offset = context.compile_unit.?.addr_base + debug_addr_index; |
| 319 | if (offset >= context.debug_addr.?.len) return error.InvalidExpression; |
| 320 | const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]); |
| 321 | try self.stack.append(allocator, .{ .generic = value }); |
| 318 | 322 | }, |
| 319 | 323 | |
| 320 | 324 | // 2.5.1.2: Register Values |
| ... | ... | @@ -464,7 +468,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 464 | 468 | // 2.5.1.4: Arithmetic and Logical Operations |
| 465 | 469 | OP.abs => { |
| 466 | 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 | 472 | self.stack.items[self.stack.items.len - 1] = .{ |
| 469 | 473 | .generic = std.math.absCast(value), |
| 470 | 474 | }; |
| ... | ... | @@ -478,10 +482,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 478 | 482 | }, |
| 479 | 483 | OP.div => { |
| 480 | 484 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 481 | | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 482 | | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 485 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 486 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 483 | 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 | 491 | OP.minus => { |
| ... | ... | @@ -493,16 +497,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 493 | 497 | }, |
| 494 | 498 | OP.mod => { |
| 495 | 499 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 496 | | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 497 | | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 500 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 501 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 498 | 502 | self.stack.items[self.stack.items.len - 1] = .{ |
| 499 | 503 | .generic = @bitCast(@mod(b, a)), |
| 500 | 504 | }; |
| 501 | 505 | }, |
| 502 | 506 | OP.mul => { |
| 503 | 507 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 504 | | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 505 | | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 508 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); |
| 509 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 506 | 510 | self.stack.items[self.stack.items.len - 1] = .{ |
| 507 | 511 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), |
| 508 | 512 | }; |
| ... | ... | @@ -512,7 +516,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 512 | 516 | self.stack.items[self.stack.items.len - 1] = .{ |
| 513 | 517 | .generic = @bitCast( |
| 514 | 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 | 567 | OP.shra => { |
| 564 | 568 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 565 | 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 | 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 | 575 | OP.xor => { |
| ... | ... | @@ -589,8 +593,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 589 | 593 | const b = self.stack.items[self.stack.items.len - 1]; |
| 590 | 594 | |
| 591 | 595 | if (a == .generic and b == .generic) { |
| 592 | | const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable); |
| 593 | | const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable); |
| 596 | const a_int: isize = @bitCast(a.asIntegral() catch unreachable); |
| 597 | const b_int: isize = @bitCast(b.asIntegral() catch unreachable); |
| 594 | 598 | const result = @intFromBool(switch (opcode) { |
| 595 | 599 | OP.le => b_int < a_int, |
| 596 | 600 | OP.ge => b_int >= a_int, |
| ... | ... | @@ -617,7 +621,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 617 | 621 | if (condition) { |
| 618 | 622 | const new_pos = std.math.cast( |
| 619 | 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 | 625 | ) orelse return error.InvalidExpression; |
| 622 | 626 | |
| 623 | 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 | 785 | i32 => OP.const4s, |
| 782 | 786 | u64 => OP.const8u, |
| 783 | 787 | i64 => OP.const8s, |
| 788 | else => unreachable, |
| 784 | 789 | }); |
| 785 | 790 | |
| 786 | 791 | try writer.writeInt(T, value, options.endian); |
| ... | ... | @@ -988,4 +993,85 @@ test "DWARF expressions" { |
| 988 | 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 | } |