Linking with Clang++ on Os X Generates Lots of Symbol Not Found Errors

linking with clang++ on OS X generates lots of symbol not found errors

I suspect this issue is because of the two C++ runtime libraries available under OS X. Use the following to link the dynamic library:

clang++ -stdlib=libc++ -o Analysis.dylib -shared DataFile.o CR39DataFile.o

(the -stdlib=libc++ being the key difference). I think the default C++ runtime is the GNU implementation which is confusing the linker as you compiled with the libc++ implementation.

Clang linker reports symbol not found, despite 'nm -m' revealing that the name exists in a library that is being linked against

A giveaway was the std::__1 in the linker's warning message. It was an indication that libc++ internally creates a distinct namespace for standard library classes such as string specifically to force linker errors, because the C++ library that is included by default on OS X is incompatible with use of libc++.

More details, with links, are given in a question I posted while attempting to answer this question: Boost: Why is toolset listed in ~/user-config.jam not available to ./b2?

The answer is to rebuild Boost on OS X, specifying both clang as the compiler, and libc++ as the standard C++ library. (Again, see linked question for details.)

LLVM OS X symbol(s) not found for architecture x86_64 compile error

It seems that changing the compile command to this is a fix:

clang++ -g toy.cpp llvm-config --cxxflags --ldflags --libs -O3 -o toy

clang not recognizing unitialized pointer found in static library

What exactly is going on with clang here?

TL;DR: Your Clang has a bug. You can probably work around it without modifying your code by adding -fno-common to your compile options.



More detail

Both variations of your code are correct, and as far as the C language specification is concerned, they have the same meaning. On my Linux machine, GCC 8.5 and Clang 12 both accept both variations and successfully build working executables, whether blah.o is linked directly or from a library.

But if you use nm to examine the library built with and without the initializer for p, you will likely get a hint about what is happening. Without an initializer, I see (with either compiler) that p has type 'C' (common). With an initializer (to null), I see that it has type 'B' (BSS).

That is reflective of a traditional behavior of Unix C implementations: to merge multiple definitions of the same symbol as long as no more than one is defined with an explicit initializer. That is an extension to standard C, for the language requires that there be exactly one definition of each external symbol that a program references. Among other things, that extension covers the common error of omitting extern from variable declarations in headers, provided that the header does not specify an initializer.

To implement that, the toolchain needs to distinguish between symbols defined with an explicit initializer and those defined without, and that's where (for C) symbol type "common" comes in -- it is used to convey a symbol that is defined, but without an explicit initializer. Typical linker behavior would be to treat all such symbols as undefined ones if one of the objects being linked has a definition for that symbol with a different type, or else to treat all but one of them as undefined, and the other as having type B (implying default initializtion).

But the MacOS development toolchain seems to have hatched a bug. In your example, it is erroneously failing to recognize the type C symbol as a viable definition when that appears in a library. The issue might be either in the Clang front end or in the system linker, or in a combination of both. Perhaps this arrived together with Apple's recent tightening (and subsequent re-loosening) of the compiler's default conformance settings.

You can probably work around this issue by adding --fno-common to your C compiler flags. GCC and Clang both accept that for disabling the symbol merging described above, and, at least on my machine, they both implement that by emitting the symbol as type B when it is defined without an explicit initializer, just as if it had been explicitly initialized to a null pointer. Note well, however, that this will break any code that is presently relying on that merging behavior.



Related Topics



Leave a reply



Submit