template <class Elf = elfldltl::Elf<>, RemoteLoadZygote Zygote = RemoteLoadZygote::kNo, elfldltl::ElfMachine Machine = elfldltl::ElfMachine::kNative>
class RemoteDynamicLinker
Defined at line 148 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
ld::RemoteDynamicLinker represents a single remote dynamic linking session.
It may or may not be the first or only dynamic linking session performed on
the same process. Each dynamic linking session defines its own symbolic
dynamic linking domain and has its own passive ABI (stub dynamic linker).
The second optional template parameter can select the "zygote mode"
implementation. This is used by the
<lib
/ld/remote-zygote.h> API, which
provides the ld::RemoteZygote::Linker alias. The zygote-mode linker is used
in the same ways as the plain ld::RemoteDynamicLinker described here.
Before creating an ld::RemoteDynamicLinker, the ld::RemoteAbiStub must be
provided (see
<lib
/ld/remote-abi-stub.h>). Only a single ld::RemoteAbiStub
is needed to reuse the same stub dynamic linker binary across many dynamic
linking sessions. The ld::RemoteAbiStub can be provided in a constructor
argument, or injected with the set_abi_stub method after default
construction; it must be set before Init is called.
The dynamic linking session proceeds in these phases, with one method each:
* `Init()` starts the session by finding and decoding all the modules. This
starts with root modules (such as a main executable), and acquires their
transitive DT_NEEDED dependencies via a callback function. Additional
"implicit" modules may be specified to Init, such as a vDSO: these are
linked in even if they are not referenced by any DT_NEEDED dependency;
they always appear in the passive ABI, and if unreferenced will be last in
the list and have `.symbols_visible = false`.
Implicit modules serve multiple purposes. One is just to satisfy any
DT_NEEDED dependencies for them--but that could just as well be handled by
giving the callback function special-case logic for certain names. What's
unique about implicit modules is that they will be there even if they are
not referenced by any DT_NEEDED entry. The use cases are things that are
going to be in the address space anyway for some reason other than use of
their symbolic ABI via dynamic linking.
One kind reason something is in the address space without its symbolic ABI
being used is that its code and/or data addresses are part of a direct ABI
used in some other way. An example of this is the stub dynamic linker
(and likewise, the traditional in-process startup dynamic linker
implementing similar logic case): it contains runtime entry points used
implicitly by certain kinds of TLS accesses, so dynamic linking may
resolve certain relocations using addresses in this implicit shared
library, even though nothing refers to its DT_SONAME, nor to any symbol it
defines. Another example is a vDSO: the process startup ABI includes
passing pointers into the vDSO image in various ways, so programs that
don't have a DT_NEEDED dependency on the vDSO anywhere might still have
code that follows those pointers and needs its code or data to be intact.
Another reason something is preemptively put into the address space is to
make sure its symbolic ABI will be available at runtime to things that use
it opportunistically or in special ways rather than via normal dynamic
linking dependencies. When a module with `.symbols_visible = false` on
the list is also what it looks like when a module was added via `dlopen`
without using the `RTLD_GLOBAL` flag: a later attempt to `dlopen` that
name (or something else that transitively reaches a DT_NEEDED for that
same name) will find the module already loaded, and not try to load it
afresh. Finally, it's important that every module that is present in the
address space for whatever reason be represented in the passive ABI of any
dynamic linking domain that might interact with it. For example, unwinder
implemnentations will consult this module list (via the `dl_iterate_phdr`
API) to map any PC they come across to a module and find its unwinding
metadata. Things go awry if a PC does not lie in a known module.
* `Allocate()` sets the load address for each module using zx_vmar_allocate.
Each module gets a VMAR to reserve its part of the address space, and the
system call chooses a random available address for it (ASLR). This is the
step that binds the dynamic linking session to a particular address layout
and set of relocation results, which depend on all the addresses. The
session is now appropriate only for a single particular process, or a
single zygote that will spawn identical processes. This is the first
point at which there is any need for a Zircon process to actually exist.
Creating the process and ultimately launching it are outside the scope of
this API. The call to Allocate() must supply a zx::unowned_vmar where
zx::vmar::allocate() calls will be made to place each module. That can be
the root VMAR of a process, or a smaller VMAR. It must be large enough to
fit all the module images (including their .bss space beyond the size of
each ELF file image), and must permit the necessary mapping operations
(read, write, and execute, usually). The absolute addresses for any
`Preplaced()` (`InitModule::WithLoadBias`) uses and their image sizes must
lie within this VMAR.
* `Relocate()` fills in all the segment data that will need to be mapped in.
That is, it performs relocation on all modules and completes the passive
ABI data in the stub dynamic linker module. When the Diagnostics object
passed to `Init()` used a policy of reporting multiple errors before
bailing out, then `Init()` returned "successfully" even if there were
errors reported such as an invalid ELF file or a `get_dep` callback that
couldn't find the file. It may be appropriate to check the Diagnostics
object's error count and bail out before attempting relocation. It's also
safe to have the policy of attempting relocation despite past errors in
the Init phase. Any modules not decoded with sufficient success to safely
attempt relocation will be skipped. Relocating the remaining modules may
produce many additional errors due to partially-decoded or corrupt
metadata or undefined symbols that would have come from missing or
corrupted files. Such additional logging of e.g. undefined symbols may be
deemed useful, or not. It is highly discouraged to proceed past the
Relocate phase on to additional calls if there were any errors reported
during or before the Relocate phase. There is likely little benefit in
doing the address space layout or loading work to report additional error
details, and actually launching the process could be disastrous.
* `Load()` loads all the segments finalized by `Relocate()` into the VMARs
created by `Allocate()`. Since the VMARs have already been created and
the VMOs of segment contents already completed, nothing can usually go
wrong here except for resource exhaustion in creating mappings new VMOs
(either zero-fill or copy-on-write copies of relocated segments). If
anything does go wrong, the process address space may be left in an
indeterminate state until the ld::RemoteDynamicLinker object is destroyed.
* `Commit()` finally ensures that the VMARs created and mappings made can't
be changed or destroyed. If Commit() is not called, then all the VMARs
will be destroyed when the ld::RuntimeDynamicLinker object is destroyed.
After Commit() the object is only available for examining what was done.
The VMAR handles are no longer available and the VmarLoader objects would
need to be reinitialized to be used again. The segment VMO handles are
still available, but when not in zygote mode they are in use by process
mappings and must not be touched. In zygote mode, the relocated segment
VMOs will be made read-only and then reused (directly for RELRO mappings or
via copy-on-write copies) to load additional as-relocated process images.
Various other methods are provided for interrogating the list of modules and
accessing the dynamic linker stub module and the ld::RemoteAbi object.
Public Methods
void RemoteDynamicLinker<Elf, Zygote, Machine> ()
If default-constructed, set_abi_stub() must be used before Init().
Defined at line 265 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
void RemoteDynamicLinker<Elf, Zygote, Machine> (RemoteDynamicLinker<Elf, Zygote, Machine> && )
The object is movable and move-assignable.
Defined at line 268 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
void RemoteDynamicLinker<Elf, Zygote, Machine> (AbiStubPtr abi_stub)
The AbiStubPtr can be set in the constructor or with set_abi_stub().
Defined at line 271 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
RemoteDynamicLinker<Elf, Zygote, Machine> & operator= (RemoteDynamicLinker<Elf, Zygote, Machine> && )
Defined at line 273 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
const AbiStubPtr & abi_stub ()
Defined at line 275 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
void set_abi_stub (AbiStubPtr abi_stub)
Defined at line 277 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModule RootModule (DecodedModulePtr decoded_module, Soname visible_name)
Shorthand to create an InitialModuleList element for a root module, which
always has an explicit name.
Defined at line 281 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModule Executable (DecodedModulePtr decoded_module)
Shorthand to create an InitialModuleList element for the common case: the
main executable as root module.
Defined at line 290 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModule Implicit (DecodedModulePtr decoded_module)
Shorthand to create an InitialModuleList element for an implicit module,
such as the vDSO.
Defined at line 296 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModule Preplaced (DecodedModulePtr decoded_module, size_type load_bias, std::optional<Soname> visible_name)
Shorthand to create an InitialModuleList element for a module whose load
bias is chosen rather than left to ASLR. If the optional visible_name is
given, this is a root module; otherwise it's an implicit module.
Defined at line 303 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModule Preloaded (DecodedModulePtr decoded_module, size_type load_bias, std::optional<Soname> visible_name)
Shorthand to create an InitialModuleList element for a module already
loaded in place.
Defined at line 313 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
InitModuleList PreloadedImplicit (const InitResult & list)
Shorthand for turning a previous set of initial modules into a new one.
This produces the list for a secondary dynamic linking session that takes
the initial modules from this session as preloaded implicit modules. This
takes the (successful) return value from Init, but it should be used only
after the Allocate phase when all the load addresses are known.
Defined at line 326 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
RemoteAbi<Module, Machine> & remote_abi ()
Other accessors should be used only after a successful Init call (below).
Defined at line 337 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
const RemoteAbi<Module, Machine> & remote_abi ()
Defined at line 338 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
List & modules ()
Defined at line 340 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
const List & modules ()
Defined at line 341 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
Module & abi_stub_module ()
Defined at line 343 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
const Module & abi_stub_module ()
Defined at line 344 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
Module & main_module ()
When loading a main executable in the normal fashion, it's always the
first of the root modules given to Init() and so the first in the
modules() list.
Defined at line 349 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
const Module & main_module ()
Defined at line 350 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
size_type main_entry ()
Return the runtime address for the main module's Ehdr::e_entry PC address.
This should be used after Allocate(), below.
Defined at line 354 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
std::optional<size_type> main_stack_size ()
Return any PT_GNU_STACK size request from the main module.
Defined at line 360 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
size_type abi_vaddr ()
Return the runtime address of the _ld_abi symbol in the stub dynamic
linker, which is the root of the passive ABI for this dynamic linking
namespace. This should be used after Allocate(), below. The data
structure to be mapped at that address will be completed by Relocate().
Defined at line 368 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
size_type rdebug_vaddr ()
Return the runtime address of the traditional r_debug struct for this
dynamic linking namespace, which might be understood by a debugger.
Defined at line 372 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
Module * FindModule (Soname soname)
Find an existing Module in the modules() list by name or SONAME. Returns
nullptr if none matches. The returned pointer is invalidated by adding
modules to the list.
Defined at line 379 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
bool AllModulesValid ()
This returns false if any module was not successfully decoded enough to
attempt relocation on it. If this returns true, some modules may still
have errors like missing or incomplete symbol or relocation information,
but it's at least valid to call Relocate on them to generate whatever
specific errors might result.
Defined at line 392 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
auto ValidModules ()
This is a shorthand for a view filtered down to modules that have been
decoded successfully enough to attempt relocation on them, i.e. where
.HasModule() returns true.
Defined at line 397 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
auto ValidModules ()
Defined at line 398 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
template <class Diagnostics, typename GetDep>
std::optional<InitResult> Init (Diagnostics & diag, InitModuleList initial_modules, GetDep && get_dep, std::optional<elfldltl::ElfMachine> machine)
Initialize the session by finding and decoding all the modules. The root
modules from initial_modules (those with `.visible_name` set) go onto the
list first in the same order in which they appear there, and then all the
transitive dependencies are added in breadth-first order.
The implicit modules are used by DT_SONAME as needed. Any module that is
never referenced via DT_NEEDED goes onto the list at the end, with its
`.symbols_visible = false`. Note that implicit modules never have their
own DT_NEEDED lists examined: they are expected either to have no
dependencies or to have had their dependencies preloaded in some fashion.
For any other dependency, call get_dep as `GepDepResult(Soname)`. The
return value is an alias for `std::optional
<DecodedModulePtr
>`. This
callback is responsible for doing its own diagnostics logging as needed.
If it returns `std::nullopt`, then Init will return `std::nullopt`
immediately, as when the Diagnostics object returns false. If it instead
returns a null DecodedModulePtr, that will be treated like the Diagnostics
object returning true after a failure: that dependency will be omitted,
but processing continues.
The return value is `std::nullopt` if the Diagnostics object returned
false for an error or the get_dep function returned `std::nullopt`.
The InitResult on success has the same number of elements as the argument
initial_modules list, each giving the iterator into `.modules()` for that
initial module's place in the load order. The modules() list is complete,
remote_abi() has been initialized, and abi_stub_module() can be used.
Defined at line 428 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
template <class Diagnostics>
bool Allocate (Diagnostics & diag, zx::unowned_vmar vmar)
Initialize the loader and allocate the address region for each module,
updating their runtime addr fields on success. This must be called before
Relocate or Load. If Init was told to keep going after decoding errors,
then this will just skip any modules that weren't substantially decoded.
Defined at line 602 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
TlsDescResolver tls_desc_resolver ()
Acquire a StaticTlsDescResolver for Relocate that uses the stub dynamic
linker's TLSDESC entry points. This resolver handles undefined weak
resolutions and it handles symbols resolved to a definition in a module
using static_tls_bias(). This is what's used by default if Relocate is
called with one argument.
This can only be used after Allocate(), as that determines the runtime
code addresses for the stub dynamic linker; these addresses are stored in
the returned object. Note they can also be modified later with e.g.
`.SetHook(TlsdescRuntime::kStatic, custom_hook)`; see
<lib
/ld/tlsdesc.h>.
Defined at line 644 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
template <auto& diag>
bool Relocate (auto & diag)
Shorthand for the two-argument Relocate method below.
Defined at line 649 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
template <class Diagnostics, typename TlsDescResolverType>
bool Relocate (Diagnostics & diag, TlsDescResolverType && tls_desc_resolver)
Perform relocations on all modules. The modules() list gives the set and
order of modules used for symbol resolution.
For dynamic TLS references, the tls_desc_resolver is a callable object
with the signatures of ld::StaticTlsDescResolver (see
<lib
/ld/tlsdesc.h>),
usually from the tls_desc_resolver() method above to use runtime callbacks
supplied in the stub dynamic linker.
If any module was not successfully decoded sufficiently to call the
Relocate method on that ld::RemoteLoadModule, then that module is just
skipped. This doesn't cause a "failure" here because the Diagnostics
object must have reported the failures in decoding and decided to keep
going anyway, so there is nothing new to report. The caller may have
decided to attempt relocation so as to diagnose all its specific errors,
rather than bailing out immediately after decoding failed on some of the
modules. Probably callers will more often decide to bail out, since
missing dependency modules is an obvious recipe for undefined symbol
errors that aren't going to be more enlightening to the user. But this
class supports any policy.
Defined at line 671 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
template <class Diagnostics>
bool Load (Diagnostics & diag)
Load each module into the VMARs created by Allocate. This should only be
attempted after Relocate has succeeded with no errors reported to the
Diagnostics object. Loading the object will likely work if relocation was
incomplete, but using the code and data thus loaded would almost certainly
be very unsafe. There's no benefit to loading code and then not starting
the process, so loading of incomplete state should not be attempted.
After this, all the mappings are in place with their proper and final
protections. The VMAR handles still exist to allow mapping changes, but
those VMARs will be destroyed when this object is destroyed unless Commit
is called first.
Defined at line 714 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h
void Commit ()
This should only be called after Load (and everything before) has
succeeded. This commits all the mappings to their VMARs permanently. The
sole handle to each VMAR is dropped here, so no more changes to those
VMARs can be made--only unmapping a whole module's vaddr range en masse to
destroy the VMAR.
This should be the last use of the object when not in Zygote mode. The
list of modules and each module's segments can still be examined, but the
VMOs for relocated segments are now being read and written through process
mappings and must not be disturbed. The VmarLoader object for each module
will be in moved-from state, and cannot be used without reinitialization.
In zygote mode, this object can be moved into the ld::RemoteZygote
constructor after Commit(). If it's moved in without Commit(), then all
the mappings made in the original process VMAR will be destroyed and the
existing process should not be started, but the zygote will still work
just the same to start more processes.
Defined at line 736 of file ../../sdk/lib/ld/include/lib/ld/remote-dynamic-linker.h