1mod serde;
73use serde::*;
74
75mod structs;
76pub use structs::*;
77
78#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
81pub struct Ident {
82 pub class: Class,
84 pub byte_order: ByteOrder,
86 pub abi: ABI,
88 pub abi_version: u8,
90}
91
92impl Ident {
93 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 #[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 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#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
165pub struct SegmentTemplate<T: SizeT> {
166 pub p_type: SegmentType,
168 pub data: Vec<u8>,
170 pub size: T,
172 pub flags: u32,
179}
180
181impl<T: SizeT> SegmentTemplate<T> {
182 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
193impl Machine {
195 pub fn text_region_address(self) -> u64 {
196 match self {
197 Self::X86 => 0x08048000,
198 Self::X86_64 => 0x400000,
199 _ => 100, }
201 }
202}
203
204#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
210pub struct ELF<T: SizeT> {
211 pub ident: Ident,
213 pub e_type: Type,
215 pub machine: Machine,
217 pub entry_point: T,
219 pub segments: Vec<Segment<T>>,
221 pub sections: Vec<Section<T>>,
223 pub flags: u32,
225 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 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 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 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 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 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 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 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 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 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 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 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#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
475pub struct Segment<T: SizeT> {
476 pub p_type: SegmentType,
478 pub data: Vec<u8>,
480 pub offset: T,
482 pub virtual_address: T,
484 pub physical_address: T,
486 pub size: T,
488 pub flags: u32,
495 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#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
527pub struct Section<T: SizeT> {
528 __: T,
529}
530
531impl 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
564pub type Result<T> = std::result::Result<T, Error>;
567
568#[derive(Clone, Debug)]
570pub enum Error {
571 Signature(String),
573 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#[cfg(test)]
598mod tests;