In general the compiler is going to scope symbols declared in object files as being global. This means that they can be seen and bound to by any object. There are two other settings for symbol scope - "symbolic" and "hidden".
Hidden scope is easiest to describe as it just means that the symbol can only be seen within the module and is not exported for applications or libraries to use. This is basically a locally defined symbol. There are multiple advantages to using hidden scoping when possible, it reduces the number of symbols that the linker needs to handle at runtime, so reduces start up time. It also reduces the number of names, so reduces the chance of duplicate names. Finally hidden symbols cannot be bound to externally, so they cannot cause a link order problem. This makes hidden scope a good choice for all those symbols that don't need to be exported.
The other option is symbolic scope. A symbol with symbolic scope is still available for other modules to bind to - so it is like a global symbol in that respect. However, a symbolic symbol can only be satisfied from within the library or application. So if I have an unresolved symbolic symbol foo()
then that symbol can only bind within the library or application. So symbolic-scoped symbols avoid the cross-library issue that causes link order problems.
Symbols can be declared with their scoping; __global
,__symbolic
, or __hidden
. We can also use the compiler flag -xldscope=<scope>
to set the default scoping for all the symbols not otherwise scoped.
The details of all this are discussed much more thoroughly in Part 7 of the series.
The best practices for symbol scoping come in two flavours:
The easiest way of handling scoping is to declare all the defined symbols to have symbolic scoping (-xldscope=symbolic
). This ensures that these symbols end up with local binding rather than pulling in definitions that are present in other libraries. The downside of this is that it could cause multiple definitions for the same symbol to become present in the address space of an application.
The other approach is to carefully define interfaces by declaring exported symbols to be __symbolic
, so that other libraries can bind to them, but this library will bind to the local versions in preference. Then to declare imported symbols as __global
which will ensure that the library can bind to an external definition for the symbol. Then finally use -xldscope=hidden
to avoid further pollution of the name space. This is time consuming but reduces runtime link costs, and also increases the robustness of the application.