Skip to content

goddard: read DynList int operands via .ptr to fix 64-bit big-endian crash (#426) - #605

Open
Scottcjn wants to merge 1 commit into
sm64pc:nightlyfrom
Scottcjn:fix/dynlist-bigendian-int-operands
Open

goddard: read DynList int operands via .ptr to fix 64-bit big-endian crash (#426)#605
Scottcjn wants to merge 1 commit into
sm64pc:nightlyfrom
Scottcjn:fix/dynlist-bigendian-int-operands

Conversation

@Scottcjn

Copy link
Copy Markdown

Summary

Fixes #426 — on 64-bit big-endian builds (e.g. ppc64) the game crashes/hangs at the Goddard ("Mario head") intro and never reaches the title screen.

Root cause

DynList integer operands are written through the union's pointer member — the construction macros in dynlist_macros.h cast the int argument to void *:

#define SetColourNum(w2) { 13, {0}, {(void *)(w2)}, {0.0,0.0,0.0} }
#define SetType(w2)      { 19, {0}, {(void *)(w2)}, {0.0,0.0,0.0} }

…but Dyn1AsInt / Dyn2AsInt read them back through the narrower .word member:

#define Dyn2AsInt(dyn) ((dyn)->w2.word)

On little-endian the value's low bytes line up with .word, so it works. On 64-bit big-endian the value sits in the high 4 bytes of the 8-byte pointer slot, so .word reads 0 — a garbage object type that makes d_makeobj fail and the Goddard intro abort.

Fix

Read the operand back the same way it was written — through .ptr — then narrow to s32. This mirrors the sibling Dyn*AsID accessors right next to it, which already read .ptr:

#define Dyn1AsInt(dyn) ((s32)(intptr_t)((dyn)->w1.ptr))
#define Dyn2AsInt(dyn) ((s32)(intptr_t)((dyn)->w2.ptr))

No struct layout change.

Why not just widen word to s64?

That's the tempting one-liner, but it regresses 32-bit big-endian (e.g. 32-bit PowerPC), because there the pointer is 4 bytes and the widened read picks up the wrong half. Worked out across word size × endianness:

DynUnion.word 32-bit BE 64-bit BE
s32 (current) ✅ ok ❌ reads 0 (the bug)
s64 ❌ reads 0 (regression) ✅ ok
.ptr reader (this PR) ✅ ok ✅ ok

Reading via .ptr is the only option correct everywhere, and it leaves DynList's layout untouched (so LE and 32-bit builds are entirely unaffected).

Testing

  • Cross-built the full game for ppc64 (big-endian) from this branch — compiles clean.
  • A standalone reproduction using the repo's actual dynlist_macros.h confirms SetColourNum(5) reads back 5 on both endians with this change, vs 0 on 64-bit BE before it; and that the s64 alternative reads 0 on 32-bit BE.
  • 32-bit and 64-bit little-endian behavior and layout are unchanged.

Original diagnosis by @KungFuJesus in #426 — thanks!


Note: building on PowerPC also needs a small Makefile tweak (LDFLAGS hardcodes -march=$(TARGET_ARCH), which ppc GCC rejects — it wants -mcpu). I kept that out of this PR to stay focused on the #426 crash; happy to send it separately if useful.

…crash

DynList integer operands are stored through the union's pointer member --
the dynlist_macros.h commands cast their int argument to void *, e.g.
SetColourNum(x) -> { 13, {0}, {(void *)(x)}, ... }. But Dyn1AsInt /
Dyn2AsInt read them back through the narrower .word member. On 64-bit
big-endian the value sits in the high 4 bytes of the 8-byte pointer slot,
so .word reads 0 -- a garbage object type that aborts the Goddard intro
and hangs the game at boot (issue sm64pc#426).

Read the operand back through .ptr (matching the sibling Dyn*AsID
accessors) and narrow to s32. This is correct on every word-size x
endianness combination and changes no struct layout, so 32-bit
big-endian (e.g. 32-bit PowerPC) is unaffected -- unlike widening word
to s64, which would fix 64-bit BE but regress 32-bit BE.

Verified by cross-building the full game for ppc64 big-endian (compiles
clean) and by a standalone reproduction using the repo's own dynlist
macros, which reads back the correct value instead of 0 on 64-bit BE
with this change (and shows the s64 alternative reading 0 on 32-bit BE).
@Scottcjn
Scottcjn force-pushed the fix/dynlist-bigendian-int-operands branch from 431c2be to 050afb8 Compare June 23, 2026 14:36
@Scottcjn

Copy link
Copy Markdown
Author

Friendly nudge. This fixes a real 64-bit read of Goddard's DynList operands via .ptr. Small and self-contained. Anything needed from my side to get a review?

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.

64 bit big endian issues

2 participants