Skip to content

Commit f103016

Browse files
authored
NONE - LuaJIT frames and other improvements
NONE - LuaJIT frames and other improvements
2 parents 34ee87a + 60d3518 commit f103016

File tree

4 files changed

+168
-11
lines changed

4 files changed

+168
-11
lines changed

packages/autorun-luajit/src/helpers.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,8 @@ pub fn push_tvalue(state: &mut LJState, tvalue: &TValue) {
6060
state.top = state.top.add(1);
6161
}
6262
}
63+
64+
pub fn push_frame_func(state: &mut LJState, frame: &Frame) -> anyhow::Result<()> {
65+
push_tvalue(state, unsafe { &*frame.get_func_tv() });
66+
Ok(())
67+
}

packages/autorun-luajit/src/types/common.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
use anyhow::Context;
44
use std::ffi::{c_int, c_void};
5+
use std::fmt::Debug;
56

67
// IMPORTANT: GMod's LUA_IDSIZE was randomly changed to 128 instead of 60 like in vanilla LuaJIT
78
#[cfg(feature = "gmod")]
89
pub const LUA_IDSIZE: i32 = 128;
910
#[cfg(not(feature = "gmod"))]
1011
pub const LUA_IDSIZE: i32 = 60;
1112

13+
pub const LJ_FR2: u32 = 1;
14+
1215
pub const LJ_TNIL: u32 = !0u32;
1316
pub const LJ_TFALSE: u32 = !1u32;
1417
pub const LJ_TTRUE: u32 = !2u32;
@@ -79,6 +82,12 @@ pub struct GCRef {
7982
}
8083

