Undefined symbols for C++ types in Release build, fine in Debug build

Hi all,

I have a fairly large C++ library that I am writing Swift bindings for. However, I'm getting the following linker error in release builds specifically. Everything compiles and runs as expected in debug builds.

Ld /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/Binary/EDOProAI normal x86_64 (in target 'EDOProAI' from project 'EDOProAI')
    cd /Users/brandon/Developer/EDOProAI
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Xlinker -reproducible -target x86_64-apple-macos14.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -Os -L/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EagerLinkingTBDs/Release -L/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EagerLinkingTBDs/Release -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release/PackageFrameworks -F/Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Products/Release -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -filelist /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/EDOProAI.LinkFileList -Xlinker -rpath -Xlinker @executable_path/../lib -Xlinker -object_path_lto -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/EDOProAI_lto.o -Xlinker -debug_variant -fobjc-link-runtime -fprofile-instr-generate -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -L/usr/lib/swift -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/EDOProAI.swiftmodule -lc++ -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Wl,-no_warn_duplicate_libraries -Xlinker -dependency_info -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/EDOProAI_dependency_info.dat -o /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/Binary/EDOProAI -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProCore.build/Release/EDOProCore.build/Objects-normal/x86_64/EDOProCore.swiftmodule -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/TinyML.build/Release/TinyML.build/Objects-normal/x86_64/TinyML.swiftmodule -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/_Differentiation.build/Release/Differentiation.build/Objects-normal/x86_64/Differentiation.swiftmodule -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/_Differentiation.build/Release/_Differentiation.build/Objects-normal/x86_64/_Differentiation.swiftmodule -Xlinker -add_ast_path -Xlinker /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/swift-reflection-mirror.build/Release/ReflectionMirror.build/Objects-normal/x86_64/ReflectionMirror.swiftmodule

ld: Undefined symbols:
  void std::__1::__debug_db_erase_c[abi:v160006]<std::__1::__hash_table<std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::__unordered_map_hasher<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::hash<unsigned int>, std::__1::equal_to<unsigned int>, true>, std::__1::__unordered_map_equal<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::equal_to<unsigned int>, std::__1::hash<unsigned int>, true>, std::__1::allocator<std::__1::__hash_value_type<unsigned int, unsigned int>>>>(std::__1::__hash_table<std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::__unordered_map_hasher<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::hash<unsigned int>, std::__1::equal_to<unsigned int>, true>, std::__1::__unordered_map_equal<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::equal_to<unsigned int>, std::__1::hash<unsigned int>, true>, std::__1::allocator<std::__1::__hash_value_type<unsigned int, unsigned int>>>*), referenced from:
      /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/Decks.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The error above indicates the symbol is referenced in Decks.o, however my Decks.swift file does not use any C++ types.

Strangely, removing the file entirely from the project results in the same error, except with a different object file (which also does not use any C++ types):

ld: Undefined symbols:
  void std::__1::__debug_db_erase_c[abi:v160006]<std::__1::__hash_table<std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::__unordered_map_hasher<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::hash<unsigned int>, std::__1::equal_to<unsigned int>, true>, std::__1::__unordered_map_equal<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::equal_to<unsigned int>, std::__1::hash<unsigned int>, true>, std::__1::allocator<std::__1::__hash_value_type<unsigned int, unsigned int>>>>(std::__1::__hash_table<std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::__unordered_map_hasher<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::hash<unsigned int>, std::__1::equal_to<unsigned int>, true>, std::__1::__unordered_map_equal<unsigned int, std::__1::__hash_value_type<unsigned int, unsigned int>, std::__1::equal_to<unsigned int>, std::__1::hash<unsigned int>, true>, std::__1::allocator<std::__1::__hash_value_type<unsigned int, unsigned int>>>*), referenced from:
      /Users/brandon/Library/Developer/Xcode/DerivedData/EDOProAI-bhygyrwhfezktbgpejfmfyatiyvf/Build/Intermediates.noindex/EDOProAI.build/Release/EDOProAI.build/Objects-normal/x86_64/Array+Extensions.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Is there a way to ensure the symbols are not stripped during optimization?

1 Like

I've noticed the same issue. I don't have any fixes - just want to add that I'm also looking for a fix.

1 Like

Hey @brandon!

Would you by chance happen to have a Package.swift file for this project so I can look into this further? Is the offending Swift target perhaps missing the following in its swiftSettings array?

A shot in the dark:

// assuming the target is a executable target named "EDOProAI".
.executableTarget(
  name: "EDOProAI",
  dependencies: [
    .target(name: "SomeCXXTarget"),
  ],
  // missing this possibly?
  swiftSettings: [
    .interoperabilityMode(.Cxx),
  ]
),

