Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/jit-debug/src/gdb_jit_int.rs
1692 views
1
//! The GDB's JIT compilation interface. The low level module that exposes
2
//! the __jit_debug_register_code() and __jit_debug_descriptor to register
3
//! or unregister generated object images with debuggers.
4
5
extern crate alloc;
6
7
use alloc::{boxed::Box, vec::Vec};
8
use core::{pin::Pin, ptr};
9
use wasmtime_versioned_export_macros::versioned_link;
10
11
#[repr(C)]
12
struct JITCodeEntry {
13
next_entry: *mut JITCodeEntry,
14
prev_entry: *mut JITCodeEntry,
15
symfile_addr: *const u8,
16
symfile_size: u64,
17
}
18
19
const JIT_NOACTION: u32 = 0;
20
const JIT_REGISTER_FN: u32 = 1;
21
const JIT_UNREGISTER_FN: u32 = 2;
22
23
#[repr(C)]
24
struct JITDescriptor {
25
version: u32,
26
action_flag: u32,
27
relevant_entry: *mut JITCodeEntry,
28
first_entry: *mut JITCodeEntry,
29
}
30
31
unsafe extern "C" {
32
#[versioned_link]
33
fn wasmtime_jit_debug_descriptor() -> *mut JITDescriptor;
34
fn __jit_debug_register_code();
35
}
36
37
#[cfg(feature = "std")]
38
mod gdb_registration {
39
use std::sync::{Mutex, MutexGuard};
40
41
/// The process controls access to the __jit_debug_descriptor by itself --
42
/// the GDB/LLDB accesses this structure and its data at the process startup
43
/// and when paused in __jit_debug_register_code.
44
///
45
/// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect
46
/// access to the __jit_debug_descriptor within this process.
47
pub static GDB_REGISTRATION: Mutex<()> = Mutex::new(());
48
49
/// The lock guard for the GDB registration lock.
50
#[expect(
51
dead_code,
52
reason = "field used to hold the lock until the end of the scope"
53
)]
54
pub struct LockGuard<'a>(MutexGuard<'a, ()>);
55
56
pub fn lock() -> LockGuard<'static> {
57
LockGuard(GDB_REGISTRATION.lock().unwrap())
58
}
59
}
60
61
/// For no_std there's no access to synchronization primitives so a primitive
62
/// fallback for now is to panic-on-contention which is, in theory, rare to come
63
/// up as threads are rarer in no_std mode too.
64
///
65
/// This provides the guarantee that the debugger will see consistent state at
66
/// least, but if this panic is hit in practice it'll require some sort of
67
/// no_std synchronization mechanism one way or another.
68
#[cfg(not(feature = "std"))]
69
mod gdb_registration {
70
use core::sync::atomic::{AtomicBool, Ordering};
71
72
/// Whether or not a lock is held or not.
73
pub static GDB_REGISTRATION: AtomicBool = AtomicBool::new(false);
74
75
/// The lock guard for the GDB registration lock.
76
pub struct LockGuard;
77
78
/// When the `LockGuard` is dropped, it releases the lock by setting
79
/// `GDB_REGISTRATION` to false.
80
impl Drop for LockGuard {
81
fn drop(&mut self) {
82
GDB_REGISTRATION.store(false, Ordering::Release);
83
}
84
}
85
86
/// Locks the GDB registration lock. If the lock is already held, it panics
87
pub fn lock() -> LockGuard {
88
if GDB_REGISTRATION
89
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
90
.is_err()
91
{
92
panic!("GDB JIT registration lock contention detected in no_std mode");
93
}
94
LockGuard
95
}
96
}
97
98
/// Registration for JIT image
99
pub struct GdbJitImageRegistration {
100
entry: Pin<Box<JITCodeEntry>>,
101
file: Pin<Box<[u8]>>,
102
}
103
104
impl GdbJitImageRegistration {
105
/// Registers JIT image using __jit_debug_register_code
106
pub fn register(file: Vec<u8>) -> Self {
107
let file = Pin::new(file.into_boxed_slice());
108
109
// Create a code entry for the file, which gives the start and size
110
// of the symbol file.
111
let mut entry = Pin::new(Box::new(JITCodeEntry {
112
next_entry: ptr::null_mut(),
113
prev_entry: ptr::null_mut(),
114
symfile_addr: file.as_ptr(),
115
symfile_size: file.len() as u64,
116
}));
117
118
unsafe {
119
register_gdb_jit_image(&mut *entry);
120
}
121
122
Self { entry, file }
123
}
124
125
/// JIT image used in registration
126
pub fn file(&self) -> &[u8] {
127
&self.file
128
}
129
}
130
131
impl Drop for GdbJitImageRegistration {
132
fn drop(&mut self) {
133
unsafe {
134
unregister_gdb_jit_image(&mut *self.entry);
135
}
136
}
137
}
138
139
unsafe impl Send for GdbJitImageRegistration {}
140
unsafe impl Sync for GdbJitImageRegistration {}
141
142
unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) {
143
unsafe {
144
let _lock = gdb_registration::lock();
145
let desc = &mut *wasmtime_jit_debug_descriptor();
146
147
// Add it to the linked list in the JIT descriptor.
148
(*entry).next_entry = desc.first_entry;
149
if !desc.first_entry.is_null() {
150
(*desc.first_entry).prev_entry = entry;
151
}
152
desc.first_entry = entry;
153
// Point the relevant_entry field of the descriptor at the entry.
154
desc.relevant_entry = entry;
155
// Set action_flag to JIT_REGISTER and call __jit_debug_register_code.
156
desc.action_flag = JIT_REGISTER_FN;
157
__jit_debug_register_code();
158
159
desc.action_flag = JIT_NOACTION;
160
desc.relevant_entry = ptr::null_mut();
161
}
162
}
163
164
unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
165
unsafe {
166
let _lock = gdb_registration::lock();
167
let desc = &mut *wasmtime_jit_debug_descriptor();
168
169
// Remove the code entry corresponding to the code from the linked list.
170
if !(*entry).prev_entry.is_null() {
171
(*(*entry).prev_entry).next_entry = (*entry).next_entry;
172
} else {
173
desc.first_entry = (*entry).next_entry;
174
}
175
if !(*entry).next_entry.is_null() {
176
(*(*entry).next_entry).prev_entry = (*entry).prev_entry;
177
}
178
// Point the relevant_entry field of the descriptor at the code entry.
179
desc.relevant_entry = entry;
180
// Set action_flag to JIT_UNREGISTER and call __jit_debug_register_code.
181
desc.action_flag = JIT_UNREGISTER_FN;
182
__jit_debug_register_code();
183
184
desc.action_flag = JIT_NOACTION;
185
desc.relevant_entry = ptr::null_mut();
186
}
187
}
188
189