use crate::sync::Arc;
use crate::{
Result,
arch::NativeArch,
elf::ElfPhdr,
image::{LoadedCore, ModuleTls, RawDynamic},
input::{Path, PathBuf},
lazy::{LazyBinder, SupportLazy},
loader::ImageBuilder,
memory::{HostRegion, RegionAccess, VmAddr, VmOffset},
observer::RelocationObserver,
relocation::{Relocatable, RelocateArgs, RelocationArch},
segment::ElfSegments,
tls::{
TlsImageProvider, TlsImageSource, TlsModuleId, TlsResolver, TlsTemplate, TlsTpOffset,
tls_image_provider_handle,
},
};
use alloc::vec::Vec;
use core::fmt::Debug;
pub struct StaticExec<D, Arch: RelocationArch = NativeArch, R: RegionAccess = HostRegion> {
inner: Arc<StaticExecInner<D, Arch, R>>,
}
impl<D, Arch: RelocationArch, R: RegionAccess> Clone for StaticExec<D, Arch, R> {
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<D, Arch: RelocationArch, R: RegionAccess> Debug for StaticExec<D, Arch, R> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StaticExec")
.field("path", &self.inner.path)
.finish()
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess> StaticExec<D, Arch, R> {
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) -> ModuleTls {
ModuleTls::new(self.inner.tls_mod_id, self.inner.tls_tp_offset)
}
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> {
path: PathBuf,
entry: VmAddr,
user_data: D,
segments: ElfSegments<R>,
phdrs: Option<Vec<ElfPhdr<Arch::Layout>>>,
tls_mod_id: Option<TlsModuleId>,
tls_tp_offset: Option<TlsTpOffset>,
_tls_image: Option<Arc<StaticTlsImage>>,
}
struct StaticTlsImage {
template: TlsTemplate<'static>,
}
impl TlsImageProvider for StaticTlsImage {
fn with_tls_template(&self, f: &mut dyn FnMut(TlsTemplate<'_>) -> Result<()>) -> Result<()> {
f(self.template)
}
}
impl<D: '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 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 = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> where
D: 'static,
Arch: RelocationArch,
{
Dynamic(RawDynamic<D, Arch, R, Tls>),
Static(StaticExec<D, Arch, R>),
}
impl<D, 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: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> SupportLazy
for RawExec<D, Arch, R, Tls>
{
}
impl<D: '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) -> 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.base(),
RawExec::Static(image) => image.base(),
}
}
}
#[derive(Debug)]
pub struct LoadedExec<
D: 'static,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
entry: VmAddr,
inner: LoadedExecInner<D, Arch, R, Tls>,
}
#[derive(Debug)]
enum LoadedExecInner<
D: 'static,
Arch: RelocationArch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
Dynamic(LoadedCore<D, Arch, R, Tls>),
Static(StaticExec<D, Arch, R>),
}
impl<D: '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: '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: '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) -> ModuleTls {
match &self.inner {
LoadedExecInner::Dynamic(module) => module.tls(),
LoadedExecInner::Static(static_image) => static_image.tls(),
}
}
}
impl<Tls, D: '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>> {
self.parse_phdrs(phdrs)?;
let entry = self.entry;
let mut tls_image = None;
let (tls_mod_id, tls_tp_offset) = if let Some(info) = &self.tls_info {
let template = self
.segments
.read_view::<u8>(VmOffset::new(info.vaddr), info.filesz)
.ok_or_else(|| crate::ParsePhdrError::malformed("PT_TLS image is malformed"))?;
tls_image = Some(Arc::new(StaticTlsImage {
template: (*info).template(template.as_slice()),
}));
let (mod_id, offset) = Tls::register_static(info)?;
(Some(mod_id), Some(offset))
} else {
(None, 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_mod_id,
tls_tp_offset,
_tls_image: tls_image.clone(),
});
if let (Some(mod_id), Some(offset), Some(image)) =
(tls_mod_id, tls_tp_offset, tls_image.as_ref())
{
let provider = tls_image_provider_handle(image.clone());
Tls::init_tls(
TlsImageSource::new(image.template.info, Arc::downgrade(&provider)),
mod_id,
Some(offset),
)?;
}
Ok(StaticExec { inner })
}
pub(crate) fn build_exec(
self,
phdrs: &[ElfPhdr<Arch::Layout>],
has_dynamic: bool,
) -> Result<RawExec<D, Arch, R, Tls>> {
if has_dynamic {
Ok(RawExec::Dynamic(self.build_dynamic(phdrs)?))
} 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>>();
}
}