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// This is a subset of the libunwind API.
17
18extern "C" {
19    #[link_name = "_ULx86_64_init_local2"]
20    pub fn unw_init_local2(cursor: *mut UnwCursor, context: *mut UnwContext, flag: i32) -> i32;
21    #[link_name = "_ULx86_64_step"]
22    pub fn unw_step(cursor: *mut UnwCursor) -> i32;
23    #[link_name = "_ULx86_64_get_reg"]
24    pub fn unw_get_reg(cursor: *mut UnwCursor, reg: i32, valp: *mut UnwWord) -> i32;
25    #[link_name = "_ULx86_64_get_proc_name"]
26    pub fn unw_get_proc_name(
27        cursor: *mut UnwCursor,
28        name: *mut libc::c_char,
29        len: usize,
30        offset: *mut u64,
31    ) -> i32;
32    #[link_name = "unw_backtrace2"]
33    pub fn unw_backtrace2(
34        buffer: *mut *mut ::std::os::raw::c_void,
35        size: i32,
36        context: *mut UnwContext,
37        flag: i32,
38    ) -> i32;
39}
40
41// x86_64 register definitions for libunwind
42pub const UNW_REG_IP: i32 = 16; // Instruction Pointer
43pub const UNW_REG_SP: i32 = 17; // Stack Pointer
44pub const UNW_REG_FP: i32 = 15; // Frame Pointer
45pub const UNW_INIT_LOCAL_ONLY_IP: i32 = 1;
46
47/// Saves the current CPU context into `uc_mcontext.gregs`.
48/// gregs layout: [R8, R9, R10, R11, R12, R13, R14, R15,
49///                RDI, RSI, RBP, RBX, RDX, RAX, RCX, RSP, RIP, ...]
50///
51/// # Safety
52/// `context` must be a valid, non-null pointer to a zeroed or initialized `UnwContext`.
53// This is only for testing purposes and allow the tests to work with libc and musl-libc
54#[cfg(test)]
55#[inline(always)]
56pub unsafe fn getcontext(context: *mut UnwContext) -> i32 {
57    let gregs = core::ptr::addr_of_mut!((*context).uc_mcontext.gregs) as u64;
58    core::arch::asm!(
59        "mov [rdi], r8",
60        "mov [rdi + 8], r9",
61        "mov [rdi + 16], r10",
62        "mov [rdi + 24], r11",
63        "mov [rdi + 32], r12",
64        "mov [rdi + 40], r13",
65        "mov [rdi + 48], r14",
66        "mov [rdi + 56], r15",
67        "mov [rdi + 64], rdi",
68        "mov [rdi + 72], rsi",
69        "mov [rdi + 80], rbp",
70        "mov [rdi + 88], rbx",
71        "mov [rdi + 96], rdx",
72        "mov [rdi + 104], rax",
73        "mov [rdi + 112], rcx",
74        "mov [rdi + 120], rsp",
75        "lea rax, [rip + 2f]",
76        "mov [rdi + 128], rax",
77        "2:",
78        inout("rdi") gregs => _,
79        out("rax") _,
80        options(nostack, preserves_flags),
81    );
82    0
83}