From 475ad1b5f7d9cdea274a93217d339baa9ecf228d Mon Sep 17 00:00:00 2001 From: Josef Schlehofer Date: Sat, 15 Aug 2026 11:21:08 +0200 Subject: [PATCH] luajit2: fix build and runtime on powerpc luajit2 currently fails to link for powerpc: lj_vm.o emits PLT calls that force BSS-PLT, which the inline-PLT relocations emitted by GCC 12 and newer do not support. Add the same GOT-based fix that lang/lua/luajit has carried as 060-ppc-musl.patch since 2019. With that in place the package builds, but ipairs() and pairs() turn out to be broken on 32-bit big-endian soft-float targets. Both are regressions in luajit2's PPC64 patch and do not affect upstream LuaJIT. To be submitted to openresty/luajit2 as well. Signed-off-by: Josef Schlehofer --- lang/lua/luajit2/Makefile | 2 +- ...PPC-Fix-ipairs-on-soft-float-targets.patch | 40 +++++ ...-over-hash-less-tables-on-soft-float.patch | 44 ++++++ ...PC-Call-libm-helpers-via-private-GOT.patch | 142 ++++++++++++++++++ 4 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 lang/lua/luajit2/patches/030-PPC-Fix-ipairs-on-soft-float-targets.patch create mode 100644 lang/lua/luajit2/patches/040-PPC-Fix-pairs-over-hash-less-tables-on-soft-float.patch create mode 100644 lang/lua/luajit2/patches/050-PPC-Call-libm-helpers-via-private-GOT.patch diff --git a/lang/lua/luajit2/Makefile b/lang/lua/luajit2/Makefile index 313051df3e6d20..c61183cced919b 100644 --- a/lang/lua/luajit2/Makefile +++ b/lang/lua/luajit2/Makefile @@ -3,7 +3,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luajit2 PKG_SOURCE_DATE:=2026-02-27 PKG_VERSION:=2.1.$(subst -,.,$(PKG_SOURCE_DATE)) -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/openresty/luajit2 diff --git a/lang/lua/luajit2/patches/030-PPC-Fix-ipairs-on-soft-float-targets.patch b/lang/lua/luajit2/patches/030-PPC-Fix-ipairs-on-soft-float-targets.patch new file mode 100644 index 00000000000000..2a0d68332b404f --- /dev/null +++ b/lang/lua/luajit2/patches/030-PPC-Fix-ipairs-on-soft-float-targets.patch @@ -0,0 +1,40 @@ +From 2b0b4f7fdb5fdefd13c5cbdeb159881da3098c96 Mon Sep 17 00:00:00 2001 +From: Josef Schlehofer +Date: Sat, 15 Aug 2026 09:11:08 +0200 +Subject: [PATCH] PPC: Fix ipairs() on soft-float targets + +The soft-float branch of the ipairs_aux fast function loads the array +slot value from WORD_HI instead of WORD_LO. On big-endian targets, +WORD_HI contains the itype while the value word is in WORD_LO, so the +itype is loaded twice and the actual value is never loaded. + +Every element therefore comes back carrying the itype in its payload. +Numbers surface as -14, the LJ_TNUMX tag read as an int32. For GC +types the payload is the GCref, so the tag becomes a fabricated +pointer that tostring() then dereferences. + +The original soft-float code used a hardcoded 4(TMP1), which is +WORD_LO on big-endian. Commit 2763a421 ("Patch for PPC64 support") +rewrote it as WORD_HI. Upstream LuaJIT is unaffected. + +Reproducer on Turris 1.x (e500v2, 32-bit big-endian, soft-float): + + $ luajit -e 'local s=0 for i,v in ipairs({10,20,30}) do s=s+v end print(s)' + -42 -- expected 60 + +Signed-off-by: Josef Schlehofer +--- + src/vm_ppc.dasc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/src/vm_ppc.dasc ++++ b/src/vm_ppc.dasc +@@ -1833,7 +1833,7 @@ static void build_subroutines(BuildCtx * + | lwz TMP2, WORD_HI(TMP1) + |.else + | lwzux TMP2, TMP1, TMP3 +- | lwz TMP3, WORD_HI(TMP1) ++ | lwz TMP3, WORD_LO(TMP1) + |.endif + |1: + | checknil TMP2 diff --git a/lang/lua/luajit2/patches/040-PPC-Fix-pairs-over-hash-less-tables-on-soft-float.patch b/lang/lua/luajit2/patches/040-PPC-Fix-pairs-over-hash-less-tables-on-soft-float.patch new file mode 100644 index 00000000000000..def3dbc8afdd06 --- /dev/null +++ b/lang/lua/luajit2/patches/040-PPC-Fix-pairs-over-hash-less-tables-on-soft-float.patch @@ -0,0 +1,44 @@ +From 0abde25c632e6b3343d07d978d9cc4209fe05f58 Mon Sep 17 00:00:00 2001 +From: Josef Schlehofer +Date: Sat, 15 Aug 2026 09:11:08 +0200 +Subject: [PATCH] PPC: Fix pairs() over hash-less tables on soft-float targets + +BC_ITERN checks the itype of the node value in RB to skip empty slots +in the hash part. Upstream loads RB unconditionally before the FPU +split; commit 2763a421 ("Patch for PPC64 support") moved that load +into the FPU branch only. On soft-float builds, RB still contains +RC*8 from the hash-part setup, so the nil check never succeeds and +iterating a table whose hash part is empty yields one extra +(nil, nil) pair. + +Check CARG1 instead, which the soft-float branch already loads with +the itype. The FPU branch is left untouched. Upstream LuaJIT is +unaffected. + +Reproducer on Turris 1.x (e500v2, 32-bit big-endian, soft-float): + + $ luajit -e 'for k,v in pairs({7,8,9}) do print(k,v) end' + 1 7 + 2 8 + 3 9 + nil nil -- spurious + +Signed-off-by: Josef Schlehofer +--- + src/vm_ppc.dasc | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/src/vm_ppc.dasc ++++ b/src/vm_ppc.dasc +@@ -5807,7 +5807,11 @@ static void build_ins(BuildCtx *ctx, BCO + | lwz CARG2, 4(CARG3) + | add NODE:TMP3, TMP2, TMP3 + |.endif ++ |.if FPU + | checknil RB ++ |.else ++ | checknil CARG1 ++ |.endif + | lwz INS, -4(PC) + | beq >7 + |.if FPU diff --git a/lang/lua/luajit2/patches/050-PPC-Call-libm-helpers-via-private-GOT.patch b/lang/lua/luajit2/patches/050-PPC-Call-libm-helpers-via-private-GOT.patch new file mode 100644 index 00000000000000..c850259e556331 --- /dev/null +++ b/lang/lua/luajit2/patches/050-PPC-Call-libm-helpers-via-private-GOT.patch @@ -0,0 +1,142 @@ +From 3bc16e0b58704b8c3bd596b23d907516c7cc2493 Mon Sep 17 00:00:00 2001 +From: Clint Bland +Date: Thu, 14 Mar 2019 02:19:16 +0000 +Subject: [PATCH] PPC: Call libm helpers via private GOT instead of the PLT + +A bl sym@plt call emitted by the VM assembler produces an +R_PPC_PLTREL24 relocation with addend 0, which forces the linker to +use BSS-PLT for the entire link. GCC 12 and newer emit inline-PLT +relocations (R_PPC_PLTSEQ, R_PPC_PLTCALL, R_PPC_PLT16_*) that have no +BSS-PLT equivalent, so linking libluajit.so fails outright: + + ld: bss-plt forced due to lj_vm.o + ld: crtstuff.c:(.text+0x46): R_PPC_PLT16_HA relocation unsupported + for bss-plt + +Route these calls through a private GOT stored in GG_State, the way +the MIPS port already does. No PLT relocation is emitted, the linker +selects secure-PLT and the link succeeds. + +Originally submitted upstream as LuaJIT/LuaJIT#486, against 2.0 and +addressing LuaJIT/LuaJIT#481, and closed without being merged. OpenWrt +has carried it for the upstream luajit package as 060-ppc-musl.patch +since 2019, extended with the soft-float helpers the 2.1 branch needs +in the GOT. Adapted here for luajit2 and verified on OpenWrt. + +Tested on Turris 1.x (e500v2, 32-bit big-endian, musl, soft-float) +with BUILDMODE=dynamic. No R_PPC_PLTREL24 relocations remain, and the +resulting binary runs correctly, including all libm calls routed +through the new GOT. The previously produced dynamic binary crashed +during startup. + +Co-authored-by: Josef Schlehofer +Signed-off-by: Josef Schlehofer +--- + src/lj_dispatch.c | 11 ++++++++++- + src/lj_dispatch.h | 32 +++++++++++++++++++++++++++++++- + src/vm_ppc.dasc | 12 +++++++++++- + 3 files changed, 52 insertions(+), 3 deletions(-) + +--- a/src/lj_dispatch.c ++++ b/src/lj_dispatch.c +@@ -56,6 +56,15 @@ static const ASMFunction dispatch_got[] + #undef GOTFUNC + #endif + ++#if LJ_TARGET_PPC && LJ_32 ++#include ++#define GOTFUNC(name) (ASMFunction)name, ++static const ASMFunction dispatch_got[] = { ++ GOTDEF(GOTFUNC) ++}; ++#undef GOTFUNC ++#endif ++ + /* Initialize instruction dispatch table and hot counters. */ + void lj_dispatch_init(GG_State *GG) + { +@@ -76,7 +85,7 @@ void lj_dispatch_init(GG_State *GG) + GG->g.bc_cfunc_ext = GG->g.bc_cfunc_int = BCINS_AD(BC_FUNCC, LUA_MINSTACK, 0); + for (i = 0; i < GG_NUM_ASMFF; i++) + GG->bcff[i] = BCINS_AD(BC__MAX+i, 0, 0); +-#if LJ_TARGET_MIPS ++#if LJ_TARGET_MIPS || (LJ_TARGET_PPC && LJ_32) + memcpy(GG->got, dispatch_got, LJ_GOT__MAX*sizeof(ASMFunction *)); + #endif + } +--- a/src/lj_dispatch.h ++++ b/src/lj_dispatch.h +@@ -66,6 +66,36 @@ GOTDEF(GOTENUM) + }; + #endif + ++#if LJ_TARGET_PPC && LJ_32 ++/* Call libm/libgcc helpers via our own GOT instead of the PLT. A PLT call ++** from the VM assembler forces the obsolete BSS-PLT for the whole link, ++** which fails against secure-PLT objects emitted by GCC 12 and newer. ++*/ ++#if LJ_SOFTFP ++#ifndef _LJ_IRCALL_H ++extern double __ledf2(double a, double b); ++extern double __adddf3(double a, double b); ++extern double __subdf3(double a, double b); ++extern double __muldf3(double a, double b); ++extern double __divdf3(double a, double b); ++#endif ++#define SFGOTDEF(_) _(__ledf2) _(__adddf3) _(__subdf3) _(__muldf3) _(__divdf3) ++#else ++#define SFGOTDEF(_) ++#endif ++#define GOTDEF(_) \ ++ _(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \ ++ _(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \ ++ _(pow) _(fmod) _(ldexp) _(sqrt) SFGOTDEF(_) ++ ++enum { ++#define GOTENUM(name) LJ_GOT_##name, ++GOTDEF(GOTENUM) ++#undef GOTENUM ++ LJ_GOT__MAX ++}; ++#endif ++ + /* Type of hot counter. Must match the code in the assembler VM. */ + /* 16 bits are sufficient. Only 0.0015% overhead with maximum slot penalty. */ + typedef uint16_t HotCount; +@@ -93,7 +123,7 @@ typedef struct GG_State { + /* Make g reachable via K12 encoded DISPATCH-relative addressing. */ + uint8_t align1[(16-sizeof(global_State))&15]; + #endif +-#if LJ_TARGET_MIPS ++#if LJ_TARGET_MIPS || (LJ_TARGET_PPC && LJ_32) + ASMFunction got[LJ_GOT__MAX]; /* Global offset table. */ + #endif + #if LJ_HASJIT +--- a/src/vm_ppc.dasc ++++ b/src/vm_ppc.dasc +@@ -50,7 +50,13 @@ + |.macro blex, target; bl extern target; nop; .endmacro + |.macro .toc, a, b; a, b; .endmacro + |.else +-|.macro blex, target; bl extern target@plt; .endmacro ++|// Call via our own GOT to avoid PLT relocations, which force the obsolete ++|// BSS-PLT on PPC32 and break linking against secure-PLT objects. ++|.macro blex, target ++| lwz TMP0, DISPATCH_GOT(target)(DISPATCH) ++| mtctr TMP0 ++| bctrl ++|.endmacro + |.macro .toc, a, b; .endmacro + |.endif + |.if OPD +@@ -577,6 +583,10 @@ + |// Assumes DISPATCH is relative to GL. + #define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) + #define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) ++#if LJ_TARGET_PPC && LJ_32 ++#define GG_DISP2GOT (GG_OFS(got) - GG_OFS(dispatch)) ++#define DISPATCH_GOT(name) (GG_DISP2GOT + 4*LJ_GOT_##name) ++#endif + | + #define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) + |