orecc_elf/
lib.rs

1//! Easy read/write ELF 32/64 relocatibles/executables/dynamics
2//!
3//! To read an elf file:
4//! ```
5//! let mut file = std::fs::File::open("test.o").unwrap();
6//! dbg!(orecc_elf::ELF::<u64>::read(&mut file));
7//! ```
8//!
9//! To write an elf file (source: https://www.youtube.com/watch?v=XH6jDiKxod8):
10//! ```
11//! let mut file = std::fs::File::create("test.o").unwrap();
12//! // An x86 program that just exits
13//! let data = [
14//!     0xB8, 0x01, 0x00, 0x00, 0x00,
15//!     0xBB, 0x00, 0x00, 0x00, 0x00,
16//!     0xCD, 0x80,
17//! ];
18//! orecc_elf::ELF::<u32>::new(
19//!     orecc_elf::Ident::new(
20//!         orecc_elf::Class::ELF32,
21//!         orecc_elf::ByteOrder::LSB,
22//!         orecc_elf::ABI::None,
23//!         0,
24//!     ),
25//!     orecc_elf::Type::Exec,
26//!     orecc_elf::Machine::X86,
27//!     true,
28//!     vec![
29//!         orecc_elf::SegmentTemplate::new(
30//!             orecc_elf::SegmentType::Load,
31//!             data.to_vec(),
32//!             data.len() as _,
33//!             orecc_elf::SegmentFlags::Readable as u32 | orecc_elf::SegmentFlags::Executable as u32,
34//!         )
35//!     ],
36//!     Vec::new(),
37//! )
38//! .unwrap()
39//! .write(&mut file)
40//! .unwrap();
41//! ```
42//!
43//! x86_64 version:
44//! ```
45//! let mut file = std::fs::File::create("test.o").unwrap();
46//! // An x86_64 program that just exits
47//! let data = [
48//!     0x48, 0xC7, 0xC0, 0x3C, 0x00, 0x00, 0x00,
49//!     0x48, 0xC7, 0xC7, 0x2A, 0x00, 0x00, 0x00,
50//!     0x0F, 0x05,
51//! ];
52//! orecc_elf::ELF::<u64>::new(
53//!     orecc_elf::Ident::default(),
54//!     orecc_elf::Type::Exec,
55//!     orecc_elf::Machine::X86_64,
56//!     true,
57//!     vec![
58//!         orecc_elf::SegmentTemplate::new(
59//!             orecc_elf::SegmentType::Load,
60//!             data.to_vec(),
61//!             data.len() as _,
62//!             orecc_elf::SegmentFlags::Readable as u32 | orecc_elf::SegmentFlags::Executable as u32,
63//!         )
64//!     ],
65//!     Vec::new(),
66//! )
67//! .unwrap()
68//! .write(&mut file)
69//! .unwrap();
70//! ```
71
72mod serde;
73use serde::*;
74
75mod structs;
76pub use structs::*;
77
78// * ------------------------------------ E_IDENT ----------------------------------- * //
79/// e_ident. Specifies class, byte order and ABI of the ELF
80#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
81pub struct Ident {
82    /// Class, ELF32/ELF64
83    pub class: Class,
84    /// Byte order
85    pub byte_order: ByteOrder,
86    /// Identifies the target operating system ABI
87    pub abi: ABI,
88    /// ABI version, usually 0
89    pub abi_version: u8,
90}
91
92impl Ident {
93    /// Constructs a new Ident to write it afterwards
94    pub fn new(
95        class: Class,
96        byte_order: impl Into<ByteOrder>,
97        abi: impl Into<ABI>,
98        abi_version: u8,
99    ) -> Self {
100        Self {
101            class,
102            byte_order: byte_order.into(),
103            abi: abi.into(),
104            abi_version,
105        }
106    }
107
108    /// Write the Ident to a file. It's done automatically by the [`ELF::write()`]
109    #[rustfmt::skip]
110    pub fn write<W: std::io::Write>(&self, file: &mut W) -> Result<()> {
111        file.write_all(&[
112            0x7f, 0x45, 0x4c, 0x46,
113            self.class.int_value(), self.byte_order.int_value(), 1,
114            self.abi.int_value(), self.abi_version,
115            0, 0, 0, 0, 0, 0, 0,
116        ]).map_err(|err|Error::io(err, "e_ident"))
117    }
118
119    /// Read the Ident from a file. It's done automatically by the [`ELF::read()`].
120    /// Can be used with [`ELF::read_remainder()`] to determine ELF class:
121    /// ```
122    /// use orecc_elf::{ELF, Ident, Class};
123    ///
124    /// let mut file = std::fs::File::open("test.o").unwrap();
125    /// let ident = Ident::read(&mut file).unwrap();
126    /// if ident.class == Class::ELF64 {
127    ///     dbg!(ELF::<u64>::read_remainder(&mut file, ident)).unwrap();
128    /// } else {
129    ///     dbg!(ELF::<u32>::read_remainder(&mut file, ident)).unwrap();
130    /// }
131    /// ```
132    pub fn read<R: std::io::Read>(file: &mut R) -> Result<Self> {
133        let mut buffer = [0_u8; 4];
134        file.read_exact(&mut buffer).map_err(|err| {
135            Error::Signature(format!("failed to read ELF signature from a file: {err}"))
136        })?;
137        if buffer != [0x7f, 0x45, 0x4c, 0x46] {
138            return Err(Error::Signature(format!(
139                "incorrect ELF signature: {buffer:02X?}, expected: [0x7f, 0x45, 0x4c, 0x46]"
140            )));
141        }
142        let mut buffer = [0_u8; 12];
143        file.read_exact(&mut buffer)
144            .map_err(|err| Error::io(err, "e_ident"))?;
145        let [class, byte_order, version, abi, abi_version] = buffer[..5] else {
146            panic!("Internal error: buffer size is not compatible with e_ident size!");
147        };
148        if version != 1 {
149            return Err(Error::parse("version should be always 1", "version"));
150        }
151
152        let class = Class::from_int(class);
153        let byte_order = ByteOrder::from_int(byte_order)?;
154        let abi = ABI::from_int(abi);
155        Ok(Self {
156            class,
157            byte_order,
158            abi,
159            abi_version,
160        })
161    }
162}
163/// A segment template, [Segment] will be generated from it
164#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
165pub struct SegmentTemplate<T: SizeT> {
166    /// Identifies the type of the segment
167    pub p_type: SegmentType,
168    /// Data of this segment
169    pub data: Vec<u8>,
170    /// Size in bytes of the segment in memory. May be 0. May be more then data to pad segment with zeros
171    pub size: T,
172    /// Segment-dependent flags. Essentially permissions, specified with [SegmentFlags] like that:
173    /// ```
174    /// use orecc_elf::SegmentFlags;
175    /// let flags = SegmentFlags::Readable as u32 | SegmentFlags::Executable as u32;
176    /// assert_eq!(flags, 0x05);
177    /// ```
178    pub flags: u32,
179}
180
181impl<T: SizeT> SegmentTemplate<T> {
182    /// Create a new segment template. Used with [`ELF::new()`]
183    pub fn new(p_type: SegmentType, data: Vec<u8>, size: T, flags: u32) -> Self {
184        Self {
185            p_type,
186            data,
187            size,
188            flags,
189        }
190    }
191}
192
193// * ------------------------------------ Header ------------------------------------ * //
194impl Machine {
195    pub fn text_region_address(self) -> u64 {
196        match self {
197            Self::X86 => 0x08048000,
198            Self::X86_64 => 0x400000,
199            _ => 100, // TODO: not tested
200        }
201    }
202}
203
204/// The ELF file itself. This what you will be using.
205/// Use [`Self::read()`] to read if from a file,
206/// [`Self::new()`] to construct it from scratch
207/// and [`Self::write()`] to write it to a file
208/// `T` generic can be [u32] for ELF32 and [u64] for ELF64
209#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
210pub struct ELF<T: SizeT> {
211    /// An ident
212    pub ident: Ident,
213    /// Identifies object file type
214    pub e_type: Type,
215    /// Specifies target instruction set architecture
216    pub machine: Machine,
217    /// This is the memory address of the entry point from where the process starts executing. Should be 0 if no entry point
218    pub entry_point: T,
219    /// Segments/Program headers (Loaded when executed). Do not add them, offsets will probably mess up
220    pub segments: Vec<Segment<T>>,
221    /// Sections (When linking turned into segment). Do not add them, offsets will probably mess up
222    pub sections: Vec<Section<T>>,
223    /// Flags, usually 0. Interpretation of this field depends on the target architecture
224    pub flags: u32,
225    /// Contains index of the section header table entry that contains the section names
226    pub section_header_string_table_index: u16,
227}
228
229macro_rules! rw_enum {
230    (read $type: ident, $name: ident, $storage: ident, $ident: ident, $file: ident) => {
231        $type::from_int($storage::read(
232            $file,
233            $ident.byte_order,
234            stringify!($display_part),
235        )?)
236    };
237
238    (write $self: ident, $part: ident, $file: ident) => {
239        $self
240            .$part
241            .int_value()
242            .write($file, $self.ident.byte_order, stringify!($part))?
243    };
244}
245
246impl<T: SizeT> ELF<T> {
247    /// Constructs a new ELF from scratch.
248    /// To make an [Ident] you will need to call [`Ident::new()`]
249    /// This is a simplified constructor:
250    /// - Assumes, that string table is the last section (if exists)
251    /// - Sets flags to 0
252    /// - If has_entry_point is true, entry point will be set to the start of the first segment
253    /// - Places segments continuously into memory, starting from address
254    ///     `machine.text_region_address() + (T::ELF_HEADER_SIZE + T::SEGMENT_HEADER_SIZE * segments.len() + T::SECTION_HEADER_SIZE * sections.len()) % 0x1000`
255    /// For more flexability you can do:
256    /// ```
257    /// use orecc_elf::*;
258    /// dbg!(
259    ///     ELF::<u64> {
260    ///         ident: Ident::default(),
261    ///         e_type: Type::Exec,
262    ///         machine: Machine::X86_64,
263    ///         entry_point: 0xDEADBEEF,
264    ///         segments: Vec::new(),
265    ///         sections: Vec::new(),
266    ///         flags: 0,
267    ///         section_header_string_table_index: 0,
268    ///     }
269    /// );
270    /// ```
271    pub fn new(
272        ident: Ident,
273        e_type: Type,
274        machine: impl Into<Machine>,
275        has_entry_point: bool,
276        segments: Vec<SegmentTemplate<T>>,
277        sections: Vec<Section<T>>,
278    ) -> Result<Self> {
279        let machine = machine.into();
280        if ident.class != T::CLASS {
281            return Err(Error::Error(format!(
282                "Expected ELF class {:?}, got class {:?}",
283                T::CLASS,
284                ident.class
285            )));
286        }
287
288        let section_header_string_table_index = sections.len().saturating_sub(1) as _;
289
290        let segments = {
291            let align = 0x1000;
292
293            let mut offset = T::ELF_HEADER_SIZE as u64
294                + T::SEGMENT_HEADER_SIZE as u64 * segments.len() as u64
295                + T::SECTION_HEADER_SIZE as u64 * sections.len() as u64;
296            let mut virtual_address = machine.text_region_address() + (offset % align);
297
298            segments
299                .into_iter()
300                .map(|segment| {
301                    let segment_offset = offset;
302                    let segment_virtual_address = virtual_address;
303                    offset += segment.data.len() as u64;
304                    virtual_address += segment.size.value();
305                    Segment {
306                        p_type: segment.p_type,
307                        data: segment.data,
308                        offset: T::new(segment_offset),
309                        virtual_address: T::new(segment_virtual_address),
310                        physical_address: T::new(0),
311                        size: segment.size,
312                        flags: segment.flags,
313                        align: T::new(align),
314                    }
315                })
316                .collect::<Vec<_>>()
317        };
318
319        Ok(Self {
320            ident,
321            e_type,
322            machine,
323            entry_point: if has_entry_point {
324                T::new(
325                    segments
326                        .get(0)
327                        .ok_or(Error::Error(String::from(
328                            "Can't have an entry point with no segments",
329                        )))?
330                        .virtual_address
331                        .value(),
332                )
333            } else {
334                T::new(0)
335            },
336            segments,
337            sections,
338            flags: 0,
339            section_header_string_table_index,
340        })
341    }
342
343    /// Write an ELF to a file
344    pub fn write<W: std::io::Write + std::io::Seek>(&self, file: &mut W) -> Result<()> {
345        let segment_headers_offset = T::ELF_HEADER_SIZE as u64;
346        let section_headers_offset =
347            segment_headers_offset + T::SEGMENT_HEADER_SIZE as u64 * self.segments.len() as u64;
348
349        // * Ident
350        self.ident.write(file)?;
351        rw_enum!(write self, e_type, file);
352        rw_enum!(write self, machine, file);
353        1_u32.write(file, self.ident.byte_order, "e_version")?;
354        self.entry_point
355            .write(file, self.ident.byte_order, "e_entry")?;
356
357        // * Offsets
358        T::new(if self.segments.is_empty() {
359            0
360        } else {
361            segment_headers_offset
362        })
363        .write(file, self.ident.byte_order, "e_phoff")?;
364        T::new(if self.sections.is_empty() {
365            0
366        } else {
367            section_headers_offset
368        })
369        .write(file, self.ident.byte_order, "e_shoff")?;
370
371        // * Flags
372        self.flags.write(file, self.ident.byte_order, "e_flags")?;
373        T::ELF_HEADER_SIZE.write(file, self.ident.byte_order, "e_ehsize")?;
374
375        // * Segments
376        T::SEGMENT_HEADER_SIZE.write(file, self.ident.byte_order, "e_phentsize")?;
377        (self.segments.len() as u16).write(file, self.ident.byte_order, "e_phnum")?;
378
379        // * Sections
380        T::SECTION_HEADER_SIZE.write(file, self.ident.byte_order, "e_shentsize")?;
381        (self.sections.len() as u16).write(file, self.ident.byte_order, "e_shnum")?;
382
383        self.section_header_string_table_index
384            .write(file, self.ident.byte_order, "e_shstrndx")?;
385
386        // * Segment headers
387        let mut cursor =
388            section_headers_offset + T::SECTION_HEADER_SIZE as u64 * self.sections.len() as u64;
389        for segment in &self.segments {
390            segment.write_header(file, &mut cursor, self.ident.byte_order)?;
391        }
392
393        // * Segment data
394        for segment in &self.segments {
395            file.seek(std::io::SeekFrom::Start(segment.offset.value()))
396                .map_err(|err| Error::io(err, "segment data"))?;
397            file.write_all(&segment.data)
398                .map_err(|err| Error::io(err, "segment data"))?;
399        }
400
401        Ok(())
402    }
403
404    /// Read an ELF from a file
405    pub fn read<R: std::io::Read + std::io::Seek>(file: &mut R) -> Result<Self> {
406        let ident = Ident::read(file)?;
407        if ident.class != T::CLASS {
408            return Err(Error::Error(format!(
409                "Expected ELF class {:?}, got class {:?}",
410                T::CLASS,
411                ident.class
412            )));
413        }
414
415        Self::read_remainder(file, ident)
416    }
417
418    /// Read an ELF from a file when you already read [Ident]
419    pub fn read_remainder<R: std::io::Read>(file: &mut R, ident: Ident) -> Result<Self> {
420        let e_type = rw_enum!(read Type, e_type, u16, ident, file);
421        let machine = rw_enum!(read Machine, machine, u16, ident, file);
422        if u32::read(file, ident.byte_order, "e_version")? != 1 {
423            return Err(Error::parse("ELF version should be always 1", "e_version"));
424        }
425        let entry_point = T::read(file, ident.byte_order, "e_entry")?;
426
427        let program_headers_offset = T::read(file, ident.byte_order, "e_phoff")?;
428        let section_headers_offset = T::read(file, ident.byte_order, "e_shoff")?;
429
430        let flags = u32::read(file, ident.byte_order, "e_flags")?;
431        let elf_header_size = u16::read(file, ident.byte_order, "e_ehsize")?;
432        if elf_header_size != T::ELF_HEADER_SIZE {
433            return Err(Error::Error(format!(
434                "Invalid elf header size, expected {}, got {elf_header_size}!",
435                T::ELF_HEADER_SIZE
436            )));
437        }
438
439        let segment_header_size = u16::read(file, ident.byte_order, "e_phentsize")?;
440        if segment_header_size != T::SEGMENT_HEADER_SIZE {
441            return Err(Error::Error(format!(
442                "Invalid segment header size, expected {}, got {segment_header_size}!",
443                T::SEGMENT_HEADER_SIZE
444            )));
445        }
446        let num_segments = u16::read(file, ident.byte_order, "e_phnum")?;
447
448        let section_header_size = u16::read(file, ident.byte_order, "e_shentsize")?;
449        if section_header_size != T::SECTION_HEADER_SIZE {
450            return Err(Error::Error(format!(
451                "Invalid section header size, expected {}, got {section_header_size}!",
452                T::SECTION_HEADER_SIZE
453            )));
454        }
455        let num_sections = u16::read(file, ident.byte_order, "e_shnum")?;
456
457        let section_header_string_table_index = u16::read(file, ident.byte_order, "e_shstrndx")?;
458
459        Ok(Self {
460            ident,
461            e_type,
462            machine,
463            entry_point,
464            flags,
465            segments: Vec::new(),
466            sections: Vec::new(),
467            section_header_string_table_index,
468        })
469    }
470}
471
472// * ----------------------------------- Segments ----------------------------------- * //
473/// A segment, this gets loaded into memory when elf file gets executed
474#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
475pub struct Segment<T: SizeT> {
476    /// Identifies the type of the segment
477    pub p_type: SegmentType,
478    /// Data of this segment
479    pub data: Vec<u8>,
480    /// Offset of the segment in the file image
481    pub offset: T,
482    /// Virtual address of the segment in memory
483    pub virtual_address: T,
484    /// On systems where physical address is relevant, reserved for segment's physical address
485    pub physical_address: T,
486    /// Size in bytes of the segment in memory. May be 0. May be more then data to pad segment with zeros
487    pub size: T,
488    /// Segment-dependent flags. Essentially permissions, specified with [SegmentFlags] like that:
489    /// ```
490    /// use orecc_elf::SegmentFlags;
491    /// let flags = SegmentFlags::Readable as u32 | SegmentFlags::Executable as u32;
492    /// assert_eq!(flags, 0x05);
493    /// ```
494    pub flags: u32,
495    /// 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2. I don't really know how it works
496    pub align: T,
497}
498
499impl<T: SizeT> Segment<T> {
500    fn write_header<W: std::io::Write>(
501        &self,
502        file: &mut W,
503        cursor: &mut u64,
504        byte_order: ByteOrder,
505    ) -> Result<()> {
506        self.p_type.int_value().write(file, byte_order, "p_type")?;
507        if T::MOVE_PFLAGS {
508            self.flags.write(file, byte_order, "p_flags")?;
509        }
510        T::new(*cursor).write(file, byte_order, "p_offset")?;
511        self.virtual_address.write(file, byte_order, "p_vaddr")?;
512        self.physical_address.write(file, byte_order, "p_paddr")?;
513        T::new(self.data.len() as _).write(file, byte_order, "p_filesz")?;
514        self.size.write(file, byte_order, "p_memsz")?;
515        if !T::MOVE_PFLAGS {
516            self.flags.write(file, byte_order, "p_flags")?;
517        }
518        self.align.write(file, byte_order, "p_align")?;
519        *cursor += self.data.len() as u64;
520        Ok(())
521    }
522}
523
524// * ----------------------------------- Sections ----------------------------------- * //
525/// Section, when linking turned into segment
526#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
527pub struct Section<T: SizeT> {
528    __: T,
529}
530
531// * ------------------------------------- Size ------------------------------------- * //
532impl SizeT for u32 {
533    fn new(value: u64) -> Self {
534        value as _
535    }
536
537    fn value(&self) -> u64 {
538        *self as _
539    }
540
541    const CLASS: Class = Class::ELF32;
542    const ELF_HEADER_SIZE: u16 = 52;
543    const SEGMENT_HEADER_SIZE: u16 = 32;
544    const SECTION_HEADER_SIZE: u16 = 40;
545    const MOVE_PFLAGS: bool = false;
546}
547
548impl SizeT for u64 {
549    fn new(value: u64) -> Self {
550        value
551    }
552
553    fn value(&self) -> u64 {
554        *self
555    }
556
557    const CLASS: Class = Class::ELF64;
558    const ELF_HEADER_SIZE: u16 = 64;
559    const SEGMENT_HEADER_SIZE: u16 = 56;
560    const SECTION_HEADER_SIZE: u16 = 64;
561    const MOVE_PFLAGS: bool = true;
562}
563
564// * ------------------------------------ Errors ------------------------------------ * //
565/// A custom result type
566pub type Result<T> = std::result::Result<T, Error>;
567
568/// A custom error type
569#[derive(Clone, Debug)]
570pub enum Error {
571    /// The file you reading is not an ELF file
572    Signature(String),
573    /// The ELF file is corrupted
574    Error(String),
575}
576
577impl Error {
578    fn io(err: std::io::Error, part: &str) -> Self {
579        Self::Error(format!("failed to read/write {part} to/from a file: {err}"))
580    }
581
582    fn parse(err: impl ToString, part: &str) -> Self {
583        Self::Error(format!("failed to parse {part}: {}", err.to_string()))
584    }
585}
586
587impl std::fmt::Display for Error {
588    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
589        match self {
590            Error::Signature(msg) => f.write_str(msg),
591            Error::Error(msg) => f.write_str(msg),
592        }
593    }
594}
595
596// * ------------------------------------- Tests ------------------------------------ * //
597#[cfg(test)]
598mod tests;