Skip to main content

calltrace/
register_reader.rs

1//! x86_64 Register Reading for Function Argument Capture
2//!
3//! This module implements the x86_64 System V ABI argument passing conventions
4//! to extract function arguments from CPU registers and stack memory.
5
6use crate::dwarf_analyzer::{ParameterInfo, TypeInfo};
7use crate::error::{CallTraceError, Result};
8use serde::{Deserialize, Serialize};
9use std::arch::asm;
10
11/// x86_64 register context captured at function entry
12#[repr(C)]
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct RegisterContext {
15    // Integer argument registers (System V ABI)
16    pub rdi: u64, // 1st integer/pointer argument
17    pub rsi: u64, // 2nd integer/pointer argument
18    pub rdx: u64, // 3rd integer/pointer argument
19    pub rcx: u64, // 4th integer/pointer argument
20    pub r8: u64,  // 5th integer/pointer argument
21    pub r9: u64,  // 6th integer/pointer argument
22
23    // Stack pointers
24    pub rsp: u64, // Stack pointer
25    pub rbp: u64, // Base pointer (frame pointer)
26
27    // SSE registers for floating point arguments
28    pub xmm: [f64; 8], // XMM0-XMM7
29
30    // Additional context
31    pub rip: u64,    // Instruction pointer (approximate)
32    pub rflags: u64, // Flags register
33
34    // Metadata
35    pub valid: bool,
36    pub timestamp: u64,
37
38    // Return value registers (captured on function exit)
39    pub return_rax: u64,    // Primary integer/pointer return value
40    pub return_xmm0: f64,   // Primary floating point return value
41    pub return_valid: bool, // Whether return values were captured
42}
43
44/// Argument classification according to x86_64 System V ABI
45#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
46pub enum ArgumentClass {
47    Integer,    // Passed in integer register or stack
48    Sse,        // Passed in SSE register
49    Memory,     // Passed on stack (large structures)
50    X87,        // Passed in x87 register (long double)
51    ComplexX87, // Complex numbers in x87
52    NoClass,    // Empty or unclassified
53}
54
55/// Location where an argument is stored
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ArgumentLocation {
58    pub class: ArgumentClass,
59    pub register_index: Option<usize>, // Which register (if applicable)
60    pub stack_offset: Option<usize>,   // Stack offset (if applicable)
61    pub size: usize,                   // Size in bytes
62}
63
64/// Captured argument value with type information
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct CapturedArgument {
67    pub name: String,
68    pub type_name: String,
69    pub location: ArgumentLocation,
70    pub value: ArgumentValue,
71    pub valid: bool,
72    pub error: Option<String>,
73}
74
75/// Union-like enum for different argument value types
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum ArgumentValue {
78    Integer(u64),
79    Float(f32),
80    Double(f64),
81    Pointer(u64),
82    String(String),
83    Raw(Vec<u8>),
84
85    // Complex types
86    Struct {
87        type_name: String,
88        members: Vec<StructMember>,
89        size: usize,
90    },
91    Array {
92        element_type: String,
93        elements: Vec<ArgumentValue>,
94        count: usize,
95        element_size: usize,
96    },
97    Union {
98        type_name: String,
99        raw_data: Vec<u8>,
100        size: usize,
101    },
102
103    // Special values
104    Null,
105    Unknown {
106        type_name: String,
107        raw_data: Vec<u8>,
108        error: Option<String>,
109    },
110}
111
112/// Structure member information
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct StructMember {
115    pub name: String,
116    pub type_name: String,
117    pub offset: usize,
118    pub size: usize,
119    pub value: Box<ArgumentValue>,
120}
121
122impl RegisterContext {
123    /// Capture current register context using inline assembly
124    ///
125    /// # Safety
126    /// This function uses inline assembly and must be called very early
127    /// in the function entry hook before registers are modified.
128    pub unsafe fn capture() -> Result<Self> {
129        let mut context = RegisterContext {
130            rdi: 0,
131            rsi: 0,
132            rdx: 0,
133            rcx: 0,
134            r8: 0,
135            r9: 0,
136            rsp: 0,
137            rbp: 0,
138            rip: 0,
139            rflags: 0,
140            xmm: [0.0; 8],
141            valid: false,
142            timestamp: 0,
143            return_rax: 0,
144            return_xmm0: 0.0,
145            return_valid: false,
146        };
147
148        #[cfg(target_arch = "x86_64")]
149        {
150            // Capture integer registers
151            asm!(
152                "mov {rdi}, rdi",
153                "mov {rsi}, rsi",
154                "mov {rdx}, rdx",
155                "mov {rcx}, rcx",
156                "mov {r8}, r8",
157                "mov {r9}, r9",
158                "mov {rsp}, rsp",
159                "mov {rbp}, rbp",
160                "pushfq",
161                "pop {rflags}",
162                rdi = out(reg) context.rdi,
163                rsi = out(reg) context.rsi,
164                rdx = out(reg) context.rdx,
165                rcx = out(reg) context.rcx,
166                r8 = out(reg) context.r8,
167                r9 = out(reg) context.r9,
168                rsp = out(reg) context.rsp,
169                rbp = out(reg) context.rbp,
170                rflags = out(reg) context.rflags,
171                options(nostack, preserves_flags)
172            );
173
174            // Capture SSE registers for floating point arguments
175            asm!(
176                "movsd {xmm0}, xmm0",
177                "movsd {xmm1}, xmm1",
178                "movsd {xmm2}, xmm2",
179                "movsd {xmm3}, xmm3",
180                "movsd {xmm4}, xmm4",
181                "movsd {xmm5}, xmm5",
182                "movsd {xmm6}, xmm6",
183                "movsd {xmm7}, xmm7",
184                xmm0 = out(xmm_reg) context.xmm[0],
185                xmm1 = out(xmm_reg) context.xmm[1],
186                xmm2 = out(xmm_reg) context.xmm[2],
187                xmm3 = out(xmm_reg) context.xmm[3],
188                xmm4 = out(xmm_reg) context.xmm[4],
189                xmm5 = out(xmm_reg) context.xmm[5],
190                xmm6 = out(xmm_reg) context.xmm[6],
191                xmm7 = out(xmm_reg) context.xmm[7],
192                options(nostack, preserves_flags)
193            );
194
195            // Get approximate instruction pointer
196            asm!(
197                "lea {rip}, [rip]",
198                rip = out(reg) context.rip,
199                options(nostack, preserves_flags)
200            );
201
202            context.valid = true;
203            context.timestamp = std::time::SystemTime::now()
204                .duration_since(std::time::UNIX_EPOCH)
205                .unwrap_or_default()
206                .as_micros() as u64;
207
208            Ok(context)
209        }
210
211        #[cfg(not(target_arch = "x86_64"))]
212        {
213            Err(CallTraceError::NotSupported)
214        }
215    }
216
217    /// Get integer register value by index (0-5 for RDI, RSI, RDX, RCX, R8, R9)
218    #[inline]
219    pub fn get_integer_register(&self, index: usize) -> Option<u64> {
220        match index {
221            0 => Some(self.rdi),
222            1 => Some(self.rsi),
223            2 => Some(self.rdx),
224            3 => Some(self.rcx),
225            4 => Some(self.r8),
226            5 => Some(self.r9),
227            _ => None,
228        }
229    }
230
231    /// Get SSE register value by index (0-7 for XMM0-XMM7)
232    #[inline]
233    pub fn get_sse_register(&self, index: usize) -> Option<f64> {
234        self.xmm.get(index).copied()
235    }
236}
237
238/// Classify argument according to x86_64 System V ABI
239#[inline]
240pub fn classify_argument(type_name: &str, size: usize, arg_index: usize) -> ArgumentLocation {
241    const MAX_INTEGER_REGS: usize = 6;
242    const MAX_SSE_REGS: usize = 8;
243
244    // Basic classification based on type and size
245    if size <= 8 {
246        if type_name.contains("float") || type_name.contains("double") {
247            // Floating point argument
248            if arg_index < MAX_SSE_REGS {
249                ArgumentLocation {
250                    class: ArgumentClass::Sse,
251                    register_index: Some(arg_index),
252                    stack_offset: None,
253                    size,
254                }
255            } else {
256                ArgumentLocation {
257                    class: ArgumentClass::Memory,
258                    register_index: None,
259                    stack_offset: Some((arg_index - MAX_SSE_REGS) * 8 + 8),
260                    size,
261                }
262            }
263        } else {
264            // Integer/pointer argument
265            if arg_index < MAX_INTEGER_REGS {
266                ArgumentLocation {
267                    class: ArgumentClass::Integer,
268                    register_index: Some(arg_index),
269                    stack_offset: None,
270                    size,
271                }
272            } else {
273                ArgumentLocation {
274                    class: ArgumentClass::Memory,
275                    register_index: None,
276                    stack_offset: Some((arg_index - MAX_INTEGER_REGS) * 8 + 8),
277                    size,
278                }
279            }
280        }
281    } else {
282        // Large structures go on stack
283        ArgumentLocation {
284            class: ArgumentClass::Memory,
285            register_index: None,
286            stack_offset: Some(arg_index * 8 + 8),
287            size,
288        }
289    }
290}
291
292/// Extract argument value from register context
293#[inline]
294pub fn extract_argument(
295    context: &RegisterContext,
296    location: &ArgumentLocation,
297    type_name: &str,
298    is_pointer: bool,
299) -> Result<ArgumentValue> {
300    match location.class {
301        ArgumentClass::Integer => {
302            if let Some(reg_index) = location.register_index {
303                if let Some(reg_value) = context.get_integer_register(reg_index) {
304                    if is_pointer {
305                        // Try to read string if it's a char*
306                        if type_name.contains("char") && is_valid_pointer(reg_value as *const u8) {
307                            match read_string_safe(reg_value as *const u8, 256) {
308                                Ok(s) => return Ok(ArgumentValue::String(s)),
309                                Err(_) => return Ok(ArgumentValue::Pointer(reg_value)),
310                            }
311                        } else {
312                            return Ok(ArgumentValue::Pointer(reg_value));
313                        }
314                    } else {
315                        return Ok(ArgumentValue::Integer(reg_value));
316                    }
317                }
318            }
319            Err(CallTraceError::RegisterError(
320                "Invalid integer register".to_string(),
321            ))
322        }
323
324        ArgumentClass::Sse => {
325            if let Some(reg_index) = location.register_index {
326                if let Some(reg_value) = context.get_sse_register(reg_index) {
327                    if type_name.contains("double") {
328                        return Ok(ArgumentValue::Double(reg_value));
329                    } else if type_name.contains("float") {
330                        return Ok(ArgumentValue::Float(reg_value as f32));
331                    } else {
332                        return Ok(ArgumentValue::Double(reg_value));
333                    }
334                }
335            }
336            Err(CallTraceError::RegisterError(
337                "Invalid SSE register".to_string(),
338            ))
339        }
340
341        ArgumentClass::Memory => {
342            if let Some(stack_offset) = location.stack_offset {
343                // Read from stack - this is quite dangerous and may not always work
344                unsafe {
345                    let stack_ptr = context.rsp as *const u8;
346                    let value_ptr = stack_ptr.add(stack_offset) as *const u64;
347                    if is_valid_pointer(value_ptr as *const u8) {
348                        let value = *value_ptr;
349                        if is_pointer {
350                            Ok(ArgumentValue::Pointer(value))
351                        } else {
352                            Ok(ArgumentValue::Integer(value))
353                        }
354                    } else {
355                        Err(CallTraceError::RegisterError(
356                            "Invalid stack memory".to_string(),
357                        ))
358                    }
359                }
360            } else {
361                Err(CallTraceError::RegisterError("No stack offset".to_string()))
362            }
363        }
364
365        _ => Err(CallTraceError::NotSupported),
366    }
367}
368
369/// Check if a pointer is likely valid for reading
370#[inline]
371fn is_valid_pointer(ptr: *const u8) -> bool {
372    if ptr.is_null() {
373        return false;
374    }
375
376    let addr = ptr as usize;
377
378    // Basic sanity checks
379    if addr < 0x1000 {
380        return false; // Null page
381    }
382
383    if addr >= 0xffff_8000_0000_0000 {
384        return false; // Kernel space
385    }
386
387    true
388}
389
390/// Safely read a null-terminated string from memory
391fn read_string_safe(ptr: *const u8, max_len: usize) -> Result<String> {
392    if !is_valid_pointer(ptr) {
393        return Err(CallTraceError::RegisterError(
394            "Invalid string pointer".to_string(),
395        ));
396    }
397
398    unsafe {
399        let mut bytes = Vec::new();
400        let mut current = ptr;
401
402        for _ in 0..max_len {
403            let byte = *current;
404            if byte == 0 {
405                break;
406            }
407            bytes.push(byte);
408            current = current.add(1);
409        }
410
411        String::from_utf8(bytes)
412            .map_err(|_| CallTraceError::RegisterError("Invalid UTF-8 string".to_string()))
413    }
414}
415
416/// Enhanced argument extraction with DWARF type information
417#[inline]
418pub fn extract_argument_with_type_info(
419    context: &RegisterContext,
420    location: &ArgumentLocation,
421    type_info: &TypeInfo,
422    param_info: &ParameterInfo,
423) -> Result<ArgumentValue> {
424    // Fast path: handle null pointers for integer registers
425    if location.class == ArgumentClass::Integer && type_info.is_pointer {
426        if let Some(reg_index) = location.register_index {
427            if let Some(reg_value) = context.get_integer_register(reg_index) {
428                if reg_value == 0 {
429                    return Ok(ArgumentValue::Null);
430                }
431            }
432        }
433    }
434
435    // Optimized dispatch based on type information using match for better branch prediction
436    match (
437        type_info.is_array,
438        type_info.is_struct,
439        type_info.is_pointer,
440    ) {
441        (true, _, _) => extract_array_argument(context, location, type_info, param_info),
442        (false, true, _) => extract_struct_argument(context, location, type_info, param_info),
443        (false, false, true) => extract_pointer_argument(context, location, type_info, param_info),
444        _ => extract_basic_argument(context, location, type_info, param_info),
445    }
446}
447
448/// Extract array argument with element analysis
449fn extract_array_argument(
450    context: &RegisterContext,
451    location: &ArgumentLocation,
452    type_info: &TypeInfo,
453    _param_info: &ParameterInfo,
454) -> Result<ArgumentValue> {
455    let array_size = type_info.array_size.unwrap_or(0) as usize;
456    let element_size = if let Some(ref base_type) = type_info.base_type {
457        base_type.size.unwrap_or(1) as usize
458    } else {
459        1
460    };
461
462    // For arrays, we need to read from memory (usually stack or pointer)
463    match location.class {
464        ArgumentClass::Integer | ArgumentClass::Memory => {
465            let data_ptr = if location.class == ArgumentClass::Integer {
466                // Array passed as pointer
467                if let Some(reg_index) = location.register_index {
468                    if let Some(ptr_value) = context.get_integer_register(reg_index) {
469                        ptr_value as *const u8
470                    } else {
471                        return Ok(ArgumentValue::Unknown {
472                            type_name: type_info.name.clone(),
473                            raw_data: vec![],
474                            error: Some("Failed to read array pointer register".to_string()),
475                        });
476                    }
477                } else {
478                    return Ok(ArgumentValue::Unknown {
479                        type_name: type_info.name.clone(),
480                        raw_data: vec![],
481                        error: Some("No register for array pointer".to_string()),
482                    });
483                }
484            } else {
485                // Array on stack
486                if let Some(stack_offset) = location.stack_offset {
487                    unsafe { (context.rsp as *const u8).add(stack_offset) }
488                } else {
489                    return Ok(ArgumentValue::Unknown {
490                        type_name: type_info.name.clone(),
491                        raw_data: vec![],
492                        error: Some("No stack offset for array".to_string()),
493                    });
494                }
495            };
496
497            if !is_valid_pointer(data_ptr) {
498                return Ok(ArgumentValue::Unknown {
499                    type_name: type_info.name.clone(),
500                    raw_data: vec![],
501                    error: Some("Invalid array pointer".to_string()),
502                });
503            }
504
505            // Extract array elements (limit to reasonable size)
506            let max_elements = std::cmp::min(array_size, 64);
507            let mut elements = Vec::new();
508
509            for i in 0..max_elements {
510                unsafe {
511                    let element_ptr = data_ptr.add(i * element_size);
512                    if !is_valid_pointer(element_ptr) {
513                        break;
514                    }
515
516                    let element_value = extract_primitive_from_memory(
517                        element_ptr,
518                        element_size,
519                        type_info
520                            .base_type
521                            .as_ref()
522                            .map(|t| t.name.as_str())
523                            .unwrap_or("unknown"),
524                    )?;
525                    elements.push(element_value);
526                }
527            }
528
529            Ok(ArgumentValue::Array {
530                element_type: type_info
531                    .base_type
532                    .as_ref()
533                    .map(|t| t.name.clone())
534                    .unwrap_or_else(|| "unknown".to_string()),
535                elements,
536                count: array_size,
537                element_size,
538            })
539        }
540        _ => Ok(ArgumentValue::Unknown {
541            type_name: type_info.name.clone(),
542            raw_data: vec![],
543            error: Some("Unsupported array argument class".to_string()),
544        }),
545    }
546}
547
548/// Extract struct argument with member analysis
549fn extract_struct_argument(
550    context: &RegisterContext,
551    location: &ArgumentLocation,
552    type_info: &TypeInfo,
553    _param_info: &ParameterInfo,
554) -> Result<ArgumentValue> {
555    let struct_size = type_info.size.unwrap_or(0) as usize;
556
557    // Read raw struct data
558    let _raw_data = match location.class {
559        ArgumentClass::Integer => {
560            // Small struct passed by value in register
561            if let Some(reg_index) = location.register_index {
562                if let Some(reg_value) = context.get_integer_register(reg_index) {
563                    reg_value.to_le_bytes().to_vec()
564                } else {
565                    return Ok(ArgumentValue::Unknown {
566                        type_name: type_info.name.clone(),
567                        raw_data: vec![],
568                        error: Some("Failed to read struct register".to_string()),
569                    });
570                }
571            } else {
572                return Ok(ArgumentValue::Unknown {
573                    type_name: type_info.name.clone(),
574                    raw_data: vec![],
575                    error: Some("No register for struct".to_string()),
576                });
577            }
578        }
579        ArgumentClass::Memory => {
580            // Struct on stack
581            if let Some(stack_offset) = location.stack_offset {
582                unsafe {
583                    let struct_ptr = (context.rsp as *const u8).add(stack_offset);
584                    if is_valid_pointer(struct_ptr) && struct_size <= 256 {
585                        std::slice::from_raw_parts(struct_ptr, struct_size).to_vec()
586                    } else {
587                        return Ok(ArgumentValue::Unknown {
588                            type_name: type_info.name.clone(),
589                            raw_data: vec![],
590                            error: Some("Invalid struct memory".to_string()),
591                        });
592                    }
593                }
594            } else {
595                return Ok(ArgumentValue::Unknown {
596                    type_name: type_info.name.clone(),
597                    raw_data: vec![],
598                    error: Some("No stack offset for struct".to_string()),
599                });
600            }
601        }
602        _ => {
603            return Ok(ArgumentValue::Unknown {
604                type_name: type_info.name.clone(),
605                raw_data: vec![],
606                error: Some("Unsupported struct argument class".to_string()),
607            });
608        }
609    };
610
611    // For now, return basic struct info
612    // TODO: In future, parse DWARF member information
613    Ok(ArgumentValue::Struct {
614        type_name: type_info.name.clone(),
615        members: vec![], // TODO: Extract member info from DWARF
616        size: struct_size,
617    })
618}
619
620/// Extract pointer argument with enhanced dereferencing
621fn extract_pointer_argument(
622    context: &RegisterContext,
623    location: &ArgumentLocation,
624    type_info: &TypeInfo,
625    _param_info: &ParameterInfo,
626) -> Result<ArgumentValue> {
627    // Get pointer value
628    let pointer_value = match location.class {
629        ArgumentClass::Integer => {
630            if let Some(reg_index) = location.register_index {
631                context.get_integer_register(reg_index).unwrap_or(0)
632            } else {
633                return Ok(ArgumentValue::Unknown {
634                    type_name: type_info.name.clone(),
635                    raw_data: vec![],
636                    error: Some("No register for pointer".to_string()),
637                });
638            }
639        }
640        ArgumentClass::Memory => {
641            if let Some(stack_offset) = location.stack_offset {
642                unsafe {
643                    let ptr_ptr = (context.rsp as *const u8).add(stack_offset) as *const u64;
644                    if is_valid_pointer(ptr_ptr as *const u8) {
645                        *ptr_ptr
646                    } else {
647                        return Ok(ArgumentValue::Unknown {
648                            type_name: type_info.name.clone(),
649                            raw_data: vec![],
650                            error: Some("Invalid pointer memory".to_string()),
651                        });
652                    }
653                }
654            } else {
655                return Ok(ArgumentValue::Unknown {
656                    type_name: type_info.name.clone(),
657                    raw_data: vec![],
658                    error: Some("No stack offset for pointer".to_string()),
659                });
660            }
661        }
662        _ => {
663            return Ok(ArgumentValue::Unknown {
664                type_name: type_info.name.clone(),
665                raw_data: vec![],
666                error: Some("Unsupported pointer argument class".to_string()),
667            });
668        }
669    };
670
671    if pointer_value == 0 {
672        return Ok(ArgumentValue::Null);
673    }
674
675    // Try to dereference based on pointed-to type
676    if let Some(ref base_type) = type_info.base_type {
677        if base_type.name.contains("char") {
678            // String pointer
679            // Fall through to pointer value on error
680            if let Ok(s) = read_string_safe(pointer_value as *const u8, 256) {
681                return Ok(ArgumentValue::String(s));
682            }
683        }
684        // TODO: Handle other pointed-to types (struct*, int*, etc.)
685    }
686
687    Ok(ArgumentValue::Pointer(pointer_value))
688}
689
690/// Extract basic argument (non-complex types)
691#[inline]
692fn extract_basic_argument(
693    context: &RegisterContext,
694    location: &ArgumentLocation,
695    type_info: &TypeInfo,
696    _param_info: &ParameterInfo,
697) -> Result<ArgumentValue> {
698    // Use existing extract_argument function
699    extract_argument(context, location, &type_info.name, type_info.is_pointer)
700}
701
702/// Extract primitive value from memory
703#[inline]
704unsafe fn extract_primitive_from_memory(
705    ptr: *const u8,
706    size: usize,
707    type_name: &str,
708) -> Result<ArgumentValue> {
709    if !is_valid_pointer(ptr) {
710        return Err(CallTraceError::RegisterError(
711            "Invalid memory pointer".to_string(),
712        ));
713    }
714
715    // Optimized match with frequently used sizes first
716    match size {
717        8 => {
718            if type_name.contains("double") {
719                let value = *(ptr as *const f64);
720                Ok(ArgumentValue::Double(value))
721            } else {
722                let value = *(ptr as *const u64);
723                Ok(ArgumentValue::Integer(value))
724            }
725        }
726        4 => {
727            if type_name.contains("float") {
728                let value = *(ptr as *const f32);
729                Ok(ArgumentValue::Float(value))
730            } else {
731                let value = *(ptr as *const u32) as u64;
732                Ok(ArgumentValue::Integer(value))
733            }
734        }
735        1 => {
736            let value = *ptr as u64;
737            Ok(ArgumentValue::Integer(value))
738        }
739        2 => {
740            let value = *(ptr as *const u16) as u64;
741            Ok(ArgumentValue::Integer(value))
742        }
743        _ => {
744            // Unknown size, read as raw bytes (limit to 64 bytes for safety)
745            let read_size = std::cmp::min(size, 64);
746            let data = std::slice::from_raw_parts(ptr, read_size).to_vec();
747            Ok(ArgumentValue::Raw(data))
748        }
749    }
750}
751
752/// Capture return values from function exit context
753///
754/// # Safety
755/// This function uses inline assembly and must be called at function exit
756/// before return registers are modified.
757pub unsafe fn capture_return_values() -> Result<RegisterContext> {
758    let mut context = RegisterContext {
759        rdi: 0,
760        rsi: 0,
761        rdx: 0,
762        rcx: 0,
763        r8: 0,
764        r9: 0,
765        rsp: 0,
766        rbp: 0,
767        rip: 0,
768        rflags: 0,
769        xmm: [0.0; 8],
770        valid: false,
771        timestamp: 0,
772        return_rax: 0,
773        return_xmm0: 0.0,
774        return_valid: false,
775    };
776
777    #[cfg(target_arch = "x86_64")]
778    {
779        // Capture return value registers
780        asm!(
781            "mov {rax}, rax",        // Capture RAX (integer/pointer return value)
782            "movsd {xmm0}, xmm0",    // Capture XMM0 (floating point return value)
783            "mov {rsp}, rsp",        // Also capture stack pointer for context
784            rax = out(reg) context.return_rax,
785            xmm0 = out(xmm_reg) context.return_xmm0,
786            rsp = out(reg) context.rsp,
787            options(nostack, preserves_flags)
788        );
789
790        context.return_valid = true;
791        context.valid = true;
792        context.timestamp = std::time::SystemTime::now()
793            .duration_since(std::time::UNIX_EPOCH)
794            .unwrap_or_default()
795            .as_micros() as u64;
796
797        Ok(context)
798    }
799
800    #[cfg(not(target_arch = "x86_64"))]
801    {
802        Err(CallTraceError::NotSupported)
803    }
804}
805
806/// Extract return value based on function type information
807#[inline]
808pub fn extract_return_value(
809    context: &RegisterContext,
810    return_type: Option<&TypeInfo>,
811) -> Option<ArgumentValue> {
812    if !context.return_valid {
813        return None;
814    }
815
816    // If we don't have type information, return raw RAX value
817    let type_info = return_type?;
818
819    // Handle different return types according to x86_64 ABI
820    if type_info.name.contains("void") {
821        None // void functions don't return values
822    } else if type_info.name.contains("float") && type_info.size == Some(4) {
823        // float return value in XMM0
824        Some(ArgumentValue::Float(context.return_xmm0 as f32))
825    } else if type_info.name.contains("double")
826        || (type_info.name.contains("float") && type_info.size == Some(8))
827    {
828        // double return value in XMM0
829        Some(ArgumentValue::Double(context.return_xmm0))
830    } else if type_info.is_pointer {
831        // Pointer return value in RAX
832        if context.return_rax == 0 {
833            Some(ArgumentValue::Null)
834        } else {
835            // Try to dereference if it's a string pointer
836            if let Some(ref base_type) = type_info.base_type {
837                if base_type.name.contains("char") {
838                    // Fall through to pointer value on error
839                    if let Ok(s) = read_string_safe(context.return_rax as *const u8, 256) {
840                        return Some(ArgumentValue::String(s));
841                    }
842                }
843            }
844            Some(ArgumentValue::Pointer(context.return_rax))
845        }
846    } else if type_info.size.is_some_and(|s| s <= 8) {
847        // Integer return value in RAX (up to 64-bit)
848        Some(ArgumentValue::Integer(context.return_rax))
849    } else {
850        // Large structures or unknown types - return raw RAX value
851        Some(ArgumentValue::Unknown {
852            type_name: type_info.name.clone(),
853            raw_data: context.return_rax.to_le_bytes().to_vec(),
854            error: Some("Complex return type not fully supported".to_string()),
855        })
856    }
857}