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