Skip to content

Fix macOS arm64 (Apple Silicon) linker support - #470

Open
metaneutrons wants to merge 1 commit into
freebasic:masterfrom
metaneutrons:macos-arm64-final
Open

metaneutrons wants to merge 1 commit into
freebasic:masterfrom
metaneutrons:macos-arm64-final

Conversation

@metaneutrons

Copy link
Copy Markdown

Summary

Fix linking on macOS arm64 (Apple Silicon, M1/M2/M3/M4).

Changes (3 files, +47/-11)

makefile:

  • Normalize arm64 (from uname -m) to aarch64 before the 32-bit armv% catch-all

src/compiler/fbc.bas:

  • line input in hGet1stOutputLineFromCommand (handles spaces in SDK paths)
  • Add -arch arm64 for FB_CPUFAMILY_AARCH64 on darwin
  • Replace deprecated -macosx_version_min with -platform_version macos <min> <sdk>
    • min: 11.0 for arm64, 10.4 for x86_64 (preserves original minimum)
    • sdk: detected via xcrun --show-sdk-version (fallback 14.0)
  • SDK path via xcrun --show-sdk-path (only on darwin host, respects user -sysroot, quoted with QUOTE)
  • Skip GNU --sysroot= on darwin (Apple ld uses -syslibroot)
  • Remove --eh-frame-hdr on darwin (unsupported by Apple ld)
  • Remove -lgcc from darwin default libs (unavailable with Apple clang)

src/rtlib/profile_cycles.c:

  • Guard ELF __start_/__stop_ section boundary symbol declarations and usage with !defined(HOST_DARWIN)
  • Provide data=NULL, length=0 fallback on darwin (cycle profiling compiles but produces no output)

Testing

make bootstrap-minimal ENABLE_STANDALONE=1

Successfully builds fbc and runtime library on macOS 15 (Sequoia) with Apple clang 21 on M4.

Notes

  • x86_64 darwin builds unaffected (minimum version preserved at 10.4)
  • -profile cycles compiles on darwin but produces no per-procedure data (Mach-O lacks ELF section boundary symbols)
  • Bootstrap requires bootstrap/darwin-aarch64/ (copy from bootstrap/linux-aarch64/ + fix clang computed goto)

Copilot AI review requested due to automatic review settings May 15, 2026 20:00

Copilot AI 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.

Pull request overview

This PR updates the build/link logic to support successful linking on macOS arm64 (Apple Silicon) by adjusting architecture detection and Darwin-specific linker flags/behaviors.

Changes:

  • Normalize arm64 to aarch64 in the makefile’s TARGET_ARCH detection before the 32-bit ARM normalization.
  • Update the Darwin linker invocation in fbc.bas to add -arch arm64 for AArch64 and use modern -platform_version/-syslibroot behavior (including xcrun SDK detection on Darwin hosts), while avoiding GNU-ld-only flags/libs on Darwin.
  • Make cycle profiling compile on Darwin by disabling ELF section-boundary-symbol usage and emitting an empty report payload.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
makefile Normalizes arm64aarch64 ahead of 32-bit ARM matching to select the correct target CPU family on macOS Apple Silicon.
src/compiler/fbc.bas Adjusts Darwin link flags for arm64 and modern Apple ld usage (SDK/sysroot handling, platform version, and default libs).
src/rtlib/profile_cycles.c Guards ELF __start/__stop usage on Darwin and provides a safe no-data fallback so it compiles on Mach-O.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

agorangetek added a commit to agorangetek/fbc that referenced this pull request Sep 18, 2026
uname -m reports "arm64" on Apple Silicon, which matched the arm% pattern
and was normalized to "arm" -- the 32-bit family.  A plain "make" on macOS
therefore resolved FBTARGET to darwin-arm instead of darwin-aarch64, so
objects and libraries landed in directories that do not match the ones fbc
looks for at run time (lib/freebasic/darwin-aarch64).

Normalize aarch64/arm64 first, then let the remaining 32-bit spellings
match armv%/arm.  Verified that arm-linux-gnueabihf and
armv7-linux-gnueabihf still resolve to linux-arm, and that
arm64-apple-darwin resolves to darwin-aarch64.

Based on the corresponding change in freebasic#470 by @metaneutrons.
@agorangetek

Copy link
Copy Markdown

Nice work on getting the platform pieces in order — the arm64aarch64
normalization here is exactly right; without it a plain make on Apple Silicon
resolves to darwin-arm, and I've borrowed that change for #479 with credit.

While comparing our two approaches I ran into two things that stop this branch
from producing a working link, in case they are useful:

