xmas_elf/
sections.rs

1#[cfg(feature = "compression")]
2use std::borrow::Cow;
3#[cfg(feature = "compression")]
4use std::vec::Vec;
5
6use core::fmt;
7use core::mem;
8use core::slice;
9
10#[cfg(feature = "compression")]
11use flate2::{Decompress, FlushDecompress};
12
13use {P32, P64, ElfFile};
14use header::{Header, Class};
15use zero::{read, read_array, read_str, read_strs_to_null, StrReaderIterator, Pod};
16use symbol_table;
17use dynamic::Dynamic;
18use hash::HashTable;
19
20pub fn parse_section_header<'a>(input: &'a [u8],
21                                header: Header<'a>,
22                                index: u16)
23                                -> Result<SectionHeader<'a>, &'static str> {
24    // Trying to get index 0 (SHN_UNDEF) is also probably an error, but it is a legitimate section.
25    assert!(index < SHN_LORESERVE,
26            "Attempt to get section for a reserved index");
27
28    let start = (index as u64 * header.pt2.sh_entry_size() as u64 +
29                 header.pt2.sh_offset() as u64) as usize;
30    let end = start + header.pt2.sh_entry_size() as usize;
31
32    if input.len() < end {
33        return Err("File is shorter than section header offset");
34    }
35
36    Ok(match header.pt1.class() {
37        Class::ThirtyTwo => {
38            let header: &'a SectionHeader_<P32> = read(&input[start..end]);
39            SectionHeader::Sh32(header)
40        }
41        Class::SixtyFour => {
42            let header: &'a SectionHeader_<P64> = read(&input[start..end]);
43            SectionHeader::Sh64(header)
44        }
45        Class::None | Class::Other(_) => unreachable!(),
46    })
47}
48
49#[derive(Debug, Clone)]
50pub struct SectionIter<'b, 'a: 'b> {
51    pub file: &'b ElfFile<'a>,
52    pub next_index: u16,
53}
54
55impl<'b, 'a> Iterator for SectionIter<'b, 'a> {
56    type Item = SectionHeader<'a>;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        let count = self.file.header.pt2.sh_count();
60        if self.next_index >= count {
61            return None;
62        }
63
64        let result = self.file.section_header(self.next_index);
65        self.next_index += 1;
66        result.ok()
67    }
68}
69
70// Distinguished section indices.
71pub const SHN_UNDEF: u16 = 0;
72pub const SHN_LORESERVE: u16 = 0xff00;
73pub const SHN_LOPROC: u16 = 0xff00;
74pub const SHN_HIPROC: u16 = 0xff1f;
75pub const SHN_LOOS: u16 = 0xff20;
76pub const SHN_HIOS: u16 = 0xff3f;
77pub const SHN_ABS: u16 = 0xfff1;
78pub const SHN_COMMON: u16 = 0xfff2;
79pub const SHN_XINDEX: u16 = 0xffff;
80pub const SHN_HIRESERVE: u16 = 0xffff;
81
82#[derive(Clone, Copy, Debug)]
83pub enum SectionHeader<'a> {
84    Sh32(&'a SectionHeader_<P32>),
85    Sh64(&'a SectionHeader_<P64>),
86}
87
88macro_rules! getter {
89    ($name: ident, $typ: ident) => {
90        pub fn $name(&self) -> $typ {
91            match *self {
92                SectionHeader::Sh32(h) => h.$name as $typ,
93                SectionHeader::Sh64(h) => h.$name as $typ,
94            }
95        }
96    }
97}
98
99impl<'a> SectionHeader<'a> {
100    // Note that this function is O(n) in the length of the name.
101    pub fn get_name(&self, elf_file: &ElfFile<'a>) -> Result<&'a str, &'static str> {
102        self.get_type().and_then(|typ| match typ {
103            ShType::Null => Err("Attempt to get name of null section"),
104            _ => elf_file.get_shstr(self.name()),
105        })
106    }
107
108    pub fn get_type(&self) -> Result<ShType, &'static str> {
109        self.type_().as_sh_type()
110    }
111
112    pub fn get_data(&self, elf_file: &ElfFile<'a>) -> Result<SectionData<'a>, &'static str> {
113        macro_rules! array_data {
114            ($data32: ident, $data64: ident) => {{
115                let data = self.raw_data(elf_file);
116                match elf_file.header.pt1.class() {
117                    Class::ThirtyTwo => SectionData::$data32(read_array(data)),
118                    Class::SixtyFour => SectionData::$data64(read_array(data)),
119                    Class::None | Class::Other(_) => unreachable!(),
120                }
121            }}
122        }
123
124        self.get_type().and_then(|typ| Ok(match typ {
125            ShType::Null | ShType::NoBits => SectionData::Empty,
126            ShType::ProgBits |
127            ShType::ShLib |
128            ShType::OsSpecific(_) |
129            ShType::ProcessorSpecific(_) |
130            ShType::User(_) => SectionData::Undefined(self.raw_data(elf_file)),
131            ShType::SymTab => array_data!(SymbolTable32, SymbolTable64),
132            ShType::DynSym => array_data!(DynSymbolTable32, DynSymbolTable64),
133            ShType::StrTab => SectionData::StrArray(self.raw_data(elf_file)),
134            ShType::InitArray | ShType::FiniArray | ShType::PreInitArray => {
135                array_data!(FnArray32, FnArray64)
136            }
137            ShType::Rela => array_data!(Rela32, Rela64),
138            ShType::Rel => array_data!(Rel32, Rel64),
139            ShType::Dynamic => array_data!(Dynamic32, Dynamic64),
140            ShType::Group => {
141                let data = self.raw_data(elf_file);
142                unsafe {
143                    let flags: &'a u32 = mem::transmute(&data[0]);
144                    let indicies: &'a [u32] = read_array(&data[4..]);
145                    SectionData::Group {
146                        flags,
147                        indicies,
148                    }
149                }
150            }
151            ShType::SymTabShIndex => {
152                SectionData::SymTabShIndex(read_array(self.raw_data(elf_file)))
153            }
154            ShType::Note => {
155                let data = self.raw_data(elf_file);
156                match elf_file.header.pt1.class() {
157                    Class::ThirtyTwo => return Err("32-bit binaries not implemented"),
158                    Class::SixtyFour => {
159                        let header: &'a NoteHeader = read(&data[0..12]);
160                        let index = &data[12..];
161                        SectionData::Note64(header, index)
162                    }
163                    Class::None | Class::Other(_) => return Err("Unknown ELF class"),
164                }
165            }
166            ShType::Hash => {
167                let data = self.raw_data(elf_file);
168                SectionData::HashTable(HashTable::read(data))
169            }
170        }))
171    }
172
173    pub fn raw_data(&self, elf_file: &ElfFile<'a>) -> &'a [u8] {
174        assert_ne!(self.get_type().unwrap(), ShType::Null);
175        &elf_file.input[self.offset() as usize..(self.offset() + self.size()) as usize]
176    }
177
178    #[cfg(feature = "compression")]
179    pub fn decompressed_data(&self, elf_file: &ElfFile<'a>) -> Result<Cow<'a, [u8]>, &'static str> {
180        let raw = self.raw_data(elf_file);
181        Ok(if (self.flags() & SHF_COMPRESSED) == 0 {
182            Cow::Borrowed(raw)
183        } else {
184            fn read_compression_header<'a, T: Pod + Clone>(raw: &'a [u8]) -> Result<(T, &'a [u8]), &'static str> {
185                if raw.len() < mem::size_of::<T>() {
186                    return Err("Unexpected EOF in compressed section");
187                }
188
189                let (header, rest) = raw.split_at(mem::size_of::<T>());
190                let mut header_bytes = Vec::with_capacity(mem::size_of::<T>());
191                header_bytes.resize(mem::size_of::<T>(), 0);
192                assert!(header_bytes.as_ptr() as usize % mem::align_of::<T>() == 0);
193                header_bytes.copy_from_slice(header);
194                let header: &T = read(&header_bytes);
195                Ok((header.clone(), rest))
196            }
197            let (compression_type, size, compressed_data) = match elf_file.header.pt1.class() {
198                Class::ThirtyTwo => {
199                    let (header, rest) = read_compression_header::<CompressionHeader32>(raw)?;
200                    (header.type_.as_compression_type(), header.size as usize, rest)
201                },
202                Class::SixtyFour => {
203                    let (header, rest) = read_compression_header::<CompressionHeader64>(raw)?;
204                    (header.type_.as_compression_type(), header.size as usize, rest)
205                },
206                Class::None | Class::Other(_) => unreachable!(),
207            };
208
209            match compression_type {
210                Ok(CompressionType::Zlib) => {
211                    let mut decompressed = Vec::with_capacity(size);
212                    let mut decompress = Decompress::new(true);
213                    if let Err(_) = decompress.decompress_vec(
214                        compressed_data, &mut decompressed, FlushDecompress::Finish) {
215                        return Err("Decompression error");
216                    }
217                    Cow::Owned(decompressed)
218                }
219                Ok(CompressionType::Zstd) => {
220                    let mut decompressed = Vec::with_capacity(size);
221                    if let Err(_) = zstd::stream::copy_decode(compressed_data, &mut decompressed) {
222                        return Err("Decompression error");
223                    }
224                    Cow::Owned(decompressed)
225                }
226                _ => return Err("Unknown compression type"),
227            }
228        })
229    }
230
231    getter!(flags, u64);
232    getter!(name, u32);
233    getter!(address, u64);
234    getter!(offset, u64);
235    getter!(size, u64);
236    getter!(type_, ShType_);
237    getter!(link, u32);
238    getter!(info, u32);
239    getter!(entry_size, u32);
240    getter!(align, u64);
241}
242
243impl<'a> fmt::Display for SectionHeader<'a> {
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245        macro_rules! sh_display {
246            ($sh: ident) => {{
247                writeln!(f, "Section header:")?;
248                writeln!(f, "    name:             {:?}", $sh.name)?;
249                writeln!(f, "    type:             {:?}", self.get_type())?;
250                writeln!(f, "    flags:            {:?}", $sh.flags)?;
251                writeln!(f, "    address:          {:?}", $sh.address)?;
252                writeln!(f, "    offset:           {:?}", $sh.offset)?;
253                writeln!(f, "    size:             {:?}", $sh.size)?;
254                writeln!(f, "    link:             {:?}", $sh.link)?;
255                writeln!(f, "    align:            {:?}", $sh.align)?;
256                writeln!(f, "    entry size:       {:?}", $sh.entry_size)?;
257                Ok(())
258            }}
259        }
260
261        match *self {
262            SectionHeader::Sh32(sh) => sh_display!(sh),
263            SectionHeader::Sh64(sh) => sh_display!(sh),
264        }
265    }
266}
267
268#[derive(Debug)]
269#[repr(C)]
270pub struct SectionHeader_<P> {
271    name: u32,
272    type_: ShType_,
273    flags: P,
274    address: P,
275    offset: P,
276    size: P,
277    link: u32,
278    info: u32,
279    align: P,
280    entry_size: P,
281}
282
283unsafe impl<P> Pod for SectionHeader_<P> {}
284
285#[derive(Copy, Clone)]
286pub struct ShType_(u32);
287
288#[derive(Copy, Clone, Debug, PartialEq, Eq)]
289pub enum ShType {
290    Null,
291    ProgBits,
292    SymTab,
293    StrTab,
294    Rela,
295    Hash,
296    Dynamic,
297    Note,
298    NoBits,
299    Rel,
300    ShLib,
301    DynSym,
302    InitArray,
303    FiniArray,
304    PreInitArray,
305    Group,
306    SymTabShIndex,
307    OsSpecific(u32),
308    ProcessorSpecific(u32),
309    User(u32),
310}
311
312impl ShType_ {
313    fn as_sh_type(self) -> Result<ShType, &'static str> {
314        match self.0 {
315            0 => Ok(ShType::Null),
316            1 => Ok(ShType::ProgBits),
317            2 => Ok(ShType::SymTab),
318            3 => Ok(ShType::StrTab),
319            4 => Ok(ShType::Rela),
320            5 => Ok(ShType::Hash),
321            6 => Ok(ShType::Dynamic),
322            7 => Ok(ShType::Note),
323            8 => Ok(ShType::NoBits),
324            9 => Ok(ShType::Rel),
325            10 => Ok(ShType::ShLib),
326            11 => Ok(ShType::DynSym),
327            // sic.
328            14 => Ok(ShType::InitArray),
329            15 => Ok(ShType::FiniArray),
330            16 => Ok(ShType::PreInitArray),
331            17 => Ok(ShType::Group),
332            18 => Ok(ShType::SymTabShIndex),
333            st if (SHT_LOOS..=SHT_HIOS).contains(&st) => Ok(ShType::OsSpecific(st)),
334            st if (SHT_LOPROC..=SHT_HIPROC).contains(&st) => Ok(ShType::ProcessorSpecific(st)),
335            st if (SHT_LOUSER..=SHT_HIUSER).contains(&st) => Ok(ShType::User(st)),
336            _ => Err("Invalid sh type"),
337        }
338    }
339}
340
341impl fmt::Debug for ShType_ {
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343        self.as_sh_type().fmt(f)
344    }
345}
346
347#[derive(Debug)]
348pub enum SectionData<'a> {
349    Empty,
350    Undefined(&'a [u8]),
351    Group { flags: &'a u32, indicies: &'a [u32] },
352    StrArray(&'a [u8]),
353    FnArray32(&'a [u32]),
354    FnArray64(&'a [u64]),
355    SymbolTable32(&'a [symbol_table::Entry32]),
356    SymbolTable64(&'a [symbol_table::Entry64]),
357    DynSymbolTable32(&'a [symbol_table::DynEntry32]),
358    DynSymbolTable64(&'a [symbol_table::DynEntry64]),
359    SymTabShIndex(&'a [u32]),
360    // Note32 uses 4-byte words, which I'm not sure how to manage.
361    // The pointer is to the start of the name field in the note.
362    Note64(&'a NoteHeader, &'a [u8]),
363    Rela32(&'a [Rela<P32>]),
364    Rela64(&'a [Rela<P64>]),
365    Rel32(&'a [Rel<P32>]),
366    Rel64(&'a [Rel<P64>]),
367    Dynamic32(&'a [Dynamic<P32>]),
368    Dynamic64(&'a [Dynamic<P64>]),
369    HashTable(HashTable<'a>),
370}
371
372#[derive(Debug)]
373pub struct SectionStrings<'a> {
374    inner: StrReaderIterator<'a>,
375}
376
377impl<'a> Iterator for SectionStrings<'a> {
378    type Item = &'a str;
379
380    #[inline]
381    fn next(&mut self) -> Option<&'a str> {
382        self.inner.next()
383    }
384}
385
386impl<'a> SectionData<'a> {
387    pub fn strings(&self) -> Result<SectionStrings<'a>, ()> {
388        if let SectionData::StrArray(data) = *self {
389            Ok(SectionStrings { inner: read_strs_to_null(data) })
390        } else {
391            Err(())
392        }
393    }
394}
395
396// Distinguished ShType values.
397pub const SHT_LOOS: u32 = 0x60000000;
398pub const SHT_HIOS: u32 = 0x6fffffff;
399pub const SHT_LOPROC: u32 = 0x70000000;
400pub const SHT_HIPROC: u32 = 0x7fffffff;
401pub const SHT_LOUSER: u32 = 0x80000000;
402pub const SHT_HIUSER: u32 = 0xffffffff;
403
404// Flags (SectionHeader::flags)
405pub const SHF_WRITE: u64 = 0x1;
406pub const SHF_ALLOC: u64 = 0x2;
407pub const SHF_EXECINSTR: u64 = 0x4;
408pub const SHF_MERGE: u64 = 0x10;
409pub const SHF_STRINGS: u64 = 0x20;
410pub const SHF_INFO_LINK: u64 = 0x40;
411pub const SHF_LINK_ORDER: u64 = 0x80;
412pub const SHF_OS_NONCONFORMING: u64 = 0x100;
413pub const SHF_GROUP: u64 = 0x200;
414pub const SHF_TLS: u64 = 0x400;
415pub const SHF_COMPRESSED: u64 = 0x800;
416pub const SHF_MASKOS: u64 = 0x0ff00000;
417pub const SHF_MASKPROC: u64 = 0xf0000000;
418
419#[derive(Copy, Clone, Debug)]
420#[repr(C)]
421pub struct CompressionHeader64 {
422    type_: CompressionType_,
423    _reserved: u32,
424    size: u64,
425    align: u64,
426}
427
428#[derive(Copy, Clone, Debug)]
429#[repr(C)]
430pub struct CompressionHeader32 {
431    type_: CompressionType_,
432    size: u32,
433    align: u32,
434}
435
436unsafe impl Pod for CompressionHeader64 {}
437unsafe impl Pod for CompressionHeader32 {}
438
439#[derive(Copy, Clone)]
440pub struct CompressionType_(u32);
441
442#[derive(Copy, Clone, Debug, PartialEq, Eq)]
443pub enum CompressionType {
444    Zlib,
445    Zstd,
446    OsSpecific(u32),
447    ProcessorSpecific(u32),
448}
449
450impl CompressionType_ {
451    fn as_compression_type(&self) -> Result<CompressionType, &'static str> {
452        match self.0 {
453            1 => Ok(CompressionType::Zlib),
454            2 => Ok(CompressionType::Zstd),
455            ct if (COMPRESS_LOOS..=COMPRESS_HIOS).contains(&ct) => Ok(CompressionType::OsSpecific(ct)),
456            ct if (COMPRESS_LOPROC..=COMPRESS_HIPROC).contains(&ct) => {
457                Ok(CompressionType::ProcessorSpecific(ct))
458            }
459            _ => Err("Invalid compression type"),
460        }
461    }
462}
463
464impl fmt::Debug for CompressionType_ {
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        self.as_compression_type().fmt(f)
467    }
468}
469
470// Distinguished CompressionType values.
471pub const COMPRESS_LOOS: u32 = 0x60000000;
472pub const COMPRESS_HIOS: u32 = 0x6fffffff;
473pub const COMPRESS_LOPROC: u32 = 0x70000000;
474pub const COMPRESS_HIPROC: u32 = 0x7fffffff;
475
476// Group flags
477pub const GRP_COMDAT: u64 = 0x1;
478pub const GRP_MASKOS: u64 = 0x0ff00000;
479pub const GRP_MASKPROC: u64 = 0xf0000000;
480
481#[derive(Debug)]
482#[repr(C)]
483pub struct Rela<P> {
484    offset: P,
485    info: P,
486    addend: P,
487}
488
489#[derive(Debug)]
490#[repr(C)]
491pub struct Rel<P> {
492    offset: P,
493    info: P,
494}
495
496unsafe impl<P> Pod for Rela<P> {}
497unsafe impl<P> Pod for Rel<P> {}
498
499impl Rela<P32> {
500    pub fn get_offset(&self) -> u32 {
501        self.offset
502    }
503    pub fn get_addend(&self) -> u32 {
504        self.addend
505    }
506    pub fn get_symbol_table_index(&self) -> u32 {
507        self.info >> 8
508    }
509    pub fn get_type(&self) -> u8 {
510        self.info as u8
511    }
512}
513impl Rela<P64> {
514    pub fn get_offset(&self) -> u64 {
515        self.offset
516    }
517    pub fn get_addend(&self) -> u64 {
518        self.addend
519    }
520    pub fn get_symbol_table_index(&self) -> u32 {
521        (self.info >> 32) as u32
522    }
523    pub fn get_type(&self) -> u32 {
524        (self.info & 0xffffffff) as u32
525    }
526}
527impl Rel<P32> {
528    pub fn get_offset(&self) -> u32 {
529        self.offset
530    }
531    pub fn get_symbol_table_index(&self) -> u32 {
532        self.info >> 8
533    }
534    pub fn get_type(&self) -> u8 {
535        self.info as u8
536    }
537}
538impl Rel<P64> {
539    pub fn get_offset(&self) -> u64 {
540        self.offset
541    }
542    pub fn get_symbol_table_index(&self) -> u32 {
543        (self.info >> 32) as u32
544    }
545    pub fn get_type(&self) -> u32 {
546        (self.info & 0xffffffff) as u32
547    }
548}
549
550#[derive(Copy, Clone, Debug)]
551#[repr(C)]
552pub struct NoteHeader {
553    name_size: u32,
554    desc_size: u32,
555    type_: u32,
556}
557
558unsafe impl Pod for NoteHeader {}
559
560impl NoteHeader {
561    pub fn type_(&self) -> u32 {
562        self.type_
563    }
564
565    pub fn name<'a>(&'a self, input: &'a [u8]) -> &'a str {
566        let result = read_str(input);
567        // - 1 is due to null terminator
568        assert_eq!(result.len(), (self.name_size - 1) as usize);
569        result
570    }
571
572    pub fn desc<'a>(&'a self, input: &'a [u8]) -> &'a [u8] {
573        // Account for padding to the next u32.
574        unsafe {
575            let offset = (self.name_size + 3) & !0x3;
576            let ptr = (&input[0] as *const u8).offset(offset as isize);
577            slice::from_raw_parts(ptr, self.desc_size as usize)
578        }
579    }
580}
581
582pub fn sanity_check<'a>(header: SectionHeader<'a>, _file: &ElfFile<'a>) -> Result<(), &'static str> {
583    if header.get_type()? == ShType::Null {
584        return Ok(());
585    }
586    // TODO
587    Ok(())
588}