use crate::{
ParsePhdrError, Result,
arch::NativeArch,
elf::ElfPhdr,
image::{LoadedCore, Module, RawDynamic, SearchPathPool},
input::{Path, PathBuf},
lazy::{LazyBinder, SupportLazy},
loader::ImageBuilder,
memory::{HostRegion, RegionAccess, VmAddr, VmOffset},
observer::RelocationObserver,
relocation::{Relocatable, RelocateArgs, RelocationArch},
runtime::DomainId,
segment::ElfSegments,
sync::{Arc, arc_unsize},
tls::{ModuleTls, TlsImageProvider, TlsImageSource, TlsRequest, TlsResolver},
};
use alloc::vec::Vec;
use core::fmt::Debug;
pub struct StaticExec<
D,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
inner: Arc<StaticExecInner<D, Arch, R, Tls>>,
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Clone
for StaticExec<D, Arch, R, Tls>
{
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Debug
for StaticExec<D, Arch, R, Tls>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StaticExec")
.field("path", &self.inner.path)
.finish()
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
StaticExec<D, Arch, R, Tls>
{
pub fn path(&self) -> &Path {
self.inner.path.as_path()
}
pub fn name(&self) -> &str {
self.path().file_name()
}
pub fn entry(&self) -> usize {
self.entry_addr().get()
}
pub(crate) fn entry_addr(&self) -> VmAddr {
self.inner.entry
}
pub fn tls(&self) -> Option<ModuleTls> {
self.inner.tls
}
pub fn user_data(&self) -> &D {
&self.inner.user_data
}
pub fn phdrs(&self) -> Option<&[ElfPhdr<Arch::Layout>]> {
self.inner.phdrs.as_deref()
}
pub fn base(&self) -> VmAddr {
self.inner.segments.base()
}
pub fn segments(&self) -> &ElfSegments<R> {
&self.inner.segments
}
}
struct StaticExecInner<
D,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
path: PathBuf,
entry: VmAddr,
user_data: D,
segments: ElfSegments<R>,
phdrs: Option<Vec<ElfPhdr<Arch::Layout>>>,
tls: Option<ModuleTls>,
tls_resolver: Tls,
domain: DomainId,
_tls_image: Option<Arc<StaticTlsImage>>,
}
impl<D, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Drop
for StaticExecInner<D, Arch, R, Tls>
{
fn drop(&mut self) {
if let Some(tls) = self.tls {
self.tls_resolver.unregister(tls.mod_id());
}
}
}
struct StaticTlsImage {
image: &'static [u8],
}
impl TlsImageProvider for StaticTlsImage {
fn with_tls_image(&self, f: &mut dyn FnMut(&[u8]) -> Result<()>) -> Result<()> {
f(self.image)
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
Relocatable<D> for RawExec<D, Arch, R, Tls>
{
type Output = LoadedExec<D, Arch, R, Tls>;
type Arch = Arch;
type Tls = Tls;
fn domain_id(&self) -> DomainId {
match self {
Self::Dynamic(image) => image.domain_id(),
Self::Static(image) => image.inner.domain,
}
}
fn relocate<Obs, Binder>(
self,
args: RelocateArgs<'_, Arch, Tls, Obs, Binder>,
) -> Result<Self::Output>
where
Obs: RelocationObserver<Arch> + ?Sized,
Binder: LazyBinder<Arch> + ?Sized,
{
match self {
RawExec::Dynamic(image) => {
let entry = image.entry_addr();
let inner = Relocatable::relocate(image, args)?;
Ok(LoadedExec {
entry,
inner: LoadedExecInner::Dynamic(inner),
})
}
RawExec::Static(image) => Ok(LoadedExec {
entry: image.entry_addr(),
inner: LoadedExecInner::Static(image),
}),
}
}
}
#[allow(clippy::large_enum_variant)]
pub enum RawExec<D, Arch = NativeArch, R: RegionAccess = HostRegion, Tls: TlsResolver<Arch> = ()>
where
D: Send + Sync + 'static,
Arch: RelocationArch,
{
Dynamic(RawDynamic<D, Arch, R, Tls>),
Static(StaticExec<D, Arch, R, Tls>),
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Debug
for RawExec<D, Arch, R, Tls>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RawExec")
.field("name", &self.name())
.finish()
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
SupportLazy for RawExec<D, Arch, R, Tls>
{
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
RawExec<D, Arch, R, Tls>
{
pub fn path(&self) -> &Path {
match self {
RawExec::Dynamic(image) => image.path(),
RawExec::Static(image) => image.path(),
}
}
pub fn name(&self) -> &str {
match self {
RawExec::Dynamic(image) => image.name(),
RawExec::Static(image) => image.name(),
}
}
pub fn entry(&self) -> usize {
match self {
RawExec::Dynamic(image) => image.entry(),
RawExec::Static(image) => image.entry(),
}
}
pub fn tls(&self) -> Option<ModuleTls> {
match self {
RawExec::Dynamic(image) => image.tls(),
RawExec::Static(image) => image.tls(),
}
}
pub fn interp(&self) -> Option<&str> {
match self {
RawExec::Dynamic(image) => image.interp(),
RawExec::Static(_) => None,
}
}
pub fn needed_libs(&self) -> &[&str] {
match self {
RawExec::Dynamic(image) => image.needed_libs(),
RawExec::Static(_) => &[],
}
}
pub fn phdrs(&self) -> Option<&[ElfPhdr<Arch::Layout>]> {
match self {
RawExec::Dynamic(image) => Some(image.phdrs()),
RawExec::Static(image) => image.phdrs(),
}
}
pub fn contains_addr(&self, addr: VmAddr) -> bool {
match self {
RawExec::Dynamic(image) => image.segments().contains_addr(addr),
RawExec::Static(image) => image.segments().contains_addr(addr),
}
}
pub fn base(&self) -> VmAddr {
match self {
RawExec::Dynamic(image) => image.segments().base(),
RawExec::Static(image) => image.base(),
}
}
}
#[derive(Debug)]
pub struct LoadedExec<
D: Send + Sync + 'static,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
entry: VmAddr,
inner: LoadedExecInner<D, Arch, R, Tls>,
}
#[derive(Debug)]
enum LoadedExecInner<
D: Send + Sync + 'static,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
Dynamic(LoadedCore<D, Arch, R, Tls>),
Static(StaticExec<D, Arch, R, Tls>),
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Clone
for LoadedExec<D, Arch, R, Tls>
{
#[inline]
fn clone(&self) -> Self {
Self {
entry: self.entry,
inner: self.inner.clone(),
}
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Clone
for LoadedExecInner<D, Arch, R, Tls>
{
#[inline]
fn clone(&self) -> Self {
match self {
Self::Dynamic(module) => Self::Dynamic(module.clone()),
Self::Static(module) => Self::Static(module.clone()),
}
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
LoadedExec<D, Arch, R, Tls>
{
#[inline]
pub fn entry(&self) -> usize {
self.entry.get()
}
#[inline]
pub fn path(&self) -> &Path {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.path(),
LoadedExecInner::Static(static_image) => static_image.path(),
}
}
#[inline]
pub fn name(&self) -> &str {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.name(),
LoadedExecInner::Static(static_image) => static_image.name(),
}
}
pub fn contains_addr(&self, addr: VmAddr) -> bool {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.segments().contains_addr(addr),
LoadedExecInner::Static(static_image) => static_image.segments().contains_addr(addr),
}
}
pub fn user_data(&self) -> &D {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.user_data(),
LoadedExecInner::Static(static_image) => &static_image.inner.user_data,
}
}
pub fn is_static(&self) -> bool {
match &self.inner {
LoadedExecInner::Dynamic(_) => false,
LoadedExecInner::Static(_) => true,
}
}
pub fn core_ref(&self) -> Option<&LoadedCore<D, Arch, R, Tls>> {
match &self.inner {
LoadedExecInner::Dynamic(module) => Some(module),
LoadedExecInner::Static(_) => None,
}
}
pub fn tls(&self) -> Option<ModuleTls> {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.tls(),
LoadedExecInner::Static(static_image) => static_image.tls(),
}
}
pub fn base(&self) -> VmAddr {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.segments().base(),
LoadedExecInner::Static(static_image) => static_image.base(),
}
}
}
impl<Tls, D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess>
ImageBuilder<Tls, D, Arch, R>
where
Tls: TlsResolver<Arch>,
{
pub(crate) fn build_static_exec(
mut self,
phdrs: &[ElfPhdr<Arch::Layout>],
) -> Result<StaticExec<D, Arch, R, Tls>> {
self.parse_phdrs(phdrs)?;
let entry = self.entry;
let mut tls_image = None;
let module_tls = if let Some(info) = &self.tls_info {
let image = self
.segments
.read_view::<u8>(VmOffset::new(info.vaddr), info.filesz)
.ok_or_else(|| ParsePhdrError::malformed("PT_TLS image is malformed"))?;
tls_image = Some(Arc::new(StaticTlsImage {
image: image.as_slice(),
}));
Some(
self.tls_resolver
.register(*info, TlsRequest::Static(None))?,
)
} else {
None
};
let inner = Arc::new(StaticExecInner {
entry,
path: self.path,
user_data: self.user_data,
segments: self.segments,
phdrs: if phdrs.is_empty() {
None
} else {
Some(phdrs.to_vec())
},
tls: module_tls,
tls_resolver: self.tls_resolver.clone(),
domain: self.domain,
_tls_image: tls_image.clone(),
});
if let Some(image) = tls_image.as_ref() {
let provider = arc_unsize!(image.clone() => dyn TlsImageProvider);
let mod_id = module_tls
.expect("static TLS image must have registered module metadata")
.mod_id();
self.tls_resolver
.publish(TlsImageSource::new(Arc::downgrade(&provider)), mod_id)?;
}
Ok(StaticExec { inner })
}
pub(crate) fn build_exec(
self,
phdrs: &[ElfPhdr<Arch::Layout>],
has_dynamic: bool,
search_paths: Option<&mut SearchPathPool>,
) -> Result<RawExec<D, Arch, R, Tls>> {
if has_dynamic {
Ok(RawExec::Dynamic(self.build_dynamic(phdrs, search_paths)?))
} else {
Ok(RawExec::Static(self.build_static_exec(phdrs)?))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NonCloneData;
#[test]
fn exec_handles_clone_without_user_data_clone() {
fn assert_clone<T: Clone>() {}
assert_clone::<StaticExec<NonCloneData>>();
assert_clone::<LoadedExec<NonCloneData>>();
}
}