Skip to main content

libdd_libunwind_sys/
libunwind_x86_64.rs

1// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4// Context is platform ucontext_t (from libc)
5pub type UnwContext = libc::ucontext_t;
6
7pub type UnwWord = u64;
8
9// Opaque cursor structure
10#[repr(C)]
11#[derive(Debug, Copy, Clone)]
12pub struct UnwCursor {
13    pub opaque: [UnwWord; 127],
14}
15
16// Opaque address space handle (unw_addr_space_t)
17pub type UnwAddrSpaceT = *mut libc::c_void;
18
19#[repr(C)]
20pub struct UnwAccessors;
21
22// This is a subset of the libunwind API.
23
24extern "C" {
25    #[link_name = "_ULx86_64_init_local2"]
26    pub fn unw_init_local2(cursor: *mut UnwCursor, context: *mut UnwContext, flag: i32) -> i32;
27    #[link_name = "_ULx86_64_step"]
28    pub fn unw_step(cursor: *mut UnwCursor) -> i32;
29    #[link_name = "_ULx86_64_get_reg"]
30    pub fn unw_get_reg(cursor: *mut UnwCursor, reg: i32, valp: *mut UnwWord) -> i32;
31    #[link_name = "_ULx86_64_get_proc_name"]
32    pub fn unw_get_proc_name(
33        cursor: *mut UnwCursor,
34        name: *mut libc::c_char,
35        len: usize,
36        offset: *mut u64,
37    ) -> i32;
38    #[link_name = "unw_backtrace2"]
39    pub fn unw_backtrace2(
40        buffer: *mut *mut ::std::os::raw::c_void,
41        size: i32,
42        context: *mut UnwContext,
43        flag: i32,
44    ) -> i32;
45}
46
47// Remote unwinding API. Uses _Ux86_64_* symbols from libunwind-x86_64
48// because the _ULx86_64_* dont have remote support
49#[allow(improper_ctypes)]
50extern "C" {
51    #[link_name = "_Ux86_64_init_remote"]
52    pub fn unw_init_remote(
53        cursor: *mut UnwCursor,
54        addr_space: UnwAddrSpaceT,
55        arg: *mut libc::c_void,
56    ) -> i32;
57    #[link_name = "_Ux86_64_step"]
58    pub fn unw_step_remote(cursor: *mut UnwCursor) -> i32;
59    #[link_name = "_Ux86_64_get_reg"]
60    pub fn unw_get_reg_remote(cursor: *mut UnwCursor, reg: i32, valp: *mut UnwWord) -> i32;
61    #[link_name = "_Ux86_64_get_proc_name"]
62    pub fn unw_get_proc_name_remote(
63        cursor: *mut UnwCursor,
64        name: *mut libc::c_char,
65        len: usize,
66        offset: *mut UnwWord,
67    ) -> i32;
68    #[link_name = "_Ux86_64_create_addr_space"]
69    pub fn unw_create_addr_space(accessors: *mut UnwAccessors, byteorder: i32) -> UnwAddrSpaceT;
70    #[link_name = "_Ux86_64_destroy_addr_space"]
71    pub fn unw_destroy_addr_space(addr_space: UnwAddrSpaceT);
72    pub fn _UPT_create(pid: libc::pid_t) -> *mut libc::c_void;
73    pub fn _UPT_destroy(upt_info: *mut libc::c_void);
74    pub static _UPT_accessors: UnwAccessors;
75}
76
77// x86_64 register definitions for libunwind
78pub const UNW_REG_IP: i32 = 16; // Instruction Pointer
79pub const UNW_REG_SP: i32 = 17; // Stack Pointer
80pub const UNW_REG_FP: i32 = 15; // Frame Pointer
81pub const UNW_INIT_LOCAL_ONLY_IP: i32 = 1;
82
83/// Saves the current CPU context into `uc_mcontext.gregs`.
84/// gregs layout: [R8, R9, R10, R11, R12, R13, R14, R15,
85///                RDI, RSI, RBP, RBX, RDX, RAX, RCX, RSP, RIP, ...]
86///
87/// # Safety
88/// `context` must be a valid, non-null pointer to a zeroed or initialized `UnwContext`.
89// This is only for testing purposes and allow the tests to work with libc and musl-libc
90#[cfg(test)]
91#[inline(always)]
92pub unsafe fn getcontext(context: *mut UnwContext) -> i32 {
93    let gregs = core::ptr::addr_of_mut!((*context).uc_mcontext.gregs) as u64;
94    core::arch::asm!(
95        "mov [rdi], r8",
96        "mov [rdi + 8], r9",
97        "mov [rdi + 16], r10",
98        "mov [rdi + 24], r11",
99        "mov [rdi + 32], r12",
100        "mov [rdi + 40], r13",
101        "mov [rdi + 48], r14",
102        "mov [rdi + 56], r15",
103        "mov [rdi + 64], rdi",
104        "mov [rdi + 72], rsi",
105        "mov [rdi + 80], rbp",
106        "mov [rdi + 88], rbx",
107        "mov [rdi + 96], rdx",
108        "mov [rdi + 104], rax",
109        "mov [rdi + 112], rcx",
110        "mov [rdi + 120], rsp",
111        "lea rax, [rip + 2f]",
112        "mov [rdi + 128], rax",
113        "2:",
114        inout("rdi") gregs => _,
115        out("rax") _,
116        options(nostack, preserves_flags),
117    );
118    0
119}