Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup dynamic linker for monitor refactor #190

Merged
merged 2 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions src/bin/bootstrap/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::process::exit;

use dynlink::{
engines::{Backing, Engine},
compartment::MONITOR_COMPARTMENT_ID,
engines::{Backing, ContextEngine},
library::UnloadedLibrary,
symbol::LookupFlags,
DynlinkError, DynlinkErrorKind,
};
use tracing::{debug, info, warn, Level};
use tracing_subscriber::FmtSubscriber;
Expand All @@ -20,24 +22,55 @@ fn find_init_name(name: &str) -> Option<ObjID> {
None
}

struct Engine;

impl ContextEngine for Engine {
fn load_segments(
&mut self,
src: &Backing,
ld: &[dynlink::engines::LoadDirective],
) -> Result<Vec<Backing>, dynlink::DynlinkError> {
dynlink::engines::twizzler::load_segments(src, ld)
}

fn load_object(&mut self, unlib: &UnloadedLibrary) -> Result<Backing, DynlinkError> {
let id = name_resolver(&unlib.name)?;
Ok(Backing::new(
twizzler_runtime_api::get_runtime()
.map_object(id, MapFlags::READ)
.map_err(|_err| DynlinkErrorKind::NewBackingFail)?,
))
}

fn select_compartment(
&mut self,
_unlib: &UnloadedLibrary,
) -> Option<dynlink::compartment::CompartmentId> {
Some(MONITOR_COMPARTMENT_ID)
}
}

fn name_resolver(mut name: &str) -> Result<ObjID, DynlinkError> {
if name.starts_with("libstd") {
name = "libstd.so";
}
find_init_name(name).ok_or(
DynlinkErrorKind::NameNotFound {
name: name.to_string(),
}
.into(),
)
}

