1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Definitions for errors for the dynamic linker.
use std::alloc::Layout;

use elf::file::Class;
use itertools::{Either, Itertools};
use miette::Diagnostic;
use thiserror::Error;

use crate::{
    compartment::CompartmentId,
    engines::LoadDirective,
    library::{LibraryId, UnloadedLibrary},
};

#[derive(Debug, Error, Diagnostic)]
#[error("{kind}")]
pub struct DynlinkError {
    pub kind: DynlinkErrorKind,
    #[related]
    pub related: Vec<DynlinkError>,
}

impl DynlinkError {
    pub fn new_collect(kind: DynlinkErrorKind, related: Vec<DynlinkError>) -> Self {
        Self { kind, related }
    }

    pub fn new(kind: DynlinkErrorKind) -> Self {
        Self {
            kind,
            related: vec![],
        }
    }

    pub fn collect<I, T>(parent_kind: DynlinkErrorKind, it: I) -> Result<Vec<T>, DynlinkError>
    where
        I: IntoIterator<Item = Result<T, DynlinkError>>,
    {
        // Collect errors and values, and then if there any errors, build a new error from them.
        let (vals, errs): (Vec<T>, Vec<DynlinkError>) =
            it.into_iter().partition_map(|item| match item {
                Ok(o) => Either::Left(o),
                Err(e) => Either::Right(e),
            });

        if errs.is_empty() {
            Ok(vals)
        } else {
            Err(DynlinkError {
                kind: parent_kind,
                related: errs,
            })
        }
    }
}

impl From<DynlinkErrorKind> for DynlinkError {
    fn from(value: DynlinkErrorKind) -> Self {
        Self {
            kind: value,
            related: vec![],
        }
    }
}

#[derive(Debug, Error, Diagnostic)]
pub enum DynlinkErrorKind {
    #[error("failed to load library {library}")]
    LibraryLoadFail { library: UnloadedLibrary },
    #[error("name not found: {name}")]
    NameNotFound { name: String },
    #[error("failed to find symbol '{symname}' for '{sourcelib}'")]
    SymbolLookupFail { symname: String, sourcelib: String },
    #[error("name already exists: {name}")]
    NameAlreadyExists { name: String },
    #[error("parse failed: {err}")]
    ParseError {
        #[from]
        err: elf::ParseError,
    },
    #[error("dynamic object is missing a required segment or section '{name}'")]
    MissingSection { name: String },
    #[error("failed to allocate {:?} within compartment {}", layout, comp)]
    FailedToAllocate { comp: String, layout: Layout },
    #[error("invalid allocation layout: {err}")]
    LayoutError {
        #[from]
        err: std::alloc::LayoutError,
    },
    #[error("failed to enumerate dependencies for {library}")]
    DepEnumerationFail { library: String },
    #[error("library {library} had no TLS data for request")]
    NoTLSInfo { library: String },
    #[error("library {library} requested relocation that is unsupported")]
    UnsupportedReloc { library: String, reloc: String },
    #[error("failed to process relocation section '{secname}' for library '{library}'")]
    RelocationSectionFail { secname: String, library: String },
    #[error("library '{library}' failed to relocate")]
    RelocationFail { library: String },
    #[error("failed to create new backing data")]
    NewBackingFail,
    #[error("failed to satisfy load directive")]
    LoadDirectiveFail { dir: LoadDirective },
    #[error("tried to operate on an unloaded library '{library}'")]
    UnloadedLibrary { library: String },
    #[error("dependencies of '{library}' failed to relocate")]
    DepsRelocFail { library: String },
    #[error("invalid library ID '{id}'")]
    InvalidLibraryId { id: LibraryId },
    #[error("invalid compartment ID '{id}'")]
    InvalidCompartmentId { id: CompartmentId },
    #[error("invalid ELF header: {hdr_err}")]
    InvalidELFHeader {
        #[source]
        #[from]
        #[diagnostic_source]
        hdr_err: HeaderError,
    },
    #[error("no entry address present")]
    NoEntryAddress { name: String },
}

#[derive(Debug, Error, Diagnostic)]
pub enum HeaderError {
    #[error("class mismatch: expected {expect:?}, got {got:?}")]
    ClassMismatch { expect: Class, got: Class },
    #[error("ELF version mismatch: expected {expect}, got {got}")]
    VersionMismatch { expect: u32, got: u32 },
    #[error("OS/ABI mismatch: expected {expect}, got {got}")]
    OSABIMismatch { expect: u8, got: u8 },
    #[error("ABI version mismatch: expected {expect}, got {got}")]
    ABIVersionMismatch { expect: u8, got: u8 },
    #[error("ELF type mismatch: expected {expect}, got {got}")]
    ELFTypeMismatch { expect: u16, got: u16 },
    #[error("machine mismatch: expected {expect}, got {got}")]
    MachineMismatch { expect: u16, got: u16 },
}

impl From<elf::ParseError> for DynlinkError {
    fn from(value: elf::ParseError) -> Self {
        Self {
            kind: DynlinkErrorKind::ParseError { err: value },
            related: vec![],
        }
    }
}