elf_riscv32/
lib.rs

1#![no_std]
2
3use core::{mem::{size_of, align_of}, fmt};
4
5macro_rules! c_enum {
6    (
7        $vis:vis $name:ident($ty:ty) {
8            $($item:ident = $value:expr),*
9        } $catch:pat => $return:expr
10    ) => {
11        #[derive(::core::clone::Clone, ::core::marker::Copy, ::core::cmp::PartialEq, ::core::cmp::Eq)]
12        #[repr(transparent)]
13        $vis struct $name($ty);
14        impl $name {
15            $(
16                #[allow(non_upper_case_globals)]
17                $vis const $item: Self = Self($value);
18            )*
19            $vis fn validate(self) -> Result<()> {
20                match self.0 {
21                    $($value => Ok(()),)*
22                    $catch => $return
23                }
24            }
25        }
26        impl ::core::fmt::Debug for $name {
27            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
28                #[derive(Debug)]
29                struct Unknown($ty);
30                match *self {
31                    $(Self::$item => f.write_str(stringify!($item)),)*
32                    Self(value) => Unknown(value).fmt(f)
33                }
34            }
35        }
36        impl ::core::convert::TryFrom<$ty> for $name {
37            type Error = $crate::Error;
38            fn try_from(value: $ty) -> $crate::Result<Self> {
39                match value {
40                    $($value => Ok(Self::$item),)*
41                    $catch => $return
42                }
43            }
44        }
45        impl ::core::convert::From<$name> for $ty {
46            fn from(value: $name) -> Self {
47                value.0
48            }
49        }
50    }
51}
52macro_rules! c_flags {
53    (
54        $vis:vis $name:ident($ty:ty) {
55            $($item:ident = $value:expr),*
56        } $catch:pat => $return:expr
57    ) => {
58        #[derive(::core::clone::Clone, ::core::marker::Copy, ::core::cmp::PartialEq, ::core::cmp::Eq)]
59        #[repr(transparent)]
60        $vis struct $name($ty);
61        impl $name {
62            #[allow(non_upper_case_globals)]
63            $vis const None: Self = Self(0);
64            $(
65                #[allow(non_upper_case_globals)]
66                $vis const $item: Self = Self($value);
67            )*
68            #[allow(non_upper_case_globals)]
69            $vis const Mask: Self = Self($($value|)* 0);
70            /// Returns true if any of the bits are set
71            pub fn any(self, bits: Self) -> bool {
72                self & bits != Self::None
73            }
74            /// Returns true if all of the bits are set
75            pub fn all(self, bits: Self) -> bool {
76                self & bits == bits
77            }
78        }
79        impl ::core::ops::BitAnd for $name {
80            type Output = Self;
81            fn bitand(self, rhs: Self) -> Self::Output {
82                Self(self.0 & rhs.0)
83            }
84        }
85        impl ::core::ops::BitAndAssign for $name {
86            fn bitand_assign(&mut self, rhs: Self) {
87                self.0 &= rhs.0
88            }
89        }
90        impl ::core::ops::BitOr for $name {
91            type Output = Self;
92            fn bitor(self, rhs: Self) -> Self::Output {
93                Self(self.0 | rhs.0)
94            }
95        }
96        impl ::core::ops::BitOrAssign for $name {
97            fn bitor_assign(&mut self, rhs: Self) {
98                self.0 |= rhs.0
99            }
100        }
101        impl ::core::ops::BitXor for $name {
102            type Output = Self;
103            fn bitxor(self, rhs: Self) -> Self::Output {
104                Self(self.0 ^ rhs.0)
105            }
106        }
107        impl ::core::ops::BitXorAssign for $name {
108            fn bitxor_assign(&mut self, rhs: Self) {
109                self.0 ^= rhs.0
110            }
111        }
112        impl ::core::ops::Not for $name {
113            type Output = Self;
114            fn not(self) -> Self::Output {
115                Self(!self.0)
116            }
117        }
118        impl ::core::fmt::Debug for $name {
119            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
120                #[derive(Debug)]
121                struct Unknown($ty);
122                $(
123                    #[derive(Debug)]
124                    struct $item;
125                )*
126                let mut list = f.debug_tuple(stringify!($name));
127                $(if self.all(Self($value)) { list.field(&$item); })*
128                let invalid = *self & !Self::Mask;
129                if invalid != Self::None {
130                    list.field(&Unknown(self.0));
131                }
132                list.finish()
133            }
134        }
135        impl ::core::convert::TryFrom<$ty> for $name {
136            type Error = $crate::Error;
137            fn try_from(value: $ty) -> $crate::Result<Self> {
138                match Self(value) & !Self::Mask {
139                    Self::None => Ok(Self(value)),
140                    $catch => $return
141                }
142            }
143        }
144        impl ::core::convert::From<$name> for $ty {
145            fn from(value: $name) -> Self {
146                value.0
147            }
148        }
149    }
150}
151pub type Result<T> = core::result::Result<T, Error>;
152#[derive(Debug)]
153pub enum Error {
154    IntegerOverflow,
155    IndexOutOfRange,
156    Unaligned,
157    UnexpectedEoF,
158    InvalidMagic,
159    InvalidFormat,
160    InvalidVersion,
161    UnsupportedFileType(u16),
162    UnsupportedMachine(u16),
163    UnsupportedProgramType(u32),
164    UnsupportedProgramFlags(ProgramFlags),
165    UnsupportedSectionType(u32),
166    UnsupportedSectionFlags(SectionFlags),
167    WrongProgramType { expected: ProgramType, actual: ProgramType },
168    WrongProgramFlags { expected: ProgramFlags, actual: ProgramFlags },
169    WrongSectionType { expected: SectionType, actual: SectionType },
170    WrongSectionFlags { expected: SectionFlags, actual: SectionFlags },
171    UnterminatedString,
172    NotUtf8(core::str::Utf8Error)
173}
174
175#[derive(Debug, Clone, Copy)]
176#[repr(transparent)]
177pub struct Address(u32);
178impl Address {
179    pub fn as_usize(self) -> Result<usize> {
180        self.0.try_into().map_err(|_| Error::IntegerOverflow)
181    }
182}
183#[derive(Debug, Clone, Copy)]
184#[repr(transparent)]
185pub struct Offset(u32);
186impl Offset {
187    pub fn as_usize(self) -> Result<usize> {
188        self.0.try_into().map_err(|_| Error::IntegerOverflow)
189    }
190}
191
192/// A 32bit little-endian RISC-V ELF file.
193/// 
194/// ```
195/// use elf_riscv32::*;
196/// # (|| -> Result<()> {
197/// # let mut data = [0u32; 8192];
198/// # let elf = include_bytes!("../examples/test.elf");
199/// # if elf.len() > data.len() * core::mem::size_of::<u32>() {
200/// #     panic!("array too small")
201/// # }
202/// # unsafe { (data.as_mut_ptr() as *mut u8).copy_from_nonoverlapping(elf.as_ptr(), elf.len()) };
203/// let elf = Elf::new(&data).unwrap();
204/// for section in elf.sections().unwrap() {
205///     let section = section.unwrap();
206///     println!("{} = {section:X?}", elf.section_name(&section).unwrap())
207/// }
208/// for program in elf.programs().unwrap() {
209///     let program = program.unwrap();
210///     println!("{program:X?}")
211/// }
212/// # Ok(()) })().unwrap()
213/// ```
214pub struct Elf<'a> {
215    data: &'a [u8],
216    pub header: &'a Header,
217    pub section_names: StringTable<'a>
218}
219impl<'a> Elf<'a> {
220    pub fn new(elf: &'a [u32]) -> Result<Self> {
221        assert_eq!(align_of::<u32>(), align_of::<Header>());
222        let data = unsafe { core::slice::from_raw_parts(elf.as_ptr() as *const u8, elf.len() * size_of::<u32>()) };
223        let header = unsafe { Header::new_assume_aligned(data)? };
224        let section_name_table = header.section_header(data, header.section_name_table)?;
225        let section_names = StringTable(section_name_table.data(data)?);
226        Ok(Self {
227            data,
228            header,
229            section_names
230        })
231    }
232    pub fn program(&self, index: u16) -> Result<Program<'a>> {
233        let header = self.header.program_header(self.data, index)?;
234        header.data(self.data).map(|data| Program::new(header, data))
235    }
236    /// Get an iterator over programs.
237    pub fn programs(&'a self) -> Result<TableIter<Program<'a>>> {
238        TableIter::new(self.data, self.header.ph_offset, self.header.ph_count, self.header.ph_entry_size)
239    }
240    /// Get the section name string given an offset into the section header string table.
241    pub fn section_name(&'a self, section: &Section<'a>) -> Result<&'a str> {
242        self.section_names.get_str(section.header.name)
243    }
244    pub fn section(&self, index: u16) -> Result<Section<'a>> {
245        let header = self.header.section_header(self.data, index)?;
246        header.data(self.data).map(|data| Section::new(header, data))
247    }
248    /// Get an iterator over sections.
249    pub fn sections(&'a self) -> Result<TableIter<Section<'a>>> {
250        TableIter::new(self.data, self.header.sh_offset, self.header.sh_count, self.header.sh_entry_size)
251    }
252}
253impl<'a> fmt::Debug for Elf<'a> {
254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        f.debug_struct("Elf")
256            .field("data", &[..])
257            .field("header", &self.header)
258            .field("section_names", &self.section_names)
259            .finish()
260    }
261}
262
263#[derive(Debug)]
264#[repr(C)]
265pub struct Header {
266    pub ident: [u8; 16],
267    pub ty: FileType,
268    pub machine: Machine,
269    pub version: u32,
270    pub entry: Address,
271    pub ph_offset: Offset,
272    pub sh_offset: Offset,
273    pub flags: u32,
274    pub header_size: u16,
275    pub ph_entry_size: u16,
276    pub ph_count: u16,
277    pub sh_entry_size: u16,
278    pub sh_count: u16,
279    pub section_name_table: u16
280}
281impl Header {
282    pub fn new(elf: &[u32]) -> Result<&Self> {
283        assert_eq!(align_of::<u32>(), align_of::<Self>());
284        unsafe {
285            let len = elf.len() * size_of::<u32>();
286            Self::new_assume_aligned(core::slice::from_raw_parts(elf.as_ptr() as *const u8, len))
287        }
288    }
289    /// Like `Header::new` but takes a byte slice and checks the alignment.
290    pub fn new_aligned(elf: &[u8]) -> Result<&Self> {
291        assert_eq!(align_of::<u32>(), align_of::<Self>());
292        if elf.as_ptr() as usize & 0b11 != 0 {
293            Err(Error::Unaligned)
294        } else {
295            unsafe { Self::new_assume_aligned(elf) }
296        }
297    }
298    /// Coerce a byte slice into an ELF header.
299    /// 
300    /// # Safety
301    /// It is undefined behaviour for `elf` to have a smaller alignment than `Header`.
302    pub unsafe fn new_assume_aligned(elf: &[u8]) -> Result<&Self> {
303        if elf.len() < size_of::<Self>() {
304            return Err(Error::UnexpectedEoF)
305        }
306        let elf = &*(elf.as_ptr() as *const Header);
307        if &elf.ident[..4] != b"\x7fELF" {
308            return Err(Error::InvalidMagic)
309        }
310        if elf.ident[4] != 1 || elf.ident[5] != 1 {
311            // Not riscv32-le
312            return Err(Error::InvalidFormat)
313        }
314        if elf.ident[6] != 1 || elf.version != 1 {
315            return Err(Error::InvalidVersion)
316        }
317        elf.machine.validate()?;
318        Ok(elf)
319    }
320    pub fn program_header<'a>(&'a self, elf: &'a [u8], index: u16) -> Result<&'a ProgramHeader> {
321        if index >= self.ph_count {
322            Err(Error::IndexOutOfRange)
323        } else {
324            let offset: usize = self.ph_offset.as_usize()? + (self.ph_entry_size as usize * index as usize);
325            ProgramHeader::new(&elf[offset..])
326        }
327    }
328    pub fn section_header<'a>(&'a self, elf: &'a [u8], index: u16) -> Result<&'a SectionHeader> {
329        if index >= self.sh_count {
330            Err(Error::IndexOutOfRange)
331        } else {
332            let offset: usize = self.sh_offset.as_usize()? + (self.sh_entry_size as usize * index as usize);
333            SectionHeader::new(&elf[offset..])
334        }
335    }
336}
337
338pub struct TableIter<'a, T: 'a + TableEntry<'a>> {
339    elf: &'a [u8],
340    offset: Offset,
341    count: u16,
342    size: u16,
343    index: u16,
344    _marker: core::marker::PhantomData<T>
345}
346impl<'a, T: 'a + TableEntry<'a>> TableIter<'a, T> {
347    pub fn new(elf: &'a [u8], offset: Offset, count: u16, size: u16) -> Result<Self> {
348        assert_eq!(align_of::<u32>(), align_of::<T::Header>());
349        if elf.as_ptr() as usize & 0b11 != 0 || size & 0b11 != 0 {
350            Err(Error::Unaligned)
351        } else {
352            Ok(Self {
353                elf,
354                offset,
355                count,
356                size,
357                index: 0,
358                _marker: core::marker::PhantomData,
359            })
360        }
361    }
362}
363impl<'a, T: 'a + TableEntry<'a>> Iterator for TableIter<'a, T> {
364    type Item = Result<T>;
365    fn next(&mut self) -> Option<Self::Item> {
366        if self.index == self.count {
367            None
368        } else {
369            let offset = match self.offset.as_usize() {
370                Ok(offset) => offset,
371                Err(e) => return Some(Err(e))
372            };
373            let offset = offset + self.index as usize * self.size as usize;
374            self.index += 1;
375            self.elf.get(offset..).map(|header| T::new(self.elf, header))
376        }
377    }
378}
379
380pub trait TableEntry<'a> where Self: Sized {
381    type Header;
382    fn new(elf: &'a [u8], header: &'a [u8]) -> Result<Self>;
383}
384
385
386/// A program header and its associated data.
387pub struct Program<'a> {
388    pub header: &'a ProgramHeader,
389    pub data: &'a [u8]
390}
391impl<'a> Program<'a> {
392    pub fn new(header: &'a ProgramHeader, data: &'a [u8]) -> Self {
393        Self {
394            header,
395            data
396        }
397    }
398    /// Ensure that the type of the program header matches that expected, returning an `Err` otherwise.
399    pub fn check_type(&self, ty: ProgramType) -> Result<()> {
400        if self.header.ty != ty {
401            Err(Error::WrongProgramType { expected: ty, actual: self.header.ty })
402        } else {
403            Ok(())
404        }
405    }
406    pub fn check_flag(&self, flags: ProgramFlags) -> Result<()> {
407        if self.header.flags.all(flags) {
408            Ok(())
409        } else {
410            Err(Error::WrongProgramFlags { expected: flags, actual: self.header.flags })
411        }
412    }
413}
414impl<'a> fmt::Debug for Program<'a> {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        self.header.fmt(f)
417    }
418}
419impl<'a> TableEntry<'a> for Program<'a> {
420    type Header = ProgramHeader;
421    fn new(elf: &'a [u8], header: &'a [u8]) -> Result<Self> {
422        let header = ProgramHeader::new(header)?;
423        Ok(Self::new(header, header.data(elf)?))
424    }
425}
426#[derive(Debug)]
427#[repr(C)]
428pub struct ProgramHeader {
429    pub ty: ProgramType,
430    pub offset: Offset,
431    pub virt_addr: Address,
432    pub phys_addr: Address,
433    pub file_size: u32,
434    pub mem_size: u32,
435    pub flags: ProgramFlags,
436    pub align: u32
437}
438impl ProgramHeader {
439    pub fn new(header: &[u8]) -> Result<&Self> {
440        if header.len() < size_of::<Self>() {
441            return Err(Error::UnexpectedEoF)
442        }
443        if header.as_ptr() as usize & 0b11 != 0 {
444            return Err(Error::Unaligned)
445        }
446        let header = unsafe { &*(header.as_ptr() as *const ProgramHeader) };
447        Ok(header)
448    }
449    pub fn data<'a>(&'a self, elf: &'a [u8]) -> Result<&'a [u8]> {
450        let size: usize = self.file_size.try_into().map_err(|_| Error::IntegerOverflow)?;
451        let offset = self.offset.as_usize()?;
452        elf.get(offset..offset + size).ok_or(Error::UnexpectedEoF)
453    }
454}
455
456/// A section header and its associated data.
457pub struct Section<'a> {
458    pub header: &'a SectionHeader,
459    pub data: &'a [u8]
460}
461impl<'a> Section<'a> {
462    pub fn new(header: &'a SectionHeader, data: &'a [u8]) -> Self {
463        Self {
464            header,
465            data
466        }
467    }
468    /// Ensure that the type of the section matches that expected, returning an `Err` otherwise.
469    pub fn check_type(&self, ty: SectionType) -> Result<()> {
470        if self.header.ty != ty {
471            Err(Error::WrongSectionType { expected: ty, actual: self.header.ty })
472        } else {
473            Ok(())
474        }
475    }
476    pub fn check_flag(&self, flags: SectionFlags) -> Result<()> {
477        if self.header.flags.all(flags) {
478            Ok(())
479        } else {
480            Err(Error::WrongSectionFlags { expected: flags, actual: self.header.flags })
481        }
482    }
483}
484impl<'a> fmt::Debug for Section<'a> {
485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486        self.header.fmt(f)
487    }
488}
489impl<'a> TableEntry<'a> for Section<'a> {
490    type Header = SectionHeader;
491    fn new(elf: &'a [u8], header: &'a [u8]) -> Result<Self> {
492        let header = SectionHeader::new(header)?;
493        Ok(Self::new(header, header.data(elf)?))
494    }
495}
496#[derive(Debug)]
497#[repr(C)]
498pub struct SectionHeader {
499    pub name: u32,
500    pub ty: SectionType,
501    pub flags: SectionFlags,
502    pub address: Address,
503    pub offset: Offset,
504    pub size: u32,
505    pub link: u32,
506    pub info: u32,
507    pub alignment: u32,
508    pub entry_size: u32
509}
510impl SectionHeader {
511    pub fn new(header: &[u8]) -> Result<&Self> {
512        if header.len() < size_of::<Self>() {
513            return Err(Error::UnexpectedEoF)
514        }
515        if header.as_ptr() as usize & 0b11 != 0 {
516            return Err(Error::Unaligned)
517        }
518        let header = unsafe { &*(header.as_ptr() as *const SectionHeader) };
519        Ok(header)
520    }
521    pub fn data<'a>(&'a self, elf: &'a [u8]) -> Result<&'a [u8]> {
522        let size: usize = self.size.try_into().map_err(|_| Error::IntegerOverflow)?;
523        let offset = self.offset.as_usize()?;
524        elf.get(offset..offset + size).ok_or(Error::UnexpectedEoF)
525    }
526}
527
528#[derive(Copy, Clone)]
529pub struct StringTable<'a>(&'a [u8]);
530impl<'a> StringTable<'a> {
531    /// Coerce a Section into a string table.
532    /// 
533    /// The section must be of type `SHT_STRTAB` and have the `SHF_STRINGS` flag.
534    pub fn new(section: Section<'a>) -> Result<Self> {
535        section.check_type(SectionType::StringTable)?;
536        section.check_flag(SectionFlags::Strings)?;
537        Ok(Self(section.data))
538    }
539    pub fn get_str(self, index: u32) -> Result<&'a str> {
540        self.get_bytes(index)
541            .and_then(|b|
542                core::str::from_utf8(b)
543                    .map_err(Error::NotUtf8)
544            )
545    }
546    /// Get the byte slice of a string in the string table, not including the null terminator.
547    pub fn get_bytes(self, index: u32) -> Result<&'a [u8]> {
548        self.0.get(index as usize..).ok_or(Error::IndexOutOfRange)
549            .and_then(|s| {
550                let end = memchr::memchr(0, s).ok_or(Error::UnterminatedString)?;
551                Ok(&s[0..end])
552            })
553    }
554    /// Returns a pointer to the start of the string table.
555    /// 
556    /// The string table is not confirmed to be null terminated.
557    pub fn get_ptr(self) -> *const u8 {
558        self.0.as_ptr()
559    }
560    /// Returns the length of the string table in bytes.
561    pub fn len(self) -> usize {
562        self.0.len()
563    }
564    /// Returns true if the string table has a length of 0.
565    pub fn is_empty(self) -> bool {
566        self.0.is_empty()
567    }
568}
569impl<'a> fmt::Debug for StringTable<'a> {
570    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571        f.debug_tuple("StringTable")
572            .field(&[..])
573            .finish()
574    }
575}
576
577c_enum!{
578    pub FileType(u16) {
579        None = 0,
580        Relocatable = 1,
581        Executable = 2,
582        SharedObject = 3,
583        Core = 4
584    } v => Err(Error::UnsupportedFileType(v))
585}
586c_enum!{
587    pub Machine(u16) {
588        RiscV = 243
589    } v => Err(Error::UnsupportedMachine(v))
590}
591c_enum!{
592    pub ProgramType(u32) {
593        Null = 0,
594        Load = 1,
595        Dynamic = 2,
596        Interpreter = 3,
597        Note = 4,
598        ProgramHeader = 6,
599        ThreadLocalStorage = 7,
600        GnuStack = 0x6474E551,
601        RiscVAttributes = 0x70000003
602    } v => Err(Error::UnsupportedProgramType(v))
603}
604c_flags!{
605    pub ProgramFlags(u32) {
606        Read = 0b001,
607        Exec = 0b010,
608        Write = 0b100
609    } v => Err(Error::UnsupportedProgramFlags(v))
610}
611c_enum!{
612    pub SectionType(u32) {
613        Null = 0,
614        Program = 1,
615        SymbolTable = 2,
616        StringTable = 3,
617        Rela = 4,
618        HashTable = 5,
619        Dynamic = 6,
620        Note = 7,
621        NoBits = 8,
622        Rel = 9,
623        DynamicSymbolTable = 11,
624        InitArray = 14,
625        FiniArray = 15,
626        PreinitArray = 16,
627        Group = 17,
628        SymbolIndex = 18
629    } v => Err(Error::UnsupportedSectionType(v))
630}
631c_flags!{
632    pub SectionFlags(u32) {
633        Write = 0x1,
634        Alloc = 0x2,
635        Exec = 0x4,
636        Merge = 0x10,
637        Strings = 0x20,
638        InfoLink = 0x40,
639        LinkOrder = 0x80,
640        OsNonconforming = 0x100,
641        Group = 0x200,
642        Tls = 0x400,
643        Compressed = 0x800
644    } v => Err(Error::UnsupportedSectionFlags(v))
645}