Skip to content

arm64: register the fake-stack-frame istruct class - #615

Closed
cyclistmass wants to merge 1 commit into
Clozure:arm64from
cyclistmass:issue-610-fake-stack-frame-istruct
Closed

cyclistmass wants to merge 1 commit into
Clozure:arm64from
cyclistmass:issue-610-fake-stack-frame-istruct

Conversation

@cyclistmass

Copy link
Copy Markdown
Contributor

Closes #610.

After any error that a trap raises, a backtrace faults:

> Error: Fault during read of memory address #x30000000015BF4
> While executing: CCL::CLASS-CELL-TYPEP

The address is the same on the reporter's v1.13-397 build and on mine, because
it is deterministic. The chain, each step observed in a live image:

  1. On arm64, as on PPC, the trap-support callbacks pass %error a
    FAKE-STACK-FRAME istruct as its error-pointer. x86-64 passes a fixnum frame
    pointer, which is why x86-64 never faults here.
  2. Backtrace argument recovery is correct. The %error frame's symbol map homes
    ERROR-POINTER at vsp slot 1, and the recovered value is a valid, live
    fake-stack-frame istruct.
  3. map-entry-value asks (typep value 'value-cell) about every recovered
    value, at lib/backtrace.lisp:354. class-cell-typep takes the wrapper from
    (istruct-cell-info (%svref instance 0)).
  4. Nothing on arm64 calls make-istruct-class for CCL::FAKE-STACK-FRAME, so
    the cell info is NIL. class-cell-typep then chases a NIL wrapper at
    safety 0. The reads are relative to NIL in static space, so the faulting
    address is a per-architecture constant.

Two files in the tree call make-istruct-class: compiler/ARM/arm-arch.lisp and
level-1/l1-clos-boot.lisp. ARM32 registers this istruct at
compiler/ARM/arm-arch.lisp:1446. arm64 does not.

Neither does PPC, so PPC carries the same latent fault. I have not tested that
and cannot: there is no PPC machine here.

This patch adds the one form to compiler/ARM64/arm64-arch.lisp, matching ARM32.
The istruct name is the CCL-package symbol, from lispequ.lisp's
%cons-fake-stack-frame.

Control. Both halves built and run on linuxarm64 at d15b911b, the commit
before this branch tip. The two builds are identical apart from this patch, and
the kernel binary is the same on both sides. The boot image is byte-identical
too, because this is a compiler file and the change lands in the fasl set. The
fasl sets differ.

  • RED, this patch withheld:
    (typep (ccl::%istruct 'ccl::fake-stack-frame 0 0 0 0 0 0 0) 'ccl::value-cell)
    gives Fault during read of memory address #x30000000015BF4 in
    CLASS-CELL-TYPEP, and no later form in the probe runs.
  • GREEN, this patch applied: the same form returns NIL, and
    print-call-history inside a handler-bind for an error that a trap
    raises completes, producing a 6046-character backtrace.

On the GREEN build the ANSI suite reports 21679 tests and 0 failures, and the
CCL-specific suite (tests/ccl.lsp) reports 243 tests and 0 failures.

The patch also applies with git apply --check to 2bf519ce itself, which
changes only comments in level-0/ARM64/arm64-clos.lisp.

Not taken here. class-cell-typep has no guard for a NIL wrapper, so any
unregistered istruct name reproduces this with
(typep (%istruct '<name> 0) '<some-class>). A guard there would turn a fault
into a type error for every such name. That is wider than this issue needs, and
it belongs with l1-clos-boot.lisp rather than with an arm64 fix.

VERIFIED: built and run on linuxarm64. The file is under compiler/ARM64/, so
no other target is affected. Not built on any other target.

Reproduced from Clozure/ccl issue Clozure#610: after any trap-raised error,
:b (or print-call-history) faults:

  > Error: Fault during read of memory address #x30000000015BF4
  > While executing: CCL::CLASS-CELL-TYPEP

The address is the same on the reporter's v1.13-397 build and on ours,
because it is deterministic.  The chain, each step observed live:

1. On arm64 (the PPC model), the trap-support callbacks pass %error a
   FAKE-STACK-FRAME istruct as its error-pointer.  x86-64 passes a
   fixnum frame pointer, which is why x86-64 never faults here.
2. Backtrace argument recovery is correct: the %error frame's symbol
   map homes ERROR-POINTER at vsp slot 1, and the recovered value is a
   valid, live fake-stack-frame istruct.
3. map-entry-value asks (typep value 'value-cell) about every recovered
   value, at lib/backtrace.lisp:354.  class-cell-typep takes the
   wrapper from (istruct-cell-info (%svref instance 0)).
4. Nothing on arm64 calls make-istruct-class for the name
   CCL::FAKE-STACK-FRAME, so the cell info is NIL.  class-cell-typep
   then chases the NIL wrapper at safety 0.  The reads are relative to
   NIL in static space, so the faulting address is a per-architecture
   constant.

Two files in the tree call make-istruct-class: compiler/ARM/arm-arch.lisp
and level-1/l1-clos-boot.lisp.  ARM32 registers this istruct at
arm-arch.lisp:1446.  arm64 does not.  Neither does PPC, so PPC carries
the same latent fault; I have not tested that, because there is no PPC
machine here.

This patch adds the one form, matching ARM32.  The istruct name is the
CCL-package symbol, from lispequ.lisp's %cons-fake-stack-frame.

Controls, both halves built and run on linuxarm64 at d15b911, the
commit before this branch tip.  The two builds are identical apart from
this patch, and the kernel binary is the same on both sides.  The boot
image is byte-identical too, because this is a compiler file and the
change lands in the fasl set; the fasl sets differ.

  RED, this patch withheld:
    (typep (ccl::%istruct 'ccl::fake-stack-frame 0 0 0 0 0 0 0)
           'ccl::value-cell)
    => Error: Fault during read of memory address #x30000000015BF4
       While executing: CCL::CLASS-CELL-TYPEP
    and no later form in the probe runs.

  GREEN, this patch applied:
    the same form returns NIL, and print-call-history inside a
    handler-bind for a trap-raised error completes, producing a
    6046-character backtrace.

On the GREEN build the ANSI suite reports 21679 tests and 0 failures,
and the CCL-specific suite (tests/ccl.lsp) reports 243 tests and 0
failures.

The patch also applies with git apply --check to 2bf519c itself, which
changes only comments in level-0/ARM64/arm64-clos.lisp.

Note for a wider fix, not taken here: class-cell-typep has no guard
for a NIL wrapper, so ANY unregistered istruct name reproduces this
with (typep (ccl::%istruct '<name> 0) '<some-class>).
@xrme

xrme commented Aug 26, 2026

Copy link
Copy Markdown
Member

This is a necessary fix; thanks for finding it.

I included it in my commit 6a52f2a, which also corrects a misunderstanding about how the control stack is really laid out on arm64.

@xrme xrme closed this Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants