GNU’s GCC has a useful (and perhaps not very well known) feature known as ‘weak symbols’. I first discovered this a while back when building a Linux kernel – however unbeknown to me the Linux kernel makes great use of GCC weak symbols yet the compiler I used did not correctly support them. Rather than a failed build, the kernel built fine and even run – I was instead presented with a number of interesting bugs, but more on this later.
In a nutshell, GCC weak symbols permit you to define a symbol that doesn’t need to be resolved at link time, i.e. it allows you to tell the compiler that this function may not have a body and that is OK.
Furthermore, if later the compiler comes across another symbol with the same name that doesn’t have the weak attribute the original symbol will be overwritten with the stronger symbol (Without getting a multiple definition linker error). And finally, you can also use the symbol to determine, at runtime, if such a body exists.
To give you an example of its use let’s refer back to my original bug…
v2.6.27/arch/sh/kernel/cpu/clock.c:
..._x000D_
void_init_attribute_ ((weak))_x000D_
arch_init_clk_ops(struct clk_ops **ops, int type)_x000D_
{_x000D_
}
|
This function is part of the architecture specific (SH) code for setting up the various clocks of the device. The function defined above is used to return a structure of clock operations (struct clk_ops) which is later used to register the clock within the kernel. As you can see the function is declared with a weak symbol via the “weak” attribute. Therefore, when built correctly, the function can be overridden.
The design of this part of the kernel is such that generic clock operations are defined in clock.c and can be later overridden via weak symbols by implementations for specific CPU subtypes – for example, this function is overridden in the clock-sh7712.c file…
v2.6.27/arch/sh/kernel/cpu/clock-sh7712.c;
..._x000D_ void_init arch_init_clk_ops(struct clk_ops **ops, int idx)_x000D_ {_x000D_ ... |
The function hasn’t been defined as a GCC weak symbol and so will override the weak symbol. In this case, the function will provide the caller with the clock operations specific to the SH7712. In this manner, the existing generic clock support code has been designed such that it can be easily extended to support future SH subtypes. Likewise, weak symbols are used elsewhere in the kernel (since 2.4.0) for a similar effect.
Whilst my version of GCC claimed to support weak symbols there was a known GCC bug that prevented this from working correctly. I found that the code would only work correctly if the weak arch_init_clk_ops function had code in its body – what was happening was that the compiler was optimising out the function altogether (with the -O2 optimisation GCC flag) and resulted in the non-weak symbol not being called (There is a quick hack to fix this which is to use the -fno-unit-at-a-time flag, however, this is expected to be removed from GCC in the future.)
It’s always worth looking at the “/Documentation/Changes” file included in the kernel, it contains a list of the tools required and the minimal version of each tool. Just because the kernel builds doesn’t mean that it has built in the way intended by the Linux contributors!
References