1. crt1.o cannot be found on current macOS. hLinkFiles() adds it
explicitly for Darwin executables through hFindLib("crt1.o"):

$ clang --sysroot=$(xcrun --show-sdk-path) -print-file-name=crt1.o
crt1.o          # bare name => unresolved, even though the SDK contains the file

fbcBuildPathToLibFile() treats an unresolved name as failure
(hStripPath(found) == found), so fbc reports

error 23: File not found, crt1.o

and no startup object reaches the link line. ld64 then fails with:

ld: Undefined symbols for architecture arm64:
  "_main", referenced from: <initial-undefines>

because the SDK's crt1.o is what provides the entry point that calls main.
Letting clang be the link driver (or simply skipping crt1.o on Darwin) is what
resolved it for me.

2. arm64 needs the DATA descriptor aligned. It is emitted as a packed
{ short, void* } table, so the embedded pointers sit at 2-byte offsets and
ld64 rejects the relocation outright, regardless of how the link line is built:

ld: pointer not aligned in '_label$N'+0x16

That one is a hard error on arm64 (it is only a warning on x86_64).

#479 fixes both, plus a couple of other arm64-specific items: va_list is a
plain pointer on Apple arm64 rather than the AAPCS64 __va_list_tag struct, and
the X11 XGETKEYBOARDMAPPING typedef no longer matches modern Xlib when
compiled with clang. It also passes the fbcunit suite and handles -dylib.

Not trying to compete with this PR — happy for whichever lands first to win; I
just did not want the crt1.o part to be a surprise, since it is the first
thing that fails.

metaneutrons added a commit to metaneutrons/freebasic-ng that referenced this pull request Sep 19, 2026
The compiler reads what a toolchain binary prints with INPUT, which is
meant for data records: it ends the field at the first comma and strips
surrounding quotes. A path like /opt/toolchains/gcc,15/lib/libgcc.a comes
back as /opt/toolchains/gcc, and the remainder is left in the pipe, so a
following read returns the wrong line.

fbcQueryCC() is where this bites in this tree. It answers
-print-libgcc-file-name, and the win32 aarch64 link line takes the
compiler runtime from it: with a comma in the path fbc stops with
"File not found, compiler runtime" instead of linking.

Reproduced with a stub toolchain that reports such a path: the cross
target for win32-aarch64 failed on the truncated path before and now
passes the whole one to the linker.

The same read in hGet1stOutputLineFromCommand() is fixed along with it;
that one is what upstream freebasic/fbc#470 changes. Contrary to that
PR's description, spaces were never the problem — INPUT returns
"/Users/me/My SDKs/MacOSX.sdk" unharmed; commas and quotes are what it
mangles.
metaneutrons added a commit to metaneutrons/freebasic-ng that referenced this pull request Sep 19, 2026
## Summary

The compiler reads what a toolchain binary prints with `INPUT`, which is
meant
for data records: it ends the field at the first comma and strips
surrounding
quotes. A path like `/opt/toolchains/gcc,15/lib/libgcc.a` comes back as
`/opt/toolchains/gcc`, and the remainder stays in the pipe, so a
following read
returns the wrong line.

`fbcQueryCC()` is where this bites in this tree. It answers
`-print-libgcc-file-name`, and the win32 aarch64 link line takes the
compiler
runtime from it: with a comma in the path fbc stops with
`File not found, compiler runtime` instead of linking. The same read in
`hGet1stOutputLineFromCommand()`, which feeds the gold-linker probe, is
fixed
along with it — that is the one upstream freebasic/fbc#470 changes.

Contrary to that PR's description, spaces were never the problem.
Measured
against a file holding realistic tool output:

| line | `INPUT #` | `LINE INPUT #` |
| --- | --- | --- |
| `/Users/me/My SDKs/MacOSX.sdk` | unharmed | unharmed |
| `/opt/toolchains/gcc,15/lib/libgcc.a` | `/opt/toolchains/gcc` |
unharmed |
| `"/quoted path"/lib/libgcc.a` | `/quoted path` | unharmed |

## Verification

- [x] CMake build completed on the affected host (Linux x86_64).
- [x] `fbc --version` or a focused compiler/runtime test passed — the
three
      compiler regressions are green.
- [x] Copyright and licence notices remain intact.
- [x] No generated binaries or unrelated changes are included.

Reproduced end to end with a stub toolchain whose
`-print-libgcc-file-name`
answers a path containing a comma: cross-targeting win32-aarch64 failed
with
`File not found, compiler runtime` before the change and now hands the
whole
path to the linker.
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.

3 participants