fn start_runtime(_runtime_monitor: ObjID, _runtime_library: ObjID) -> ! {
//miette::set_hook(Box::new(|_| Box::new(miette::DebugReportHandler::new()))).unwrap();
let engine = Engine;
let mut ctx = dynlink::context::Context::new(engine);
let mut ctx = dynlink::context::Context::new(Box::new(engine));
let unlib = UnloadedLibrary::new("libmonitor.so");
let monitor_comp_id = ctx.add_compartment("monitor").unwrap();

let monitor_id = ctx
.load_library_in_compartment(monitor_comp_id, unlib, |mut name| {
if name.starts_with("libstd") {
name = "libstd.so";
}
let id = find_init_name(name)?;
let obj = twizzler_runtime_api::get_runtime()
.map_object(id, MapFlags::READ)
.ok()?;
Some(Backing::new(obj))
})
.load_library_in_compartment(monitor_comp_id, unlib)
.unwrap();

ctx.relocate_all(monitor_id).unwrap();
Expand Down
23 changes: 18 additions & 5 deletions src/runtime/dynlink/src/compartment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use std::{
use petgraph::stable_graph::NodeIndex;
use talc::{ErrOnOom, Talc};

use crate::{library::BackingData, tls::TlsInfo};
use crate::{engines::Backing, library::LibraryId, tls::TlsInfo};

mod tls;

#[repr(C)]
/// A compartment that contains libraries (and a local runtime).
pub struct Compartment<Backing: BackingData> {
pub struct Compartment {
pub name: String,
pub id: CompartmentId,
// Library names are per-compartment.
Expand All @@ -39,7 +39,15 @@ impl Display for CompartmentId {
}
}

impl<Backing: BackingData> Compartment<Backing> {
impl CompartmentId {
/// Get the raw integer representing compartment ID.
pub fn raw(&self) -> usize {
self.0
}
}

pub const MONITOR_COMPARTMENT_ID: CompartmentId = CompartmentId(0);
impl Compartment {
pub(crate) fn new(name: String, id: CompartmentId) -> Self {
Self {
name,
Expand All @@ -51,15 +59,20 @@ impl<Backing: BackingData> Compartment<Backing> {
tls_gen: 0,
}
}

/// Get an iterator over the IDs of libraries in this compartment.
pub fn library_ids(&self) -> impl Iterator<Item = LibraryId> + '_ {
self.library_names.values().map(|idx| LibraryId(*idx))
}
}

impl<Backing: BackingData> core::fmt::Display for Compartment<Backing> {
impl core::fmt::Display for Compartment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}

impl<Backing: BackingData> Debug for Compartment<Backing> {
impl Debug for Compartment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Compartment[{}]", self.name)
}
Expand Down
3 changes: 1 addition & 2 deletions src/runtime/dynlink/src/compartment/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ use tracing::{debug, trace};

use super::Compartment;
use crate::{
library::BackingData,
tls::{TlsInfo, TlsModId, TlsModule, TlsRegion},
DynlinkError, DynlinkErrorKind,
};

impl<Backing: BackingData> Compartment<Backing> {
impl Compartment {
pub(crate) fn insert(&mut self, tm: TlsModule) -> TlsModId {
let prev_gen = self.tls_gen;
self.tls_gen += 1;
Expand Down
68 changes: 31 additions & 37 deletions src/runtime/dynlink/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,44 @@ use std::{collections::HashMap, fmt::Display};

use petgraph::stable_graph::{NodeIndex, StableDiGraph};

use self::engine::ContextEngine;
use crate::{
compartment::{Compartment, CompartmentId},
library::{BackingData, Library, LibraryId, UnloadedLibrary},
engines::ContextEngine,
library::{Library, LibraryId, UnloadedLibrary},
DynlinkError, DynlinkErrorKind,
};

mod deps;
pub mod engine;
mod load;
mod relocate;
pub mod runtime;
mod syms;

#[repr(C)]
/// A dynamic linker context, the main state struct for this crate.
pub struct Context<Engine: ContextEngine> {
pub struct Context {
// Implementation callbacks.
pub(crate) engine: Engine,
pub(crate) engine: Box<dyn ContextEngine + Send>,
// Track all the compartment names.
compartment_names: HashMap<String, usize>,
// Compartments get stable IDs from StableVec.
compartments: Vec<Compartment<Engine::Backing>>,
compartments: Vec<Compartment>,

// This is the primary list of libraries, all libraries have an entry here, and they are
// placed here independent of compartment. Edges denote dependency relationships, and may also
// cross compartments.
pub(crate) library_deps: StableDiGraph<LoadedOrUnloaded<Engine::Backing>, ()>,
pub(crate) library_deps: StableDiGraph<LoadedOrUnloaded, ()>,
}

// Libraries in the dependency graph are placed there before loading, so that they can participate
// in dependency search. So we need to track both kinds of libraries that may be at a given index in
// the graph.
pub enum LoadedOrUnloaded<Backing: BackingData> {
pub enum LoadedOrUnloaded {
Unloaded(UnloadedLibrary),
Loaded(Library<Backing>),
Loaded(Library),
}

impl<Backing: BackingData> Display for LoadedOrUnloaded<Backing> {
impl Display for LoadedOrUnloaded {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LoadedOrUnloaded::Unloaded(unlib) => write!(f, "(unloaded){}", unlib),
Expand All @@ -51,7 +50,7 @@ impl<Backing: BackingData> Display for LoadedOrUnloaded<Backing> {
}
}

impl<Backing: BackingData> LoadedOrUnloaded<Backing> {
impl LoadedOrUnloaded {
/// Get the name of this library, loaded or unloaded.
pub fn name(&self) -> &str {
match self {
Expand All @@ -61,25 +60,25 @@ impl<Backing: BackingData> LoadedOrUnloaded<Backing> {
}

/// Get back a reference to the underlying loaded library, if loaded.
pub fn loaded(&self) -> Option<&Library<Backing>> {
pub fn loaded(&self) -> Option<&Library> {
match self {
LoadedOrUnloaded::Unloaded(_) => None,
LoadedOrUnloaded::Loaded(lib) => Some(lib),
}
}

/// Get back a mutable reference to the underlying loaded library, if loaded.
pub fn loaded_mut(&mut self) -> Option<&mut Library<Backing>> {
pub fn loaded_mut(&mut self) -> Option<&mut Library> {
match self {
LoadedOrUnloaded::Unloaded(_) => None,
LoadedOrUnloaded::Loaded(lib) => Some(lib),
}
}
}

impl<Engine: ContextEngine> Context<Engine> {
impl Context {
/// Construct a new dynamic linker context.
pub fn new(engine: Engine) -> Self {
pub fn new(engine: Box<dyn ContextEngine + Send>) -> Self {
Self {
engine,
compartment_names: HashMap::new(),
Expand All @@ -88,16 +87,18 @@ impl<Engine: ContextEngine> Context<Engine> {
}
}

/// Replace the callback engine for this context.
pub fn replace_engine(&mut self, engine: Box<dyn ContextEngine + Send>) {
self.engine = engine;
}

/// Lookup a compartment by name.
pub fn lookup_compartment(&self, name: &str) -> Option<CompartmentId> {
Some(CompartmentId(*self.compartment_names.get(name)?))
}

/// Get a reference to a compartment back by ID.
pub fn get_compartment(
&self,
id: CompartmentId,
) -> Result<&Compartment<Engine::Backing>, DynlinkError> {
pub fn get_compartment(&self, id: CompartmentId) -> Result<&Compartment, DynlinkError> {
if self.compartments.len() <= id.0 {
return Err(DynlinkErrorKind::InvalidCompartmentId { id }.into());
}
Expand All @@ -108,7 +109,7 @@ impl<Engine: ContextEngine> Context<Engine> {
pub fn get_compartment_mut(
&mut self,
id: CompartmentId,
) -> Result<&mut Compartment<Engine::Backing>, DynlinkError> {
) -> Result<&mut Compartment, DynlinkError> {
if self.compartments.len() <= id.0 {
return Err(DynlinkErrorKind::InvalidCompartmentId { id }.into());
}
Expand All @@ -122,7 +123,7 @@ impl<Engine: ContextEngine> Context<Engine> {
}

/// Get a reference to a library back by ID.
pub fn get_library(&self, id: LibraryId) -> Result<&Library<Engine::Backing>, DynlinkError> {
pub fn get_library(&self, id: LibraryId) -> Result<&Library, DynlinkError> {
if !self.library_deps.contains_node(id.0) {
return Err(DynlinkErrorKind::InvalidLibraryId { id }.into());
}
Expand All @@ -136,10 +137,7 @@ impl<Engine: ContextEngine> Context<Engine> {
}

/// Get a mut reference to a library back by ID.
pub fn get_library_mut(
&mut self,
id: LibraryId,
) -> Result<&mut Library<Engine::Backing>, DynlinkError> {
pub fn get_library_mut(&mut self, id: LibraryId) -> Result<&mut Library, DynlinkError> {
if !self.library_deps.contains_node(id.0) {
return Err(DynlinkErrorKind::InvalidLibraryId { id }.into());
}
Expand All @@ -156,7 +154,7 @@ impl<Engine: ContextEngine> Context<Engine> {
pub fn with_dfs_postorder<R>(
&self,
root_id: LibraryId,
mut f: impl FnMut(&LoadedOrUnloaded<Engine::Backing>) -> R,
mut f: impl FnMut(&LoadedOrUnloaded) -> R,
) -> Vec<R> {
let mut rets = vec![];
let mut visit = petgraph::visit::DfsPostOrder::new(&self.library_deps, root_id.0);
Expand All @@ -172,7 +170,7 @@ impl<Engine: ContextEngine> Context<Engine> {
pub fn with_dfs_postorder_mut<R>(
&mut self,
root_id: LibraryId,
mut f: impl FnMut(&mut LoadedOrUnloaded<Engine::Backing>) -> R,
mut f: impl FnMut(&mut LoadedOrUnloaded) -> R,
) -> Vec<R> {
let mut rets = vec![];
let mut visit = petgraph::visit::DfsPostOrder::new(&self.library_deps, root_id.0);
Expand All @@ -184,19 +182,15 @@ impl<Engine: ContextEngine> Context<Engine> {
}

/// Traverse the library graph with BFS, calling the callback for each library.
pub fn with_bfs(
&self,
root_id: LibraryId,
mut f: impl FnMut(&LoadedOrUnloaded<Engine::Backing>),
) {
pub fn with_bfs(&self, root_id: LibraryId, mut f: impl FnMut(&LoadedOrUnloaded)) {
let mut visit = petgraph::visit::Bfs::new(&self.library_deps, root_id.0);
while let Some(node) = visit.next(&self.library_deps) {
let dep = &self.library_deps[node];
f(dep)
}
}

pub fn libraries(&self) -> LibraryIter<'_, Engine> {
pub fn libraries(&self) -> LibraryIter<'_> {
LibraryIter { ctx: self, next: 0 }
}

Expand All @@ -223,13 +217,13 @@ impl<Engine: ContextEngine> Context<Engine> {
}
}

pub struct LibraryIter<'a, Engine: ContextEngine> {
ctx: &'a Context<Engine>,
pub struct LibraryIter<'a> {
ctx: &'a Context,
next: usize,
}

impl<'a, Engine: ContextEngine> Iterator for LibraryIter<'a, Engine> {
type Item = &'a Library<Engine::Backing>;
impl<'a> Iterator for LibraryIter<'a> {
type Item = &'a Library;

fn next(&mut self) -> Option<Self::Item> {
loop {
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/dynlink/src/context/deps.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use elf::abi::DT_NEEDED;
use tracing::trace;

use super::{engine::ContextEngine, Context};
use super::Context;
use crate::{
library::{Library, UnloadedLibrary},
DynlinkError, DynlinkErrorKind,
};

impl<Engine: ContextEngine> Context<Engine> {
impl Context {
/// Get a list of dependencies for this library.
pub(crate) fn enumerate_needed(
&self,
lib: &Library<Engine::Backing>,
lib: &Library,
) -> Result<Vec<UnloadedLibrary>, DynlinkError> {
trace!("{}: enumerating dependencies", lib);
let elf = lib.get_elf()?;
Expand Down
Loading
Loading