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