Each auditor provides one or more callbacks in the form of global
functions that the dynamic linker may call. la_version is
required of all auditors, but every other callback is optional. The
auditor defines the callback if it wants those events.
Each auditor may associate private data with each shared object in the
system via a word of storage in ld.so (as a uintptr_t, so type
casting is always needed when storing pointers). There is one word of
private data per shared object, per auditor.
This word may be an index into an array, a bitmask, or a pointer, and
the address of it is passed to most callbacks. This word is
initialized just before calling la_objopen (or, for the first
object in a new namespace, la_activity), to be a pointer to the
link map for the shared object. The auditor may choose to initialize
it to something else (typically in la_objopen()) in order to
store more or different data per object. There is no guaranteed way
to free or clean up allocated data used for this purpose, but a
reasonable option is to clean it up in la_objclose() and
set the word to zero at that time. With that, all callbacks would
need to check for a zero word and assume the related object has been
or is being closed.
unsigned int la_version (unsigned int version) ¶This mandatory callback is called when the auditor is loaded. It is
passed the API version the dynamic linker is using. It should return
the API version the module is using, or zero to silently unload the
module. If the module and the dynamic linker are using incompatible
API versions, the auditor is unloaded and an error is printed. The
expectation is that the module will return the LAV_CURRENT
value from link.h.
Note that la_version() may return values other than
LAV_CURRENT if it has knowledge of what the various versions
provide and can adapt to the version given, but doing this is
outside the scope of this document. This may be complicated by
LAV_CURRENT being different for different architectures.
void la_activity (uintptr_t *privdata, unsigned int flag) ¶This callback is used to signal activity in a namespace. privdata points to the private data for a shared object in that namespace. The flag is one of the following:
LA_ACT_ADDA shared object is being added to the namespace. Further additions are not signalled until the namespace is marked consistent again.
LA_ACT_DELETEA shared object is being removed from the namespace. Further deletions will not be signalled until the namespace is marked consistent again.
LA_ACT_CONSISTENTAll adds or deletes are complete for this namespace. After this callback, the namespace is considered consistent, and further additions or deletions will cause another activity callback. Note that this callback will not happen if the namespace is empty.
char * la_objsearch (const char *name, uintptr_t *privdata, unsigned int flag) ¶Called when the dynamic linker tries to locate a shared object to load
it. name is the name of the object to be loaded. The callback
must return name if it does not want to change the loader’s
behavior. Otherwise, it may return a different string to change the
object to be searched for. For LA_SER_ORIG, name is the
name as requested, such as a DT_NEEDED entry (like
libfoo.so.1) or the string passed to dlopen. If it
contains a slash, no search is done and there are no further calls.
For the other flags, it is the path of the file about to be tried
(like /usr/lib/libfoo.so.1).
If the auditor returns an alternate string for this callback, the
string must remain valid for the duration of the application. There
is no mechanism for freeing any allocated memory. The alternate
string will be passed to the remaining auditors in the audit chain,
unless an auditor returns NULL, which terminates the chain.
A NULL returned for LA_SER_ORIG causes that object to
not be loaded at all, while a NULL for other flags causes that
path to be skipped.
The privdata points to the private data for the object doing the search. The flag is one of the following:
LA_SER_ORIGThe dynamic linker is starting a search for name, usually via a
call to dlopen() or a DT_NEEDED entry. A callback with
this flag will happen before a callback with any of the other flags,
which indicate progress through the search.
LA_SER_LIBPATHThe dynamic loader is searching for name in one of the paths
specified by LD_LIBRARY_PATH.
LA_SER_RUNPATHThe dynamic loader is searching for name in one of the paths
specified by DT_RPATH or DT_RUNPATH.
LA_SER_CONFIGThe dynamic loader is trying a candidate for name in the paths stored in ld.so.cache.
LA_SER_DEFAULTThe dynamic loader is searching for name in one of the default paths hardcoded in the dynamic linker itself.
LA_SER_SECUREUnused.
unsigned int la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *privdata) ¶Called when a shared object is loaded, including the main, ld.so, and
vDSO objects. Unlike la_activity this callback is called once
for each object. map is the link map for the object. The
privdata points to the private data for the object being loaded,
is initialized to map before this callback7, and may be
initialized to point to auditor-specific data by this callback.
lmid is the namespace the object was loaded into, either
LM_ID_BASE for the base namespace, or some other value for
namespaces created with dlmopen().
The value returned by this callback is a bitmask of zero or more of these flags:
LA_FLG_BINDTOAudit symbols bound to this object.
LA_FLG_BINDFROMAudit symbols bound from this object.
unsigned int la_objclose (uintptr_t *privdata) ¶Called when a shared object is unloaded by dlclose, and at
process exit, after its destructors have run, for every non-auditor
object that is still loaded (except the vDSO). This is called after
la_activity signals its deletion. The return value is ignored.
void la_preinit (uintptr_t *privdata) ¶This callback is called after ELF constructors, just before
main is called. The privdata always points to the
private data for the main application.
uintptr_t la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refdata, uintptr_t *defdata, unsigned int *flags, const char *symname) ¶uintptr_t la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refdata, uintptr_t *defdata, unsigned int *flags, const char *symname) ¶Called when a binding occurs between the reference to a symbol symname and its definition sym. refdata points to the private data for the object referencing the symbol, and defdata points to the private data for the object providing the definition of the symbol. ndx is the index of the symbol in the symbol table of the shared object in which it is defined.
This callback will be called only for symbols in objects where
la_objopen returned a nonzero value. It will also only be
called for symbols that represent a function call and use the PLT for
that call, and for queries from dlsym/dlvsym. For
dlsym/dlvsym, either LA_FLG_BINDFROM must have
been returned for the referencing object, or LA_FLG_BINDTO for
the defining object. For all other cases, both flags must be set.
This callback is actually one of two callbacks, either
la_symbind32 or la_symbind64, with corresponding
Elf32_Sym or Elf64_Sym parameters. Both callbacks
function the same other than the symbol size. The auditor may define
either or both, but if both, only one is ever called—the one that
corresponds to the application’s architecture.
flags points to a bitmask which may include any of the following:
LA_SYMB_NOPLTENTERLA_SYMB_NOPLTEXITThe auditor may add these flags to *flags to request skipping
callbacks for the architecture-specific la_pltenter and/or la_pltexit
events. Note that to avoid the performance overhead associated with
the PLT callback hooks, all auditors must have neither an
la_pltenter nor an la_pltexit callback.
LA_SYMB_STRUCTCALLUnused.
LA_SYMB_DLSYMThe symbol is being bound due to a dlsym() or dlvsym()
call.
LA_SYMB_ALTVALUEThe symbol’s value has been altered by an earlier auditor’s
la_symbind callback.
The value returned by la_symbind() is used as the address of
the symbol. The auditor should return sym->st_value if it does
not wish to modify the application’s behavior.
uintptr_t la_pltenter (…) ¶This “callback” represents one of many architecture-defined
callbacks listed in link.h, refer to that for specifics. The
parameters are similar to la_symbind, with the addition of one
or more parameters for passing register contents. For example, the
x86-64 prototype is:
Elf64_Addr la_x86_64_gnu_pltenter (Elf64_Sym *sym,
unsigned int ndx, uintptr_t *refdata, uintptr_t *defdata,
La_x86_64_regs *regs, unsigned int *flags,
const char *symname, long int *framesizep);
Note that decoding the arguments is very dependent on the platform and compiler flags, and is outside the scope of this document.
The la_pltenter callback is called when a caller is about to
call a function through a PLT entry which is lazily bound, if the
caller and callee objects have been marked for auditing by
la_objopen(). The callback returns the address to actually
call, or sym->st_value if it does not wish to modify the call.
Note that the PLT remains lazily bound for the life of the program if
this callback is defined.
Calls are not traced if the PLT is bound at load time with
LD_BIND_NOW=1, or if the code does not use the PLT at all (for
example, when built with -fno-plt). Linking with -z now
does not prevent tracing, because the dynamic linker keeps lazy
binding when an auditor defines la_pltenter or
la_pltexit.
*framesizep is initially -1, which means la_pltexit is
not wanted. To receive la_pltexit, set it to the number of
bytes of the caller’s stack (the arguments passed on the stack) that
must be copied for the call. The dynamic linker calls the function
from its own frame, so that the function returns to the dynamic
linker, and copies that many bytes into that frame. If the value is
too small, the function reads garbage for its stack arguments; 0 is
fine for functions with no stack arguments. If several auditors set
it, the largest value is used.
unsigned int la_pltexit (…) ¶Architecture-specific function similar to la_pltenter. This
callback is called when a function, called through the PLT, returns to
the caller. It will only be called if la_pltenter specified a
frame size for it. The parameters are architecture-dependent, but all
include an in-registers parameter, which contains the original
registers at the time of la_pltenter, and an out-registers
parameter, which contains the return value of the function, which the
auditor may modify. For example, the x86-64 function is:
unsigned int la_x86_64_gnu_pltexit (Elf64_Sym *sym,
unsigned int ndx, uintptr_t *refdata, uintptr_t *defdata,
const La_x86_64_regs *inregs, La_x86_64_retval *outregs,
const char *symname);