use crate::{
LinkerError, Result, UnresolvedDependency,
arch::ArchKind,
image::{ModuleHandle, ModuleScope, ModuleScopeBuilder, RawDynamic, ScannedDynamic},
input::Path,
memory::{HostRegion, RegionAccess},
relocation::{BindingMode, RelocationArch},
tls::TlsResolver,
};
use alloc::boxed::Box;
pub trait DependencyOwner {
fn path(&self) -> &Path;
fn name(&self) -> &str;
fn rpath(&self) -> Option<&str>;
fn runpath(&self) -> Option<&str>;
fn interp(&self) -> Option<&str>;
fn needed_len(&self) -> usize;
fn needed_lib(&self, index: usize) -> Option<&str>;
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> DependencyOwner
for RawDynamic<D, Arch, R, Tls>
{
#[inline]
fn path(&self) -> &Path {
self.path()
}
#[inline]
fn name(&self) -> &str {
self.name()
}
#[inline]
fn rpath(&self) -> Option<&str> {
self.rpath()
}
#[inline]
fn runpath(&self) -> Option<&str> {
self.runpath()
}
#[inline]
fn interp(&self) -> Option<&str> {
self.interp()
}
#[inline]
fn needed_len(&self) -> usize {
self.needed_libs().len()
}
#[inline]
fn needed_lib(&self, index: usize) -> Option<&str> {
self.needed_libs().get(index).copied()
}
}
impl<Arch: RelocationArch> DependencyOwner for ScannedDynamic<Arch> {
#[inline]
fn path(&self) -> &Path {
self.path()
}
#[inline]
fn name(&self) -> &str {
self.name()
}
#[inline]
fn rpath(&self) -> Option<&str> {
self.rpath()
}
#[inline]
fn runpath(&self) -> Option<&str> {
self.runpath()
}
#[inline]
fn interp(&self) -> Option<&str> {
self.interp()
}
#[inline]
fn needed_len(&self) -> usize {
self.needed_libs().len()
}
#[inline]
fn needed_lib(&self, index: usize) -> Option<&str> {
self.needed_lib(index)
}
}
pub struct RootRequest<'a, K: Clone, Q: ?Sized = K> {
key: &'a K,
contains_key: &'a dyn Fn(&Q) -> bool,
}
impl<'a, K: Clone, Q: ?Sized> RootRequest<'a, K, Q> {
#[inline]
pub(crate) fn new(key: &'a K, contains_key: &'a dyn Fn(&Q) -> bool) -> Self {
Self { key, contains_key }
}
#[inline]
pub fn key(&self) -> &'a K {
self.key
}
#[inline]
pub fn contains_key(&self, key: &Q) -> bool {
(self.contains_key)(key)
}
}
pub struct DependencyRequest<'a, K: Clone, Q: ?Sized = K> {
owner_key: &'a K,
owner: &'a dyn DependencyOwner,
needed_index: usize,
contains_key: &'a dyn Fn(&Q) -> bool,
}
impl<'a, K: Clone, Q: ?Sized> DependencyRequest<'a, K, Q> {
#[inline]
pub(crate) fn new(
owner_key: &'a K,
owner: &'a dyn DependencyOwner,
needed_index: usize,
contains_key: &'a dyn Fn(&Q) -> bool,
) -> Self {
Self {
owner_key,
owner,
needed_index,
contains_key,
}
}
#[inline]
pub fn owner_key(&self) -> &'a K {
self.owner_key
}
#[inline]
pub fn owner(&self) -> &'a dyn DependencyOwner {
self.owner
}
#[inline]
pub fn owner_name(&self) -> &'a str {
self.owner.name()
}
#[inline]
pub fn owner_path(&self) -> &'a Path {
self.owner.path()
}
#[inline]
pub fn needed(&self) -> &'a str {
self.owner
.needed_lib(self.needed_index)
.expect("DT_NEEDED index out of bounds")
}
#[inline]
pub fn needed_index(&self) -> usize {
self.needed_index
}
#[inline]
pub fn rpath(&self) -> Option<&'a str> {
self.owner.rpath()
}
#[inline]
pub fn runpath(&self) -> Option<&'a str> {
self.owner.runpath()
}
#[inline]
pub fn interp(&self) -> Option<&'a str> {
self.owner.interp()
}
#[inline]
pub fn contains_key(&self, key: &Q) -> bool {
(self.contains_key)(key)
}
#[inline]
pub fn unresolved(&self) -> crate::Error {
LinkerError::UnresolvedDependency(Box::new(UnresolvedDependency::new(
self.owner_name(),
self.needed(),
)))
.into()
}
}
pub struct VisibleModule<
K,
Arch: RelocationArch = crate::arch::NativeArch,
Tls: TlsResolver<Arch> = (),
> {
module: ModuleHandle<Arch, Tls>,
direct_deps: Box<[K]>,
}
impl<K, Arch, Tls> VisibleModule<K, Arch, Tls>
where
Arch: RelocationArch,
Tls: TlsResolver<Arch>,
{
#[inline]
pub fn new(module: impl Into<ModuleHandle<Arch, Tls>>, direct_deps: Box<[K]>) -> Self {
Self {
module: module.into(),
direct_deps,
}
}
#[inline]
pub fn module(&self) -> &ModuleHandle<Arch, Tls> {
&self.module
}
#[inline]
pub fn direct_deps(&self) -> &[K] {
&self.direct_deps
}
#[inline]
pub fn into_parts(self) -> (ModuleHandle<Arch, Tls>, Box<[K]>) {
(self.module, self.direct_deps)
}
}
pub trait VisibleModules<
K: Clone,
Arch: RelocationArch = crate::arch::NativeArch,
Q: ?Sized = K,
Tls: TlsResolver<Arch> = (),
>
{
fn contains(&self, key: &Q) -> bool {
self.module(key).is_some()
}
fn module(&self, _key: &Q) -> Option<VisibleModule<K, Arch, Tls>> {
None
}
}
impl<K: Clone, Arch: RelocationArch, Q: ?Sized, Tls: TlsResolver<Arch>>
VisibleModules<K, Arch, Q, Tls> for ()
{
}
impl<K: Clone, Arch, Q, Tls, V> VisibleModules<K, Arch, Q, Tls> for &V
where
Arch: RelocationArch,
Q: ?Sized,
Tls: TlsResolver<Arch>,
V: VisibleModules<K, Arch, Q, Tls> + ?Sized,
{
#[inline]
fn contains(&self, key: &Q) -> bool {
(**self).contains(key)
}
#[inline]
fn module(&self, key: &Q) -> Option<VisibleModule<K, Arch, Tls>> {
(**self).module(key)
}
}
pub struct RelocationRequest<
'a,
K,
D: 'static,
Arch: RelocationArch = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
key: &'a K,
raw: RawDynamic<D, Arch, R, Tls>,
scope: &'a ModuleScope<Arch, Tls>,
}
impl<'a, K, D: 'static, Arch, R, Tls> RelocationRequest<'a, K, D, Arch, R, Tls>
where
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
#[inline]
pub(crate) fn new(
key: &'a K,
raw: RawDynamic<D, Arch, R, Tls>,
scope: &'a ModuleScope<Arch, Tls>,
) -> Self {
Self { key, raw, scope }
}
#[inline]
pub fn key(&self) -> &'a K {
self.key
}
#[inline]
pub fn arch_kind(&self) -> ArchKind {
Arch::KIND
}
#[inline]
pub fn scope(&self) -> &ModuleScope<Arch, Tls> {
self.scope
}
#[inline]
pub fn raw(&self) -> &RawDynamic<D, Arch, R, Tls> {
&self.raw
}
#[inline]
pub(crate) fn into_raw(self) -> RawDynamic<D, Arch, R, Tls> {
self.raw
}
}
pub struct RelocationInputs<
Arch: RelocationArch = crate::arch::NativeArch,
Tls: TlsResolver<Arch> = (),
> {
scope: ModuleScope<Arch, Tls>,
binding: BindingMode,
}
impl<Arch, Tls> RelocationInputs<Arch, Tls>
where
Arch: RelocationArch,
Tls: TlsResolver<Arch>,
{
#[inline]
pub fn new<I, R>(scope: I) -> Self
where
I: IntoIterator<Item = R>,
R: Into<ModuleHandle<Arch, Tls>>,
{
let mut modules = ModuleScopeBuilder::new();
modules.extend(scope);
Self {
scope: modules.into_scope(),
binding: BindingMode::Default,
}
}
#[inline]
pub fn scope(scope: ModuleScope<Arch, Tls>) -> Self {
Self {
scope,
binding: BindingMode::Default,
}
}
#[inline]
pub(crate) fn into_parts(self) -> (ModuleScope<Arch, Tls>, BindingMode) {
(self.scope, self.binding)
}
#[inline]
pub fn binding(&self) -> BindingMode {
self.binding
}
#[inline]
pub fn eager(mut self) -> Self {
self.binding = BindingMode::Eager;
self
}
#[inline]
pub fn lazy(mut self) -> Self {
self.binding = BindingMode::Lazy;
self
}
#[inline]
pub fn with_binding(mut self, binding: BindingMode) -> Self {
self.binding = binding;
self
}
}
pub trait RelocationPlanner<
K,
D: 'static,
Arch: RelocationArch = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
>
{
fn plan(
&self,
req: &RelocationRequest<'_, K, D, Arch, R, Tls>,
) -> Result<RelocationInputs<Arch, Tls>>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DefaultRelocationPlanner;
impl<K, D: 'static, Arch, R, Tls> RelocationPlanner<K, D, Arch, R, Tls> for DefaultRelocationPlanner
where
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
#[inline]
fn plan(
&self,
req: &RelocationRequest<'_, K, D, Arch, R, Tls>,
) -> Result<RelocationInputs<Arch, Tls>> {
Ok(RelocationInputs::scope(req.scope().clone()))
}
}
impl<K, D: 'static, Arch, R, Tls, F> RelocationPlanner<K, D, Arch, R, Tls> for F
where
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
F: for<'a> Fn(
&RelocationRequest<'a, K, D, Arch, R, Tls>,
) -> Result<RelocationInputs<Arch, Tls>>,
{
#[inline]
fn plan(
&self,
req: &RelocationRequest<'_, K, D, Arch, R, Tls>,
) -> Result<RelocationInputs<Arch, Tls>> {
self(req)
}
}