Path: blob/main/crates/jit-debug/src/gdb_jit_int.rs
1692 views
//! The GDB's JIT compilation interface. The low level module that exposes1//! the __jit_debug_register_code() and __jit_debug_descriptor to register2//! or unregister generated object images with debuggers.34extern crate alloc;56use alloc::{boxed::Box, vec::Vec};7use core::{pin::Pin, ptr};8use wasmtime_versioned_export_macros::versioned_link;910#[repr(C)]11struct JITCodeEntry {12next_entry: *mut JITCodeEntry,13prev_entry: *mut JITCodeEntry,14symfile_addr: *const u8,15symfile_size: u64,16}1718const JIT_NOACTION: u32 = 0;19const JIT_REGISTER_FN: u32 = 1;20const JIT_UNREGISTER_FN: u32 = 2;2122#[repr(C)]23struct JITDescriptor {24version: u32,25action_flag: u32,26relevant_entry: *mut JITCodeEntry,27first_entry: *mut JITCodeEntry,28}2930unsafe extern "C" {31#[versioned_link]32fn wasmtime_jit_debug_descriptor() -> *mut JITDescriptor;33fn __jit_debug_register_code();34}3536#[cfg(feature = "std")]37mod gdb_registration {38use std::sync::{Mutex, MutexGuard};3940/// The process controls access to the __jit_debug_descriptor by itself --41/// the GDB/LLDB accesses this structure and its data at the process startup42/// and when paused in __jit_debug_register_code.43///44/// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect45/// access to the __jit_debug_descriptor within this process.46pub static GDB_REGISTRATION: Mutex<()> = Mutex::new(());4748/// The lock guard for the GDB registration lock.49#[expect(50dead_code,51reason = "field used to hold the lock until the end of the scope"52)]53pub struct LockGuard<'a>(MutexGuard<'a, ()>);5455pub fn lock() -> LockGuard<'static> {56LockGuard(GDB_REGISTRATION.lock().unwrap())57}58}5960/// For no_std there's no access to synchronization primitives so a primitive61/// fallback for now is to panic-on-contention which is, in theory, rare to come62/// up as threads are rarer in no_std mode too.63///64/// This provides the guarantee that the debugger will see consistent state at65/// least, but if this panic is hit in practice it'll require some sort of66/// no_std synchronization mechanism one way or another.67#[cfg(not(feature = "std"))]68mod gdb_registration {69use core::sync::atomic::{AtomicBool, Ordering};7071/// Whether or not a lock is held or not.72pub static GDB_REGISTRATION: AtomicBool = AtomicBool::new(false);7374/// The lock guard for the GDB registration lock.75pub struct LockGuard;7677/// When the `LockGuard` is dropped, it releases the lock by setting78/// `GDB_REGISTRATION` to false.79impl Drop for LockGuard {80fn drop(&mut self) {81GDB_REGISTRATION.store(false, Ordering::Release);82}83}8485/// Locks the GDB registration lock. If the lock is already held, it panics86pub fn lock() -> LockGuard {87if GDB_REGISTRATION88.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)89.is_err()90{91panic!("GDB JIT registration lock contention detected in no_std mode");92}93LockGuard94}95}9697/// Registration for JIT image98pub struct GdbJitImageRegistration {99entry: Pin<Box<JITCodeEntry>>,100file: Pin<Box<[u8]>>,101}102103impl GdbJitImageRegistration {104/// Registers JIT image using __jit_debug_register_code105pub fn register(file: Vec<u8>) -> Self {106let file = Pin::new(file.into_boxed_slice());107108// Create a code entry for the file, which gives the start and size109// of the symbol file.110let mut entry = Pin::new(Box::new(JITCodeEntry {111next_entry: ptr::null_mut(),112prev_entry: ptr::null_mut(),113symfile_addr: file.as_ptr(),114symfile_size: file.len() as u64,115}));116117unsafe {118register_gdb_jit_image(&mut *entry);119}120121Self { entry, file }122}123124/// JIT image used in registration125pub fn file(&self) -> &[u8] {126&self.file127}128}129130impl Drop for GdbJitImageRegistration {131fn drop(&mut self) {132unsafe {133unregister_gdb_jit_image(&mut *self.entry);134}135}136}137138unsafe impl Send for GdbJitImageRegistration {}139unsafe impl Sync for GdbJitImageRegistration {}140141unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) {142unsafe {143let _lock = gdb_registration::lock();144let desc = &mut *wasmtime_jit_debug_descriptor();145146// Add it to the linked list in the JIT descriptor.147(*entry).next_entry = desc.first_entry;148if !desc.first_entry.is_null() {149(*desc.first_entry).prev_entry = entry;150}151desc.first_entry = entry;152// Point the relevant_entry field of the descriptor at the entry.153desc.relevant_entry = entry;154// Set action_flag to JIT_REGISTER and call __jit_debug_register_code.155desc.action_flag = JIT_REGISTER_FN;156__jit_debug_register_code();157158desc.action_flag = JIT_NOACTION;159desc.relevant_entry = ptr::null_mut();160}161}162163unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {164unsafe {165let _lock = gdb_registration::lock();166let desc = &mut *wasmtime_jit_debug_descriptor();167168// Remove the code entry corresponding to the code from the linked list.169if !(*entry).prev_entry.is_null() {170(*(*entry).prev_entry).next_entry = (*entry).next_entry;171} else {172desc.first_entry = (*entry).next_entry;173}174if !(*entry).next_entry.is_null() {175(*(*entry).next_entry).prev_entry = (*entry).prev_entry;176}177// Point the relevant_entry field of the descriptor at the code entry.178desc.relevant_entry = entry;179// Set action_flag to JIT_UNREGISTER and call __jit_debug_register_code.180desc.action_flag = JIT_UNREGISTER_FN;181__jit_debug_register_code();182183desc.action_flag = JIT_NOACTION;184desc.relevant_entry = ptr::null_mut();185}186}187188189