PR #22527 opened by Zhao Zhili (quink) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22527 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22527.patch
The PR depends on https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22526 >From 9fe980e0089dd532ff7a8a177f3f0be904fc6cc3 Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Tue, 17 Mar 2026 16:34:26 +0800 Subject: [PATCH 1/4] checkasm/aarch64: fix operator precedence bug in ARG_STACK The expression ((8*(MAX_ARGS - 8) + 15) & ~15 + 16) is equal to ((8*(MAX_ARGS - 8) + 15) & (~15 + 16)) which evaluated to zero. --- tests/checkasm/aarch64/checkasm.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/checkasm/aarch64/checkasm.S b/tests/checkasm/aarch64/checkasm.S index 6d3c738801..4b2db86a9e 100644 --- a/tests/checkasm/aarch64/checkasm.S +++ b/tests/checkasm/aarch64/checkasm.S @@ -68,7 +68,7 @@ function checkasm_stack_clobber, export=1 endfunc // + 16 for stack canary reference -#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15 + 16) +#define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16) function checkasm_checked_call, export=1 stp x29, x30, [sp, #-16]! -- 2.52.0 >From eff960998fdc5820f903ecf636dacc63ac17f76e Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Tue, 17 Mar 2026 15:59:33 +0800 Subject: [PATCH 2/4] checkasm/aarch64: eliminates serial data dependencies on sp Signed-off-by: Zhao Zhili <[email protected]> --- tests/checkasm/aarch64/checkasm.S | 46 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/checkasm/aarch64/checkasm.S b/tests/checkasm/aarch64/checkasm.S index 4b2db86a9e..2ec6d78240 100644 --- a/tests/checkasm/aarch64/checkasm.S +++ b/tests/checkasm/aarch64/checkasm.S @@ -71,17 +71,18 @@ endfunc #define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16) function checkasm_checked_call, export=1 - stp x29, x30, [sp, #-16]! - mov x29, sp - stp x19, x20, [sp, #-16]! - stp x21, x22, [sp, #-16]! - stp x23, x24, [sp, #-16]! - stp x25, x26, [sp, #-16]! - stp x27, x28, [sp, #-16]! - stp d8, d9, [sp, #-16]! - stp d10, d11, [sp, #-16]! - stp d12, d13, [sp, #-16]! - stp d14, d15, [sp, #-16]! + sub sp, sp, #160 + stp x29, x30, [sp, #144] + add x29, sp, #144 + stp x19, x20, [sp, #128] + stp x21, x22, [sp, #112] + stp x23, x24, [sp, #96] + stp x25, x26, [sp, #80] + stp x27, x28, [sp, #64] + stp d8, d9, [sp, #48] + stp d10, d11, [sp, #32] + stp d12, d13, [sp, #16] + stp d14, d15, [sp] movrel x9, register_init ldp d8, d9, [x9], #16 @@ -183,16 +184,17 @@ function checkasm_checked_call, export=1 1: bl X(checkasm_fail_func) 0: - ldp x0, x1, [sp], #16 - ldp d14, d15, [sp], #16 - ldp d12, d13, [sp], #16 - ldp d10, d11, [sp], #16 - ldp d8, d9, [sp], #16 - ldp x27, x28, [sp], #16 - ldp x25, x26, [sp], #16 - ldp x23, x24, [sp], #16 - ldp x21, x22, [sp], #16 - ldp x19, x20, [sp], #16 - ldp x29, x30, [sp], #16 + ldp x0, x1, [sp] + ldp d14, d15, [sp, #16] + ldp d12, d13, [sp, #32] + ldp d10, d11, [sp, #48] + ldp d8, d9, [sp, #64] + ldp x27, x28, [sp, #80] + ldp x25, x26, [sp, #96] + ldp x23, x24, [sp, #112] + ldp x21, x22, [sp, #128] + ldp x19, x20, [sp, #144] + ldp x29, x30, [sp, #160] + add sp, sp, #176 ret endfunc -- 2.52.0 >From 2852c9b9c5b90b9adb23e0af10c931132d32a278 Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Tue, 17 Mar 2026 19:08:41 +0800 Subject: [PATCH 3/4] checkasm/aarch64: extract macros for code reuse Prepare for adding an alternative code path for Apple platform. No functional change. Signed-off-by: Zhao Zhili <[email protected]> --- tests/checkasm/aarch64/checkasm.S | 189 +++++++++++++++++------------- 1 file changed, 105 insertions(+), 84 deletions(-) diff --git a/tests/checkasm/aarch64/checkasm.S b/tests/checkasm/aarch64/checkasm.S index 2ec6d78240..24c52b2e2d 100644 --- a/tests/checkasm/aarch64/checkasm.S +++ b/tests/checkasm/aarch64/checkasm.S @@ -56,6 +56,107 @@ endconst #define CLOBBER_STACK ((8*MAX_ARGS + 15) & ~15) +.macro save_callee_regs + sub sp, sp, #160 + stp x29, x30, [sp, #144] + add x29, sp, #144 + stp x19, x20, [sp, #128] + stp x21, x22, [sp, #112] + stp x23, x24, [sp, #96] + stp x25, x26, [sp, #80] + stp x27, x28, [sp, #64] + stp d8, d9, [sp, #48] + stp d10, d11, [sp, #32] + stp d12, d13, [sp, #16] + stp d14, d15, [sp] +.endm + +.macro load_callee_init + movrel x9, register_init + ldp d8, d9, [x9], #16 + ldp d10, d11, [x9], #16 + ldp d12, d13, [x9], #16 + ldp d14, d15, [x9], #16 + ldp x19, x20, [x9], #16 + ldp x21, x22, [x9], #16 + ldp x23, x24, [x9], #16 + ldp x25, x26, [x9], #16 + ldp x27, x28, [x9], #16 +.endm + +.macro fill_garbage_regs + movrel x9, register_init + ldp x10, x11, [x9], #32 + ldp x12, x13, [x9], #32 + ldp x14, x15, [x9], #32 + ldp x16, x17, [x9], #32 + ldp x8, x9, [x9] +.endm + +.macro check_reg_neon reg1, reg2 + ldr q1, [x9], #16 + uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d + eor v1.16b, v1.16b, v2.16b + orr v3.16b, v3.16b, v1.16b +.endm + +.macro check_reg reg1, reg2 + ldp x0, x1, [x9], #16 + eor x0, x0, \reg1 + eor x1, x1, \reg2 + orr x3, x3, x0 + orr x3, x3, x1 +.endm + +// verify_and_return: check callee-saved registers, report errors, restore and return. +// Entry: x2 = canary current, x3 = canary reference (inverted) +// [sp] = saved x0/x1 (return values) +// [sp, #16..#160] = saved callee-saved regs +.macro verify_and_return + mvn x3, x3 + cmp x2, x3 + b.ne 2f + + movrel x9, register_init + movi v3.8h, #0 + + check_reg_neon 8, 9 + check_reg_neon 10, 11 + check_reg_neon 12, 13 + check_reg_neon 14, 15 + uqxtn v3.8b, v3.8h + umov x3, v3.d[0] + + check_reg x19, x20 + check_reg x21, x22 + check_reg x23, x24 + check_reg x25, x26 + check_reg x27, x28 + + cbz x3, 0f + + movrel x0, error_message_register + b 1f +2: + movrel x0, error_message_stack +1: + bl X(checkasm_fail_func) +0: + ldp x0, x1, [sp] + ldp d14, d15, [sp, #16] + ldp d12, d13, [sp, #32] + ldp d10, d11, [sp, #48] + ldp d8, d9, [sp, #64] + ldp x27, x28, [sp, #80] + ldp x25, x26, [sp, #96] + ldp x23, x24, [sp, #112] + ldp x21, x22, [sp, #128] + ldp x19, x20, [sp, #144] + ldp x29, x30, [sp, #160] + add sp, sp, #176 + ret +.endm + function checkasm_stack_clobber, export=1 mov x3, sp mov x2, #CLOBBER_STACK @@ -71,29 +172,8 @@ endfunc #define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16) function checkasm_checked_call, export=1 - sub sp, sp, #160 - stp x29, x30, [sp, #144] - add x29, sp, #144 - stp x19, x20, [sp, #128] - stp x21, x22, [sp, #112] - stp x23, x24, [sp, #96] - stp x25, x26, [sp, #80] - stp x27, x28, [sp, #64] - stp d8, d9, [sp, #48] - stp d10, d11, [sp, #32] - stp d12, d13, [sp, #16] - stp d14, d15, [sp] - - movrel x9, register_init - ldp d8, d9, [x9], #16 - ldp d10, d11, [x9], #16 - ldp d12, d13, [x9], #16 - ldp d14, d15, [x9], #16 - ldp x19, x20, [x9], #16 - ldp x21, x22, [x9], #16 - ldp x23, x24, [x9], #16 - ldp x25, x26, [x9], #16 - ldp x27, x28, [x9], #16 + save_callee_regs + load_callee_init sub sp, sp, #ARG_STACK .equ pos, 0 @@ -106,12 +186,7 @@ function checkasm_checked_call, export=1 // Fill x8-x17 with garbage. This doesn't have to be preserved, // but avoids relying on them having any particular value. - movrel x9, register_init - ldp x10, x11, [x9], #32 - ldp x12, x13, [x9], #32 - ldp x14, x15, [x9], #32 - ldp x16, x17, [x9], #32 - ldp x8, x9, [x9] + fill_garbage_regs // For stack overflows, the callee is free to overwrite the parameters // that were passed on the stack (if any), so we can only check after @@ -142,59 +217,5 @@ function checkasm_checked_call, export=1 add sp, sp, #ARG_STACK stp x0, x1, [sp, #-16]! - mvn x3, x3 - cmp x2, x3 - b.ne 2f - - movrel x9, register_init - movi v3.8h, #0 - -.macro check_reg_neon reg1, reg2 - ldr q1, [x9], #16 - uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d - eor v1.16b, v1.16b, v2.16b - orr v3.16b, v3.16b, v1.16b -.endm - check_reg_neon 8, 9 - check_reg_neon 10, 11 - check_reg_neon 12, 13 - check_reg_neon 14, 15 - uqxtn v3.8b, v3.8h - umov x3, v3.d[0] - -.macro check_reg reg1, reg2 - ldp x0, x1, [x9], #16 - eor x0, x0, \reg1 - eor x1, x1, \reg2 - orr x3, x3, x0 - orr x3, x3, x1 -.endm - check_reg x19, x20 - check_reg x21, x22 - check_reg x23, x24 - check_reg x25, x26 - check_reg x27, x28 - - cbz x3, 0f - - movrel x0, error_message_register - b 1f -2: - movrel x0, error_message_stack -1: - bl X(checkasm_fail_func) -0: - ldp x0, x1, [sp] - ldp d14, d15, [sp, #16] - ldp d12, d13, [sp, #32] - ldp d10, d11, [sp, #48] - ldp d8, d9, [sp, #64] - ldp x27, x28, [sp, #80] - ldp x25, x26, [sp, #96] - ldp x23, x24, [sp, #112] - ldp x21, x22, [sp, #128] - ldp x19, x20, [sp, #144] - ldp x29, x30, [sp, #160] - add sp, sp, #176 - ret + verify_and_return endfunc -- 2.52.0 >From f0411fa023f82ace8fa0d6846f3dad4877aa39d0 Mon Sep 17 00:00:00 2001 From: Zhao Zhili <[email protected]> Date: Tue, 17 Mar 2026 19:09:31 +0800 Subject: [PATCH 4/4] checkasm/aarch64: add Apple ARM64 support using wrapper pattern Apple's ARM64 ABI passes variadic arguments on the stack rather than in registers, so the existing checkasm_checked_call approach (which relies on variadic register passing) doesn't work on macOS. Use a wrapper pattern (similar to RISC-V): the C side calls checkasm_set_function() to store the target function pointer, then calls checkasm_checked_call_wrapper() with a normal (non-variadic) prototype. The wrapper loads the stored function pointer and calls it after the usual register init and stack clobber setup. The stored function pointer uses a static global variable for now. Thread-local storage can be considered in the future if needed. --- tests/checkasm/aarch64/checkasm.S | 68 +++++++++++++++++++++++++++++++ tests/checkasm/checkasm.h | 13 +++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/tests/checkasm/aarch64/checkasm.S b/tests/checkasm/aarch64/checkasm.S index 24c52b2e2d..cb927882b0 100644 --- a/tests/checkasm/aarch64/checkasm.S +++ b/tests/checkasm/aarch64/checkasm.S @@ -157,6 +157,8 @@ endconst ret .endm +#if !defined(__APPLE__) + function checkasm_stack_clobber, export=1 mov x3, sp mov x2, #CLOBBER_STACK @@ -219,3 +221,69 @@ function checkasm_checked_call, export=1 verify_and_return endfunc + +#else /* __APPLE__ */ + + .pushsection __DATA,__bss + .align 3 +.Lstored_func: + .space 8 + .popsection + +function checkasm_set_function, export=1 + movrel x9, .Lstored_func + str x0, [x9] + ret +endfunc + +function checkasm_get_wrapper, export=1 + movrel x0, checkasm_checked_call_wrapper + ret +endfunc + +// clobber area + 8-byte canary reference, aligned to 16 +#define ARG_STACK ((CLOBBER_STACK + 8 + 15) & ~15) + +function checkasm_checked_call_wrapper + save_callee_regs + load_callee_init + + sub sp, sp, #ARG_STACK + mov x9, #CLOBBER_STACK + mov x10, sp + mov x11, #0xbeef + movk x11, #0xdead, lsl #16 + movk x11, #0xbeef, lsl #32 + movk x11, #0xdead, lsl #48 +1: + stp x11, x11, [x10], #16 + subs x9, x9, #16 + b.gt 1b + +.equ pos, 0 +.rept MAX_ARGS-8 + ldr x9, [x29, #16 + pos] + str x9, [sp, #pos] +.equ pos, pos + 8 +.endr + + ldr x9, [sp, #(MAX_ARGS-8)*8] + mvn x9, x9 + str x9, [sp, #(ARG_STACK-8)] + + fill_garbage_regs + + movrel x16, .Lstored_func + ldr x16, [x16] + blr x16 + + ldr x2, [sp, #(MAX_ARGS-8)*8] + ldr x3, [sp, #(ARG_STACK-8)] + + add sp, sp, #(ARG_STACK) + stp x0, x1, [sp, #-16]! + + verify_and_return +endfunc + +#endif /* __APPLE__ */ diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index 25654b20ba..e0b4be5273 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -295,7 +295,18 @@ extern void (*checkasm_checked_call)(void *func, int dummy, ...); (checkasm_set_signal_handler_state(1),\ checked_call((func), 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0));\ checkasm_set_signal_handler_state(0) -#elif ARCH_AARCH64 && !defined(__APPLE__) +#elif ARCH_AARCH64 && defined(__APPLE__) +/* Apple ARM64: Use wrapper pattern (like RISC-V) to avoid variadic ABI issues. + * Apple's ABI passes variadic arguments on the stack, not in registers. */ +void checkasm_set_function(void *); +void *checkasm_get_wrapper(void); +#define declare_new(ret, ...) \ + ret (*checked_call)(__VA_ARGS__) = checkasm_get_wrapper(); +#define checkasm_call_checked(func, ...) \ + (checkasm_set_signal_handler_state(1),\ + checkasm_set_function(func), checked_call(__VA_ARGS__));\ + checkasm_set_signal_handler_state(0) +#elif ARCH_AARCH64 void checkasm_stack_clobber(uint64_t clobber, ...); void checkasm_checked_call(void *func, ...); #define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, int, int, int, __VA_ARGS__,\ -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
