1use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
40
41pub const ERR_FAULT: i32 = -64; pub const LIBA_R: u32 = 1; pub const LIBA_W: u32 = 2;
45pub const LIBA_X: u32 = 4;
46
47pub const LIBE_IO: u32 = 1; pub const LIBE_FORMAT: u32 = 2; pub const LIBE_DEPS: u32 = 3; pub const LIBE_RELOC: u32 = 4; pub const LIBE_NOMEM: u32 = 5;
52pub const LIBE_INIT: u32 = 6; pub const LIBE_MAP: u32 = 7; pub const LIB_FILE_MAX: u64 = 64 << 20;
55pub const LH_SEGMAX: usize = 16;
56
57pub const GK_NONE: u32 = 0;
58pub const GK_ACCESS: u32 = 1; pub const GK_ILLEGAL: u32 = 2; pub const GK_ARITH: u32 = 3; pub const GK_BUS: u32 = 4; pub const GK_BREAK: u32 = 5; pub const GK_STACK: u32 = 6; pub const GT_MAX: usize = 128;
65
66#[derive(Clone, Copy, Default, Debug)]
68pub struct LibSeg {
69 pub start: usize,
70 pub end: usize,
71 pub flags: u32,
72}
73
74pub struct Lib {
76 pub(crate) base: usize,
77 pub(crate) span: usize,
78 pub(crate) os: usize, pub(crate) nseg: u32,
80 #[allow(dead_code)]
81 pub(crate) nsym: u32, pub(crate) symtab: usize, #[allow(dead_code)]
84 pub(crate) strtab: usize, pub(crate) strsz: usize, pub(crate) segs: [LibSeg; LH_SEGMAX],
87}
88
89impl Lib {
90 pub(crate) fn empty() -> Lib {
91 Lib { base: 0, span: 0, os: 0, nseg: 0, nsym: 0, symtab: 0, strtab: 0, strsz: 0, segs: [LibSeg::default(); LH_SEGMAX] }
92 }
93
94 pub fn base(&self) -> usize {
96 self.base
97 }
98
99 pub fn span(&self) -> usize {
101 self.span
102 }
103
104 pub fn segs(&self) -> &[LibSeg] {
106 &self.segs[..(self.nseg as usize).min(LH_SEGMAX)]
107 }
108}
109
110impl Drop for Lib {
111 fn drop(&mut self) {
112 crate::lib_unload(self);
113 }
114}
115
116pub fn lib_close(h: Lib) {
118 drop(h)
119}
120
121pub fn lib_seg(h: &Lib, addr: usize) -> (usize, u32) {
123 for s in h.segs() {
124 if addr >= s.start && addr < s.end {
125 return (s.end, s.flags);
126 }
127 }
128 (0, 0)
129}
130
131#[repr(C)]
135#[derive(Clone, Copy, Default, Debug)]
136pub struct GuardInfo {
137 pub kind: u32, pub code: u32, pub pc: u64, pub addr: u64, pub tag: u64, }
143
144#[repr(C, align(16))]
146pub(crate) struct GuardRec {
147 pub(crate) prev: u64, pub(crate) rsp: u64, pub(crate) rbp: u64, pub(crate) ent: u64, pub(crate) kind: u32, pub(crate) code: u32, pub(crate) pc: u64, pub(crate) addr: u64, tag: u64, mxcsr: u32, fcw: u16, pad: [u8; 10],
159 xmm: [u8; 160], rax: u64, rdx: u64, }
163
164const _: () = assert!(core::mem::size_of::<GuardRec>() == 256);
165
166#[repr(C)]
168pub(crate) struct GtEnt {
169 pub(crate) tid: AtomicU64,
170 pub(crate) cur: AtomicU64,
171}
172
173#[allow(clippy::declare_interior_mutable_const)]
174const GT0: GtEnt = GtEnt { tid: AtomicU64::new(0), cur: AtomicU64::new(0) };
175static GT: [GtEnt; GT_MAX] = [GT0; GT_MAX];
176static GT_N: AtomicU32 = AtomicU32::new(0);
177static GUARD_ON: AtomicU32 = AtomicU32::new(0);
178
179core::arch::global_asm!(
183 ".globl jfgui_guard_run",
184 ".globl jfgui_guard_fault_ip",
185 "jfgui_guard_run:",
186 " push rbp",
187 " mov rbp, rsp",
188 " push rbx",
189 " push rsi",
190 " push rdi",
191 " push r12",
192 " push r13",
193 " push r14",
194 " push r15",
195 " sub rsp, 88",
196 " mov [rsp + 64], rcx",
197 " mov rsi, rcx",
198 " mov r12, rdx",
199 " mov r13, r8",
200 " mov [rsi + 8], rsp",
201 " mov [rsi + 16], rbp",
202 " mov dword ptr [rsi + 64], 0",
203 " mov dword ptr [rsi + 68], 0",
204 " stmxcsr dword ptr [rsi + 64]",
205 " fnstcw word ptr [rsi + 68]",
206 " movdqu [rsi + 80], xmm6",
207 " movdqu [rsi + 96], xmm7",
208 " movdqu [rsi + 112], xmm8",
209 " movdqu [rsi + 128], xmm9",
210 " movdqu [rsi + 144], xmm10",
211 " movdqu [rsi + 160], xmm11",
212 " movdqu [rsi + 176], xmm12",
213 " movdqu [rsi + 192], xmm13",
214 " movdqu [rsi + 208], xmm14",
215 " movdqu [rsi + 224], xmm15",
216 " mov rbx, [rsi + 24]",
217 " mov [rbx + 8], rsi", " mov rcx, [r13]",
219 " mov rdx, [r13 + 8]",
220 " mov r8, [r13 + 16]",
221 " mov r9, [r13 + 24]",
222 " call r12",
223 " mov rsi, [rsp + 64]",
225 " mov rbp, [rsi + 16]",
226 " mov [rsi + 240], rax",
227 " mov [rsi + 248], rdx",
228 " mov rbx, [rsi + 24]",
229 " mov rcx, [rsi]",
230 " mov [rbx + 8], rcx", " call 2f",
232 " cld",
233 " xor eax, eax",
234 " jmp 3f",
235 "jfgui_guard_fault_ip:",
238 " cld",
239 " fninit",
240 " mov rsi, [rsp + 64]",
241 " fldcw word ptr [rsi + 68]",
242 " ldmxcsr dword ptr [rsi + 64]",
243 " call 2f",
244 " mov eax, 1",
245 "3:",
246 " lea rsp, [rbp - 56]",
247 " pop r15",
248 " pop r14",
249 " pop r13",
250 " pop r12",
251 " pop rdi",
252 " pop rsi",
253 " pop rbx",
254 " pop rbp",
255 " ret",
256 "2:",
258 " movdqu xmm6, [rsi + 80]",
259 " movdqu xmm7, [rsi + 96]",
260 " movdqu xmm8, [rsi + 112]",
261 " movdqu xmm9, [rsi + 128]",
262 " movdqu xmm10, [rsi + 144]",
263 " movdqu xmm11, [rsi + 160]",
264 " movdqu xmm12, [rsi + 176]",
265 " movdqu xmm13, [rsi + 192]",
266 " movdqu xmm14, [rsi + 208]",
267 " movdqu xmm15, [rsi + 224]",
268 " ret",
269);
270
271unsafe extern "win64" {
272 fn jfgui_guard_run(rec: *mut GuardRec, f: usize, args: *const [u64; 4]) -> u64;
273 fn jfgui_guard_fault_ip();
274}
275
276pub(crate) fn guard_fault_ip() -> usize {
278 jfgui_guard_fault_ip as *const () as usize
279}
280
281pub(crate) fn guard_lookup(tid: u64) -> Option<&'static GtEnt> {
284 let n = (GT_N.load(Ordering::SeqCst) as usize).min(GT_MAX);
285 GT[..n].iter().find(|e| e.tid.load(Ordering::SeqCst) == tid)
286}
287
288fn guard_self() -> Option<&'static GtEnt> {
290 let tid = crate::plat_tid();
291 if let Some(e) = guard_lookup(tid) {
292 return Some(e);
293 }
294 let i = GT_N.fetch_add(1, Ordering::SeqCst) as usize;
295 if i >= GT_MAX {
296 GT_N.store(GT_MAX as u32, Ordering::SeqCst);
297 return None;
298 }
299 GT[i].cur.store(0, Ordering::SeqCst);
300 GT[i].tid.store(tid, Ordering::SeqCst);
301 Some(>[i])
302}
303
304pub fn guard_init() -> i32 {
306 if GUARD_ON.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst).is_ok() {
307 crate::guard_os_install();
308 }
309 0
310}
311
312pub fn guard_tag() -> u64 {
314 let Some(e) = guard_lookup(crate::plat_tid()) else { return 0 };
315 let r = e.cur.load(Ordering::SeqCst);
316 if r == 0 {
317 return 0;
318 }
319 unsafe { (*(r as *const GuardRec)).tag }
322}
323
324pub fn guard_call(f: usize, a1: u64, a2: u64, a3: u64, a4: u64, info: Option<&mut GuardInfo>) -> (u64, u64) {
326 if GUARD_ON.load(Ordering::SeqCst) == 0 {
327 guard_init();
328 }
329 let Some(ent) = guard_self() else {
330 let e = crate::ERR_LIMIT as i64 as u64;
331 return (e, e);
332 };
333 if ent.cur.load(Ordering::SeqCst) == 0 {
334 crate::guard_thread(); }
336 let mut rec = GuardRec {
337 prev: ent.cur.load(Ordering::SeqCst),
338 rsp: 0,
339 rbp: 0,
340 ent: ent as *const GtEnt as u64,
341 kind: 0,
342 code: 0,
343 pc: 0,
344 addr: 0,
345 tag: info.as_ref().map_or(0, |i| i.tag),
346 mxcsr: 0,
347 fcw: 0,
348 pad: [0; 10],
349 xmm: [0; 160],
350 rax: 0,
351 rdx: 0,
352 };
353 let args = [a1, a2, a3, a4];
354 let faulted = unsafe { jfgui_guard_run(&mut rec, f, &args) };
358 let rec = &rec;
359 if faulted == 0 {
360 if let Some(i) = info {
361 i.kind = 0;
362 i.code = 0;
363 i.pc = 0;
364 i.addr = 0;
365 }
366 return (rec.rax, rec.rdx);
367 }
368 if let Some(i) = info {
369 i.kind = rec.kind;
370 i.code = rec.code;
371 i.pc = rec.pc;
372 i.addr = rec.addr;
373 }
374 let e = ERR_FAULT as i64 as u64;
375 (e, e)
376}
377
378pub(crate) fn guard_hit(tid: u64, kind: u32, code: u32, pc: u64, addr: u64) -> Option<(u64, u64)> {
381 let e = guard_lookup(tid)?;
382 let r = e.cur.load(Ordering::SeqCst);
383 if r == 0 {
384 return None;
385 }
386 let gr = r as *mut GuardRec;
387 unsafe {
390 e.cur.store((*gr).prev, Ordering::SeqCst); (*gr).kind = kind;
392 (*gr).code = code;
393 (*gr).pc = pc;
394 (*gr).addr = addr;
395 Some(((*gr).rsp, (*gr).rbp))
396 }
397}
398
399pub fn guard_run<R, F: FnOnce() -> R>(f: F, info: Option<&mut GuardInfo>) -> Result<R, i32> {
404 struct Ctx<F, R> {
405 f: Option<F>,
406 r: Option<R>,
407 }
408 extern "win64" fn thunk<R, F: FnOnce() -> R>(p: u64) -> u64 {
409 let c = unsafe { &mut *(p as *mut Ctx<F, R>) };
411 if let Some(f) = c.f.take() {
412 c.r = Some(f());
413 }
414 0
415 }
416 let mut c = Ctx { f: Some(f), r: None };
417 guard_call(thunk::<R, F> as *const () as usize, &mut c as *mut Ctx<F, R> as u64, 0, 0, 0, info);
418 match c.r.take() {
419 Some(r) => Ok(r),
420 None if c.f.is_some() => Err(crate::ERR_LIMIT), None => Err(ERR_FAULT), }
423}
424
425pub fn guard_read(addr: usize, len: usize) -> Option<alloc::vec::Vec<u8>> {
431 if addr == 0 {
432 return None;
433 }
434 let mut v = alloc::vec::Vec::new();
435 if v.try_reserve_exact(len).is_err() {
436 return None;
437 }
438 let r = guard_run(
439 || {
440 let s = unsafe { core::slice::from_raw_parts(addr as *const u8, len) };
443 v.extend_from_slice(s);
444 },
445 None,
446 );
447 if r.is_ok() && v.len() == len { Some(v) } else { None }
448}
449
450pub fn guard_write(addr: usize, data: &[u8]) -> bool {
453 if addr == 0 {
454 return false;
455 }
456 guard_run(|| unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), addr as *mut u8, data.len()) }, None).is_ok()
459}
460
461pub fn lib_read(h: &Lib, addr: usize, len: usize) -> Option<alloc::vec::Vec<u8>> {
464 let (end, fl) = lib_seg(h, addr);
465 if end == 0 || fl & LIBA_R == 0 || len > end - addr {
466 return None;
467 }
468 Some(unsafe { core::slice::from_raw_parts(addr as *const u8, len) }.to_vec())
470}
471
472core::arch::global_asm!(
478 ".globl jfgui_gt_code",
479 "jfgui_gt_code:",
480 "0:",
481 " lea rax, [rcx + rdx*2]",
482 " lea r10, [r8 + r8*2]",
483 " add rax, r10",
484 " lea rax, [rax + r9*4]",
485 " mov rdx, rcx",
486 " xor rdx, r9",
487 " ret",
488 ".p2align 4",
489 "1:",
490 " mov dword ptr [rcx], 0x0BADF00D",
491 " ret",
492 ".p2align 4",
493 "2:",
494 " ud2",
495 " ret",
496 ".p2align 4",
497 "3:",
498 " int3",
499 " ret",
500 ".p2align 4",
501 "4:",
502 " xor ecx, ecx",
503 " mov eax, 1",
504 " xor edx, edx",
505 " div ecx",
506 " ret",
507 ".p2align 4",
508 "5:",
509 " sub rsp, 4096",
510 " mov [rsp], rax",
511 " call 5b",
512 " add rsp, 4096",
513 " ret",
514 ".p2align 4",
515 "6:",
516 " mov rax, 0x5A5A5A5A5A5A5A5A",
517 " mov rbx, rax",
518 " mov rsi, rax",
519 " mov rdi, rax",
520 " mov rbp, rax",
521 " mov r12, rax",
522 " mov r13, rax",
523 " mov r14, rax",
524 " mov r15, rax",
525 " movq xmm6, rax",
526 " movq xmm7, rax",
527 " movq xmm8, rax",
528 " movq xmm9, rax",
529 " movq xmm10, rax",
530 " movq xmm11, rax",
531 " movq xmm12, rax",
532 " movq xmm13, rax",
533 " movq xmm14, rax",
534 " movq xmm15, rax",
535 " push 0x3F80",
536 " ldmxcsr dword ptr [rsp]",
537 " mov dword ptr [rsp], 0x7F",
538 " fldcw word ptr [rsp]",
539 " add rsp, 8",
540 " std",
541 " mov dword ptr [rcx], eax",
542 " cld",
543 " ret",
544 ".p2align 4",
545 ".globl jfgui_gt_table",
546 "jfgui_gt_table:",
547 " lea rax, [rip + 0b]",
548 " lea rdx, [rip + 1b]",
549 " lea r8, [rip + 2b]",
550 " lea r9, [rip + 3b]",
551 " mov [rcx], rax",
552 " mov [rcx + 8], rdx",
553 " mov [rcx + 16], r8",
554 " mov [rcx + 24], r9",
555 " lea rax, [rip + 4b]",
556 " lea rdx, [rip + 5b]",
557 " lea r8, [rip + 6b]",
558 " mov [rcx + 32], rax",
559 " mov [rcx + 40], rdx",
560 " mov [rcx + 48], r8",
561 " ret",
562 ".globl jfgui_gt_state",
564 "jfgui_gt_state:",
565 " sub rsp, 8",
566 " stmxcsr dword ptr [rsp]",
567 " mov eax, [rsp]",
568 " fnstcw word ptr [rsp + 4]",
569 " movzx edx, word ptr [rsp + 4]",
570 " pushfq",
571 " pop rcx",
572 " and ecx, 0x400",
573 " shl ecx, 6",
574 " or edx, ecx",
575 " shl rdx, 32",
576 " or rax, rdx",
577 " add rsp, 8",
578 " ret",
579);
580
581unsafe extern "win64" {
582 fn jfgui_gt_table(t: *mut [usize; 7]);
583 fn jfgui_gt_state() -> u64;
584}
585
586pub fn guard_test_code(k: u32) -> usize {
588 let mut t = [0usize; 7];
589 unsafe { jfgui_gt_table(&mut t) };
591 t.get(k as usize).copied().unwrap_or(0)
592}
593
594pub fn guard_test_state() -> (u32, u32, bool) {
596 let v = unsafe { jfgui_gt_state() };
598 let d = (v >> 32) as u32;
599 (v as u32, d & 0xFFFF, d >> 16 & 1 != 0)
600}