Skip to content

gc: refuse native roots off aarch64, and stop the ELF map forcing DT_TEXTREL - #7324

Merged
proggeramlug merged 1 commit into
mainfrom
gc/statepoints-aarch64-only
Aug 3, 2026
Merged

gc: refuse native roots off aarch64, and stop the ELF map forcing DT_TEXTREL#7324
proggeramlug merged 1 commit into
mainfrom
gc/statepoints-aarch64-only

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The gc-native-roots gate went red on main after #7314. Good news first: the ELF question it existed to answer is resolvedSHF_GNU_RETAIN did keep .perry_gcmap through --gc-sections, and .llvm_stackmaps was gone. Both section assertions passed.

It failed with exit 139 — SIGSEGV on 02_survivor_promotion, the evacuation-heaviest probe. Two separate defects.

The backend is aarch64-only and did not say so

The runner is x86-64. I cross-compiled a probe to x86_64-unknown-linux-gnu and decoded the emitted stack map rather than guessing:

x86-64 location kinds:        {Constant: 222, Indirect: 178}
x86-64 base regs (8B slots):  {7: 178}

Every root is Indirect [RSP + off] — DWARF register 7. The runtime's chain_walkable admits only aarch64's FP/SP (29 and 31), so on x86-64 it is always false and every frame falls back to the unwinder, which resolves the base with _Unwind_GetGR(ctx, 7). That call does not reliably return the stack pointer_Unwind_GetCFA is the supported way to obtain it. So the walker computed wild addresses and the collector segfaulted writing through them.

The mode is opt-in, so the compiler now refuses the combination with a message naming the target, rather than emitting a binary that crashes under collection. Verified: --target linux with PERRY_STATEPOINTS=1 now fails the compile cleanly.

Making x86-64 actually work means per-architecture base resolution (_Unwind_GetCFA for SP, per-arch DWARF constants) — real work, tracked under #7173, and not something to fake in a refusal.

The ELF section was read-only but holds relocated addresses

ld: warning: relocation against `main' in read-only section `.perry_gcmap'
ld: warning: creating DT_TEXTREL in a PIE

The section carries .quad <function symbol> entries the loader must relocate. Without SHF_WRITE that forces a text relocation in a PIE — a hardening regression and a portability hazard independent of the segfault. Now "awR" (ALLOC | WRITE | RETAIN).

The gate moves to ARM64

ubuntu-24.04-arm. On x86-64 the gate would now exercise nothing but the refusal; on ARM64 it tests the configuration that is actually supported and still answers what it was built for — whether the compact map survives ELF linking.

Verification

macOS arms unchanged: explicit bridge, RS4GC and the default shadow stack all 9/9 against the pinned Node oracle, statepoint arms under forced evacuation with the verifying walker.

Summary by CodeRabbit

  • Bug Fixes

    • Improved native garbage collection root handling on ARM64 Linux.
    • Native GC validation now rejects unsupported non-ARM64 targets with a clear error instead of failing during execution.
    • Improved retention and relocation handling for garbage collection metadata, reducing linker-related runtime issues and improving runtime stability.
  • Documentation

    • Added release notes describing ARM64 requirements and native GC behavior.
    • Documented supported environments and validation expectations for native garbage collection.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ee9925a1-31e3-4ca4-a6dd-fa64a5d43fae

📥 Commits

Reviewing files that changed from the base of the PR and between 571cad9 and 806e379.

📒 Files selected for processing (3)
  • .github/workflows/gc-native-roots.yml
  • changelog.d/7321-statepoints-aarch64-only.md
  • crates/perry-codegen/src/gc_map.rs

📝 Walkthrough

Walkthrough

The compiler now rejects native GC map assembly on non-AArch64 targets and emits writable, retained ELF GC map sections. The workflow and changelog document ARM64 validation and the target restriction.

Changes

Native GC ARM64 support

