extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
use core::{pin::Pin, ptr};
use wasmtime_versioned_export_macros::versioned_link;
#[repr(C)]
struct JITCodeEntry {
next_entry: *mut JITCodeEntry,
prev_entry: *mut JITCodeEntry,
symfile_addr: *const u8,
symfile_size: u64,
}
const JIT_NOACTION: u32 = 0;
const JIT_REGISTER_FN: u32 = 1;
const JIT_UNREGISTER_FN: u32 = 2;
#[repr(C)]
struct JITDescriptor {
version: u32,
action_flag: u32,
relevant_entry: *mut JITCodeEntry,
first_entry: *mut JITCodeEntry,
}
unsafe extern "C" {
#[versioned_link]
fn wasmtime_jit_debug_descriptor() -> *mut JITDescriptor;
fn __jit_debug_register_code();
}
#[cfg(feature = "std")]
mod gdb_registration {
use std::sync::{Mutex, MutexGuard};
pub static GDB_REGISTRATION: Mutex<()> = Mutex::new(());
#[expect(
dead_code,
reason = "field used to hold the lock until the end of the scope"
)]
pub struct LockGuard<'a>(MutexGuard<'a, ()>);
pub fn lock() -> LockGuard<'static> {
LockGuard(GDB_REGISTRATION.lock().unwrap())
}
}
#[cfg(not(feature = "std"))]
mod gdb_registration {
use core::sync::atomic::{AtomicBool, Ordering};
pub static GDB_REGISTRATION: AtomicBool = AtomicBool::new(false);
pub struct LockGuard;
impl Drop for LockGuard {
fn drop(&mut self) {
GDB_REGISTRATION.store(false, Ordering::Release);
}
}
pub fn lock() -> LockGuard {
if GDB_REGISTRATION
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
panic!("GDB JIT registration lock contention detected in no_std mode");
}
LockGuard
}
}
pub struct GdbJitImageRegistration {
entry: Pin<Box<JITCodeEntry>>,
file: Pin<Box<[u8]>>,
}
impl GdbJitImageRegistration {
pub fn register(file: Vec<u8>) -> Self {
let file = Pin::new(file.into_boxed_slice());
let mut entry = Pin::new(Box::new(JITCodeEntry {
next_entry: ptr::null_mut(),
prev_entry: ptr::null_mut(),
symfile_addr: file.as_ptr(),
symfile_size: file.len() as u64,
}));
unsafe {
register_gdb_jit_image(&mut *entry);
}
Self { entry, file }
}
pub fn file(&self) -> &[u8] {
&self.file
}
}
impl Drop for GdbJitImageRegistration {
fn drop(&mut self) {
unsafe {
unregister_gdb_jit_image(&mut *self.entry);
}
}
}
unsafe impl Send for GdbJitImageRegistration {}
unsafe impl Sync for GdbJitImageRegistration {}
unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) {
unsafe {
let _lock = gdb_registration::lock();
let desc = &mut *wasmtime_jit_debug_descriptor();
(*entry).next_entry = desc.first_entry;
if !desc.first_entry.is_null() {
(*desc.first_entry).prev_entry = entry;
}
desc.first_entry = entry;
desc.relevant_entry = entry;
desc.action_flag = JIT_REGISTER_FN;
__jit_debug_register_code();
desc.action_flag = JIT_NOACTION;
desc.relevant_entry = ptr::null_mut();
}
}
unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
unsafe {
let _lock = gdb_registration::lock();
let desc = &mut *wasmtime_jit_debug_descriptor();
if !(*entry).prev_entry.is_null() {
(*(*entry).prev_entry).next_entry = (*entry).next_entry;
} else {
desc.first_entry = (*entry).next_entry;
}
if !(*entry).next_entry.is_null() {
(*(*entry).next_entry).prev_entry = (*entry).prev_entry;
}
desc.relevant_entry = entry;
desc.action_flag = JIT_UNREGISTER_FN;
__jit_debug_register_code();
desc.action_flag = JIT_NOACTION;
desc.relevant_entry = ptr::null_mut();
}
}