Weak symbol

A weak symbol denotes a specially annotated symbol during linking of Executable and Linkable Format (ELF) object files. By default, without any annotation, a symbol in an object file is strong. During linking, a strong symbol can override a weak symbol of the same name. In contrast, in the presence of two strong symbols by the same name, the linker resolves the symbol in favor of the first one found. This behavior allows an executable to override standard library functions, such as malloc(3). When linking a binary executable, a weakly declared symbol does not need a definition. In comparison, (by default) a declared strong symbol without a definition triggers an undefined symbol link error.

Weak symbols are not mentioned by the C or C++ language standards; as such, inserting them into code is not very portable. Even if two platforms support the same or similar syntax for marking symbols as weak, the semantics may differ in subtle points, e.g. whether weak symbols during dynamic linking at runtime lose their semantics or not.

Syntax
The GNU Compiler Collection and the Solaris Studio C compiler share the same syntax for annotating symbols as weak, namely a special #pragma,, and, alternatively, a function and variable attribute,.

Tools support
The nm command identifies weak symbols in object files, libraries, and executables. On Linux a weak function symbol is marked with "W" if a weak default definition is available, and with "w" if it is not. Weakly defined variable symbols are marked with "V" and "v". On Solaris "nm" prints "WEAK" instead of "GLOB" for a weak symbol.

Examples
The following examples work on Linux and Solaris with GCC and Solaris Studio.

Static example
main.c:

power_slow.h: power_slow.c:

power.c:

Build commands:

cc -g -c -o main.o main.c cc -g -c -o power_slow.o power_slow.c cc -g -c -o power.o power.c cc main.o power_slow.o         -o slow cc main.o power_slow.o power.o -o fast

Output:

When removing the weak attribute and re-executing the build commands, the last one fails with the following error message (on Linux):

multiple definition of `power2'

The second-last one still succeeds, and  has the same output.

If there are no definition for a weak symbol function as a default implementation or as another weak symbol function definition or a strong symbol function definition in any of the object files linked together, the linking will be done successfully without any undefined symbol error for that weak symbol, but the execution may cause runtime crash.

Shared example
Taking main.c from the preceding example and adding:

Replacing power_slow.c with:

Build commands:

Output:

Removing the weak attribute and re-executing the build commands does not yield build errors and leads to the same output (on Linux) for  and. The build commands for the  lead to following warning and error messages (on Linux):

warning: the address of ‘user_hook’ will always evaluate as ‘true’ libpowerslow.so: undefined reference to `user_hook'

The warning is issued by the compiler because it can statically determine that in  the expression   evaluates always to true, because it contains an ELF jump table entry. The error message is issued by the linker. The build for  includes the same warning but no link error.

Use cases
Weak symbols can be used as a mechanism to provide default implementations of functions that can be replaced by more specialized (e.g. optimized) ones at link-time. The default implementation is then declared as weak, and, on certain targets, object files with strongly declared symbols are added to the linker command line.

If a library defines a symbol as weak, a program that links that library is free to provide a strong one for, say, customization purposes.

Another use case for weak symbols is the maintenance of binary backward compatibility.

Limitations
On UNIX System V descendent systems, during program runtime the dynamic linker resolves weak symbols definitions like strong ones. For example, a binary is dynamically linked against libraries libfoo.so and libbar.so. libfoo defines symbol  and declares it as weak. libbar also defines  and declares it as strong. Depending on the library ordering on the link command line (i.e. ) the dynamic linker uses the weak f from libfoo.so although a strong version is available at runtime. The GNU  provides the environment variable   to provide weak semantics for the dynamic linker.

When using constructs like

, depending on the compiler and used optimization level, the compiler may interpret the conditional as always true (because  can be seen as undefined from a standards point of view). An alternative to the above construct is using a system API to check if  is defined (e.g. dlsym with  ). The above check may also fail for other reasons, e.g. when func contains an elf jump table entry.

Using weak symbols in static libraries has other semantics than in shared ones, i.e. with a static library the symbol lookup stops at the first symbol – even if it is just weak and an object file with a strong symbol is also included in the library archive. On Linux, the linker option  changes that behavior.

The weak function attribute is supposed to be used on function declarations. Using it on a function definition may yield unexpected results, depending on the compiler and optimization level.

In Solaris, the weak symbols are also used within kernel. The generic part of kernel (called ) specifies weak functions that are overridden in platform specific part of the kernel (called  ) such as Virtual memory routines. The kernel runtime linker sets the addresses of these functions when the kernel is combined in memory during boot. This does not work for kernel loadable modules though - weak symbol in the kernel is not replaced with kernel module symbol when the module is loaded.

Related methods
C preprocessor (CPP) conditional constructs can also be used to switch between different versions of a symbol. The difference from weak symbols is that weak symbols are interpreted by the linker. The CPP is run during the compilation of each translation unit before the C compiler.

The build process (e.g. make) can be implemented in a conditional way such that just different versions of a symbol are created or different (specialized) libraries are used and linked depending on the target.