Duplicate symbols when linking a static library containing Objective-C++

ld_prime of Xcode 15 produces duplicate symbols when linking a static library containing Objective-C++ object in case using the flags -ObjC and -Wl,force_load.

// foo.h

#pragma once

int Inc(int i);

// foo.mm

#include "foo.h"

#include <Foundation/Foundation.h>

@interface Foo : NSObject
@end

@implementation Foo
@end

int Inc(int i) {
    return i + 1;
};

// main.cpp

#include "foo.h"

int main(int argc, const char * argv[]) {
    return Inc(0);
}
❯ clang++ -c foo.mm
❯ ar rcs libfoo.a foo.o
❯ clang++ -framework Foundation libfoo.a -x c++ main.cpp -Wl,-force_load,libfoo.a -ObjC
duplicate symbol '__Z3Inci' in:
    libfoo.a[2](foo.o)
    libfoo.a[2](foo.o)
duplicate symbol '_OBJC_CLASS_$_Foo' in:
    libfoo.a[2](foo.o)
    libfoo.a[2](foo.o)
duplicate symbol '_OBJC_METACLASS_$_Foo' in:
    libfoo.a[2](foo.o)
    libfoo.a[2](foo.o)
ld: 3 duplicate symbols
clang: error: linker command failed with exit code 1 (use -v to see invocation)```

Accepted Reply

You’re telling the linker to load libfoo.a twice, first via the libfoo.a argument to the clang++ compiler driver and again via the -force_load argument you pass directly to the link. Because you want to include Objective-C stuff even if it’s unreferenced, it makes sense to remove the first of these. That fixes the link step and the resulting binary still has class Foo in it:

% nm a.out 
00000001000080b8 S _OBJC_CLASS_$_Foo
                 U _OBJC_CLASS_$_NSObject
0000000100008090 S _OBJC_METACLASS_$_Foo
                 U _OBJC_METACLASS_$_NSObject
0000000100008048 s __OBJC_CLASS_RO_$_Foo
0000000100008000 s __OBJC_METACLASS_RO_$_Foo
0000000100003f90 T __Z3Inci
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
0000000100003f60 T _main

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

FB13258697

You’re telling the linker to load libfoo.a twice, first via the libfoo.a argument to the clang++ compiler driver and again via the -force_load argument you pass directly to the link. Because you want to include Objective-C stuff even if it’s unreferenced, it makes sense to remove the first of these. That fixes the link step and the resulting binary still has class Foo in it:

% nm a.out 
00000001000080b8 S _OBJC_CLASS_$_Foo
                 U _OBJC_CLASS_$_NSObject
0000000100008090 S _OBJC_METACLASS_$_Foo
                 U _OBJC_METACLASS_$_NSObject
0000000100008048 s __OBJC_CLASS_RO_$_Foo
0000000100008000 s __OBJC_METACLASS_RO_$_Foo
0000000100003f90 T __Z3Inci
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
0000000100003f60 T _main

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Indeed, thanks. Somehow I was expecting the same behaviour as with ld64.

By the way do you know how to access the source of ld_prime? ld64 wasn't updated recently, and ld -v points to dyld. The Xcode project there indeed has an ld-prime target and also references an ld directory but it's missing from the source tree.

Somehow I was expecting the same behaviour as with ld64.

ld_prime is a lot snarkier about duplicate symbols. I think that’s a good thing in the long term — when I dig into issues like this, I usually see that they’re a sign of a bigger problem — but it’s certainly causing some short-term pain.

By the way do you know how to access the source of ld_prime?

No, sorry, I’ve no idea what’s happening on that front.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"