MINGW-packages icon indicating copy to clipboard operation
MINGW-packages copied to clipboard

clang issues linker error

Open RuiCuco opened this issue 5 months ago • 9 comments

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!

RuiCuco avatar Sep 20 '25 16:09 RuiCuco

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

lb90 avatar Sep 21 '25 08:09 lb90

To work around the issue you can compile with -fobjc-runtime=gnustep-2.0. We should make this the default in CLang

lb90 avatar Sep 21 '25 08:09 lb90

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?

RuiCuco avatar Sep 22 '25 21:09 RuiCuco

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

lb90 avatar Sep 25 '25 14:09 lb90

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

lb90 avatar Sep 26 '25 08:09 lb90

Wasn't that implemented by https://github.com/bminor/binutils-gdb/commit/37513c1efbe5e8e1863f8ddf078cd395aa663388?

mati865 avatar Oct 24 '25 20:10 mati865

Ah, indeed! Lemme check...

lb90 avatar Oct 27 '25 10:10 lb90

@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?

lb90 avatar Oct 28 '25 16:10 lb90

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?

mati865 avatar Oct 28 '25 17:10 mati865