Skip to main content

vivisect/elf/
section_header.rs

1macro_rules! elf_section_header {
2    ($size:ident) => {
3        // XXX: Do not import scroll traits here.
4        // See: https://github.com/rust-lang/rust/issues/65090#issuecomment-538668155
5
6        #[repr(C)]
7        #[derive(Copy, Clone, PartialEq, Eq, Default)]
8        #[cfg_attr(
9            feature = "alloc",
10            derive(scroll::Pread, scroll::Pwrite, scroll::SizeWith)
11        )]
12        /// Section Headers are typically used by humans and static linkers for additional information or how to relocate the object
13        ///
14        /// **NOTE** section headers are strippable from a binary without any loss of portability/executability; _do not_ rely on them being there!
15        pub struct SectionHeader {
16            /// Section name (string tbl index)
17            pub sh_name: u32,
18            /// Section type
19            pub sh_type: u32,
20            /// Section flags
21            pub sh_flags: $size,
22            /// Section virtual addr at execution
23            pub sh_addr: $size,
24            /// Section file offset
25            pub sh_offset: $size,
26            /// Section size in bytes
27            pub sh_size: $size,
28            /// Link to another section
29            pub sh_link: u32,
30            /// Additional section information
31            pub sh_info: u32,
32            /// Section alignment
33            pub sh_addralign: $size,
34            /// Entry size if section holds table
35            pub sh_entsize: $size,
36        }
37
38        use plain;
39        // Declare that this is a plain type.
40        unsafe impl plain::Plain for SectionHeader {}
41
42        impl ::core::fmt::Debug for SectionHeader {
43            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
44                f.debug_struct("SectionHeader")
45                    .field("sh_name", &self.sh_name)
46                    .field("sh_type", &sht_to_str(self.sh_type))
47                    .field("sh_flags", &format_args!("0x{:x}", self.sh_flags))
48                    .field("sh_addr", &format_args!("0x{:x}", self.sh_addr))
49                    .field("sh_offset", &format_args!("0x{:x}", self.sh_offset))
50                    .field("sh_size", &format_args!("0x{:x}", self.sh_size))
51                    .field("sh_link", &format_args!("0x{:x}", self.sh_link))
52                    .field("sh_info", &format_args!("0x{:x}", self.sh_info))
53                    .field("sh_addralign", &format_args!("0x{:x}", self.sh_addralign))
54                    .field("sh_entsize", &format_args!("0x{:x}", self.sh_entsize))
55                    .finish()
56            }
57        }
58    };
59}
60
61/// Undefined section.
62pub const SHN_UNDEF: u32 = 0;
63/// Start of reserved indices.
64pub const SHN_LORESERVE: u32 = 0xff00;
65/// Start of processor-specific.
66pub const SHN_LOPROC: u32 = 0xff00;
67/// Order section before all others (Solaris).
68pub const SHN_BEFORE: u32 = 0xff00;
69/// Order section after all others (Solaris).
70pub const SHN_AFTER: u32 = 0xff01;
71/// End of processor-specific.
72pub const SHN_HIPROC: u32 = 0xff1f;
73/// Start of OS-specific.
74pub const SHN_LOOS: u32 = 0xff20;
75/// End of OS-specific.
76pub const SHN_HIOS: u32 = 0xff3f;
77/// Associated symbol is absolute.
78pub const SHN_ABS: u32 = 0xfff1;
79/// Associated symbol is common.
80pub const SHN_COMMON: u32 = 0xfff2;
81/// Index is in extra table.
82pub const SHN_XINDEX: u32 = 0xffff;
83/// End of reserved indices.
84pub const SHN_HIRESERVE: u32 = 0xffff;
85
86// === Legal values for sh_type (section type). ===
87/// Section header table entry unused.
88pub const SHT_NULL: u32 = 0;
89/// Program data.
90pub const SHT_PROGBITS: u32 = 1;
91/// Symbol table.
92pub const SHT_SYMTAB: u32 = 2;
93/// String table.
94pub const SHT_STRTAB: u32 = 3;
95/// Relocation entries with addends.
96pub const SHT_RELA: u32 = 4;
97/// Symbol hash table.
98pub const SHT_HASH: u32 = 5;
99/// Dynamic linking information.
100pub const SHT_DYNAMIC: u32 = 6;
101/// Notes.
102pub const SHT_NOTE: u32 = 7;
103/// Program space with no data (bss).
104pub const SHT_NOBITS: u32 = 8;
105/// Relocation entries, no addends.
106pub const SHT_REL: u32 = 9;
107/// Reserved.
108pub const SHT_SHLIB: u32 = 10;
109/// Dynamic linker symbol table.
110pub const SHT_DYNSYM: u32 = 11;
111/// Array of constructors.
112pub const SHT_INIT_ARRAY: u32 = 14;
113/// Array of destructors.
114pub const SHT_FINI_ARRAY: u32 = 15;
115/// Array of pre-constructors.
116pub const SHT_PREINIT_ARRAY: u32 = 16;
117/// Section group.
118pub const SHT_GROUP: u32 = 17;
119/// Extended section indeces.
120pub const SHT_SYMTAB_SHNDX: u32 = 18;
121/// Number of defined types.
122pub const SHT_NUM: u32 = 19;
123/// Start OS-specific.
124pub const SHT_LOOS: u32 = 0x6000_0000;
125/// Object attributes.
126pub const SHT_GNU_ATTRIBUTES: u32 = 0x6fff_fff5;
127/// GNU-style hash table.
128pub const SHT_GNU_HASH: u32 = 0x6fff_fff6;
129/// Prelink library list.
130pub const SHT_GNU_LIBLIST: u32 = 0x6fff_fff7;
131/// Checksum for DSO content.
132pub const SHT_CHECKSUM: u32 = 0x6fff_fff8;
133/// Sun-specific low bound.
134pub const SHT_LOSUNW: u32 = 0x6fff_fffa;
135pub const SHT_SUNW_MOVE: u32 = 0x6fff_fffa;
136pub const SHT_SUNW_COMDAT: u32 = 0x6fff_fffb;
137pub const SHT_SUNW_SYMINFO: u32 = 0x6fff_fffc;
138/// Version definition section.
139pub const SHT_GNU_VERDEF: u32 = 0x6fff_fffd;
140/// Version needs section.
141pub const SHT_GNU_VERNEED: u32 = 0x6fff_fffe;
142/// Version symbol table.
143pub const SHT_GNU_VERSYM: u32 = 0x6fff_ffff;
144/// Sun-specific high bound.
145pub const SHT_HISUNW: u32 = 0x6fff_ffff;
146/// End OS-specific type.
147pub const SHT_HIOS: u32 = 0x6fff_ffff;
148/// Start of processor-specific.
149pub const SHT_LOPROC: u32 = 0x7000_0000;
150/// X86-64 unwind information.
151pub const SHT_X86_64_UNWIND: u32 = 0x7000_0001;
152/// End of processor-specific.
153pub const SHT_HIPROC: u32 = 0x7fff_ffff;
154/// Start of application-specific.
155pub const SHT_LOUSER: u32 = 0x8000_0000;
156/// End of application-specific.
157pub const SHT_HIUSER: u32 = 0x8fff_ffff;
158
159// Legal values for sh_flags (section flags)
160/// Writable.
161pub const SHF_WRITE: u32 = 0x1;
162/// Occupies memory during execution.
163pub const SHF_ALLOC: u32 = 0x2;
164/// Executable.
165pub const SHF_EXECINSTR: u32 = 0x4;
166/// Might be merged.
167pub const SHF_MERGE: u32 = 0x10;
168/// Contains nul-terminated strings.
169pub const SHF_STRINGS: u32 = 0x20;
170/// `sh_info' contains SHT index.
171pub const SHF_INFO_LINK: u32 = 0x40;
172/// Preserve order after combining.
173pub const SHF_LINK_ORDER: u32 = 0x80;
174/// Non-standard OS specific handling required.
175pub const SHF_OS_NONCONFORMING: u32 = 0x100;
176/// Section is member of a group.
177pub const SHF_GROUP: u32 = 0x200;
178/// Section hold thread-local data.
179pub const SHF_TLS: u32 = 0x400;
180/// Section with compressed data.
181pub const SHF_COMPRESSED: u32 = 0x800;
182/// OS-specific..
183pub const SHF_MASKOS: u32 = 0x0ff0_0000;
184/// Processor-specific.
185pub const SHF_MASKPROC: u32 = 0xf000_0000;
186/// Special ordering requirement (Solaris).
187pub const SHF_ORDERED: u32 = 1 << 30;
188/// Number of "regular" section header flags
189pub const SHF_NUM_REGULAR_FLAGS: usize = 12;
190/// Section is excluded unless referenced or allocated (Solaris).
191pub const SHF_EXCLUDE: u32 = 0x80000000; // 1U << 31
192
193pub const SHF_FLAGS: [u32; SHF_NUM_REGULAR_FLAGS] = [
194    SHF_WRITE,
195    SHF_ALLOC,
196    SHF_EXECINSTR,
197    SHF_MERGE,
198    SHF_STRINGS,
199    SHF_INFO_LINK,
200    SHF_LINK_ORDER,
201    SHF_OS_NONCONFORMING,
202    SHF_GROUP,
203    SHF_TLS,
204    SHF_COMPRESSED,
205    SHF_ORDERED,
206];
207
208pub fn sht_to_str(sht: u32) -> &'static str {
209    match sht {
210        SHT_NULL => "SHT_NULL",
211        SHT_PROGBITS => "SHT_PROGBITS",
212        SHT_SYMTAB => "SHT_SYMTAB",
213        SHT_STRTAB => "SHT_STRTAB",
214        SHT_RELA => "SHT_RELA",
215        SHT_HASH => "SHT_HASH",
216        SHT_DYNAMIC => "SHT_DYNAMIC",
217        SHT_NOTE => "SHT_NOTE",
218        SHT_NOBITS => "SHT_NOBITS",
219        SHT_REL => "SHT_REL",
220        SHT_SHLIB => "SHT_SHLIB",
221        SHT_DYNSYM => "SHT_DYNSYM",
222        SHT_INIT_ARRAY => "SHT_INIT_ARRAY",
223        SHT_FINI_ARRAY => "SHT_FINI_ARRAY",
224        SHT_PREINIT_ARRAY => "SHT_PREINIT_ARRAY",
225        SHT_GROUP => "SHT_GROUP",
226        SHT_SYMTAB_SHNDX => "SHT_SYMTAB_SHNDX",
227        SHT_NUM => "SHT_NUM",
228        SHT_LOOS => "SHT_LOOS",
229        SHT_GNU_ATTRIBUTES => "SHT_GNU_ATTRIBUTES",
230        SHT_GNU_HASH => "SHT_GNU_HASH",
231        SHT_GNU_LIBLIST => "SHT_GNU_LIBLIST",
232        SHT_CHECKSUM => "SHT_CHECKSUM",
233        SHT_SUNW_MOVE => "SHT_SUNW_MOVE",
234        SHT_SUNW_COMDAT => "SHT_SUNW_COMDAT",
235        SHT_SUNW_SYMINFO => "SHT_SUNW_SYMINFO",
236        SHT_GNU_VERDEF => "SHT_GNU_VERDEF",
237        SHT_GNU_VERNEED => "SHT_GNU_VERNEED",
238        SHT_GNU_VERSYM => "SHT_GNU_VERSYM",
239        SHT_LOPROC => "SHT_LOPROC",
240        SHT_X86_64_UNWIND => "SHT_X86_64_UNWIND",
241        SHT_HIPROC => "SHT_HIPROC",
242        SHT_LOUSER => "SHT_LOUSER",
243        SHT_HIUSER => "SHT_HIUSER",
244        _ => "UNKNOWN_SHT",
245    }
246}
247
248pub fn shf_to_str(shf: u32) -> &'static str {
249    match shf {
250        SHF_WRITE => "SHF_WRITE",
251        SHF_ALLOC => "SHF_ALLOC",
252        SHF_EXECINSTR => "SHF_EXECINSTR",
253        SHF_MERGE => "SHF_MERGE",
254        SHF_STRINGS => "SHF_STRINGS",
255        SHF_INFO_LINK => "SHF_INFO_LINK",
256        SHF_LINK_ORDER => "SHF_LINK_ORDER",
257        SHF_OS_NONCONFORMING => "SHF_OS_NONCONFORMING",
258        SHF_GROUP => "SHF_GROUP",
259        SHF_TLS => "SHF_TLS",
260        SHF_COMPRESSED => "SHF_COMPRESSED",
261        //SHF_MASKOS..SHF_MASKPROC => "SHF_OSFLAG",
262        SHF_ORDERED => "SHF_ORDERED",
263        _ => "SHF_UNKNOWN",
264    }
265}
266
267macro_rules! elf_section_header_std_impl { ($size:ty) => {
268
269    #[cfg(test)]
270    mod tests {
271        use super::*;
272        #[test]
273        fn size_of() {
274            assert_eq!(::std::mem::size_of::<SectionHeader>(), SIZEOF_SHDR);
275        }
276    }
277
278    if_alloc! {
279        use crate::elf::section_header::SectionHeader as ElfSectionHeader;
280
281        use plain::Plain;
282        use alloc::vec::Vec;
283
284        if_std! {
285            use crate::error::Result;
286
287            use std::fs::File;
288            use std::io::{Read, Seek};
289            use std::io::SeekFrom::Start;
290        }
291
292        impl From<SectionHeader> for ElfSectionHeader {
293            fn from(sh: SectionHeader) -> Self {
294                ElfSectionHeader {
295                    sh_name: sh.sh_name as usize,
296                    sh_type: sh.sh_type,
297                    sh_flags: u64::from(sh.sh_flags),
298                    sh_addr: u64::from(sh.sh_addr),
299                    sh_offset: u64::from(sh.sh_offset),
300                    sh_size: u64::from(sh.sh_size),
301                    sh_link: sh.sh_link,
302                    sh_info: sh.sh_info,
303                    sh_addralign: u64::from(sh.sh_addralign),
304                    sh_entsize: u64::from(sh.sh_entsize),
305                }
306            }
307        }
308        impl From<ElfSectionHeader> for SectionHeader {
309            fn from(sh: ElfSectionHeader) -> Self {
310                SectionHeader {
311                    sh_name     : sh.sh_name as u32,
312                    sh_type     : sh.sh_type,
313                    sh_flags    : sh.sh_flags as $size,
314                    sh_addr     : sh.sh_addr as $size,
315                    sh_offset   : sh.sh_offset as $size,
316                    sh_size     : sh.sh_size as $size,
317                    sh_link     : sh.sh_link,
318                    sh_info     : sh.sh_info,
319                    sh_addralign: sh.sh_addralign as $size,
320                    sh_entsize  : sh.sh_entsize as $size,
321                }
322            }
323        }
324
325        impl SectionHeader {
326            // FIXME: > 65535 sections
327            pub fn from_bytes(bytes: &[u8], shnum: usize) -> Vec<SectionHeader> {
328                let mut shdrs = vec![SectionHeader::default(); shnum];
329                shdrs.copy_from_bytes(bytes).expect("buffer is too short for given number of entries");
330                shdrs
331            }
332
333            #[cfg(feature = "std")]
334            // FIXME: > 65535 sections
335            pub fn from_fd(fd: &mut File, offset: u64, shnum: usize) -> Result<Vec<SectionHeader>> {
336                let mut shdrs = vec![SectionHeader::default(); shnum];
337                fd.seek(Start(offset))?;
338                unsafe {
339                    fd.read_exact(plain::as_mut_bytes(&mut *shdrs))?;
340                }
341                Ok(shdrs)
342            }
343        }
344    } // end if_alloc
345};}
346
347pub mod section_header32 {
348    pub use crate::elf::section_header::*;
349
350    elf_section_header!(u32);
351
352    pub const SIZEOF_SHDR: usize = 40;
353
354    elf_section_header_std_impl!(u32);
355}
356
357pub mod section_header64 {
358
359    pub use crate::elf::section_header::*;
360
361    elf_section_header!(u64);
362
363    pub const SIZEOF_SHDR: usize = 64;
364
365    elf_section_header_std_impl!(u64);
366}
367
368///////////////////////////////
369// Std/analysis/Unified Structs
370///////////////////////////////
371
372if_alloc! {
373    use crate::error;
374    use core::fmt;
375    use core::result;
376    use core::ops::Range;
377    use scroll::ctx;
378    use crate::container::{Container, Ctx};
379
380    #[cfg(feature = "endian_fd")]
381    use alloc::vec::Vec;
382
383    #[derive(Default, PartialEq, Eq, Clone)]
384    /// A unified SectionHeader - convertable to and from 32-bit and 64-bit variants
385    pub struct SectionHeader {
386        /// Section name (string tbl index)
387        pub sh_name: usize,
388        /// Section type
389        pub sh_type: u32,
390        /// Section flags
391        pub sh_flags: u64,
392        /// Section virtual addr at execution
393        pub sh_addr: u64,
394        /// Section file offset
395        pub sh_offset: u64,
396        /// Section size in bytes
397        pub sh_size: u64,
398        /// Link to another section
399        pub sh_link: u32,
400        /// Additional section information
401        pub sh_info: u32,
402        /// Section alignment
403        pub sh_addralign: u64,
404        /// Entry size if section holds table
405        pub sh_entsize: u64,
406    }
407
408    impl SectionHeader {
409        /// Return the size of the underlying program header, given a `container`
410        #[inline]
411        pub fn size(ctx: Ctx) -> usize {
412            use scroll::ctx::SizeWith;
413            Self::size_with(&ctx)
414        }
415        pub fn new() -> Self {
416            SectionHeader {
417                sh_name: 0,
418                sh_type: SHT_PROGBITS,
419                sh_flags: u64::from(SHF_ALLOC),
420                sh_addr: 0,
421                sh_offset: 0,
422                sh_size: 0,
423                sh_link: 0,
424                sh_info: 0,
425                sh_addralign: 2 << 8,
426                sh_entsize: 0,
427            }
428        }
429        /// Returns this section header's file offset range,
430        /// if the section occupies space in fhe file.
431        pub fn file_range(&self) -> Option<Range<usize>> {
432            // Sections with type SHT_NOBITS have no data in the file itself,
433            // they only exist in memory.
434            if self.sh_type == SHT_NOBITS {
435                None
436            } else {
437                Some(self.sh_offset as usize..(self.sh_offset as usize).saturating_add(self.sh_size as usize))
438            }
439        }
440        /// Returns this section header's virtual memory range
441        pub fn vm_range(&self) -> Range<usize> {
442            self.sh_addr as usize..(self.sh_addr as usize).saturating_add(self.sh_size as usize)
443        }
444        /// Parse `count` section headers from `bytes` at `offset`, using the given `ctx`
445        #[cfg(feature = "endian_fd")]
446        pub fn parse(bytes: &[u8], mut offset: usize, mut count: usize, ctx: Ctx) -> error::Result<Vec<SectionHeader>> {
447            use scroll::Pread;
448            // Zero offset means no section headers, not even the null section header.
449            if offset == 0 {
450                return Ok(Vec::new());
451            }
452            let empty_sh = bytes.gread_with::<SectionHeader>(&mut offset, ctx)?;
453            if count == 0_usize {
454                // Zero count means either no section headers if offset is also zero (checked
455                // above), or the number of section headers overflows SHN_LORESERVE, in which
456                // case the count is stored in the sh_size field of the null section header.
457                count = empty_sh.sh_size as usize;
458            }
459
460            // Sanity check to avoid OOM
461            if count > bytes.len() / Self::size(ctx) {
462                return Err(error::Error::BufferTooShort(count, "section headers"));
463            }
464            let mut section_headers = Vec::with_capacity(count);
465            section_headers.push(empty_sh);
466            for _ in 1..count {
467                let shdr = bytes.gread_with(&mut offset, ctx)?;
468                section_headers.push(shdr);
469            }
470            Ok(section_headers)
471        }
472        pub fn check_size(&self, size: usize) -> error::Result<()> {
473            if self.sh_type == SHT_NOBITS {
474                return Ok(());
475            }
476            let (end, overflow) = self.sh_offset.overflowing_add(self.sh_size);
477            if overflow || end > size as u64 {
478                let message = format!("Section {} size ({}) + offset ({}) is out of bounds. Overflowed: {}",
479                    self.sh_name, self.sh_offset, self.sh_size, overflow);
480                return Err(error::Error::Malformed(message));
481            }
482            let (_, overflow) = self.sh_addr.overflowing_add(self.sh_size);
483            if overflow {
484                let message = format!("Section {} size ({}) + addr ({}) is out of bounds. Overflowed: {}",
485                    self.sh_name, self.sh_addr, self.sh_size, overflow);
486                return Err(error::Error::Malformed(message));
487            }
488            Ok(())
489        }
490        pub fn is_relocation(&self) -> bool {
491            self.sh_type == SHT_RELA
492        }
493        pub fn is_executable(&self) -> bool {
494            self.is_alloc() && self.sh_flags as u32 & SHF_EXECINSTR == SHF_EXECINSTR
495        }
496        pub fn is_writable(&self) -> bool {
497            self.is_alloc() && self.sh_flags as u32 & SHF_WRITE == SHF_WRITE
498        }
499        pub fn is_alloc(&self) -> bool {
500            self.sh_flags as u32 & SHF_ALLOC == SHF_ALLOC
501        }
502    }
503
504    impl fmt::Debug for SectionHeader {
505        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
506            f.debug_struct("SectionHeader")
507                .field("sh_name", &self.sh_name)
508                .field("sh_type", &sht_to_str(self.sh_type))
509                .field("sh_flags", &format_args!("0x{:x}", self.sh_flags))
510                .field("sh_addr", &format_args!("0x{:x}", self.sh_addr))
511                .field("sh_offset", &format_args!("0x{:x}", self.sh_offset))
512                .field("sh_size", &format_args!("0x{:x}", self.sh_size))
513                .field("sh_link", &format_args!("0x{:x}", self.sh_link))
514                .field("sh_info", &format_args!("0x{:x}", self.sh_info))
515                .field("sh_addralign", &format_args!("0x{:x}", self.sh_addralign))
516                .field("sh_entsize", &format_args!("0x{:x}", self.sh_entsize))
517                .finish()
518        }
519    }
520
521    impl ctx::SizeWith<Ctx> for SectionHeader {
522        fn size_with( &Ctx { container, .. }: &Ctx) -> usize {
523            match container {
524                Container::Little => {
525                    section_header32::SIZEOF_SHDR
526                },
527                Container::Big => {
528                    section_header64::SIZEOF_SHDR
529                },
530            }
531        }
532    }
533
534    impl<'a> ctx::TryFromCtx<'a, Ctx> for SectionHeader {
535        type Error = crate::error::Error;
536        fn try_from_ctx(bytes: &'a [u8], Ctx {container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> {
537            use scroll::Pread;
538            let res = match container {
539                Container::Little => {
540                    (bytes.pread_with::<section_header32::SectionHeader>(0, le)?.into(), section_header32::SIZEOF_SHDR)
541                },
542                Container::Big => {
543                    (bytes.pread_with::<section_header64::SectionHeader>(0, le)?.into(), section_header64::SIZEOF_SHDR)
544                }
545            };
546            Ok(res)
547        }
548    }
549
550    impl ctx::TryIntoCtx<Ctx> for SectionHeader {
551        type Error = crate::error::Error;
552        fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result<usize, Self::Error> {
553            use scroll::Pwrite;
554            match container {
555                Container::Little => {
556                    let shdr: section_header32::SectionHeader = self.into();
557                    Ok(bytes.pwrite_with(shdr, 0, le)?)
558                },
559                Container::Big => {
560                    let shdr: section_header64::SectionHeader = self.into();
561                    Ok(bytes.pwrite_with(shdr, 0, le)?)
562                }
563            }
564        }
565    }
566    impl ctx::IntoCtx<Ctx> for SectionHeader {
567        fn into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) {
568            use scroll::Pwrite;
569            match container {
570                Container::Little => {
571                    let shdr: section_header32::SectionHeader = self.into();
572                    bytes.pwrite_with(shdr, 0, le).unwrap();
573                },
574                Container::Big => {
575                    let shdr: section_header64::SectionHeader = self.into();
576                    bytes.pwrite_with(shdr, 0, le).unwrap();
577                }
578            }
579        }
580    }
581} // end if_alloc