| author | |
| committer | |
| log | 92bc619c49fb4b7281bdc1c3c0bc3e41a9d20d4d |
| tree | 01de65c840b64ee0958afe44daa0df48061ef4cc |
| parent | 49e19fc94fe70cecc1cba21a04c1f2f1c1e4deb8 |
It's easy to do FP unwinding from a CPU context: you just report the
captured ip/pc value first, and then unwind from the captured fp value.
All this really needed was a couple of new functions on the
`std.debug.cpu_context` implementations so that we don't need to rely on
`std.debug.Dwarf` to access the captured registers.
Resolves: #255763 files changed, 169 insertions(+), 41 deletions(-)
lib/std/debug.zig+30-25| ... | ... | @@ -617,7 +617,7 @@ pub const StackUnwindOptions = struct { |
| 617 | 617 | pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace { |
| 618 | 618 | const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} }; |
| 619 | 619 | if (!std.options.allow_stack_tracing) return empty_trace; |
| 620 | var it = StackIterator.init(options.context) catch return empty_trace; | |
| 620 | var it: StackIterator = .init(options.context); | |
| 621 | 621 | defer it.deinit(); |
| 622 | 622 | if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; |
| 623 | 623 | var total_frames: usize = 0; |
| ... | ... | @@ -671,14 +671,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri |
| 671 | 671 | return; |
| 672 | 672 | }, |
| 673 | 673 | }; |
| 674 | var it = StackIterator.init(options.context) catch |err| switch (err) { | |
| 675 | error.CannotUnwindFromContext => { | |
| 676 | tty_config.setColor(writer, .dim) catch {}; | |
| 677 | try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{}); | |
| 678 | tty_config.setColor(writer, .reset) catch {}; | |
| 679 | return; | |
| 680 | }, | |
| 681 | }; | |
| 674 | var it: StackIterator = .init(options.context); | |
| 682 | 675 | defer it.deinit(); |
| 683 | 676 | if (!it.stratOk(options.allow_unsafe_unwind)) { |
| 684 | 677 | tty_config.setColor(writer, .dim) catch {}; |
| ... | ... | @@ -821,22 +814,32 @@ pub fn dumpStackTrace(st: *const StackTrace) void { |
| 821 | 814 | } |
| 822 | 815 | |
| 823 | 816 | const StackIterator = union(enum) { |
| 817 | /// We will first report the current PC of this `CpuContextPtr`, then we will switch to a | |
| 818 | /// different strategy to actually unwind. | |
| 819 | ctx_first: CpuContextPtr, | |
| 824 | 820 | /// Unwinding using debug info (e.g. DWARF CFI). |
| 825 | di: if (SelfInfo != void and SelfInfo.can_unwind) SelfInfo.UnwindContext else noreturn, | |
| 826 | /// We will first report the *current* PC of this `UnwindContext`, then we will switch to `di`. | |
| 827 | di_first: if (SelfInfo != void and SelfInfo.can_unwind) SelfInfo.UnwindContext else noreturn, | |
| 821 | di: if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal) | |
| 822 | SelfInfo.UnwindContext | |
| 823 | else | |
| 824 | noreturn, | |
| 828 | 825 | /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable. |
| 829 | 826 | fp: usize, |
| 830 | 827 | |
| 831 | 828 | /// It is important that this function is marked `inline` so that it can safely use |
| 832 | 829 | /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and |
| 833 | 830 | /// our own are one and the same. |
| 834 | inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator { | |
| 831 | /// | |
| 832 | /// `opt_context_ptr` must remain valid while the `StackIterator` is used. | |
| 833 | inline fn init(opt_context_ptr: ?CpuContextPtr) StackIterator { | |
| 835 | 834 | if (opt_context_ptr) |context_ptr| { |
| 836 | if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext; | |
| 837 | // Use `di_first` here so we report the PC in the context before unwinding any further. | |
| 838 | return .{ .di_first = .init(context_ptr) }; | |
| 835 | // Use `ctx_first` here so we report the PC in the context before unwinding any further. | |
| 836 | return .{ .ctx_first = context_ptr }; | |
| 839 | 837 | } |
| 838 | ||
| 839 | // Otherwise, we're going to capture the current context or frame address, so we don't need | |
| 840 | // `ctx_first`, because the first PC is in `std.debug` and we need to unwind before reaching | |
| 841 | // a frame we want to report. | |
| 842 | ||
| 840 | 843 | // Workaround the C backend being unable to use inline assembly on MSVC by disabling the |
| 841 | 844 | // call to `current`. This effectively constrains stack trace collection and dumping to FP |
| 842 | 845 | // unwinding when building with CBE for MSVC. |
| ... | ... | @@ -846,8 +849,6 @@ const StackIterator = union(enum) { |
| 846 | 849 | cpu_context.Native != noreturn and |
| 847 | 850 | fp_usability != .ideal) |
| 848 | 851 | { |
| 849 | // We don't need `di_first` here, because our PC is in `std.debug`; we're only interested | |
| 850 | // in our caller's frame and above. | |
| 851 | 852 | return .{ .di = .init(&.current()) }; |
| 852 | 853 | } |
| 853 | 854 | return .{ |
| ... | ... | @@ -866,8 +867,9 @@ const StackIterator = union(enum) { |
| 866 | 867 | } |
| 867 | 868 | fn deinit(si: *StackIterator) void { |
| 868 | 869 | switch (si.*) { |
| 870 | .ctx_first => {}, | |
| 869 | 871 | .fp => {}, |
| 870 | .di, .di_first => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()), | |
| 872 | .di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()), | |
| 871 | 873 | } |
| 872 | 874 | } |
| 873 | 875 | |
| ... | ... | @@ -931,7 +933,7 @@ const StackIterator = union(enum) { |
| 931 | 933 | /// Whether the current unwind strategy is allowed given `allow_unsafe`. |
| 932 | 934 | fn stratOk(it: *const StackIterator, allow_unsafe: bool) bool { |
| 933 | 935 | return switch (it.*) { |
| 934 | .di, .di_first => true, | |
| 936 | .ctx_first, .di => true, | |
| 935 | 937 | // If we omitted frame pointers from *this* compilation, FP unwinding would crash |
| 936 | 938 | // immediately regardless of anything. But FPs could also be omitted from a different |
| 937 | 939 | // linked object, so it's not guaranteed to be safe, unless the target specifically |
| ... | ... | @@ -959,13 +961,16 @@ const StackIterator = union(enum) { |
| 959 | 961 | |
| 960 | 962 | fn next(it: *StackIterator) Result { |
| 961 | 963 | switch (it.*) { |
| 962 | .di_first => |unwind_context| { | |
| 963 | const first_pc = unwind_context.pc; | |
| 964 | if (first_pc == 0) return .end; | |
| 965 | it.* = .{ .di = unwind_context }; | |
| 964 | .ctx_first => |context_ptr| { | |
| 965 | // After the first frame, start actually unwinding. | |
| 966 | it.* = if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal) | |
| 967 | .{ .di = .init(context_ptr) } | |
| 968 | else | |
| 969 | .{ .fp = context_ptr.getFp() }; | |
| 970 | ||
| 966 | 971 | // The caller expects *return* addresses, where they will subtract 1 to find the address of the call. |
| 967 | 972 | // However, we have the actual current PC, which should not be adjusted. Compensate by adding 1. |
| 968 | return .{ .frame = first_pc +| 1 }; | |
| 973 | return .{ .frame = context_ptr.getPc() +| 1 }; | |
| 969 | 974 | }, |
| 970 | 975 | .di => |*unwind_context| { |
| 971 | 976 | const di = getSelfDebugInfo() catch unreachable; |
lib/std/debug/Dwarf/SelfUnwinder.zig+2-16| ... | ... | @@ -47,16 +47,9 @@ pub const CacheEntry = struct { |
| 47 | 47 | }; |
| 48 | 48 | |
| 49 | 49 | pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder { |
| 50 | // `@constCast` is safe because we aren't going to store to the resulting pointer. | |
| 51 | const raw_pc_ptr = regNative(@constCast(cpu_context), ip_reg_num) catch |err| switch (err) { | |
| 52 | error.InvalidRegister => unreachable, // `ip_reg_num` is definitely valid | |
| 53 | error.UnsupportedRegister => unreachable, // the implementation needs to support ip | |
| 54 | error.IncompatibleRegisterSize => unreachable, // ip is definitely `usize`-sized | |
| 55 | }; | |
| 56 | const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*); | |
| 57 | 50 | return .{ |
| 58 | 51 | .cpu_state = cpu_context.*, |
| 59 | .pc = pc, | |
| 52 | .pc = stripInstructionPtrAuthCode(cpu_context.getPc()), | |
| 60 | 53 | .cfi_vm = .{}, |
| 61 | 54 | .expr_vm = .{}, |
| 62 | 55 | }; |
| ... | ... | @@ -69,13 +62,7 @@ pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void { |
| 69 | 62 | } |
| 70 | 63 | |
| 71 | 64 | pub fn getFp(unwinder: *const SelfUnwinder) usize { |
| 72 | // `@constCast` is safe because we aren't going to store to the resulting pointer. | |
| 73 | const ptr = regNative(@constCast(&unwinder.cpu_state), fp_reg_num) catch |err| switch (err) { | |
| 74 | error.InvalidRegister => unreachable, // `fp_reg_num` is definitely valid | |
| 75 | error.UnsupportedRegister => unreachable, // the implementation needs to support fp | |
| 76 | error.IncompatibleRegisterSize => unreachable, // fp is a pointer so is `usize`-sized | |
| 77 | }; | |
| 78 | return ptr.*; | |
| 65 | return unwinder.cpu_state.getFp(); | |
| 79 | 66 | } |
| 80 | 67 | |
| 81 | 68 | /// Compute the rule set for the address `unwinder.pc` from the information in `unwind`. The caller |
| ... | ... | @@ -332,7 +319,6 @@ fn applyOffset(base: usize, offset: i64) !usize { |
| 332 | 319 | } |
| 333 | 320 | |
| 334 | 321 | const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?; |
| 335 | const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch); | |
| 336 | 322 | const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch); |
| 337 | 323 | |
| 338 | 324 | const std = @import("std"); |
lib/std/debug/cpu_context.zig+137| ... | ... | @@ -250,6 +250,13 @@ const Aarch64 = extern struct { |
| 250 | 250 | return ctx; |
| 251 | 251 | } |
| 252 | 252 | |
| 253 | pub fn getFp(ctx: *const Aarch64) u64 { | |
| 254 | return ctx.x[29]; | |
| 255 | } | |
| 256 | pub fn getPc(ctx: *const Aarch64) u64 { | |
| 257 | return ctx.pc; | |
| 258 | } | |
| 259 | ||
| 253 | 260 | pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 { |
| 254 | 261 | // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names" |
| 255 | 262 | switch (register_num) { |
| ... | ... | @@ -324,6 +331,13 @@ const Arc = extern struct { |
| 324 | 331 | return ctx; |
| 325 | 332 | } |
| 326 | 333 | |
| 334 | pub fn getFp(ctx: *const Arc) u32 { | |
| 335 | return ctx.r[27]; | |
| 336 | } | |
| 337 | pub fn getPc(ctx: *const Arc) u32 { | |
| 338 | return ctx.pcl; | |
| 339 | } | |
| 340 | ||
| 327 | 341 | pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 { |
| 328 | 342 | switch (register_num) { |
| 329 | 343 | 0...31 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -356,6 +370,13 @@ const Arm = struct { |
| 356 | 370 | return ctx; |
| 357 | 371 | } |
| 358 | 372 | |
| 373 | pub fn getFp(ctx: *const Arm) u32 { | |
| 374 | return ctx.r[11]; | |
| 375 | } | |
| 376 | pub fn getPc(ctx: *const Arm) u32 { | |
| 377 | return ctx.r[15]; | |
| 378 | } | |
| 379 | ||
| 359 | 380 | pub fn dwarfRegisterBytes(ctx: *Arm, register_num: u16) DwarfRegisterError![]u8 { |
| 360 | 381 | // DWARF for the Arm(r) Architecture § 4.1 "DWARF register names" |
| 361 | 382 | switch (register_num) { |
| ... | ... | @@ -415,6 +436,13 @@ const Csky = extern struct { |
| 415 | 436 | return ctx; |
| 416 | 437 | } |
| 417 | 438 | |
| 439 | pub fn getFp(ctx: *const Csky) u32 { | |
| 440 | return ctx.r[14]; | |
| 441 | } | |
| 442 | pub fn getPc(ctx: *const Csky) u32 { | |
| 443 | return ctx.pc; | |
| 444 | } | |
| 445 | ||
| 418 | 446 | pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 { |
| 419 | 447 | switch (register_num) { |
| 420 | 448 | 0...31 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -476,6 +504,13 @@ const Hexagon = extern struct { |
| 476 | 504 | return ctx; |
| 477 | 505 | } |
| 478 | 506 | |
| 507 | pub fn getFp(ctx: *const Hexagon) u32 { | |
| 508 | return ctx.r[30]; | |
| 509 | } | |
| 510 | pub fn getPc(ctx: *const Hexagon) u32 { | |
| 511 | return ctx.pc; | |
| 512 | } | |
| 513 | ||
| 479 | 514 | pub fn dwarfRegisterBytes(ctx: *Hexagon, register_num: u16) DwarfRegisterError![]u8 { |
| 480 | 515 | // Sourced from LLVM's HexagonRegisterInfo.td, which disagrees with LLDB... |
| 481 | 516 | switch (register_num) { |
| ... | ... | @@ -544,6 +579,13 @@ const Kvx = extern struct { |
| 544 | 579 | return ctx; |
| 545 | 580 | } |
| 546 | 581 | |
| 582 | pub fn getFp(ctx: *const Kvx) u64 { | |
| 583 | return ctx.r[14]; | |
| 584 | } | |
| 585 | pub fn getPc(ctx: *const Kvx) u64 { | |
| 586 | return ctx.pc; | |
| 587 | } | |
| 588 | ||
| 547 | 589 | pub fn dwarfRegisterBytes(ctx: *Kvx, register_num: u16) DwarfRegisterError![]u8 { |
| 548 | 590 | switch (register_num) { |
| 549 | 591 | 0...63 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -604,6 +646,13 @@ const Lanai = extern struct { |
| 604 | 646 | return ctx; |
| 605 | 647 | } |
| 606 | 648 | |
| 649 | pub fn getFp(ctx: *const Lanai) u32 { | |
| 650 | return ctx.r[5]; | |
| 651 | } | |
| 652 | pub fn getPc(ctx: *const Lanai) u32 { | |
| 653 | return ctx.r[2]; | |
| 654 | } | |
| 655 | ||
| 607 | 656 | pub fn dwarfRegisterBytes(ctx: *Lanai, register_num: u16) DwarfRegisterError![]u8 { |
| 608 | 657 | switch (register_num) { |
| 609 | 658 | 0...31 => return @ptrCast(&ctx.s[register_num]), |
| ... | ... | @@ -701,6 +750,13 @@ const LoongArch = extern struct { |
| 701 | 750 | return ctx; |
| 702 | 751 | } |
| 703 | 752 | |
| 753 | pub fn getFp(ctx: *const LoongArch) Gpr { | |
| 754 | return ctx.r[22]; | |
| 755 | } | |
| 756 | pub fn getPc(ctx: *const LoongArch) Gpr { | |
| 757 | return ctx.pc; | |
| 758 | } | |
| 759 | ||
| 704 | 760 | pub fn dwarfRegisterBytes(ctx: *LoongArch, register_num: u16) DwarfRegisterError![]u8 { |
| 705 | 761 | switch (register_num) { |
| 706 | 762 | 0...31 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -733,6 +789,13 @@ const M68k = extern struct { |
| 733 | 789 | return ctx; |
| 734 | 790 | } |
| 735 | 791 | |
| 792 | pub fn getFp(ctx: *const M68k) u32 { | |
| 793 | return ctx.a[6]; | |
| 794 | } | |
| 795 | pub fn getPc(ctx: *const M68k) u32 { | |
| 796 | return ctx.pc; | |
| 797 | } | |
| 798 | ||
| 736 | 799 | pub fn dwarfRegisterBytes(ctx: *M68k, register_num: u16) DwarfRegisterError![]u8 { |
| 737 | 800 | switch (register_num) { |
| 738 | 801 | 0...7 => return @ptrCast(&ctx.d[register_num]), |
| ... | ... | @@ -845,6 +908,15 @@ const Mips = extern struct { |
| 845 | 908 | return ctx; |
| 846 | 909 | } |
| 847 | 910 | |
| 911 | pub fn getFp(ctx: *const Mips) usize { | |
| 912 | // On N32, `Gpr` is 64 bits but `usize` is 32 bits. | |
| 913 | return @intCast(ctx.r[30]); | |
| 914 | } | |
| 915 | pub fn getPc(ctx: *const Mips) usize { | |
| 916 | // On N32, `Gpr` is 64 bits but `usize` is 32 bits. | |
| 917 | return @intCast(ctx.pc); | |
| 918 | } | |
| 919 | ||
| 848 | 920 | pub fn dwarfRegisterBytes(ctx: *Mips, register_num: u16) DwarfRegisterError![]u8 { |
| 849 | 921 | switch (register_num) { |
| 850 | 922 | 0...31 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -917,6 +989,13 @@ const Or1k = extern struct { |
| 917 | 989 | return ctx; |
| 918 | 990 | } |
| 919 | 991 | |
| 992 | pub fn getFp(ctx: *const Or1k) u32 { | |
| 993 | return ctx.r[2]; | |
| 994 | } | |
| 995 | pub fn getPc(ctx: *const Or1k) u32 { | |
| 996 | return ctx.pc; | |
| 997 | } | |
| 998 | ||
| 920 | 999 | pub fn dwarfRegisterBytes(ctx: *Or1k, register_num: u16) DwarfRegisterError![]u8 { |
| 921 | 1000 | switch (register_num) { |
| 922 | 1001 | 0...31 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -1022,6 +1101,13 @@ const Powerpc = extern struct { |
| 1022 | 1101 | return ctx; |
| 1023 | 1102 | } |
| 1024 | 1103 | |
| 1104 | pub fn getFp(ctx: *const Powerpc) Gpr { | |
| 1105 | return ctx.r[1]; | |
| 1106 | } | |
| 1107 | pub fn getPc(ctx: *const Powerpc) Gpr { | |
| 1108 | return ctx.pc; | |
| 1109 | } | |
| 1110 | ||
| 1025 | 1111 | pub fn dwarfRegisterBytes(ctx: *Powerpc, register_num: u16) DwarfRegisterError![]u8 { |
| 1026 | 1112 | // References: |
| 1027 | 1113 | // |
| ... | ... | @@ -1168,6 +1254,13 @@ const Riscv = extern struct { |
| 1168 | 1254 | return ctx; |
| 1169 | 1255 | } |
| 1170 | 1256 | |
| 1257 | pub fn getFp(ctx: *const Riscv) Gpr { | |
| 1258 | return ctx.x[8]; | |
| 1259 | } | |
| 1260 | pub fn getPc(ctx: *const Riscv) Gpr { | |
| 1261 | return ctx.pc; | |
| 1262 | } | |
| 1263 | ||
| 1171 | 1264 | pub fn dwarfRegisterBytes(ctx: *Riscv, register_num: u16) DwarfRegisterError![]u8 { |
| 1172 | 1265 | switch (register_num) { |
| 1173 | 1266 | 0...31 => return @ptrCast(&ctx.x[register_num]), |
| ... | ... | @@ -1208,6 +1301,13 @@ const S390x = extern struct { |
| 1208 | 1301 | return ctx; |
| 1209 | 1302 | } |
| 1210 | 1303 | |
| 1304 | pub fn getFp(ctx: *const S390x) u64 { | |
| 1305 | return ctx.r[11]; | |
| 1306 | } | |
| 1307 | pub fn getPc(ctx: *const S390x) u64 { | |
| 1308 | return ctx.psw.addr; | |
| 1309 | } | |
| 1310 | ||
| 1211 | 1311 | pub fn dwarfRegisterBytes(ctx: *S390x, register_num: u16) DwarfRegisterError![]u8 { |
| 1212 | 1312 | switch (register_num) { |
| 1213 | 1313 | 0...15 => return @ptrCast(&ctx.r[register_num]), |
| ... | ... | @@ -1310,6 +1410,13 @@ const Sparc = extern struct { |
| 1310 | 1410 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS |
| 1311 | 1411 | } |
| 1312 | 1412 | |
| 1413 | pub fn getFp(ctx: *const Sparc) Gpr { | |
| 1414 | return ctx.i[6]; | |
| 1415 | } | |
| 1416 | pub fn getPc(ctx: *const Sparc) Gpr { | |
| 1417 | return ctx.pc; | |
| 1418 | } | |
| 1419 | ||
| 1313 | 1420 | pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 { |
| 1314 | 1421 | switch (register_num) { |
| 1315 | 1422 | 0...7 => return @ptrCast(&ctx.g[register_num]), |
| ... | ... | @@ -1404,6 +1511,13 @@ const Ve = extern struct { |
| 1404 | 1511 | return ctx; |
| 1405 | 1512 | } |
| 1406 | 1513 | |
| 1514 | pub fn getFp(ctx: *const Ve) u64 { | |
| 1515 | return ctx.s[9]; | |
| 1516 | } | |
| 1517 | pub fn getPc(ctx: *const Ve) u64 { | |
| 1518 | return ctx.ic; | |
| 1519 | } | |
| 1520 | ||
| 1407 | 1521 | pub fn dwarfRegisterBytes(ctx: *Ve, register_num: u16) DwarfRegisterError![]u8 { |
| 1408 | 1522 | switch (register_num) { |
| 1409 | 1523 | 0...63 => return @ptrCast(&ctx.s[register_num]), |
| ... | ... | @@ -1444,6 +1558,13 @@ const X86_16 = struct { |
| 1444 | 1558 | return ctx; |
| 1445 | 1559 | } |
| 1446 | 1560 | |
| 1561 | pub fn getFp(ctx: *const X86_16) u16 { | |
| 1562 | return ctx.regs.get(.bp); | |
| 1563 | } | |
| 1564 | pub fn getPc(ctx: *const X86_16) u16 { | |
| 1565 | return ctx.regs.get(.ip); | |
| 1566 | } | |
| 1567 | ||
| 1447 | 1568 | // NOTE: There doesn't seem to be any standard for DWARF x86-16 so we'll just reuse the ones for x86. |
| 1448 | 1569 | pub fn dwarfRegisterBytes(ctx: *X86_16, register_num: u16) DwarfRegisterError![]u8 { |
| 1449 | 1570 | switch (register_num) { |
| ... | ... | @@ -1490,6 +1611,13 @@ const X86 = struct { |
| 1490 | 1611 | return ctx; |
| 1491 | 1612 | } |
| 1492 | 1613 | |
| 1614 | pub fn getFp(ctx: *const X86) u32 { | |
| 1615 | return ctx.gprs.get(.ebp); | |
| 1616 | } | |
| 1617 | pub fn getPc(ctx: *const X86) u32 { | |
| 1618 | return ctx.gprs.get(.eip); | |
| 1619 | } | |
| 1620 | ||
| 1493 | 1621 | pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 { |
| 1494 | 1622 | // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1 |
| 1495 | 1623 | // § 2.4.2 "DWARF Register Number Mapping" |
| ... | ... | @@ -1558,6 +1686,15 @@ const X86_64 = struct { |
| 1558 | 1686 | return ctx; |
| 1559 | 1687 | } |
| 1560 | 1688 | |
| 1689 | pub fn getFp(ctx: *const X86_64) usize { | |
| 1690 | // On x32, registers are 64 bits but `usize` is 32 bits. | |
| 1691 | return @intCast(ctx.gprs.get(.rbp)); | |
| 1692 | } | |
| 1693 | pub fn getPc(ctx: *const X86_64) usize { | |
| 1694 | // On x32, registers are 64 bits but `usize` is 32 bits. | |
| 1695 | return @intCast(ctx.gprs.get(.rip)); | |
| 1696 | } | |
| 1697 | ||
| 1561 | 1698 | pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 { |
| 1562 | 1699 | // System V Application Binary Interface AMD64 Architecture Processor Supplement |
| 1563 | 1700 | // § 3.6.2 "DWARF Register Number Mapping" |