tpoechtrager / osxcross

Mac OS X cross toolchain for Linux, FreeBSD, OpenBSD and Android (Termux)
GNU General Public License v2.0
2.85k stars 320 forks source link

Linker error when using `__builtin_available()` version checks higher than `macosx-version-min` #278

Closed akien-mga closed 3 years ago

akien-mga commented 3 years ago

Host: Fedora 34 OSXCross: 8a716a43a72dab1db9630d7824ee0af3730cb8f9 SDK: Xcode 12.4, MacOSX 11.1

This is a follow-up to https://github.com/FNA-XNA/FNA/issues/318, both FNA and Godot are building Mono with osxcross for x86_64 and arm64, and we run into issues when using a macosx-version-min lower than 11.0 while linking to Mono which uses a __builtin_available(macOS 11, *) version check here: https://github.com/mono/mono/blob/a01309d1041ff8ae15b1f8b717420ec139e1cdb8/mono/utils/mono-codeman.c#L670 (note this is a recent addition to Mono's 2020-02 branch, which is the current Preview release).

Here's an example of the error from building Godot with osxcross 8a716a43 and mono-6.12.0.144:

/root/osxcross/target/bin/x86_64-apple-darwin20.2-c++ -o bin/godot.osx.opt.tools.x86_64.mono -arch x86_64 -mmacosx-version-min=10.12 -framework Cocoa -framework Carbon -framework OpenGL -framework AGL -framework AudioUnit -framework CoreAudio -framework CoreMIDI -lz -framework IOKit -framework ForceFeedback -framework AVFoundation -framework CoreMedia -framework CoreVideo -rdynamic -Wl,-force_load,/root/dependencies/mono/lib/libmonosgen-2.0.a platform/osx/crash_handler_osx.osx.opt.tools.x86_64.o platform/osx/os_osx.osx.opt.tools.x86_64.o platform/osx/godot_main_osx.osx.opt.tools.x86_64.o platform/osx/dir_access_osx.osx.opt.tools.x86_64.o platform/osx/joypad_osx.osx.opt.tools.x86_64.o platform/osx/power_osx.osx.opt.tools.x86_64.o -L/root/dependencies/mono/lib main/libmain.osx.opt.tools.x86_64.a main/tests/libtests.osx.opt.tools.x86_64.a modules/libmodules.osx.opt.tools.x86_64.a platform/libplatform.osx.opt.tools.x86_64.a drivers/libdrivers.osx.opt.tools.x86_64.a editor/libeditor.osx.opt.tools.x86_64.a scene/libscene.osx.opt.tools.x86_64.a servers/libservers.osx.opt.tools.x86_64.a core/libcore.osx.opt.tools.x86_64.a modules/freetype/libfreetype_builtin.osx.opt.tools.x86_64.a -lpthread -liconv -lpthread
Undefined symbols for architecture x86_64:
  "___isPlatformVersionAtLeast", referenced from:
      _free_chunklist in libmonosgen-2.0.a(libmonoutils_la-mono-codeman.o)
      _new_codechunk in libmonosgen-2.0.a(libmonoutils_la-mono-codeman.o)
      _mono_codeman_enable_write in libmonosgen-2.0.a(libmonoutils_la-mono-codeman.o)
      _mono_codeman_disable_write in libmonosgen-2.0.a(libmonoutils_la-mono-codeman.o)
      _codechunk_valloc in libmonosgen-2.0.a(libmonoutils_la-mono-codeman.o)
ld: symbol(s) not found for architecture x86_64
clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
scons: *** [bin/godot.osx.opt.tools.x86_64.mono] Error 1
scons: building terminated because of errors.

Here's a more minimal example:

#include <stdio.h>
int main() {
  if (__builtin_available(macOS 11, *)) {
    printf("macOS 11 available\n");
  }
  return 0;
}

With osxcross:

$ x86_64-apple-darwin20.2-clang -mmacosx-version-min=10.12 main.c
Undefined symbols for architecture x86_64:
  "___isPlatformVersionAtLeast", referenced from:
      _main in main-5a94c9.o
ld: symbol(s) not found for architecture x86_64
clang-12: error: linker command failed with exit code 1 (use -v to see invocation)

$ x86_64-apple-darwin20.2-clang -mmacosx-version-min=11.0 main.c
// success

With Apple Clang on a Mac running Big Sur:

$ clang -mmacosx-version-min=10.12 main.c
$ ./a.out 
macOS 11 available

$ clang -mmacosx-version-min=11.0 main.c
$ ./a.out 
macOS 11 available
Side note on checking against future SDKs As a side note, Apple Clang also seems to allow checking for theoretical future SDK versions: ```c #include int main() { if (__builtin_available(macOS 11, *)) { printf("macOS 11 available\n"); } if (__builtin_available(macOS 12, *)) { printf("macOS 12 available\n"); } return 0; } ``` ``` $ clang -mmacosx-version-min=11.0 main.c $ ./a.out macOS 11 available $ clang -mmacosx-version-min=12.0 main.c $ ./a.out macOS 11 available macOS 12 available ``` Where osxcross errors: ``` $ x86_64-apple-darwin20.2-clang -mmacosx-version-min=12.0 main.c osxcross: error: targeted macOS version must be <= 11.1.0 (SDK) osxcross: error: while detecting target ``` But that's not a big deal compared to the main linking issue reported above. And I'm not sure Apple Clang's behavior even makes sense since it lets me run a binary on Big Sur compiled with `mmacosx-version-min=12.0`...
tpoechtrager commented 3 years ago

You must install compiler-rt to get rid of the linker error. Run build_compiler_rt.sh.

But that's not a big deal compared to the main linking issue reported above. And I'm not sure Apple Clang's behavior even makes sense since it lets me run a binary on Big Sur compiled with mmacosx-version-min=12.0...

I'd prefer to keep the SDK version check...

akien-mga commented 3 years ago

You must install compiler-rt to get rid of the linker error. Run build_compiler_rt.sh.

Thanks! I'll try that and report back.

I'd prefer to keep the SDK version check...

Agreed, I just mentioned it for completeness as I was compare the behavior of both implementations, but Apple Clang's behavior doesn't make much sense to me.

akien-mga commented 3 years ago

You must install compiler-rt to get rid of the linker error. Run build_compiler_rt.sh.

Thanks! I'll try that and report back.

As a side note, could there be an option to have build_compiler_rt.sh perform these steps automatically? https://github.com/tpoechtrager/osxcross/blob/8a716a43a72dab1db9630d7824ee0af3730cb8f9/build_compiler_rt.sh#L188-L193

I'm building osxcross in a container so I'd have to hardcode paths or replicate this in my script to work on base system upgrades: https://github.com/tpoechtrager/osxcross/blob/8a716a43a72dab1db9630d7824ee0af3730cb8f9/build_compiler_rt.sh#L28-L33

akien-mga commented 3 years ago

I confirm that building and installing compiler-rt fixes the issue :+1:

Should this be called out in the README to close this, or is it good as is?