diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 827d6168be..efc0feb1b6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,6 +14,7 @@ .gitlab-ci.yml @DataDog/apm-common-components-core .gitlab/benchmarks.yml @DataDog/apm-common-components-core .gitlab/fuzz.yml @DataDog/chaos-platform +.gitmodules @DataDog/libdatadog benchmark/ @DataDog/apm-common-components-core bin_tests/ @DataDog/libdatadog-profiling build-common/ @DataDog/apm-common-components-core @@ -44,6 +45,7 @@ libdd-data-pipeline*/ @DataDog/libdatadog-apm libdd-ddsketch*/ @DataDog/libdatadog-apm @DataDog/apm-common-components-core libdd-dogstatsd-client @DataDog/apm-common-components-core libdd-library-config*/ @DataDog/apm-sdk-capabilities +libdd-libunwind*/ @DataDog/libdatadog-profiling libdd-log*/ @DataDog/apm-common-components-core libdd-profiling*/ @DataDog/libdatadog-profiling libdd-telemetry*/ @DataDog/apm-common-components-core diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c37c9c663d..f9375476f5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -26,6 +26,8 @@ jobs: docker-images: true swap-storage: true - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Install Rust run: rustup install nightly-2026-02-08 && rustup default nightly-2026-02-08 - name: Install cargo-llvm-cov diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b33af83d94..a74cdabfff 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Run actionlint uses: devops-actions/actionlint@c6744a34774e4e1c1df0ff66bdb07ec7ee480ca0 # 0.1.9 with: @@ -22,6 +24,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Install nightly-2026-02-08 toolchain and rustfmt run: rustup install nightly-2026-02-08 && rustup default nightly-2026-02-08 && rustup component add rustfmt - name: Cache [rust] @@ -41,6 +45,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Install ${{ matrix.rust_version }} toolchain and clippy run: rustup install ${{ matrix.rust_version }} && rustup default ${{ matrix.rust_version }} && rustup component add clippy - name: Cache [rust] diff --git a/.github/workflows/miri.yml b/.github/workflows/miri.yml index ddb8bdbe69..51860ea42e 100644 --- a/.github/workflows/miri.yml +++ b/.github/workflows/miri.yml @@ -14,6 +14,8 @@ jobs: PROPTEST_CASES: 1 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Set up Rust run: | set -e diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 258cefff39..c7f3b0aac4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,8 @@ jobs: swap-storage: true - name: Checkout sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Install Rust ${{ matrix.rust_version }} if: matrix.rust_version != '' run: rustup install ${{ matrix.rust_version }} && rustup default ${{ matrix.rust_version }} @@ -155,6 +157,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Setup output dir shell: bash run: | @@ -297,6 +301,8 @@ jobs: swap-storage: true - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Cache [rust] uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # 2.8.1 with: @@ -321,6 +327,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + submodules: recursive - name: Set up Docker Buildx uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # 3.10.0 with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..72f1fea8c3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libdd-libunwind-sys/libunwind"] + path = libdd-libunwind-sys/libunwind + url = https://github.com/DataDog/libunwind.git + branch = kevin/v1.8.1-custom-2 diff --git a/Cargo.lock b/Cargo.lock index 519b6b857f..caeec13d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3187,6 +3187,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "libdd-libunwind" +version = "28.0.2" +dependencies = [ + "cc", + "libc", + "paste", +] + [[package]] name = "libdd-log" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 23cd1b87fc..437648e77f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ members = [ "libdd-tinybytes", "libdd-dogstatsd-client", "libdd-log", - "libdd-log-ffi", + "libdd-log-ffi", "libdd-libunwind-sys", ] # https://doc.rust-lang.org/cargo/reference/resolver.html diff --git a/LICENSE-3rdparty.yml b/LICENSE-3rdparty.yml index 1425aa8508..ab90880dd6 100644 --- a/LICENSE-3rdparty.yml +++ b/LICENSE-3rdparty.yml @@ -1,4 +1,4 @@ -root_name: builder, build_common, tools, libdd-alloc, libdd-crashtracker, libdd-common, libdd-telemetry, libdd-ddsketch, libdd-crashtracker-ffi, libdd-common-ffi, datadog-ffe, datadog-ffe-ffi, datadog-ipc, datadog-ipc-macros, libdd-tinybytes, tarpc, tarpc-plugins, spawn_worker, cc_utils, libdd-library-config, libdd-library-config-ffi, datadog-live-debugger, libdd-data-pipeline, libdd-dogstatsd-client, libdd-trace-protobuf, libdd-trace-stats, libdd-trace-utils, libdd-trace-normalization, libdd-log, datadog-live-debugger-ffi, libdd-profiling, libdd-profiling-protobuf, libdd-profiling-ffi, libdd-data-pipeline-ffi, libdd-ddsketch-ffi, libdd-log-ffi, libdd-telemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, datadog-remote-config, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, libdd-trace-obfuscation, datadog-tracer-flare, sidecar_mockgen, test_spawn_from_lib +root_name: builder, build_common, tools, libdd-alloc, libdd-crashtracker, libdd-common, libdd-telemetry, libdd-ddsketch, libdd-crashtracker-ffi, libdd-common-ffi, datadog-ffe, datadog-ffe-ffi, datadog-ipc, datadog-ipc-macros, libdd-tinybytes, tarpc, tarpc-plugins, spawn_worker, cc_utils, libdd-library-config, libdd-library-config-ffi, datadog-live-debugger, libdd-data-pipeline, libdd-dogstatsd-client, libdd-trace-protobuf, libdd-trace-stats, libdd-trace-utils, libdd-trace-normalization, libdd-log, datadog-live-debugger-ffi, libdd-profiling, libdd-profiling-protobuf, libdd-profiling-ffi, libdd-data-pipeline-ffi, libdd-ddsketch-ffi, libdd-log-ffi, libdd-telemetry-ffi, symbolizer-ffi, datadog-profiling-replayer, datadog-remote-config, datadog-sidecar, datadog-sidecar-macros, datadog-sidecar-ffi, libdd-trace-obfuscation, datadog-tracer-flare, sidecar_mockgen, test_spawn_from_lib, bin_tests third_party_libraries: - package_name: addr2line package_version: 0.24.2 diff --git a/libdd-libunwind-sys/Cargo.toml b/libdd-libunwind-sys/Cargo.toml new file mode 100644 index 0000000000..02bb3f6ca7 --- /dev/null +++ b/libdd-libunwind-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "libdd-libunwind" +edition.workspace = true +version.workspace = true +rust-version.workspace = true +license.workspace = true +publish = false +links = "unwind" # Tells Cargo this crate links to libunwind (prevents multiple builds) + +[lib] +crate-type = ["lib"] +bench = false + +[dependencies] +paste = "1.0" +libc = "0.2" + +[build-dependencies] +cc = "1.0" diff --git a/libdd-libunwind-sys/build.rs b/libdd-libunwind-sys/build.rs new file mode 100644 index 0000000000..d2ddeb779d --- /dev/null +++ b/libdd-libunwind-sys/build.rs @@ -0,0 +1,137 @@ +// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +fn main() { + #[cfg(target_os = "linux")] + linux::main(); +} + +#[cfg(target_os = "linux")] +mod linux { + use std::env; + use std::path::PathBuf; + + pub(crate) fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let build_dir = out_dir.join("libunwind_build"); + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let libunwind_dir = std::path::Path::new(&manifest_dir).join("libunwind"); + + if !libunwind_dir.join("src").exists() { + panic!( + "libunwind source not found at {}. \ + Did you forget to run `git submodule update --init`?", + libunwind_dir.display() + ); + } + + // Check if libunwind submodule is initialized + if !libunwind_dir.exists() || std::fs::read_dir(&libunwind_dir).unwrap().next().is_none() { + panic!( + "libunwind submodule not initialized!\n\ + Run: git submodule update --init --recursive\n\ + \n\ + For CI, ensure your workflow checks out submodules:\n\ + - GitHub Actions: add 'submodules: recursive' to actions/checkout\n\ + - GitLab CI: add 'GIT_SUBMODULE_STRATEGY: recursive'\n\ + \n\ + Directory checked: {}", + libunwind_dir.display() + ); + } + + std::fs::create_dir_all(&build_dir).unwrap(); + + let lib_file = build_dir.join("src/.libs/libunwind.a"); + + // Only build if library doesn't exist + if !lib_file.exists() { + eprintln!("Building libunwind from source..."); + + // Only run autoreconf if configure doesn't exist + let configure_script = libunwind_dir.join("configure"); + if !configure_script.exists() { + eprintln!("Running autoreconf..."); + let status = std::process::Command::new("sh") + .current_dir(&libunwind_dir) + .args(["-c", "autoreconf -i"]) + .status() + .expect( + "Failed to run autoreconf. Install with: apt install autoconf automake libtool", + ); + + if !status.success() { + panic!("autoreconf failed with exit code: {:?}", status.code()); + } + } + + eprintln!("Configuring libunwind..."); + let status = std::process::Command::new("sh") + .current_dir(&build_dir) + .args([ + "-c", + &format!( + r"{}/configure CXXFLAGS=-fPIC\ -D_GLIBCXX_USE_CXX11_ABI=0\ -O3\ -g CFLAGS=-fPIC\ -O3\ -g --disable-shared --enable-static --disable-minidebuginfo --disable-zlibdebuginfo --disable-tests", + libunwind_dir.display() + ) + ]) + .status() + .expect("Failed to run configure"); + + if !status.success() { + panic!( + "libunwind configure failed with exit code: {:?}", + status.code() + ); + } + + eprintln!("Building libunwind..."); + let status = std::process::Command::new("sh") + .current_dir(&build_dir) + .args(["-c", "make -j$(nproc)"]) + .status() + .expect("Failed to run make"); + + if !status.success() { + panic!("libunwind make failed with exit code: {:?}", status.code()); + } + + // Verify the library was actually created + if !lib_file.exists() { + panic!( + "libunwind.a was not created at expected location: {}", + lib_file.display() + ); + } + + eprintln!("libunwind built successfully at {}", lib_file.display()); + } else { + eprintln!("Using cached libunwind build"); + } + + let lib_path = build_dir.join("src/.libs"); + let include_path = build_dir.join("include"); + + #[cfg(target_arch = "x86_64")] + let arch = "x86_64"; + #[cfg(target_arch = "aarch64")] + let arch = "aarch64"; + + // Link directives for this crate + println!("cargo:rustc-link-search=native={}", lib_path.display()); + println!("cargo:rustc-link-lib=static=unwind"); + println!("cargo:rustc-link-lib=static=unwind-{}", arch); + + // Export paths to dependent crates via DEP_UNWIND_* environment variables + println!("cargo:include={}", include_path.display()); + println!("cargo:lib={}", lib_path.display()); + println!("cargo:libdir={}", lib_path.display()); + println!("cargo:root={}", build_dir.display()); + + eprintln!("libunwind library ready at {}", lib_path.display()); + + println!("cargo:rerun-if-changed={}/src", libunwind_dir.display()); + println!("cargo:rerun-if-changed={}/include", libunwind_dir.display()); + println!("cargo:rerun-if-changed=build.rs"); + } +} diff --git a/libdd-libunwind-sys/libunwind b/libdd-libunwind-sys/libunwind new file mode 160000 index 0000000000..cc1d07281b --- /dev/null +++ b/libdd-libunwind-sys/libunwind @@ -0,0 +1 @@ +Subproject commit cc1d07281b9e034c9e088733aeb4b94ffd91db9e diff --git a/libdd-libunwind-sys/src/lib.rs b/libdd-libunwind-sys/src/lib.rs new file mode 100644 index 0000000000..d84797a0be --- /dev/null +++ b/libdd-libunwind-sys/src/lib.rs @@ -0,0 +1,128 @@ +// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +mod libunwind_x86_64; + +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +mod libunwind_aarch64; + +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +pub use libunwind_aarch64::*; +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +pub use libunwind_x86_64::*; + +#[cfg(all(test, target_os = "linux"))] +mod tests { + use super::*; + + #[test] + #[cfg_attr(miri, ignore)] // Miri cannot execute FFI calls to libunwind + fn test_basic_unwind() { + unsafe { + let mut context: UnwContext = std::mem::zeroed(); + let mut cursor: UnwCursor = std::mem::zeroed(); + + let ret = getcontext(&mut context); + assert_eq!(ret, 0, "getcontext failed"); + + // Initialize cursor + let ret = unw_init_local2(&mut cursor, &mut context, 0); + assert_eq!(ret, 0, "unw_init_local2 failed"); + + // Walk the stack + let mut frames = 0; + loop { + let ret = unw_step(&mut cursor); + if ret <= 0 { + break; + } + frames += 1; + + // Limit iterations to prevent infinite loops + if frames > 100 { + break; + } + } + + // Should have at least a few frames + assert!(frames > 0, "Expected at least one stack frame"); + } + } + + #[test] + #[cfg_attr(miri, ignore)] // Miri cannot execute FFI calls to libunwind + fn test_get_register() { + unsafe { + let mut context: UnwContext = std::mem::zeroed(); + let mut cursor: UnwCursor = std::mem::zeroed(); + + assert_eq!(getcontext(&mut context), 0); + assert_eq!(unw_init_local2(&mut cursor, &mut context, 0), 0); + + // Get instruction pointer + let mut ip: UnwWord = 0; + let ret = unw_get_reg(&mut cursor, UNW_REG_IP, &mut ip); + assert_eq!(ret, 0, "Failed to get IP register"); + assert_ne!(ip, 0, "IP should not be zero"); + + // Get stack pointer + let mut sp: UnwWord = 0; + let ret = unw_get_reg(&mut cursor, UNW_REG_SP, &mut sp); + assert_eq!(ret, 0, "Failed to get SP register"); + assert_ne!(sp, 0, "SP should not be zero"); + } + } + + #[test] + #[cfg_attr(miri, ignore)] // Miri cannot execute FFI calls to libunwind + fn test_backtrace2() { + unsafe { + let mut context: UnwContext = std::mem::zeroed(); + assert_eq!(getcontext(&mut context), 0); + + // unw_backtrace2 expects an array of void pointers + let mut frames: [*mut ::std::os::raw::c_void; 100] = [std::ptr::null_mut(); 100]; + let ret = unw_backtrace2(frames.as_mut_ptr(), 100, &mut context, 0); + + // Return value should be >= 0 (number of frames captured) + assert!(ret >= 0, "unw_backtrace2 failed with error: {}", ret); + + let frame_count = ret as usize; + assert!(frame_count > 0, "Expected at least one frame"); + + // Print captured frames + for (i, &frame) in frames.iter().enumerate().take(frame_count) { + let frame_ptr = frame as usize; + println!("Frame {}: 0x{:016x}", i, frame_ptr); + } + } + } + + #[test] + #[cfg_attr(miri, ignore)] // Miri cannot execute FFI calls to libunwind + fn test_get_proc_name() { + unsafe { + let mut context: UnwContext = std::mem::zeroed(); + let mut cursor: UnwCursor = std::mem::zeroed(); + + assert_eq!(getcontext(&mut context), 0); + assert_eq!( + unw_init_local2(&mut cursor, &mut context, UNW_INIT_LOCAL_ONLY_IP), + 0 + ); + + let mut name: [libc::c_char; 100] = [0; 100]; + let ret = unw_get_proc_name(&mut cursor, name.as_mut_ptr(), 100, std::ptr::null_mut()); + assert_eq!(ret, 0, "unw_get_proc_name failed"); + let fn_name = std::ffi::CStr::from_ptr(name.as_ptr()).to_string_lossy(); + assert!(!fn_name.is_empty(), "Name should not be empty"); + // name is managed: _ZN15libdd_libunwind5tests18test_get_proc_name17hec15ec5ad6978a00E + // we should just chekc that test_get_proc_name is part of it + assert!( + fn_name.contains("test_get_proc_name"), + "Name should contain 'test_get_proc_name'" + ); + } + } +} diff --git a/libdd-libunwind-sys/src/libunwind_aarch64.rs b/libdd-libunwind-sys/src/libunwind_aarch64.rs new file mode 100644 index 0000000000..0d778a3563 --- /dev/null +++ b/libdd-libunwind-sys/src/libunwind_aarch64.rs @@ -0,0 +1,82 @@ +// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +pub type UnwContext = libc::ucontext_t; + +pub type UnwWord = u64; + +// Opaque cursor structure +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct UnwCursor { + pub opaque: [UnwWord; 4096], +} + +extern "C" { + #[link_name = "_ULaarch64_init_local2"] + pub fn unw_init_local2(cursor: *mut UnwCursor, context: *mut UnwContext, flag: i32) -> i32; + #[link_name = "_ULaarch64_step"] + pub fn unw_step(cursor: *mut UnwCursor) -> i32; + #[link_name = "_ULaarch64_get_reg"] + pub fn unw_get_reg(cursor: *mut UnwCursor, reg: i32, valp: *mut UnwWord) -> i32; + #[link_name = "_ULaarch64_get_proc_name"] + pub fn unw_get_proc_name( + cursor: *mut UnwCursor, + name: *mut libc::c_char, + len: usize, + offset: *mut u64, + ) -> i32; + #[link_name = "unw_backtrace2"] + pub fn unw_backtrace2( + buffer: *mut *mut ::std::os::raw::c_void, + size: i32, + context: *mut UnwContext, + flag: i32, + ) -> i32; +} + +pub const UNW_REG_IP: i32 = 32; // Instruction Pointer +pub const UNW_REG_SP: i32 = 31; // Stack Pointer +pub const UNW_INIT_LOCAL_ONLY_IP: i32 = 1; + +/// Saves the current CPU context into `uc_mcontext.regs`. +/// On aarch64 libunwind does not emit a callable symbol for getcontext — +/// it uses a C preprocessor macro with inline assembly. This is the Rust +/// equivalent: save all GPRs, SP, LR, and PC into `uc_mcontext`. +/// +/// # Safety +/// `context` must be a valid, non-null pointer to a zeroed or initialized `UnwContext`. +// This is only for testing purposes and allow the tests to work with libc and musl-libc +#[cfg(test)] +#[inline(always)] +pub unsafe fn getcontext(context: *mut UnwContext) -> i32 { + let base = core::ptr::addr_of_mut!((*context).uc_mcontext.regs) as u64; + let ret: u64; + core::arch::asm!( + "stp x0, x1, [x0, #0]", + "stp x2, x3, [x0, #16]", + "stp x4, x5, [x0, #32]", + "stp x6, x7, [x0, #48]", + "stp x8, x9, [x0, #64]", + "stp x10, x11, [x0, #80]", + "stp x12, x13, [x0, #96]", + "stp x14, x15, [x0, #112]", + "stp x16, x17, [x0, #128]", + "stp x18, x19, [x0, #144]", + "stp x20, x21, [x0, #160]", + "stp x22, x23, [x0, #176]", + "stp x24, x25, [x0, #192]", + "stp x26, x27, [x0, #208]", + "stp x28, x29, [x0, #224]", + "mov x1, sp", + "stp x30, x1, [x0, #240]", + "adr x1, 2f", + "str x1, [x0, #256]", + "mov x0, #0", + "2:", + inout("x0") base => ret, + out("x1") _, + options(nostack, preserves_flags), + ); + ret as i32 +} diff --git a/libdd-libunwind-sys/src/libunwind_x86_64.rs b/libdd-libunwind-sys/src/libunwind_x86_64.rs new file mode 100644 index 0000000000..8c0232a942 --- /dev/null +++ b/libdd-libunwind-sys/src/libunwind_x86_64.rs @@ -0,0 +1,82 @@ +// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +// Context is platform ucontext_t (from libc) +pub type UnwContext = libc::ucontext_t; + +pub type UnwWord = u64; + +// Opaque cursor structure +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct UnwCursor { + pub opaque: [UnwWord; 127], +} + +// This is a subset of the libunwind API. + +extern "C" { + #[link_name = "_ULx86_64_init_local2"] + pub fn unw_init_local2(cursor: *mut UnwCursor, context: *mut UnwContext, flag: i32) -> i32; + #[link_name = "_ULx86_64_step"] + pub fn unw_step(cursor: *mut UnwCursor) -> i32; + #[link_name = "_ULx86_64_get_reg"] + pub fn unw_get_reg(cursor: *mut UnwCursor, reg: i32, valp: *mut UnwWord) -> i32; + #[link_name = "_ULx86_64_get_proc_name"] + pub fn unw_get_proc_name( + cursor: *mut UnwCursor, + name: *mut libc::c_char, + len: usize, + offset: *mut u64, + ) -> i32; + #[link_name = "unw_backtrace2"] + pub fn unw_backtrace2( + buffer: *mut *mut ::std::os::raw::c_void, + size: i32, + context: *mut UnwContext, + flag: i32, + ) -> i32; +} + +// x86_64 register definitions for libunwind +pub const UNW_REG_IP: i32 = 16; // Instruction Pointer +pub const UNW_REG_SP: i32 = 17; // Stack Pointer +pub const UNW_INIT_LOCAL_ONLY_IP: i32 = 1; + +/// Saves the current CPU context into `uc_mcontext.gregs`. +/// gregs layout: [R8, R9, R10, R11, R12, R13, R14, R15, +/// RDI, RSI, RBP, RBX, RDX, RAX, RCX, RSP, RIP, ...] +/// +/// # Safety +/// `context` must be a valid, non-null pointer to a zeroed or initialized `UnwContext`. +// This is only for testing purposes and allow the tests to work with libc and musl-libc +#[cfg(test)] +#[inline(always)] +pub unsafe fn getcontext(context: *mut UnwContext) -> i32 { + let gregs = core::ptr::addr_of_mut!((*context).uc_mcontext.gregs) as u64; + core::arch::asm!( + "mov [rdi], r8", + "mov [rdi + 8], r9", + "mov [rdi + 16], r10", + "mov [rdi + 24], r11", + "mov [rdi + 32], r12", + "mov [rdi + 40], r13", + "mov [rdi + 48], r14", + "mov [rdi + 56], r15", + "mov [rdi + 64], rdi", + "mov [rdi + 72], rsi", + "mov [rdi + 80], rbp", + "mov [rdi + 88], rbx", + "mov [rdi + 96], rdx", + "mov [rdi + 104], rax", + "mov [rdi + 112], rcx", + "mov [rdi + 120], rsp", + "lea rax, [rip + 2f]", + "mov [rdi + 128], rax", + "2:", + inout("rdi") gregs => _, + out("rax") _, + options(nostack, preserves_flags), + ); + 0 +} diff --git a/tools/docker/Dockerfile.build b/tools/docker/Dockerfile.build index 208a3b627d..b6871a1101 100644 --- a/tools/docker/Dockerfile.build +++ b/tools/docker/Dockerfile.build @@ -33,6 +33,9 @@ RUN apk update \ unzip \ bash \ clang16-libclang \ + autoconf \ + automake \ + libtool \ && mkdir /usr/local/src # Tell docker to use bash as the default @@ -80,6 +83,8 @@ COPY "libdd-ddsketch/Cargo.toml" "libdd-ddsketch/" COPY "libdd-ddsketch-ffi/Cargo.toml" "libdd-ddsketch-ffi/" COPY "libdd-log/Cargo.toml" "libdd-log/" COPY "libdd-log-ffi/Cargo.toml" "libdd-log-ffi/" +COPY "libdd-libunwind-sys/Cargo.toml" "libdd-libunwind-sys/" +COPY "libdd-libunwind-sys/build.rs" "libdd-libunwind-sys/" COPY "libdd-dogstatsd-client/Cargo.toml" "libdd-dogstatsd-client/" COPY "libdd-library-config-ffi/Cargo.toml" "libdd-library-config-ffi/" COPY "libdd-library-config/Cargo.toml" "libdd-library-config/" @@ -150,6 +155,9 @@ RUN echo \ libdd-trace-utils/benches/main.rs \ | xargs -n 1 sh -c 'mkdir -p $(dirname $1); touch $1; echo $1' create_stubs +# Copy libunwind submodule for libdd-libunwind build.rs +COPY "libdd-libunwind-sys/libunwind" "libdd-libunwind-sys/libunwind/" + # cache dependencies RUN cargo fetch --locked diff --git a/tools/docker/Dockerfile.centos b/tools/docker/Dockerfile.centos index 51b5a8bbf8..3ecd959e52 100644 --- a/tools/docker/Dockerfile.centos +++ b/tools/docker/Dockerfile.centos @@ -1,12 +1,18 @@ FROM ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main-centos -# CentOS 7 is EOL -# Find and replace mirror.centos.org with vault.centos.org -# Remove the # sign at the beginning of lines containing baseurl=http to enable baseurl usage instead of disabling it -# Add a # sign to the beginning of lines containing mirrorlist=http to disable the use of mirrorlist RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ && sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \ - && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo + && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \ + && yum update -y \ + && yum install -y centos-release-scl -RUN yum update -y \ - && yum install -y unzip +# Find and replace mirror.centos.org with vault.centos.org a second time +# installing centos-release-scl may have added new repos that need to be updated +RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ + && sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \ + && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \ + && yum update -y \ + && yum install -y devtoolset-11 autoconf automake libtool \ + && yum clean all + +ENV PATH="/opt/rh/devtoolset-11/root/usr/bin:$PATH"