Lazy binding
Lazy binding (also known as lazy linking or on-demand symbol resolution) is the process by which symbol resolution isn't done until a symbol is actually used. Functions can be bound on-demand, but data references can't.
All dynamically resolved functions are called via a Procedure Linkage Table (PLT) stub. A PLT stub uses relative addressing, using the Global Offset Table (GOT) to retrieve the offset. The PLT knows where the GOT is, and uses the offset to this table (determined at program linking time) to read the destination function's address and make a jump to it.
To be able to do that, the GOT must be populated with the appropriate addresses. Lazy binding is implemented by providing some stub code that gets called the first time a function call to a lazy-resolved symbol is made. This stub is responsible for setting up the necessary information for a binding function that the runtime linker provides. The stub code then jumps to it.
The binding function sets up the arguments for the resolving function, calls it, and then jumps to the address returned from resolving function. The next time that user code calls this function, the PLT stub jumps directly to the resolved address, since the resolved value is now in the GOT. (The GOT is initially populated with the address of this special stub; the runtime linker does only a simple relocation for the load base.)
- In the bind-now case, the application fails to load if a symbol couldn't be resolved.
- In the lazy-bound case, it doesn't fail right away (since it didn't check to see if it could resolve all the symbols) but still fails on the first call to an unresolved symbol. This doesn't change even if the application later calls dlopen() to load an object that defines that symbol, because the application can't change the resolution scope. The only exceptions to this rule are objects loaded using dlopen() with the RTLD_LAZY flag (see below).
lazy
- When generating an executable or shared library, mark it to tell the runtime linker to defer function-call resolution to the point when the function is called (lazy binding), rather than at load time.
now
- When generating an executable or shared library, mark it to tell the runtime linker to resolve all symbols when the program is started, or when the shared library is linked to using dlopen(), instead of deferring function-call resolution to the point when the function is first called.
In this release, now
binding is because it's more secure than lazy binding.
If you're using
qcc
(as we recommend), use the -W option to pass the -z
option to ld.
For example, specify -Wl,-zlazy or -Wl,-znow.
ntox86_64-readelf -d my_binary
The output includes the BIND_NOW
dynamic tag if
-znow was used when linking.
You can use the DL_DEBUG environment variable to get the
runtime linker to display some debugging information.
For more information, see
Diagnostics and debugging
and
Environment variables,
later in this chapter.
Applications with many symbols—typically C++ applications—benefit the most from lazy binding. For many C applications, the difference is negligible.
- When you start an application, the runtime linker doesn't resolve all symbols, so you may expect to see the initial screen sooner, providing your initialization prior to displaying the screen doesn't end up calling most of the symbols anyway.
- When the application is running, many symbols won't be used and thus they aren't looked up.
Both of the above are typically true for C++ applications.
Lazy binding could affect realtime performance because there's a delay the first time you access each unresolved symbol, but this delay is likely insignificant, especially on fast machines. If this delay is a problem, use -znow.
now. For example, you should probably link driver executables with -znow or run drivers with LD_BIND_NOW.