master: ppc64: fix big-endian ELFv2 build
stassats via Sbcl-commits <[email protected]> Sat, 06 Jun 2026 19:23:13 +0000
| Newsgroups | gmane.lisp.steel-bank.cvs |
|---|---|
| Message-ID | <[email protected]> |
The branch "master" has been updated in SBCL:
via db0328683bd10509f6c02f5c6e41716ebf18e6c2 (commit)
from 3efad92f20327eae7c3398043dc883803aa3fb9d (commit)
- Log -----------------------------------------------------------------
commit db0328683bd10509f6c02f5c6e41716ebf18e6c2
Author: Piotr Kubaj <[email protected]>
Date: Sat Jun 6 18:36:03 2026 +0200
ppc64: fix big-endian ELFv2 build
There are numerous assumptions that big-endian => ELFv1, little-endian
=> ELFv2. That is incorrect, there are systems running on big-endian
ELFv2.
---
make-config.sh | 10 ++++++++++
src/compiler/ppc64/c-call.lisp | 14 +++++++-------
src/compiler/ppc64/parms.lisp | 4 +++-
src/runtime/ppc-arch.c | 6 ++++--
src/runtime/ppc64-assem.S | 5 ++++-
5 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/make-config.sh b/make-config.sh
index 5c11da0e5..06f33bd04 100755
--- a/make-config.sh
+++ b/make-config.sh
@@ -765,6 +765,16 @@ case "$sbcl_arch" in
fi
;;
ppc64)
+ # The ppc64 C calling convention is either ELFv1, which passes function
+ # pointers as 3-word descriptors, or ELFv2, which branches to the entry
+ # address directly. This is a property of the toolchain, independent of
+ # endianness (ppc64le is always ELFv2; ppc64 big-endian may be either), so
+ # ask the C compiler which ABI it targets via its _CALL_ELF predefine
+ # (2 = ELFv2, 1 or undefined = ELFv1) and add :ppc64-elfv1 for the
+ # descriptor ABI.
+ if [ "`echo | ${CC:-cc} -E -dM - 2>/dev/null | grep -w _CALL_ELF | awk '{print $3}'`" != 2 ]; then
+ printf ' :ppc64-elfv1' >> $ltf
+ fi
;;
riscv)
if [ "$xlen" = "64" ]; then
diff --git a/src/compiler/ppc64/c-call.lisp b/src/compiler/ppc64/c-call.lisp
index 25f667d78..9d4526e5b 100644
--- a/src/compiler/ppc64/c-call.lisp
+++ b/src/compiler/ppc64/c-call.lisp
@@ -25,7 +25,7 @@
;;;; "The stack pointer (stored in r1) shall maintain quadword alignment."
;;;; (quadword = 16 bytes)
(defconstant +stack-alignment-mask+ 15)
-(defconstant +stack-frame-size+ #+little-endian 12 #+big-endian 14)
+(defconstant +stack-frame-size+ #-ppc64-elfv1 12 #+ppc64-elfv1 14)
(defstruct arg-state
(gpr-args 0)
@@ -139,7 +139,7 @@
(arg-tns (invoke-alien-type-method :arg-tn arg-type arg-state)))
(values (make-wired-tn* 'positive-fixnum any-reg-sc-number nsp-offset)
(let ((size (arg-state-stack-frame-size arg-state)))
- (cond #+little-endian
+ (cond #-ppc64-elfv1
((= size +stack-frame-size+)
;; no stack args
0)
@@ -261,7 +261,7 @@
(make-fpr (n)
(make-random-tn (sc-or-lose 'double-reg) n)))
(let* ((segment (make-segment))
- #+big-endian
+ #+ppc64-elfv1
(function-descriptor-size 24))
(assemble (segment 'nil)
;; Copy args from registers or stack to new position
@@ -387,7 +387,7 @@
(bug "Unknown alien floating point type: ~S" type))))))
;; Leave a gap for a PPC64ELF ABIv1 function descriptor,
;; to be filled in later relative to the SAP.
- #+big-endian
+ #+ppc64-elfv1
(dotimes (k (/ function-descriptor-size 4)) ; nop is 4 bytes
(inst nop))
(mapc #'save-arg
@@ -411,9 +411,9 @@
(inst stdu stack-pointer stack-pointer (- frame-size))
;; And make the call.
- #+little-endian
+ #-ppc64-elfv1
(load-address-into r0 (callback_wrapper_trampoline))
- #+big-endian
+ #+ppc64-elfv1
(destructuring-bind (r2 r12) (mapcar #'make-gpr '(2 12))
(load-address-into r12 (callback_wrapper_trampoline))
(inst ld r0 r12 0)
@@ -461,7 +461,7 @@
;; instruction of the wrapper. This assembler wrapper only
;; cares about the address, so leave the other descriptor
;; fields filled with no-op instructions.
- #+big-endian
+ #+ppc64-elfv1
(setf (sap-ref-64 sap 0) (+ (sap-int sap) function-descriptor-size))
(alien-funcall
(extern-alien "ppc_flush_icache"
diff --git a/src/compiler/ppc64/parms.lisp b/src/compiler/ppc64/parms.lisp
index aa5ed948d..e16310630 100644
--- a/src/compiler/ppc64/parms.lisp
+++ b/src/compiler/ppc64/parms.lisp
@@ -80,7 +80,9 @@
:dynamic-space-start #x1000000000)
(defconstant alien-linkage-table-growth-direction :up)
-(defconstant alien-linkage-table-entry-size #+little-endian 28 #+big-endian 24)
+;; ELFv2 uses a 7-instruction inline jump (28 bytes); the ELFv1 ABI uses
+;; a 3-word function descriptor (24 bytes).
+(defconstant alien-linkage-table-entry-size #-ppc64-elfv1 28 #+ppc64-elfv1 24)
(defenum (:start 8)
diff --git a/src/runtime/ppc-arch.c b/src/runtime/ppc-arch.c
index b626feec5..b429cdc1d 100644
--- a/src/runtime/ppc-arch.c
+++ b/src/runtime/ppc-arch.c
@@ -616,7 +616,9 @@ arch_write_linkage_table_entry(int index, void *target_addr, int datap)
// but trick the compiler into thinking it isn't, so that it does not
// indirect through a descriptor, but instead we get its logical address.
if (target_addr != &call_into_c) {
-#ifdef LISP_FEATURE_LITTLE_ENDIAN
+#ifndef LISP_FEATURE_PPC64_ELFV1
+ /* ELFv2: no function descriptors, so the linkage entry is an inline
+ * jump that materializes the target address in r12 and branches to it. */
int* inst_ptr;
unsigned long a0,a16,a32,a48;
unsigned int inst;
@@ -670,7 +672,7 @@ arch_write_linkage_table_entry(int index, void *target_addr, int datap)
os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - reloc_addr);
#else
- // Could use either ABI, but we're assuming v1
+ /* ELFv1: function pointers are descriptors, as detailed below. */
/* In the 64-bit v1 ABI, function pointers are alway passed around
* as "function descriptors", not directly the jump target address.
* A descriptor is 3 words:
diff --git a/src/runtime/ppc64-assem.S b/src/runtime/ppc64-assem.S
index 00d481356..beeabaab6 100644
--- a/src/runtime/ppc64-assem.S
+++ b/src/runtime/ppc64-assem.S
@@ -259,7 +259,7 @@ Low Address
/* "When a function is entered through its global entry point,
* register r12 contains the entry-point address." */
-#ifdef LISP_FEATURE_BIG_ENDIAN
+#ifdef LISP_FEATURE_PPC64_ELFV1
mfctr 11
ld reg_CFUNC, 0(11)
/* In the v1 64-bit ABI, a function pointer is a pointer to a
@@ -273,6 +273,9 @@ Low Address
// ld 11, 16(11)
mtctr reg_CFUNC
#else
+ /* ELFv2: the call target is the function's address itself; reg_CFUNC
+ * is r12, which the global entry point uses to set up its own TOC.
+ * No descriptor indirection. */
mfctr reg_CFUNC
#endif
/* Into C we go. */
-----------------------------------------------------------------------
hooks/post-receive
--
SBCL