From b39984e1df38184d12b326d254b32c0825bfc6e8 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:02:31 -0700 Subject: [PATCH] ffi: preserve link register in ppc64 trampoline The ppc64 fast FFI trampoline uses `bl` to obtain the address used to load its target literal. This overwrites the caller's link register, so optimized FFI calls return into the trampoline and loop indefinitely. Save and restore the caller's link register around the branch. Also align the target literal for both even and odd GP argument counts. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol --- src/ffi/platforms/ppc64.cc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ffi/platforms/ppc64.cc b/src/ffi/platforms/ppc64.cc index a0a53cfdb44363..78cd914405618c 100644 --- a/src/ffi/platforms/ppc64.cc +++ b/src/ffi/platforms/ppc64.cc @@ -134,13 +134,21 @@ extern "C" bool node_ffi_create_fast_trampoline( } // Load the target address from the literal pool into r12, then branch through - // CTR. ELFv2 functions can use r12 to establish their TOC on global entry. - Emit32(&cursor, Bl(1)); // bl .+4 - Emit32(&cursor, Mfspr(12, 8)); // mflr r12 - Emit32(&cursor, Ld(12, 12, 20)); // ld r12, literal-mflr(r12) - Emit32(&cursor, Mtspr(9, 12)); // mtctr r12 - Emit32(&cursor, 0x4e800420); // bctr - Emit32(&cursor, 0x60000000); // nop; align literal to 8 bytes + // CTR. Save the caller's link register before using `bl` to obtain the + // trampoline's address, and restore it before the tail branch so the native + // target returns to V8 rather than back into the trampoline. ELFv2 functions + // can use r12 to establish their TOC on global entry. + Emit32(&cursor, Mfspr(0, 8)); // mflr r0 + Emit32(&cursor, Bl(1)); // bl .+4 + Emit32(&cursor, Mfspr(12, 8)); // mflr r12 + Emit32(&cursor, Mtspr(8, 0)); // mtlr r0 + const unsigned literal_offset = gp_count % 2 == 0 ? 24 : 20; + Emit32(&cursor, Ld(12, 12, literal_offset)); + Emit32(&cursor, Mtspr(9, 12)); // mtctr r12 + Emit32(&cursor, 0x4e800420); // bctr + if (gp_count % 2 == 0) { + Emit32(&cursor, 0x60000000); // nop; align literal to 8 bytes + } Emit64(&cursor, reinterpret_cast(target)); const size_t written = reinterpret_cast(cursor) -