ghostscope_platform/
register_mapping.rs

1/// Platform-specific register mappings and utilities for eBPF code generation
2///
3/// This module handles the mapping between DWARF register numbers and platform-specific
4/// register layouts (like pt_regs) for different architectures.
5use tracing::warn;
6
7/// pt_regs indices for x86_64 architecture
8///
9/// These indices are used to access pt_regs structure fields as a u64 array.
10/// The indices are calculated by dividing the field offset by the size of u64,
11/// which gives us the array index for accessing pt_regs as a u64 array.
12pub mod pt_regs_indices {
13    use aya_ebpf_bindings::bindings::pt_regs;
14
15    // Size of u64 in bytes for array index calculation
16    const U64_SIZE: usize = core::mem::size_of::<u64>();
17
18    // Core registers - calculated from pt_regs structure layout
19    pub const R15: usize = core::mem::offset_of!(pt_regs, r15) / U64_SIZE;
20    pub const R14: usize = core::mem::offset_of!(pt_regs, r14) / U64_SIZE;
21    pub const R13: usize = core::mem::offset_of!(pt_regs, r13) / U64_SIZE;
22    pub const R12: usize = core::mem::offset_of!(pt_regs, r12) / U64_SIZE;
23    pub const RBP: usize = core::mem::offset_of!(pt_regs, rbp) / U64_SIZE; // Frame pointer
24    pub const RBX: usize = core::mem::offset_of!(pt_regs, rbx) / U64_SIZE;
25    pub const R11: usize = core::mem::offset_of!(pt_regs, r11) / U64_SIZE;
26    pub const R10: usize = core::mem::offset_of!(pt_regs, r10) / U64_SIZE;
27    pub const R9: usize = core::mem::offset_of!(pt_regs, r9) / U64_SIZE;
28    pub const R8: usize = core::mem::offset_of!(pt_regs, r8) / U64_SIZE;
29    pub const RAX: usize = core::mem::offset_of!(pt_regs, rax) / U64_SIZE; // Return value
30    pub const RCX: usize = core::mem::offset_of!(pt_regs, rcx) / U64_SIZE; // 4th argument
31    pub const RDX: usize = core::mem::offset_of!(pt_regs, rdx) / U64_SIZE; // 3rd argument
32    pub const RSI: usize = core::mem::offset_of!(pt_regs, rsi) / U64_SIZE; // 2nd argument
33    pub const RDI: usize = core::mem::offset_of!(pt_regs, rdi) / U64_SIZE; // 1st argument
34
35    // Special registers
36    pub const ORIG_RAX: usize = core::mem::offset_of!(pt_regs, orig_rax) / U64_SIZE; // Original syscall number
37    pub const RIP: usize = core::mem::offset_of!(pt_regs, rip) / U64_SIZE; // Instruction pointer
38    pub const CS: usize = core::mem::offset_of!(pt_regs, cs) / U64_SIZE; // Code segment
39    pub const EFLAGS: usize = core::mem::offset_of!(pt_regs, eflags) / U64_SIZE; // Flags register
40    pub const RSP: usize = core::mem::offset_of!(pt_regs, rsp) / U64_SIZE; // Stack pointer
41    pub const SS: usize = core::mem::offset_of!(pt_regs, ss) / U64_SIZE; // Stack segment
42}
43
44/// Convert DWARF register number to pt_regs byte offset for x86_64
45///
46/// This function maps DWARF register numbers to the correct byte offset
47/// within the pt_regs structure for x86_64 architecture.
48///
49/// Reference: https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/ptrace.h
50/// pt_regs structure layout on x86_64:
51/// ```c
52/// struct pt_regs {
53///     unsigned long r15;    // offset 0
54///     unsigned long r14;    // offset 8
55///     unsigned long r13;    // offset 16
56///     unsigned long r12;    // offset 24
57///     unsigned long bp;     // offset 32  (RBP)
58///     unsigned long bx;     // offset 40  (RBX)
59///     unsigned long r11;    // offset 48
60///     unsigned long r10;    // offset 56
61///     unsigned long r9;     // offset 64
62///     unsigned long r8;     // offset 72
63///     unsigned long ax;     // offset 80  (RAX)
64///     unsigned long cx;     // offset 88  (RCX)
65///     unsigned long dx;     // offset 96  (RDX)
66///     unsigned long si;     // offset 104 (RSI)
67///     unsigned long di;     // offset 112 (RDI)
68///     unsigned long orig_ax;// offset 120
69///     unsigned long ip;     // offset 128 (RIP)
70///     unsigned long cs;     // offset 136
71///     unsigned long flags;  // offset 144
72///     unsigned long sp;     // offset 152 (RSP)
73///     unsigned long ss;     // offset 160
74/// };
75/// ```
76pub fn dwarf_reg_to_pt_regs_byte_offset_x86_64(dwarf_reg: u16) -> Option<usize> {
77    const U64_SIZE: usize = core::mem::size_of::<u64>();
78    match dwarf_reg {
79        // x86_64 DWARF register mappings to pt_regs indices (converted to byte offsets)
80        0 => Some(pt_regs_indices::RAX * U64_SIZE), // DWARF 0 = RAX
81        1 => Some(pt_regs_indices::RDX * U64_SIZE), // DWARF 1 = RDX
82        2 => Some(pt_regs_indices::RCX * U64_SIZE), // DWARF 2 = RCX
83        3 => Some(pt_regs_indices::RBX * U64_SIZE), // DWARF 3 = RBX
84        4 => Some(pt_regs_indices::RSI * U64_SIZE), // DWARF 4 = RSI
85        5 => Some(pt_regs_indices::RDI * U64_SIZE), // DWARF 5 = RDI
86        6 => Some(pt_regs_indices::RBP * U64_SIZE), // DWARF 6 = RBP
87        7 => Some(pt_regs_indices::RSP * U64_SIZE), // DWARF 7 = RSP
88        8 => Some(pt_regs_indices::R8 * U64_SIZE),  // DWARF 8 = R8
89        9 => Some(pt_regs_indices::R9 * U64_SIZE),  // DWARF 9 = R9
90        10 => Some(pt_regs_indices::R10 * U64_SIZE), // DWARF 10 = R10
91        11 => Some(pt_regs_indices::R11 * U64_SIZE), // DWARF 11 = R11
92        12 => Some(pt_regs_indices::R12 * U64_SIZE), // DWARF 12 = R12
93        13 => Some(pt_regs_indices::R13 * U64_SIZE), // DWARF 13 = R13
94        14 => Some(pt_regs_indices::R14 * U64_SIZE), // DWARF 14 = R14
95        15 => Some(pt_regs_indices::R15 * U64_SIZE), // DWARF 15 = R15
96        16 => Some(pt_regs_indices::RIP * U64_SIZE), // DWARF 16 = RIP
97        _ => {
98            warn!("Unknown DWARF register {} for x86_64", dwarf_reg);
99            None
100        }
101    }
102}
103
104/// Convert DWARF register number to register name for x86_64
105///
106/// Maps DWARF register numbers to human-readable register names
107/// for debugging and display purposes.
108pub fn dwarf_reg_to_name_x86_64(dwarf_reg: u16) -> Option<&'static str> {
109    match dwarf_reg {
110        0 => Some("RAX"),  // DWARF 0 = RAX
111        1 => Some("RDX"),  // DWARF 1 = RDX
112        2 => Some("RCX"),  // DWARF 2 = RCX
113        3 => Some("RBX"),  // DWARF 3 = RBX
114        4 => Some("RSI"),  // DWARF 4 = RSI
115        5 => Some("RDI"),  // DWARF 5 = RDI
116        6 => Some("RBP"),  // DWARF 6 = RBP
117        7 => Some("RSP"),  // DWARF 7 = RSP
118        8 => Some("R8"),   // DWARF 8 = R8
119        9 => Some("R9"),   // DWARF 9 = R9
120        10 => Some("R10"), // DWARF 10 = R10
121        11 => Some("R11"), // DWARF 11 = R11
122        12 => Some("R12"), // DWARF 12 = R12
123        13 => Some("R13"), // DWARF 13 = R13
124        14 => Some("R14"), // DWARF 14 = R14
125        15 => Some("R15"), // DWARF 15 = R15
126        16 => Some("RIP"), // DWARF 16 = RIP
127        _ => None,
128    }
129}
130
131/// Convert DWARF register number to register name
132///
133/// Currently only supports x86_64. This function can be extended
134/// to support other architectures in the future.
135pub fn dwarf_reg_to_name(dwarf_reg: u16) -> Option<&'static str> {
136    // For now, we only support x86_64
137    // TODO: Add support for other architectures (ARM64, RISC-V, etc.)
138    dwarf_reg_to_name_x86_64(dwarf_reg)
139}
140
141/// Convert DWARF register number to pt_regs byte offset
142///
143/// Currently only supports x86_64. This function can be extended
144/// to support other architectures in the future.
145pub fn dwarf_reg_to_pt_regs_byte_offset(dwarf_reg: u16) -> Option<usize> {
146    // For now, we only support x86_64
147    // TODO: Add support for other architectures (ARM64, RISC-V, etc.)
148    dwarf_reg_to_pt_regs_byte_offset_x86_64(dwarf_reg)
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_x86_64_dwarf_to_pt_regs_mapping() {
157        // Test key registers
158        assert_eq!(dwarf_reg_to_pt_regs_byte_offset_x86_64(0), Some(80)); // RAX
159        assert_eq!(dwarf_reg_to_pt_regs_byte_offset_x86_64(6), Some(32)); // RBP
160        assert_eq!(dwarf_reg_to_pt_regs_byte_offset_x86_64(7), Some(152)); // RSP
161        assert_eq!(dwarf_reg_to_pt_regs_byte_offset_x86_64(16), Some(128)); // RIP
162
163        // Test invalid register
164        assert_eq!(dwarf_reg_to_pt_regs_byte_offset_x86_64(99), None);
165    }
166
167    #[test]
168    fn test_x86_64_dwarf_to_name_mapping() {
169        // Test core registers
170        assert_eq!(dwarf_reg_to_name_x86_64(0), Some("RAX"));
171        assert_eq!(dwarf_reg_to_name_x86_64(1), Some("RDX"));
172        assert_eq!(dwarf_reg_to_name_x86_64(4), Some("RSI"));
173        assert_eq!(dwarf_reg_to_name_x86_64(5), Some("RDI"));
174        assert_eq!(dwarf_reg_to_name_x86_64(6), Some("RBP"));
175        assert_eq!(dwarf_reg_to_name_x86_64(7), Some("RSP"));
176
177        // Test extended registers
178        assert_eq!(dwarf_reg_to_name_x86_64(8), Some("R8"));
179        assert_eq!(dwarf_reg_to_name_x86_64(13), Some("R13"));
180        assert_eq!(dwarf_reg_to_name_x86_64(15), Some("R15"));
181
182        // Test special registers
183        assert_eq!(dwarf_reg_to_name_x86_64(16), Some("RIP"));
184
185        // Test invalid register
186        assert_eq!(dwarf_reg_to_name_x86_64(99), None);
187    }
188
189    #[test]
190    fn test_dwarf_reg_to_name_generic() {
191        // Test the generic function (currently just calls x86_64)
192        assert_eq!(dwarf_reg_to_name(0), Some("RAX"));
193        assert_eq!(dwarf_reg_to_name(5), Some("RDI"));
194        assert_eq!(dwarf_reg_to_name(13), Some("R13"));
195        assert_eq!(dwarf_reg_to_name(99), None);
196    }
197
198    #[test]
199    fn test_dwarf_reg_to_pt_regs_byte_offset_generic() {
200        // Test the generic function (currently just calls x86_64)
201        assert_eq!(dwarf_reg_to_pt_regs_byte_offset(0), Some(80)); // RAX
202        assert_eq!(dwarf_reg_to_pt_regs_byte_offset(5), Some(112)); // RDI
203        assert_eq!(dwarf_reg_to_pt_regs_byte_offset(99), None);
204    }
205}