1use crate::dwarf_analyzer::{ParameterInfo, TypeInfo};
7use crate::error::{CallTraceError, Result};
8use serde::{Deserialize, Serialize};
9use std::arch::asm;
10
11#[repr(C)]
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct RegisterContext {
15 pub rdi: u64, pub rsi: u64, pub rdx: u64, pub rcx: u64, pub r8: u64, pub r9: u64, pub rsp: u64, pub rbp: u64, pub xmm: [f64; 8], pub rip: u64, pub rflags: u64, pub valid: bool,
36 pub timestamp: u64,
37
38 pub return_rax: u64, pub return_xmm0: f64, pub return_valid: bool, }
43
44#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
46pub enum ArgumentClass {
47 Integer, Sse, Memory, X87, ComplexX87, NoClass, }
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ArgumentLocation {
58 pub class: ArgumentClass,
59 pub register_index: Option<usize>, pub stack_offset: Option<usize>, pub size: usize, }
63
64#[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#[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 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 Null,
105 Unknown {
106 type_name: String,
107 raw_data: Vec<u8>,
108 error: Option<String>,
109 },
110}
111
112#[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 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 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 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 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 #[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 #[inline]
233 pub fn get_sse_register(&self, index: usize) -> Option<f64> {
234 self.xmm.get(index).copied()
235 }
236}
237
238#[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 if size <= 8 {
246 if type_name.contains("float") || type_name.contains("double") {
247 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 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 ArgumentLocation {
284 class: ArgumentClass::Memory,
285 register_index: None,
286 stack_offset: Some(arg_index * 8 + 8),
287 size,
288 }
289 }
290}
291
292#[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 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 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#[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 if addr < 0x1000 {
380 return false; }
382
383 if addr >= 0xffff_8000_0000_0000 {
384 return false; }
386
387 true
388}
389
390fn 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#[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 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 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
448fn 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 match location.class {
464 ArgumentClass::Integer | ArgumentClass::Memory => {
465 let data_ptr = if location.class == ArgumentClass::Integer {
466 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 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 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
548fn 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 let _raw_data = match location.class {
559 ArgumentClass::Integer => {
560 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 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 Ok(ArgumentValue::Struct {
614 type_name: type_info.name.clone(),
615 members: vec![], size: struct_size,
617 })
618}
619
620fn extract_pointer_argument(
622 context: &RegisterContext,
623 location: &ArgumentLocation,
624 type_info: &TypeInfo,
625 _param_info: &ParameterInfo,
626) -> Result<ArgumentValue> {
627 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 if let Some(ref base_type) = type_info.base_type {
677 if base_type.name.contains("char") {
678 if let Ok(s) = read_string_safe(pointer_value as *const u8, 256) {
681 return Ok(ArgumentValue::String(s));
682 }
683 }
684 }
686
687 Ok(ArgumentValue::Pointer(pointer_value))
688}
689
690#[inline]
692fn extract_basic_argument(
693 context: &RegisterContext,
694 location: &ArgumentLocation,
695 type_info: &TypeInfo,
696 _param_info: &ParameterInfo,
697) -> Result<ArgumentValue> {
698 extract_argument(context, location, &type_info.name, type_info.is_pointer)
700}
701
702#[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 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 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
752pub 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 asm!(
781 "mov {rax}, rax", "movsd {xmm0}, xmm0", "mov {rsp}, rsp", 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#[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 let type_info = return_type?;
818
819 if type_info.name.contains("void") {
821 None } else if type_info.name.contains("float") && type_info.size == Some(4) {
823 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 Some(ArgumentValue::Double(context.return_xmm0))
830 } else if type_info.is_pointer {
831 if context.return_rax == 0 {
833 Some(ArgumentValue::Null)
834 } else {
835 if let Some(ref base_type) = type_info.base_type {
837 if base_type.name.contains("char") {
838 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 Some(ArgumentValue::Integer(context.return_rax))
849 } else {
850 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}