Skip to main content

sel4_phdrs/
lib.rs

1//
2// Copyright 2024, Colias Group, LLC
3//
4// SPDX-License-Identifier: BSD-2-Clause
5//
6
7#![no_std]
8#![feature(linkage)]
9
10use core::error::Error;
11use core::fmt;
12use core::ops::Range;
13use core::slice;
14
15pub use sel4_phdrs_constants::*;
16
17unsafe extern "Rust" {
18    safe fn __sel4_phdrs__locate_phdrs() -> Result<ProgramHeaders<'static>, &'static dyn Error>;
19}
20
21#[macro_export]
22macro_rules! register_locate_phdrs {
23    ($(#[$attrs:meta])* $path:path) => {
24        #[allow(non_snake_case)]
25        const _: () = {
26            $(#[$attrs])*
27            #[unsafe(no_mangle)]
28            fn __sel4_phdrs__locate_phdrs() -> $crate::_private::LocatePhdrsResult {
29                const F: fn() -> $crate::_private::LocatePhdrsResult = $path;
30                F()
31            }
32        };
33    };
34}
35
36register_locate_phdrs!(
37    #[linkage = "weak"]
38    default_locate_phdrs
39);
40
41pub fn locate_phdrs() -> Result<ProgramHeaders<'static>, &'static dyn Error> {
42    __sel4_phdrs__locate_phdrs()
43}
44
45pub struct ProgramHeaders<'a> {
46    slice: &'a [ProgramHeader],
47}
48
49impl<'a> ProgramHeaders<'a> {
50    #[allow(clippy::missing_safety_doc)]
51    pub unsafe fn new(start: *const ProgramHeader, n: usize) -> Self {
52        Self {
53            slice: unsafe { slice::from_raw_parts(start, n) },
54        }
55    }
56
57    pub fn as_slice(&self) -> &'a [ProgramHeader] {
58        self.slice
59    }
60
61    pub fn iter(&self) -> impl Iterator<Item = &'a ProgramHeader> {
62        self.as_slice().iter()
63    }
64
65    pub fn find_by_type(&self, ty: u32) -> Option<&'a ProgramHeader> {
66        self.iter().find(|phdr| phdr.p_type == ty)
67    }
68
69    pub fn footprint(&self) -> Option<Range<usize>> {
70        let start = self
71            .iter()
72            .filter(|phdr| phdr.p_type == PT_LOAD)
73            .map(|phdr| phdr.p_vaddr)
74            .min()?;
75        let end = self
76            .iter()
77            .filter(|phdr| phdr.p_type == PT_LOAD)
78            .map(|phdr| phdr.p_vaddr + phdr.p_memsz)
79            .max()?;
80        Some(start..end)
81    }
82}
83
84#[repr(C)]
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
86struct ElfHeader {
87    pub e_ident: ElfHeaderIdent,
88    pub e_type: u16,
89    pub e_machine: u16,
90    pub e_version: u32,
91    pub e_entry: usize,
92    pub e_phoff: usize,
93    pub e_shoff: usize,
94    pub e_flags: u32,
95    pub e_ehsize: u16,
96    pub e_phentsize: u16,
97    pub e_phnum: u16,
98    pub e_shentsize: u16,
99    pub e_shnum: u16,
100    pub e_shstrndx: u16,
101}
102
103#[repr(C)]
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
105struct ElfHeaderIdent {
106    pub magic: [u8; 4],
107    pub class: u8,
108    pub data: u8,
109    pub version: u8,
110    pub os_abi: u8,
111    pub abi_version: u8,
112    pub padding: [u8; 7],
113}
114
115const ELFMAG: [u8; 4] = [0x7f, b'E', b'L', b'F'];
116
117#[repr(C)]
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
119pub struct ProgramHeader {
120    pub p_type: u32,
121    #[cfg(target_pointer_width = "64")]
122    pub p_flags: u32,
123    pub p_offset: usize,
124    pub p_vaddr: usize,
125    pub p_paddr: usize,
126    pub p_filesz: usize,
127    pub p_memsz: usize,
128    #[cfg(target_pointer_width = "32")]
129    pub p_flags: u32,
130    pub p_align: usize,
131}
132
133pub const PT_NULL: u32 = 0;
134pub const PT_LOAD: u32 = 1;
135pub const PT_TLS: u32 = 7;
136pub const PT_GNU_EH_FRAME: u32 = 0x6474_e550;
137
138impl ProgramHeader {
139    #[allow(clippy::missing_safety_doc)]
140    pub unsafe fn bytes(&self) -> &'static [u8] {
141        unsafe { slice::from_raw_parts(self.p_vaddr as *const u8, self.p_memsz) }
142    }
143}
144
145pub fn default_locate_phdrs() -> Result<ProgramHeaders<'static>, &'static dyn Error> {
146    unsafe extern "C" {
147        safe static __ehdr_start: ElfHeader;
148    }
149    if __ehdr_start.e_ident.magic != ELFMAG {
150        return Err(&DefaultLocatePhdrsError::InvalidMagic);
151    }
152    if __ehdr_start.e_phoff != size_of::<ElfHeader>() {
153        return Err(&DefaultLocatePhdrsError::UnexpectedPhoff);
154    }
155    let start = (&raw const __ehdr_start)
156        .wrapping_byte_add(__ehdr_start.e_phoff)
157        .cast();
158    Ok(unsafe { ProgramHeaders::new(start, __ehdr_start.e_phnum.into()) })
159}
160
161#[derive(Debug, Copy, Clone)]
162pub enum DefaultLocatePhdrsError {
163    InvalidMagic,
164    UnexpectedPhoff,
165}
166
167impl fmt::Display for DefaultLocatePhdrsError {
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        match self {
170            Self::InvalidMagic => write!(f, "invalid magic in ELF header"),
171            Self::UnexpectedPhoff => write!(f, "unexpected e_phoff in ELF header"),
172        }
173    }
174}
175
176impl Error for DefaultLocatePhdrsError {}
177
178// For macros
179#[doc(hidden)]
180pub mod _private {
181    use core::error::Error;
182
183    use super::ProgramHeaders;
184
185    pub type LocatePhdrsResult = Result<ProgramHeaders<'static>, &'static dyn Error>;
186}