In brief: does LLVM/Clang support the 'weak' attribute?
I'm learning some Arduino library sources (HardwareSerial.cpp to be more detailed) and I've found some interesting attribute weak
that I've never used before:
#if defined(HAVE_HWSERIAL0)
void serialEvent() __attribute__((weak));
bool Serial0_available() __attribute__((weak));
#endif
I've found it interesting and I've read that the linker should set it to NULL if it's not defined.
However, in my tests with Clang I'm unable to use it.
File lib.cpp:
#include "lib.h"
#include <stdio.h>
void my_weak_func() __attribute__((weak));
void lib_func() {
printf("lib_func()\n");
if (my_weak_func)
my_weak_func();
}
File lib.h:
#ifndef LIB_FUNC
#define LIB_FUNC
void lib_func();
#endif
File main.cpp:
#include "lib.h"
#include <stdio.h>
#ifdef DEFINE_WEAK
void my_weak_func() {
printf("my_weak_func()\n");
}
#endif
int main() {
lib_func();
printf("finished\n");
return 0;
}
If I use g++ lib.cpp main.cpp -o main -DDEFINE_WEAK
I'm able to use it:
MBA-Anton:Weak_issue asmirnov$ ./main
lib_func()
my_weak_func()
finished
But if I use g++ lib.cpp main.cpp -o main
I'm unable to link the application:
Undefined symbols for architecture x86_64:
"my_weak_func()", referenced from:
lib_func() in lib-ceb555.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
To be more detailed about Clang:
MBA-Anton:Weak_issue asmirnov$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.3.0
Thread model: posix
What should I do? Is the weak
attribute supported by LLVM/Clang?
PS. I've already tried to rewrite lib.cpp in the way Apple describes and still get the same linker error:
#include "lib.h"
#include <stdio.h>
extern void my_weak_func() __attribute__((weak_import));
void lib_func() {
printf("lib_func()\n");
if (my_weak_func != NULL)
my_weak_func();
}
Clang operates in tandem with the LLVM compiler back end and has been a subproject of LLVM 2.6 and later. As with LLVM, it is free and open-source software under the Apache License 2.0 software license. Its contributors include Apple, Microsoft, Google, ARM, Sony, Intel, and AMD.
LLVM is a backend compiler meant to build compilers on top of it. It deals with optimizations and production of code adapted to the target architecture. CLang is a front end which parses C, C++ and Objective C code and translates it into a representation suitable for LLVM.
Clang can be configured to use one of several different linkers: GNU ld. GNU gold. LLVM's lld.
Clang is much faster and uses far less memory than GCC. Clang aims to provide extremely clear and concise diagnostics (error and warning messages), and includes support for expressive diagnostics. GCC's warnings are sometimes acceptable, but are often confusing and it does not support expressive diagnostics.
It fails by design because the linker doesn't have enough information. Specifically, it doesn't work because of a combination of two default linker settings:
-two_levelnamespace
-two_levelnamespace
instructs the linker to bind external symbols both by name and by library install path. When used, the linker associates symbols to libraries based on where it finds them at link-time, given the set of libraries that it was passed. If the linker doesn't find the symbol, then it won't know which library it came from.
You can turn off two-level namespacing with -flat_namespace
, but in general, I think that it's a good practice to leave it on.
Linux's ld.so does not support two-level namespacing, so this is not a problem. Every undefined symbol is assumed to have a definition in some library, to be discovered at runtime.
-undefined error
The -undefined
setting determines how to handle symbols that have no definition visible at link-time, and the default is to error out. The other sensible option is dynamic_lookup
, which tells the dynamic linker to figure out where the symbol is on its own.
Changing either of these settings will solve your problem, but it is heavy-handed. You can also tell the linker to use dynamic lookup for specific symbols and keep the default to error
by passing -U _my_weak_func
to ld
, or -Wl,-U,_my_weak_func
to Clang (which tells it to pass it forward to the linker). The _
symbol name prefix is necessary.
You can craft a tbd file and use it in place of dynamic libraries to tell the linker exactly where a weak symbol would be found if it was implemented instead of forcing weak functions to use dynamic lookup. Apple uses tbd files for its libraries and frameworks, which is what allows weak linking to work. The process is somewhat tedious, though, because Apple doesn't offer tools to automatically create tbd files for libraries. You'd need to pass a file of the following format as a library to the compiler:
--- !tapi-tbd-v3
archs: [ $ARCH ]
uuids: [ '$ARCH: $UUID' ]
platform: $ARCH
install-name: $INSTALL_PATH
current-version: $CURRENT_VERSION
objc-constraint: none
exports:
- archs: [ $ARCH ]
symbols: [ _my_weak_func ]
...
Where:
$ARCH
is the architecture name of the thing you want to build (like "x86_64", without quotes)$UUID
can be queried with otool -l $path_to_your_lib | grep -A 2 LC_UUID
$INSTALL_PATH
and $CURRENT_VERSION can be queried with otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB
This will let the linker know which library should contain your weak symbol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With