Weak symbol

From Wikipedia, the free encyclopedia
Jump to: navigation, search

A weak symbol denotes a specially annotated symbol during linking of 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, 2 strong symbols that share a name yield a link error during link-time. 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 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.[1]

Syntax[edit]

The GNU Compiler Collection and the Solaris Studio C compiler share the same syntax for annotating symbols as weak, namely a special #pragma, #pragma weak and, alternatively, a function and variable attribute, __attribute__((weak)).[2][3][4][5][6][7]

Pragma[edit]

// function declaration
#pragma weak power2
 
int power2(int x);

Attribute[edit]

// function declaration
 
int __attribute__((weak)) power2(int x);
 
  // or
 
int power2(int x) __attribute__((weak));
 
// variable declaration;
extern int __attribute__((weak)) global_var;

Tools Support[edit]

The nm command marks weak symbols. 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[edit]

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

Static Example[edit]

main.c:

#include <stdio.h>
#include <stdlib.h>
 
#include "power_slow.h"
 
int main(int argc, char **argv)
{
  fprintf(stderr, "power3() = %d\n", power3(atoi(argv[1])));
  return 0;
}

power_slow.h:

#ifndef POWER2_SLOW_H
#define POWER2_SLOW_H
 
// alternative syntax
// #pragma weak power2
int 
  __attribute__((weak))
    power2(int x)
      // alternatively after symbol
      // __attribute__((weak))
  ;
 
int power3(int x);
 
#endif

power_slow.c:

#include <stdio.h>
#include "power_slow.h"
 
int power2(int x)
{
  fprintf(stderr, "slow power2()\n");
  return x*x;
}
 
int power3(int x)
{
  return power2(x)*x;
}

power.c:

#include <stdio.h>
int power2(int x)
{
  fprintf(stderr, "fast power2()\n");
  return x*x;
}

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:

$ ./slow 3
slow power2
power3() = 27
$ ./fast 3
fast power2
power3() = 27

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

multiple definition of `power2'

The 2nd last one still succeeds, and ./slow has the same output.

Shared example[edit]

Taking main.c from the preceding example and adding:

#ifndef NO_USER_HOOK
void user_hook(void)
{
  fprintf(stderr, "main: user_hook()\n");
}
#endif

Replacing power_slow.c with:

#include "power_slow.h"
 
void __attribute__((weak)) user_hook(void);
#ifdef ENABLE_DEF
void user_hook(void)
{
  fprintf(stderr, "power_slow: user_hook()\n");
}
#endif
 
int power2(int x)
{
  if (user_hook) // only needed ifndef ENABLE_DEF
    user_hook();
  return x*x;
}
 
int power3(int x)
{
  return power2(x)*x;
}

Build commands:

cc -g -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o power_slow.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main
 
cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o power_slow.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main2
 
cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o power_slow.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main3
 
cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o power_slow.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main4

Output:

$ ./main 3
main: user_hook()
power3() = 27
$ ./main2 3
main: user_hook()
power3() = 27
$ ./main3 3
power3() = 27
$ ./main4 3
power_slow: user_hook()
power3() = 27

Removing the weak attribute and re-executing the build commands does not yield build errors and leads to the same output (on Linux) for main and main2. The builds commands for the main3 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 if (user_hook) the expression user_hook evaluates always to true, because it contains an elf jump table entry. The error message is issued by the linker. The build for main4 includes the same warning but no link error.

Use cases[edit]

Weak symbols can be used as an 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[edit]

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 f and declares it as weak. libbar also defines f and declares it as strong. Depending on the library ordering on the link command line (i.e. -lfoo -lbar) the dynamic linker uses the weak f from libfoo.so although a strong version is available at runtime. The GNU ld provides the environment variable LD_DYNAMIC_WEAK to provide weak semantics for the dynamic linker.[1][8]

When using constructs like

#pragma weak func
void func();
 
void bar()
{
  if (func)
    func();
}

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

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 --whole-archive changes that behavior.[10]

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.[11]

Related methods[edit]

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.

References[edit]

  1. ^ a b Drepper, Ulrich (2000-06-07). "weak handling". 
  2. ^ "GCC Manual, 6.58.9 Weak Pragmas". 
  3. ^ "GCC Manual, 6.30 Declaring Attributes of Functions". GNU. Retrieved 2013-05-29. 
  4. ^ "GCC Manual, 6.36 Specifying Attributes of Variables". 
  5. ^ "Oracle Solaris Studio 12.3: C User's Guide, 2.11.27 weak". 
  6. ^ "Oracle Solaris Studio 12.3: C User's Guide, 2.9 Supported Attributes". 
  7. ^ a b "Oracle Solaris 11 Express 11/10 Linker and Libraries Guide, 2.11 Weak Symbols". 
  8. ^ Drepper, Ulrich (October 2011). "How To Write Shared Libraries (Version 4.1.2), 1.5.2 Symbol Relocations, page 6". 
  9. ^ "Weak Linking and Linux Shared Libraries". 
  10. ^ "GNU LD man page". 
  11. ^ Kiszka, Jan (2006-05-23). "Re: weak-attribute over-optimisation with 4.1". 

See also[edit]