8184
impl GCRef {
85+
pub fn from_ptr<T>(ptr: *mut T) -> Self {
86+
Self {
87+
gcptr64: (ptr as u64) & LJ_GCVMASK,
88+
}
89+
}
90+
8291
// equivalent to the gcref macro in LuaJIT
8392
pub fn as_ptr<T>(&self) -> *mut T {
8493
self.gcptr64 as *mut T
@@ -112,6 +121,22 @@ pub union TValue {
112121
pub ftsz: u64,
113122
}
114123

124+
impl Debug for TValue {
125+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126+
write!(f, "TValue {{ itype: {}, gcr: {:x} }}", self.itype(), unsafe {
127+
self.gcr.gcptr64
128+
})
129+
}
130+
}
131+
132+
macro_rules! impl_tvalue_type_check {
133+
($function_name:ident, $lj_type_const:expr) => {
134+
pub fn $function_name(&self) -> bool {
135+
self.itype() == $lj_type_const
136+
}
137+
};
138+
}
139+
115140
impl TValue {
116141
pub fn as_ptr<T: IntoLJType>(&self) -> anyhow::Result<*mut T> {
117142
if self.itype() != T::LJ_TYPE {
@@ -132,6 +157,21 @@ impl TValue {
132157
pub fn itype(&self) -> u32 {
133158
unsafe { ((self.it64 >> 47) & 0xFFFFFFFF) as u32 }
134159
}
160+
161+
impl_tvalue_type_check!(is_nil, LJ_TNIL);
162+
impl_tvalue_type_check!(is_false, LJ_TFALSE);
163+
impl_tvalue_type_check!(is_true, LJ_TTRUE);
164+
impl_tvalue_type_check!(is_lightud, LJ_TLIGHTUD);
165+
impl_tvalue_type_check!(is_str, LJ_TSTR);
166+
impl_tvalue_type_check!(is_upval, LJ_TUPVAL);
167+
impl_tvalue_type_check!(is_thread, LJ_TTHREAD);
168+
impl_tvalue_type_check!(is_proto, LJ_TPROTO);
169+
impl_tvalue_type_check!(is_func, LJ_TFUNC);
170+
impl_tvalue_type_check!(is_trace, LJ_TTRACE);
171+
impl_tvalue_type_check!(is_cdata, LJ_TCDATA);
172+
impl_tvalue_type_check!(is_tab, LJ_TTAB);
173+
impl_tvalue_type_check!(is_udata, LJ_TUDATA);
174+
impl_tvalue_type_check!(is_numx, LJ_TNUMX);
135175
}
136176

137177
#[repr(C, packed)]
@@ -160,6 +200,17 @@ pub struct GCfuncL {
160200
pub uvptr: [GCRef; 1],
161201
}
162202

203+
impl GCfuncL {
204+
pub fn get_proto(&self) -> anyhow::Result<&GCProto> {
205+
let pc_ref = self.header.pc;
206+
// proto starts immediately before the pc pointer
207+
let proto = unsafe { pc_ref.as_ptr::<GCProto>().offset(-1) };
208+
let proto_ref = unsafe { proto.as_ref().context("Failed to dereference GCProto from GCfuncL")? };
209+
210+
Ok(proto_ref)
211+
}
212+
}
213+
163214
#[repr(C)]
164215
#[derive(Clone, Copy)]
165216
pub union GCfunc {
@@ -349,7 +400,15 @@ impl IntoLJType for GCUpval {
349400
const LJ_TYPE: u32 = LJ_TUPVAL;
350401
}
351402

352-
pub type BCIns = u32;
403+
#[derive(Debug, Clone, Copy)]
404+
pub struct BCIns(u32);
405+
406+
impl BCIns {
407+
pub fn a(&self) -> u8 {
408+
//#define bc_a(i) ((BCReg)(((i)>>8)&0xff))
409+
((self.0 >> 8) & 0xff) as u8
410+
}
411+
}
353412

354413
pub type BCLine = u32;
355414

packages/autorun-luajit/src/types/frame.rs

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Helper module for managing the frame stack in LuaJIT, which is highly complicated and fragile.
2-
use crate::{GCfunc, LJState, TValue};
2+
use crate::{BCIns, GCRef, GCfunc, LJ_FR2, LJState, TValue};
33

44
pub const FRAME_TYPE: u8 = 3;
55
pub const FRAME_P: u8 = 4;
66
pub const FRAME_TYPEP: u8 = FRAME_TYPE | FRAME_P;
7+
8+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
79
pub enum FrameType {
810
Lua = 0,
911
C = 1,
@@ -14,26 +16,65 @@ pub enum FrameType {
1416
FfPcallWithHook = 7,
1517
}
1618

17-
#[derive(Debug)]
19+
impl std::fmt::Display for FrameType {
20+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21+
let type_str = match self {
22+
FrameType::Lua => "Lua",
23+
FrameType::C => "C",
24+
FrameType::Continuation => "Continuation",
25+
FrameType::LuaVararg => "LuaVararg",
26+
FrameType::Cpcall => "Cpcall",
27+
FrameType::FfPcall => "FfPcall",
28+
FrameType::FfPcallWithHook => "FfPcallWithHook",
29+
};
30+
31+
write!(f, "{}", type_str)
32+
}
33+
}
34+
35+
#[derive(Debug, Copy, Clone)]
1836
/// Frames are stored in the LJ state as TValues with their payloads below them in the stack.
1937
/// This struct helps manage and interpret these frames.
2038
pub struct Frame {
2139
pub tvalue: *mut TValue,
22-
pub size: u32,
40+
payload_copy: TValue,
2341
}
2442

2543
impl Frame {
26-
pub fn new(tvalue: *mut TValue, size: u32) -> Self {
27-
Self { tvalue, size }
44+
pub fn new(tvalue: *mut TValue) -> Self {
45+
Self {
46+
tvalue,
47+
payload_copy: unsafe { *tvalue.offset(-1) },
48+
}
2849
}
2950

3051
pub fn from_debug_ci(state: &mut LJState, i_ci: i32) -> Self {
3152
let offset = (i_ci as u32) & 0xffff;
32-
let size = (i_ci as u32) >> 16;
3353
let tvstack = state.stack.as_ptr::<TValue>();
3454
let frametv = unsafe { tvstack.add(offset as usize) };
3555

36-
Frame::new(frametv, size)
56+
frametv.into()
57+
}
58+
59+
pub fn walk_stack(state: *mut LJState) -> Vec<Frame> {
60+
let mut frames = Vec::new();
61+
62+
// traversal is in backwards order
63+
let start = unsafe { (*state).stack.as_ptr::<TValue>().add(LJ_FR2 as usize) };
64+
let mut frame = unsafe { (*state).base.offset(-1) };
65+
66+
while frame > start {
67+
let current_frame: Frame = frame.into();
68+
frames.push(current_frame);
69+
70+
if current_frame.is_lua_frame() {
71+
frame = current_frame.get_previous_lua_frame().tvalue;
72+
} else {
73+
frame = current_frame.get_previous_delta_frame().tvalue;
74+
}
75+
}
76+
77+
frames
3778
}
3879

3980
pub fn get_type(&self) -> FrameType {
@@ -51,14 +92,14 @@ impl Frame {
5192
}
5293

5394
pub fn is_lua_frame(&self) -> bool {
54-
matches!(self.get_type(), FrameType::Lua | FrameType::LuaVararg)
95+
self.get_type() == FrameType::Lua
5596
}
5697

5798
pub fn is_c_frame(&self) -> bool {
58-
matches!(self.get_type(), FrameType::C | FrameType::Cpcall)
99+
self.get_type() == FrameType::C
59100
}
60101

61-
pub fn get_gc_func(&mut self) -> anyhow::Result<&mut GCfunc> {
102+
pub fn get_gc_func(&self) -> anyhow::Result<&mut GCfunc> {
62103
unsafe {
63104
let func_tv = self.tvalue.offset(-1);
64105
let gcfunc = (*func_tv).as_mut::<GCfunc>()?;
@@ -69,4 +110,54 @@ impl Frame {
69110
pub fn get_func_tv(&self) -> *mut TValue {
70111
unsafe { self.tvalue.offset(-1) }
71112
}
113+
114+
fn overwrite_payload_gcr(&mut self, new_gcr: GCRef) {
115+
unsafe {
116+
(*self.tvalue.offset(-1)).gcr = new_gcr;
117+
}
118+
}
119+
120+
fn restore_payload(&mut self) {
121+
unsafe {
122+
*self.tvalue.offset(-1) = self.payload_copy;
123+
}
124+
}
125+
126+
pub fn mark_as_dummy_frame(&mut self, state: *mut LJState) {
127+
// We can overwrite the payload to point to the Lua state,
128+
// which is a special GCRef that indicates a dummy frame.
129+
self.overwrite_payload_gcr(GCRef::from_ptr(state));
130+
}
131+
132+
pub fn restore_from_dummy_frame(&mut self) {
133+
self.restore_payload();
134+
}
135+
136+
pub fn get_pc(&self) -> *const BCIns {
137+
unsafe { (*self.tvalue).ftsz as *const BCIns }
138+
}
139+
140+
pub fn get_previous_lua_frame(&self) -> Self {
141+
let bc_ins_ptr = self.get_pc();
142+
let bc_ins = unsafe { bc_ins_ptr.offset(-1).read_unaligned() };
143+
let bc_a = bc_ins.a();
144+
let offset = (1 + LJ_FR2 + (bc_a as u32)) as isize;
145+
146+
unsafe { self.tvalue.offset(-offset) }.into()
147+
}
148+
149+
pub fn get_delta_size(&self) -> u64 {
150+
unsafe { (*self.tvalue).ftsz & !(FRAME_TYPEP as u64) }
151+
}
152+
153+
pub fn get_previous_delta_frame(&self) -> Self {
154+
let size = self.get_delta_size() as usize;
155+
unsafe { self.tvalue.byte_sub(size).into() }
156+
}
157+
}
158+
159+
impl Into<Frame> for *mut TValue {
160+
fn into(self) -> Frame {
161+
Frame::new(self)
162+
}
72163
}

services/autorun-lib/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub fn main() -> anyhow::Result<()> {
2525
#[cfg(target_os = "windows")]
2626
#[unsafe(no_mangle)]
2727
extern "C" fn autorun_entrypoint() {
28+
// Yield to allow gmod to initialize properly
29+
std::thread::sleep(std::time::Duration::from_secs(1));
2830
// Redirect stdout to stderr.
2931
// This is a hack because for some reason stdout isn't intercepted on windows?
3032
// Might be gmod's fault. I don't care.

0 commit comments

Comments
 (0)