paavohtl Claude Opus 4.6 commited on
Commit
559d0fd
·
1 Parent(s): 170b697

refactor: add usercall_trampoline! macro for naked asm generation

Browse files

Replaces hand-written naked trampolines in frontend.rs and scheme.rs
with a declarative macro that handles register capture + stack forwarding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

crates/openwa-wormkit/src/hook.rs CHANGED
@@ -1,4 +1,59 @@
1
  //! MinHook helper for installing inline hooks on WA.exe functions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  use std::ffi::c_void;
4
 
 
1
  //! MinHook helper for installing inline hooks on WA.exe functions.
2
+ //!
3
+ //! Also provides the [`usercall_trampoline!`] macro for generating naked asm
4
+ //! trampolines that capture implicit register params from MSVC `__usercall`
5
+ //! functions and forward them to cdecl Rust implementations.
6
+
7
+ /// Generate a naked trampoline for a `__usercall` function.
8
+ ///
9
+ /// Captures one implicit register param and optionally forwards stack params
10
+ /// to a `cdecl` Rust implementation function.
11
+ ///
12
+ /// The impl_fn signature must be: `extern "cdecl" fn(reg_value, stack_args...) -> R`
13
+ ///
14
+ /// # Variants
15
+ ///
16
+ /// ```ignore
17
+ /// // 1 register, 0 stack params, plain ret
18
+ /// usercall_trampoline!(fn name; impl_fn = path; reg = eax);
19
+ ///
20
+ /// // 1 register, 1 stack param, ret with cleanup
21
+ /// usercall_trampoline!(fn name; impl_fn = path; reg = esi;
22
+ /// stack_params = 1; ret_bytes = "0x4");
23
+ /// ```
24
+ macro_rules! usercall_trampoline {
25
+ // 1 register arg, 0 stack params, plain ret
26
+ (fn $name:ident; impl_fn = $impl:path; reg = $reg:ident) => {
27
+ #[unsafe(naked)]
28
+ unsafe extern "C" fn $name() {
29
+ core::arch::naked_asm!(
30
+ concat!("push ", stringify!($reg)),
31
+ "call {impl_fn}",
32
+ "add esp, 4",
33
+ "ret",
34
+ impl_fn = sym $impl,
35
+ );
36
+ }
37
+ };
38
+
39
+ // 1 register arg, 1 stack param, ret N
40
+ (fn $name:ident; impl_fn = $impl:path; reg = $reg:ident;
41
+ stack_params = 1; ret_bytes = $ret:literal) => {
42
+ #[unsafe(naked)]
43
+ unsafe extern "C" fn $name() {
44
+ core::arch::naked_asm!(
45
+ "push [esp+4]",
46
+ concat!("push ", stringify!($reg)),
47
+ "call {impl_fn}",
48
+ "add esp, 8",
49
+ concat!("ret ", $ret),
50
+ impl_fn = sym $impl,
51
+ );
52
+ }
53
+ };
54
+ }
55
+
56
+ pub(crate) use usercall_trampoline;
57
 
58
  use std::ffi::c_void;
59
 
crates/openwa-wormkit/src/replacements/frontend.rs CHANGED
@@ -14,24 +14,10 @@ use openwa_types::frontend::ScreenId;
14
  /// Trampoline to the original FrontendChangeScreen (for fallback if needed).
15
  static ORIG_FRONTEND_CHANGE_SCREEN: AtomicU32 = AtomicU32::new(0);
16
 
17
- /// Naked trampoline that captures ESI (the implicit dialog this pointer)
18
- /// and routes it to our cdecl Rust implementation.
19
- ///
20
- /// Stack on entry (from MinHook redirect):
21
- /// [ESP+0] = return address
22
- /// [ESP+4] = screen_id
23
- /// ESI = dialog this (MSVC __usercall convention)
24
- #[unsafe(naked)]
25
- unsafe extern "stdcall" fn trampoline(_screen_id: u32) {
26
- core::arch::naked_asm!(
27
- "push [esp+4]", // push screen_id
28
- "push esi", // push dialog this
29
- "call {impl_fn}", // cdecl call: impl(dialog, screen_id)
30
- "add esp, 8", // clean our two pushes
31
- "ret 0x4", // clean original screen_id from caller's stack
32
- impl_fn = sym frontend_change_screen_impl,
33
- );
34
- }
35
 
36
  /// Rust reimplementation of FrontendChangeScreen.
37
  ///
 
14
  /// Trampoline to the original FrontendChangeScreen (for fallback if needed).
15
  static ORIG_FRONTEND_CHANGE_SCREEN: AtomicU32 = AtomicU32::new(0);
16
 
17
+ // Naked trampoline: captures ESI (dialog this, __usercall) + 1 stack arg (screen_id).
18
+ crate::hook::usercall_trampoline!(fn trampoline;
19
+ impl_fn = frontend_change_screen_impl; reg = esi;
20
+ stack_params = 1; ret_bytes = "0x4");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  /// Rust reimplementation of FrontendChangeScreen.
23
  ///
crates/openwa-wormkit/src/replacements/scheme.rs CHANGED
@@ -168,20 +168,9 @@ unsafe fn call_original_read_file(dest: u32, path: u32, flag: u32, out_ptr: u32)
168
  // Scheme__ValidateExtendedOptions replacement (0x4D5110)
169
  // ============================================================
170
 
171
- /// Naked trampoline for Scheme__ValidateExtendedOptions.
172
- ///
173
- /// WA calls this with EAX = pointer to 110-byte extended options data.
174
- /// Plain RET (no stack cleanup). Returns EAX: 0 = valid, 1 = invalid.
175
- #[unsafe(naked)]
176
- unsafe extern "C" fn trampoline_validate_ext_opts() {
177
- core::arch::naked_asm!(
178
- "push eax", // push options pointer as arg
179
- "call {impl_fn}", // cdecl call
180
- "add esp, 4", // clean up our push
181
- "ret", // plain ret (no stack params to clean)
182
- impl_fn = sym validate_extended_options_impl,
183
- );
184
- }
185
 
186
  /// Rust implementation called by the naked trampoline.
187
  unsafe extern "cdecl" fn validate_extended_options_impl(options_ptr: u32) -> u32 {
 
168
  // Scheme__ValidateExtendedOptions replacement (0x4D5110)
169
  // ============================================================
170
 
171
+ // Naked trampoline: captures EAX (110-byte extended options pointer). Returns 0=valid, 1=invalid.
172
+ crate::hook::usercall_trampoline!(fn trampoline_validate_ext_opts;
173
+ impl_fn = validate_extended_options_impl; reg = eax);
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  /// Rust implementation called by the naked trampoline.
176
  unsafe extern "cdecl" fn validate_extended_options_impl(options_ptr: u32) -> u32 {