Layer / File(s) Summary
GC map target and section constraints
crates/perry-codegen/src/gc_map.rs
compact_and_assemble rejects non-AArch64 targets. ELF GC map sections now use writable, allocatable, and retained flags.
ARM64 validation and release documentation
.github/workflows/gc-native-roots.yml, changelog.d/7321-statepoints-aarch64-only.md
The workflow and changelog document the ARM64-only restriction, compiler refusal, linker-related section changes, and ARM64 Linux validation.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • PerryTS/perry issue 7321: The changes enforce AArch64-only native GC roots and document the related validation and linker changes.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes both primary changes: rejecting native roots off AArch64 and preventing ELF map sections from forcing DT_TEXTREL.
Description check ✅ Passed The description provides detailed change rationale, affected behavior, issue context, and verification results, although it does not follow the template headings or checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gc/statepoints-aarch64-only

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/perry-codegen/src/gc_map.rs (1)

65-76: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add regression assertions for the ELF hardening properties.

The "awR" declaration adds SHF_WRITE and SHF_GNU_RETAIN. The ARM gate currently checks only section presence and removal of .llvm_stackmaps. A regression to read-only flags could pass while reintroducing DT_TEXTREL. Add checks for a writable .perry_gcmap section and the absence of TEXTREL.

As per coding guidelines, the CI gate must assert that the behavior it measures actually executed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-codegen/src/gc_map.rs` around lines 65 - 76, Extend the ARM ELF
regression gate around the existing section-presence and .llvm_stackmaps checks
to verify that .perry_gcmap has writable flags and that the linked binary
contains no TEXTREL entry. Ensure the gate also asserts that these inspection
checks actually ran, rather than passing when the relevant ELF metadata or tool
output is missing.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/perry-codegen/src/gc_map.rs`:
- Around line 65-76: Extend the ARM ELF regression gate around the existing
section-presence and .llvm_stackmaps checks to verify that .perry_gcmap has
writable flags and that the linked binary contains no TEXTREL entry. Ensure the
gate also asserts that these inspection checks actually ran, rather than passing
when the relevant ELF metadata or tool output is missing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3345b1f6-4620-4fdd-890f-0099b4334714

📥 Commits

Reviewing files that changed from the base of the PR and between 018be14 and 571cad9.

📒 Files selected for processing (3)
  • .github/workflows/gc-native-roots.yml
  • changelog.d/7321-statepoints-aarch64-only.md
  • crates/perry-codegen/src/gc_map.rs

…TEXTREL

The gc-native-roots gate went red on main with a SIGSEGV rather than a missing
section: SHF_GNU_RETAIN did keep .perry_gcmap through --gc-sections and
.llvm_stackmaps was gone, so the ELF retention fix worked. Two defects behind
the crash.

The backend is aarch64-only and did not say so. Cross-compiled a probe to
x86_64-unknown-linux-gnu and decoded the emitted map: all 178 root slots are
Indirect [RSP + off], DWARF register 7. chain_walkable admits only aarch64's
29/31, so every frame falls back to the unwinder, which resolves the base with
_Unwind_GetGR(ctx, 7) — that does not reliably return the stack pointer
(_Unwind_GetCFA is the supported way). Wild addresses, then a segfault when the
collector writes through them. The mode is opt-in, so refuse rather than ship a
binary that crashes under collection.

The ELF section was read-only while holding relocated function addresses: ld
warned 'relocation against main in read-only section .perry_gcmap' and created
a DT_TEXTREL in a PIE. Now "awR".

Gate moves to an ARM64 runner — on x86-64 it would now test only the refusal.

macOS arms remain 9/9; x86-64 Linux now fails the compile with a message naming
the target instead of segfaulting at collection time.
@proggeramlug
proggeramlug force-pushed the gc/statepoints-aarch64-only branch from 571cad9 to 806e379 Compare August 3, 2026 18:52
@proggeramlug
proggeramlug merged commit e98dfdb into main Aug 3, 2026
0 of 16 checks passed
@proggeramlug
proggeramlug deleted the gc/statepoints-aarch64-only branch August 3, 2026 18:52
proggeramlug added a commit that referenced this pull request Aug 3, 2026
…at it found (#7334)

The diagnostic job was merged with #7331 before it had served its
purpose; it has now, so it comes out.

Its answer contradicts the narrative still at the top of this file: the
compact-map rewriter parses x86-64 stack maps fine. Every root is
Indirect [RSP + off] (DWARF 7), round-tripping through the explicit-
register tag, and no clang version or -march setting reproduced a parse
failure. The x86-64 defect is at collection time, which #7324 refuses
for. Replace the wrong explanation rather than leave two contradictory
ones in the same header.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
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.

1 participant