clang issues linker error
Description / Steps to reproduce the issue
I recently installed a fresh msys2 system and I'm trying to build the following project:
#import <objc/objc.h>
#import <objc/runtime.h>
@interface TestObject
{
Class isa;
}
+ (id)alloc;
- (void)dealloc;
- (id)init;
@end
@implementation TestObject
+ (id)alloc
{
return class_createInstance(self, 0);
}
- (void)dealloc
{
if(self != nil)
object_dispose(self);
}
- (id)init
{
return self;
}
@end
int main(int argc, char** argv)
{
TestObject *object = [[TestObject alloc] init];
[object dealloc];
}
Call it main.m and compile it with the command:
clang -Wno-objc-root-class -o main.exe main.m -lobjc
This gives the following error:
C:/Developer/msys64/ucrt64/bin/ld: C:/Developer/msys64/tmp/main-6a5981.o:main.m:(.text+0x13c): undefined reference to `__objc_exec_class' clang: error: linker command failed with exit code 1 (use -v to see invocation)
Expected behavior
I expected it to compile without any errors, as it did in my old msys2 installation.
Actual behavior
Instead, I get a linker error. Am I forgetting to install any packages? These are in my system (explicit):
base 2022.06-1 bison 3.8.2-5 filesystem 2025.05.08-2 flex 2.6.4-4 mingw-w64-ucrt-x86_64-clang 20.1.8-2 mingw-w64-ucrt-x86_64-flexdll 0.43-1 mingw-w64-ucrt-x86_64-libobjc2 2.2.1-3 mingw-w64-ucrt-x86_64-make 4.4.1-3 msys2-runtime 3.6.4-1
Verification
- [x] I have verified that my MSYS2 is up-to-date before submitting the report (see https://www.msys2.org/docs/updating/)
Windows Version
MINGW64_NT-10.0-26100
MINGW environments affected
- [ ] MINGW64
- [ ] MINGW32
- [x] UCRT64
- [ ] CLANG64
- [ ] CLANGARM64
Are you willing to submit a PR?
No! I'm not brave enough!
There's an upstream report at https://github.com/gnustep/libobjc2/issues/351
Not sure if we should build with OLDABI_COMPAT=ON or not: https://github.com/gnustep/libobjc2?tab=readme-ov-file#gnustep-objective-c-runtime
To work around the issue you can compile with -fobjc-runtime=gnustep-2.0. We should make this the default in CLang
I try to compile with the option you mentioned but I get a lot more errors from the linker. Do I need to install another package for this to work?
It works in a CLANG64 environment. By adding -Wl,-verbose we can see what's happening under the hood:
In a CLANG64 environment:
CLANG64> clang -Wl,-verbose -Wno-objc-root-class -fobjc-runtime=gnustep-2.0 -o test-objc test-objc.m -lobjc
lld-link -lldmingw -out:test-objc.exe -debug:dwarf -build-id -WX:no -verbose -opt:noref -demangle -auto-import -runtime-pseudo-reloc -opt:noicf -machine:x64 -alternatename:__image_base__=__ImageBase
...
CLANG64> echo $?
0
CLANG64>
In a UCRT64 environment:
UCRT64> clang -Wl,-verbose -Wno-objc-root-class -fobjc-runtime=gnustep-2.0 -o test-objc test-objc.m -lobjc
GNU ld (GNU Binutils) 2.45
...
D:/msys64/ucrt64/bin/ld: D:/msys64/tmp/test-objc-e4f010.o:test-objc.m:(.text+0xbd): undefined reference to `.objc_selector_init_@16@0:8'
D:/msys64/ucrt64/bin/ld: D:/msys64/tmp/test-objc-e4f010.o:test-objc.m:(.text+0xd1): undefined reference to `.objc_selector_dealloc_v16@0:8'
D:/msys64/ucrt64/bin/ld: D:/msys64/tmp/test-objc-e4f010.o:test-objc.m:(.data+0x100): undefined reference to `.objc_selector_dealloc_v16@0:8'
D:/msys64/ucrt64/bin/ld: D:/msys64/tmp/test-objc-e4f010.o:test-objc.m:(.data+0x118): undefined reference to `.objc_selector_init_@16@0:8'
D:/msys64/ucrt64/bin/ld: link errors found, deleting executable `test-objc.exe'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
So in UCRT64 the default linker is binutils ld. If we add -fuse-ld=lld everything works correctly:
UCRT64> clang -fuse-ld=lld -fobjc-runtime=gnustep-2.0 -Wno-objc-root-class -o test-objc test-objc.m -lobjc
UCRT64> echo $?
0
UCRT64>
Note that you have to install https://packages.msys2.org/packages/mingw-w64-ucrt-x86_64-lld
Relevant output from lld in verbose mode:
ld.lld: Reading D:/msys64/tmp/test-objc-249854.o
ld.lld: Directives: D:/msys64/tmp/test-objc-249854.o:
-exclude-symbols:".objcv2_load_function"
-exclude-symbols:".objc_sel_name_alloc"
-exclude-symbols:".objc_sel_types_@16@0:8"
-exclude-symbols:".objc_selector_alloc_@16@0:8"
-exclude-symbols:".objc_sel_name_dealloc"
-exclude-symbols:".objc_sel_types_v16@0:8"
-exclude-symbols:".objc_selector_dealloc_v16@0:8"
-exclude-symbols:".objc_sel_name_init"
-exclude-symbols:".objc_selector_init_@16@0:8"
-exclude-symbols:"__start_.objcrt$SEL"
-exclude-symbols:"__stop.objcrt$SEL"
-exclude-symbols:"__start_.objcrt$CLS"
-exclude-symbols:"__stop.objcrt$CLS"
-exclude-symbols:"__start_.objcrt$CLR"
-exclude-symbols:"__stop.objcrt$CLR"
-exclude-symbols:"__start_.objcrt$CAT"
-exclude-symbols:"__stop.objcrt$CAT"
-exclude-symbols:"__start_.objcrt$PCL"
-exclude-symbols:"__stop.objcrt$PCL"
-exclude-symbols:"__start_.objcrt$PCR"
-exclude-symbols:"__stop.objcrt$PCR"
-exclude-symbols:"__start_.objcrt$CAL"
-exclude-symbols:"__stop.objcrt$CAL"
-exclude-symbols:"__start_.objcrt$STR"
-exclude-symbols:"__stop.objcrt$STR"
-exclude-symbols:".objc_init"
-exclude-symbols:".objc_ctor"
It seems that GNU ld doesn't support reading exclude-symbols entries from the .drectve section: https://github.com/bminor/binutils-gdb/blob/binutils-2_45/bfd/cofflink.c#L1277. I'm preparing patch
Wasn't that implemented by https://github.com/bminor/binutils-gdb/commit/37513c1efbe5e8e1863f8ddf078cd395aa663388?
Ah, indeed! Lemme check...
@mati865 yeah, I confirm that ld is correctly reading every -exclude-symbols:... directive embedded in the .o file: https://github.com/bminor/binutils-gdb/blob/2006dea18d5a8e149ee60d5548e8b3d848e46c4c/ld/pe-dll.c#L772. However that seems to have no practical effect in the end, as ld still wants to resolve those symbols.
Actually, ld.bfd --help shows:
--exclude-symbols sym,sym,... Exclude symbols from automatic export
Which doesn't sound useful here. So maybe this issue is not related to --exclude-symbols at all?
Maybe the problem is caused by linking an object built by Clang, with objc built by GCC which doesn't emit -exclude-symbols: (so all symbols are exported)?
Maybe running nm over objc libraries from CLANG64 and UCRT64 would prove or debunk that?