Another thing you might be able to try, is adding this to your target's cxxSettings array (this goes right before the optional swiftSettings parameter):

// ...
cxxSettings: [
  // though, you should find out why these are
  // getting defined in release builds if these
  // are needed...
  // (perhaps they are getting defined in some
  // header file in your CXX library?)
  .define(
    "LIBCXX_ENABLE_DEBUG_MODE",
    to: "0",
    .when(configuration: .release)
  ),
  .define(
    "_LIBCPP_DEBUG",
    to: "0",
    .when(configuration: .release)
  )
],
swiftSettings: [
  // ...
]

But I'll be able to help you out more if I get a bit more context from your Package.swift configuration file.

1 Like

Hey!

Unfortunately, the packages weren't under version control when I was encountering issues with C++ interoperability. Since then, I ended up writing a C wrapper for extra functions I needed.

Though I don't have much motive to fix the C++ interop problems (since the C wrapper works just fine), I'll answer questions in case it helps someone else reading through. Though I appreciate the help!

Here's a recreation of the package files based off of my memory. I did have .interoperabilityMode(.Cxx) enabled under swiftSettings in both packages at the time of the issue.

EDOProAI:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "EDOProAI",
    platforms: [
        .macOS(.v14)
    ],
    products: [
        .executable(
            name: "EDOProAI",
            targets: ["EDOProAI"]
        ),
    ],
    dependencies: [
        .package(path: "../EDOProCore"),
        .package(path: "../TinyML"),
    ],
    targets: [
        .executableTarget(
            name: "EDOProAI",
            dependencies: [
                "EDOProCore",
                "TinyML",
            ],
            swiftSettings: [
                .interoperabilityMode(.Cxx),
            ]
        ),
        .testTarget(
            name: "EDOProAITests",
            dependencies: [
                "EDOProAI",
                "TinyML",
            ]),
    ],
    cxxLanguageStandard: .cxx17
)

EDOProCore:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "EDOProCore",
    platforms: [
        .macOS(.v14)
    ],
    products: [
        .library(
            name: "EDOProCore",
            type: .dynamic,
            targets: ["EDOProCore"]
        ),
    ],
    targets: [
        .target(
            name: "CLua",
            exclude: ["lua.c", "luac.c"]
        ),
        .target(
            name: "EDOProCoreCxx",
            dependencies: ["CLua"]
        ),
        .target(
            name: "EDOProCore",
            dependencies: ["EDOProCoreCxx"],
            resources: [
                .copy("Resources/cards.cdb"),
                .copy("Resources/strings.conf"),
            ], 
            swiftSettings: [
                .interoperabilityMode(.Cxx),
                // .unsafeFlags(["-Xcc", "-no-whole-module-optimization"])
                // .unsafeFlags(["-disable-cmo"])
                // .unsafeFlags(["-Xswift", "-disable-cmo"])
            ]
        ),
        .testTarget(
            name: "EDOProCoreTests",
            dependencies: ["EDOProCore"],
            swiftSettings: [
                .interoperabilityMode(.Cxx),
            ]
        ),
    ],
    cxxLanguageStandard: .cxx17
)

The third package TinyML does not use any C++/C interop features (it's pure Swift).

Also for context, this is the C++ library I'm writing Swift bindings for.

I will try and look into this later tonight, I wouldn't suppose the source is available to mess with? Would probably allow me to debug a bit easier.

But one thing I did notice when I glanced over it is the following test target: EDOProAITests is missing its .interoperabilityMode(.Cxx), one whacky thing I have noticed with C++ interop is it basically spreads like a virus - forcing you to enable that setting for pretty much all of every single target that comes into contact with that CXX target (either directly or transitively).

~ Probably won't change much, but in my experience; sometimes the tooling (xcode and swift cli) tend to give me strange errors if any of the package targets aren't in a buildable state.

One more thing, I religiously tend to just set all target dependencies with this form, vs the strings that you have in there, not sure if that could be of any help as well:

dependencies: [
  .product(name: "EDOProCore", package: "EDOProCore"),
  .product(name: "TinyML", package: "TinyML")
]
1 Like

Yeah, it most likely had .interoperabilityMode(.Cxx) set for each target at the time I was working on it (I just did some quick edits off of memory, just to give an idea of what it looked like).

Though I'd actually rather not spend too much time on this at this point.

1 Like

No worries! I’ve been pretty amazed so far with cxx interop in Swift, but I’m hoping with anyone having any issues we can get things smoothed out. I think there’s a ton of exciting opportunity here and have been looking forward to working with the rest of the swift community to see what’s possible, if you or anyone else is experiencing any issues with cxx interop I’d be happy to help out wherever I can, take care!

1 Like