1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! The GDB's JIT compilation interface. The low level module that exposes
//! the __jit_debug_register_code() and __jit_debug_descriptor to register
//! or unregister generated object images with debuggers.

use once_cell::sync::Lazy;
use std::pin::Pin;
use std::ptr;
use std::sync::Mutex;

#[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,
}

extern "C" {
    fn wasmtime_jit_debug_descriptor() -> *mut JITDescriptor;
    fn __jit_debug_register_code();
}

/// The process controls access to the __jit_debug_descriptor by itself --
/// the GDB/LLDB accesses this structure and its data at the process startup
/// and when paused in __jit_debug_register_code.
///
/// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect
/// access to the __jit_debug_descriptor within this process.
static GDB_REGISTRATION: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(Default::default()));

/// Registeration for JIT image
pub struct GdbJitImageRegistration {
    entry: Pin<Box<JITCodeEntry>>,
    file: Pin<Box<[u8]>>,
}

impl GdbJitImageRegistration {
    /// Registers JIT image using __jit_debug_register_code
    pub fn register(file: Vec<u8>) -> Self {
        let file = Pin::new(file.into_boxed_slice());

        // Create a code entry for the file, which gives the start and size
        // of the symbol file.
        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 }
    }

    /// JIT image used in registration
    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) {
    let _lock = GDB_REGISTRATION.lock().unwrap();
    let desc = &mut *wasmtime_jit_debug_descriptor();

    // Add it to the linked list in the JIT descriptor.
    (*entry).next_entry = desc.first_entry;
    if !desc.first_entry.is_null() {
        (*desc.first_entry).prev_entry = entry;
    }
    desc.first_entry = entry;
    // Point the relevant_entry field of the descriptor at the entry.
    desc.relevant_entry = entry;
    // Set action_flag to JIT_REGISTER and call __jit_debug_register_code.
    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) {
    let _lock = GDB_REGISTRATION.lock().unwrap();
    let desc = &mut *wasmtime_jit_debug_descriptor();

    // Remove the code entry corresponding to the code from the linked list.
    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;
    }
    // Point the relevant_entry field of the descriptor at the code entry.
    desc.relevant_entry = entry;
    // Set action_flag to JIT_UNREGISTER and call __jit_debug_register_code.
    desc.action_flag = JIT_UNREGISTER_FN;
    __jit_debug_register_code();

    desc.action_flag = JIT_NOACTION;
    desc.relevant_entry = ptr::null_mut();
}