Skip to content

[3.5 | Switch] Published template aborts on exit; a rebuild does not #27

Description

@ferrolho

Summary

The published 3.5.3+switch+vita export template aborts during shutdown on every clean exit. It is cosmetic, since the app has already exited and nothing is lost, but Horizon shows "The software was closed because an error occurred" every time, which looks broken.

It does not reproduce on a build made from the same source with a current toolchain. The fix looks like republishing the templates rather than a code change, which is why this is an issue and not a pull request.

Reproduction

Any project that calls get_tree().quit(). Minimal case: an empty scene, a label, and get_tree().quit() after five seconds. It happens on every exit, across reboots, and across two different host titles used for title takeover.

Evidence

Three builds, tested for a clean exit on a v1 Switch with Atmosphère:

build source newlib quits cleanly
published 3.5.3.stable template f4cf737d9c7 4.3.0.20230120 no, aborts every time
built locally f4cf737d9c7 (same source) 4.6.0.20260123 yes
built locally 5eccbcefabb 4.6.0.20260123 yes

The middle row is the point. The same source the published template was built from quits cleanly once compiled with a current toolchain. That rules out the thirteen commits between f4cf737d9c7 and 5eccbcefabb, none of which touch shutdown.

The published template names its own commit in its startup banner (v3.5.3.stable.custom_build.f4cf737d9), which is what made that comparison possible.

Where it faults

Atmosphère crash report, the same across five captures:

Process Name:  hbloader
Type:          Data Abort
PC:            godot.switch.opt + 0x1f1ca74   ->  ldr x19, [x0]
Stack:         +0x19e11ec / +0x19e0324 / +0x19e12d4

The faulting loop walks parallel arrays of object pointers and destructor pointers, calling each and clearing the slot, and the deepest frame is process entry, so it runs after main returns. That is newlib's __call_exitprocs walking __cxa_atexit registrations. It is not the ELF's .fini_array, which has 46 entries rather than the 27 the disassembly showed.

So the crash is in the C library's own exit handler walker, and that library moved three years between the two builds.

Checking this yourself

Toolchain of any NRO, without building anything:

strings -a switch_release.nro | grep -o 'newlib-[0-9.]*' | sort -u

Rebuilding the published template's exact source:

git clone --branch main/3.5 https://github.com/Homebrodot/Godot.git
cd Godot && git checkout f4cf737d9c7
docker run --rm -v "$PWD":/godot -w /godot devkitpro/devkita64 bash -c '
  apt-get update -qq && apt-get install -y -qq scons
  scons -j8 platform=switch target=release tools=no'

Two things that were not obvious: scons is not in the image, and there is no pip either, so it has to come from apt. The image is multi-arch, so this runs natively on an Apple Silicon Mac too.

A rebuilt template

If it is useful in the meantime, there is a rebuilt template here: https://github.com/ferrolho/Godot/releases/tag/switch-template-3.5.3-rc1

It is a personal build, not an official one, and it also carries #25 and #26, so it is not a clean control. The unstripped ELF is attached alongside it so that crash reports from it can be turned into function names.

Caveat

This shows the abort no longer happens, not that the underlying defect is gone. A use-after-free in an exit handler can stop showing up purely because allocation layout shifted, and newlib 4.3 to 4.6 changes plenty of layout. Finding the actual faulting entry would need a live reproduction, and no current-toolchain build reproduces it. Worth knowing before treating it as closed.

Related

OS.get_datetime() returns all zeros on the published template. That one is fixed in source, by 3618ad0db1f [Switch] Implement OS time getters, which postdates the template. Same underlying situation: the published artefact is behind the branch. Two user-visible bugs, one action.

Suggested fix

Republish the export templates from a current toolchain.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions