diff options
Diffstat (limited to 'third_party/rust/goblin/src/elf/section_header.rs')
-rw-r--r-- | third_party/rust/goblin/src/elf/section_header.rs | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/elf/section_header.rs b/third_party/rust/goblin/src/elf/section_header.rs new file mode 100644 index 0000000000..3bd891cc46 --- /dev/null +++ b/third_party/rust/goblin/src/elf/section_header.rs @@ -0,0 +1,581 @@ +macro_rules! elf_section_header { + ($size:ident) => { + // XXX: Do not import scroll traits here. + // See: https://github.com/rust-lang/rust/issues/65090#issuecomment-538668155 + + #[repr(C)] + #[derive(Copy, Clone, Eq, PartialEq, Default)] + #[cfg_attr( + feature = "alloc", + derive(scroll::Pread, scroll::Pwrite, scroll::SizeWith) + )] + /// Section Headers are typically used by humans and static linkers for additional information or how to relocate the object + /// + /// **NOTE** section headers are strippable from a binary without any loss of portability/executability; _do not_ rely on them being there! + pub struct SectionHeader { + /// Section name (string tbl index) + pub sh_name: u32, + /// Section type + pub sh_type: u32, + /// Section flags + pub sh_flags: $size, + /// Section virtual addr at execution + pub sh_addr: $size, + /// Section file offset + pub sh_offset: $size, + /// Section size in bytes + pub sh_size: $size, + /// Link to another section + pub sh_link: u32, + /// Additional section information + pub sh_info: u32, + /// Section alignment + pub sh_addralign: $size, + /// Entry size if section holds table + pub sh_entsize: $size, + } + + use plain; + // Declare that this is a plain type. + unsafe impl plain::Plain for SectionHeader {} + + impl ::core::fmt::Debug for SectionHeader { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("SectionHeader") + .field("sh_name", &self.sh_name) + .field("sh_type", &sht_to_str(self.sh_type)) + .field("sh_flags", &format_args!("0x{:x}", self.sh_flags)) + .field("sh_addr", &format_args!("0x{:x}", self.sh_addr)) + .field("sh_offset", &format_args!("0x{:x}", self.sh_offset)) + .field("sh_size", &format_args!("0x{:x}", self.sh_size)) + .field("sh_link", &format_args!("0x{:x}", self.sh_link)) + .field("sh_info", &format_args!("0x{:x}", self.sh_info)) + .field("sh_addralign", &format_args!("0x{:x}", self.sh_addralign)) + .field("sh_entsize", &format_args!("0x{:x}", self.sh_entsize)) + .finish() + } + } + }; +} + +/// Undefined section. +pub const SHN_UNDEF: u32 = 0; +/// Start of reserved indices. +pub const SHN_LORESERVE: u32 = 0xff00; +/// Start of processor-specific. +pub const SHN_LOPROC: u32 = 0xff00; +/// Order section before all others (Solaris). +pub const SHN_BEFORE: u32 = 0xff00; +/// Order section after all others (Solaris). +pub const SHN_AFTER: u32 = 0xff01; +/// End of processor-specific. +pub const SHN_HIPROC: u32 = 0xff1f; +/// Start of OS-specific. +pub const SHN_LOOS: u32 = 0xff20; +/// End of OS-specific. +pub const SHN_HIOS: u32 = 0xff3f; +/// Associated symbol is absolute. +pub const SHN_ABS: u32 = 0xfff1; +/// Associated symbol is common. +pub const SHN_COMMON: u32 = 0xfff2; +/// Index is in extra table. +pub const SHN_XINDEX: u32 = 0xffff; +/// End of reserved indices. +pub const SHN_HIRESERVE: u32 = 0xffff; + +// === Legal values for sh_type (section type). === +/// Section header table entry unused. +pub const SHT_NULL: u32 = 0; +/// Program data. +pub const SHT_PROGBITS: u32 = 1; +/// Symbol table. +pub const SHT_SYMTAB: u32 = 2; +/// String table. +pub const SHT_STRTAB: u32 = 3; +/// Relocation entries with addends. +pub const SHT_RELA: u32 = 4; +/// Symbol hash table. +pub const SHT_HASH: u32 = 5; +/// Dynamic linking information. +pub const SHT_DYNAMIC: u32 = 6; +/// Notes. +pub const SHT_NOTE: u32 = 7; +/// Program space with no data (bss). +pub const SHT_NOBITS: u32 = 8; +/// Relocation entries, no addends. +pub const SHT_REL: u32 = 9; +/// Reserved. +pub const SHT_SHLIB: u32 = 10; +/// Dynamic linker symbol table. +pub const SHT_DYNSYM: u32 = 11; +/// Array of constructors. +pub const SHT_INIT_ARRAY: u32 = 14; +/// Array of destructors. +pub const SHT_FINI_ARRAY: u32 = 15; +/// Array of pre-constructors. +pub const SHT_PREINIT_ARRAY: u32 = 16; +/// Section group. +pub const SHT_GROUP: u32 = 17; +/// Extended section indeces. +pub const SHT_SYMTAB_SHNDX: u32 = 18; +/// Number of defined types. +pub const SHT_NUM: u32 = 19; +/// Start OS-specific. +pub const SHT_LOOS: u32 = 0x6000_0000; +/// Object attributes. +pub const SHT_GNU_ATTRIBUTES: u32 = 0x6fff_fff5; +/// GNU-style hash table. +pub const SHT_GNU_HASH: u32 = 0x6fff_fff6; +/// Prelink library list. +pub const SHT_GNU_LIBLIST: u32 = 0x6fff_fff7; +/// Checksum for DSO content. +pub const SHT_CHECKSUM: u32 = 0x6fff_fff8; +/// Sun-specific low bound. +pub const SHT_LOSUNW: u32 = 0x6fff_fffa; +pub const SHT_SUNW_MOVE: u32 = 0x6fff_fffa; +pub const SHT_SUNW_COMDAT: u32 = 0x6fff_fffb; +pub const SHT_SUNW_SYMINFO: u32 = 0x6fff_fffc; +/// Version definition section. +pub const SHT_GNU_VERDEF: u32 = 0x6fff_fffd; +/// Version needs section. +pub const SHT_GNU_VERNEED: u32 = 0x6fff_fffe; +/// Version symbol table. +pub const SHT_GNU_VERSYM: u32 = 0x6fff_ffff; +/// Sun-specific high bound. +pub const SHT_HISUNW: u32 = 0x6fff_ffff; +/// End OS-specific type. +pub const SHT_HIOS: u32 = 0x6fff_ffff; +/// Start of processor-specific. +pub const SHT_LOPROC: u32 = 0x7000_0000; +/// X86-64 unwind information. +pub const SHT_X86_64_UNWIND: u32 = 0x7000_0001; +/// End of processor-specific. +pub const SHT_HIPROC: u32 = 0x7fff_ffff; +/// Start of application-specific. +pub const SHT_LOUSER: u32 = 0x8000_0000; +/// End of application-specific. +pub const SHT_HIUSER: u32 = 0x8fff_ffff; + +// Legal values for sh_flags (section flags) +/// Writable. +pub const SHF_WRITE: u32 = 0x1; +/// Occupies memory during execution. +pub const SHF_ALLOC: u32 = 0x2; +/// Executable. +pub const SHF_EXECINSTR: u32 = 0x4; +/// Might be merged. +pub const SHF_MERGE: u32 = 0x10; +/// Contains nul-terminated strings. +pub const SHF_STRINGS: u32 = 0x20; +/// `sh_info' contains SHT index. +pub const SHF_INFO_LINK: u32 = 0x40; +/// Preserve order after combining. +pub const SHF_LINK_ORDER: u32 = 0x80; +/// Non-standard OS specific handling required. +pub const SHF_OS_NONCONFORMING: u32 = 0x100; +/// Section is member of a group. +pub const SHF_GROUP: u32 = 0x200; +/// Section hold thread-local data. +pub const SHF_TLS: u32 = 0x400; +/// Section with compressed data. +pub const SHF_COMPRESSED: u32 = 0x800; +/// OS-specific.. +pub const SHF_MASKOS: u32 = 0x0ff0_0000; +/// Processor-specific. +pub const SHF_MASKPROC: u32 = 0xf000_0000; +/// Special ordering requirement (Solaris). +pub const SHF_ORDERED: u32 = 1 << 30; +/// Number of "regular" section header flags +pub const SHF_NUM_REGULAR_FLAGS: usize = 12; +/// Section is excluded unless referenced or allocated (Solaris). +pub const SHF_EXCLUDE: u32 = 0x80000000; // 1U << 31 + +pub const SHF_FLAGS: [u32; SHF_NUM_REGULAR_FLAGS] = [ + SHF_WRITE, + SHF_ALLOC, + SHF_EXECINSTR, + SHF_MERGE, + SHF_STRINGS, + SHF_INFO_LINK, + SHF_LINK_ORDER, + SHF_OS_NONCONFORMING, + SHF_GROUP, + SHF_TLS, + SHF_COMPRESSED, + SHF_ORDERED, +]; + +pub fn sht_to_str(sht: u32) -> &'static str { + match sht { + SHT_NULL => "SHT_NULL", + SHT_PROGBITS => "SHT_PROGBITS", + SHT_SYMTAB => "SHT_SYMTAB", + SHT_STRTAB => "SHT_STRTAB", + SHT_RELA => "SHT_RELA", + SHT_HASH => "SHT_HASH", + SHT_DYNAMIC => "SHT_DYNAMIC", + SHT_NOTE => "SHT_NOTE", + SHT_NOBITS => "SHT_NOBITS", + SHT_REL => "SHT_REL", + SHT_SHLIB => "SHT_SHLIB", + SHT_DYNSYM => "SHT_DYNSYM", + SHT_INIT_ARRAY => "SHT_INIT_ARRAY", + SHT_FINI_ARRAY => "SHT_FINI_ARRAY", + SHT_PREINIT_ARRAY => "SHT_PREINIT_ARRAY", + SHT_GROUP => "SHT_GROUP", + SHT_SYMTAB_SHNDX => "SHT_SYMTAB_SHNDX", + SHT_NUM => "SHT_NUM", + SHT_LOOS => "SHT_LOOS", + SHT_GNU_ATTRIBUTES => "SHT_GNU_ATTRIBUTES", + SHT_GNU_HASH => "SHT_GNU_HASH", + SHT_GNU_LIBLIST => "SHT_GNU_LIBLIST", + SHT_CHECKSUM => "SHT_CHECKSUM", + SHT_SUNW_MOVE => "SHT_SUNW_MOVE", + SHT_SUNW_COMDAT => "SHT_SUNW_COMDAT", + SHT_SUNW_SYMINFO => "SHT_SUNW_SYMINFO", + SHT_GNU_VERDEF => "SHT_GNU_VERDEF", + SHT_GNU_VERNEED => "SHT_GNU_VERNEED", + SHT_GNU_VERSYM => "SHT_GNU_VERSYM", + SHT_LOPROC => "SHT_LOPROC", + SHT_X86_64_UNWIND => "SHT_X86_64_UNWIND", + SHT_HIPROC => "SHT_HIPROC", + SHT_LOUSER => "SHT_LOUSER", + SHT_HIUSER => "SHT_HIUSER", + _ => "UNKNOWN_SHT", + } +} + +pub fn shf_to_str(shf: u32) -> &'static str { + match shf { + SHF_WRITE => "SHF_WRITE", + SHF_ALLOC => "SHF_ALLOC", + SHF_EXECINSTR => "SHF_EXECINSTR", + SHF_MERGE => "SHF_MERGE", + SHF_STRINGS => "SHF_STRINGS", + SHF_INFO_LINK => "SHF_INFO_LINK", + SHF_LINK_ORDER => "SHF_LINK_ORDER", + SHF_OS_NONCONFORMING => "SHF_OS_NONCONFORMING", + SHF_GROUP => "SHF_GROUP", + SHF_TLS => "SHF_TLS", + SHF_COMPRESSED => "SHF_COMPRESSED", + //SHF_MASKOS..SHF_MASKPROC => "SHF_OSFLAG", + SHF_ORDERED => "SHF_ORDERED", + _ => "SHF_UNKNOWN", + } +} + +macro_rules! elf_section_header_std_impl { ($size:ty) => { + + #[cfg(test)] + mod tests { + use super::*; + #[test] + fn size_of() { + assert_eq!(::std::mem::size_of::<SectionHeader>(), SIZEOF_SHDR); + } + } + + if_alloc! { + use crate::elf::section_header::SectionHeader as ElfSectionHeader; + + use plain::Plain; + use alloc::vec::Vec; + + if_std! { + use crate::error::Result; + + use std::fs::File; + use std::io::{Read, Seek}; + use std::io::SeekFrom::Start; + } + + impl From<SectionHeader> for ElfSectionHeader { + fn from(sh: SectionHeader) -> Self { + ElfSectionHeader { + sh_name: sh.sh_name as usize, + sh_type: sh.sh_type, + sh_flags: u64::from(sh.sh_flags), + sh_addr: u64::from(sh.sh_addr), + sh_offset: u64::from(sh.sh_offset), + sh_size: u64::from(sh.sh_size), + sh_link: sh.sh_link, + sh_info: sh.sh_info, + sh_addralign: u64::from(sh.sh_addralign), + sh_entsize: u64::from(sh.sh_entsize), + } + } + } + impl From<ElfSectionHeader> for SectionHeader { + fn from(sh: ElfSectionHeader) -> Self { + SectionHeader { + sh_name : sh.sh_name as u32, + sh_type : sh.sh_type, + sh_flags : sh.sh_flags as $size, + sh_addr : sh.sh_addr as $size, + sh_offset : sh.sh_offset as $size, + sh_size : sh.sh_size as $size, + sh_link : sh.sh_link, + sh_info : sh.sh_info, + sh_addralign: sh.sh_addralign as $size, + sh_entsize : sh.sh_entsize as $size, + } + } + } + + impl SectionHeader { + // FIXME: > 65535 sections + pub fn from_bytes(bytes: &[u8], shnum: usize) -> Vec<SectionHeader> { + let mut shdrs = vec![SectionHeader::default(); shnum]; + shdrs.copy_from_bytes(bytes).expect("buffer is too short for given number of entries"); + shdrs + } + + #[cfg(feature = "std")] + // FIXME: > 65535 sections + pub fn from_fd(fd: &mut File, offset: u64, shnum: usize) -> Result<Vec<SectionHeader>> { + let mut shdrs = vec![SectionHeader::default(); shnum]; + fd.seek(Start(offset))?; + unsafe { + fd.read_exact(plain::as_mut_bytes(&mut *shdrs))?; + } + Ok(shdrs) + } + } + } // end if_alloc +};} + +pub mod section_header32 { + pub use crate::elf::section_header::*; + + elf_section_header!(u32); + + pub const SIZEOF_SHDR: usize = 40; + + elf_section_header_std_impl!(u32); +} + +pub mod section_header64 { + + pub use crate::elf::section_header::*; + + elf_section_header!(u64); + + pub const SIZEOF_SHDR: usize = 64; + + elf_section_header_std_impl!(u64); +} + +/////////////////////////////// +// Std/analysis/Unified Structs +/////////////////////////////// + +if_alloc! { + use crate::error; + use core::fmt; + use core::result; + use core::ops::Range; + use scroll::ctx; + use crate::container::{Container, Ctx}; + + #[cfg(feature = "endian_fd")] + use alloc::vec::Vec; + + #[derive(Default, PartialEq, Clone)] + /// A unified SectionHeader - convertable to and from 32-bit and 64-bit variants + pub struct SectionHeader { + /// Section name (string tbl index) + pub sh_name: usize, + /// Section type + pub sh_type: u32, + /// Section flags + pub sh_flags: u64, + /// Section virtual addr at execution + pub sh_addr: u64, + /// Section file offset + pub sh_offset: u64, + /// Section size in bytes + pub sh_size: u64, + /// Link to another section + pub sh_link: u32, + /// Additional section information + pub sh_info: u32, + /// Section alignment + pub sh_addralign: u64, + /// Entry size if section holds table + pub sh_entsize: u64, + } + + impl SectionHeader { + /// Return the size of the underlying program header, given a `container` + #[inline] + pub fn size(ctx: Ctx) -> usize { + use scroll::ctx::SizeWith; + Self::size_with(&ctx) + } + pub fn new() -> Self { + SectionHeader { + sh_name: 0, + sh_type: SHT_PROGBITS, + sh_flags: u64::from(SHF_ALLOC), + sh_addr: 0, + sh_offset: 0, + sh_size: 0, + sh_link: 0, + sh_info: 0, + sh_addralign: 2 << 8, + sh_entsize: 0, + } + } + /// Returns this section header's file offset range, + /// if the section occupies space in fhe file. + pub fn file_range(&self) -> Option<Range<usize>> { + // Sections with type SHT_NOBITS have no data in the file itself, + // they only exist in memory. + if self.sh_type == SHT_NOBITS { + None + } else { + Some(self.sh_offset as usize..(self.sh_offset as usize).saturating_add(self.sh_size as usize)) + } + } + /// Returns this section header's virtual memory range + pub fn vm_range(&self) -> Range<usize> { + self.sh_addr as usize..(self.sh_addr as usize).saturating_add(self.sh_size as usize) + } + /// Parse `count` section headers from `bytes` at `offset`, using the given `ctx` + #[cfg(feature = "endian_fd")] + pub fn parse(bytes: &[u8], mut offset: usize, mut count: usize, ctx: Ctx) -> error::Result<Vec<SectionHeader>> { + use scroll::Pread; + // Zero offset means no section headers, not even the null section header. + if offset == 0 { + return Ok(Vec::new()); + } + let empty_sh = bytes.gread_with::<SectionHeader>(&mut offset, ctx)?; + if count == 0 as usize { + // Zero count means either no section headers if offset is also zero (checked + // above), or the number of section headers overflows SHN_LORESERVE, in which + // case the count is stored in the sh_size field of the null section header. + count = empty_sh.sh_size as usize; + } + + // Sanity check to avoid OOM + if count > bytes.len() / Self::size(ctx) { + return Err(error::Error::BufferTooShort(count, "section headers")); + } + let mut section_headers = Vec::with_capacity(count); + section_headers.push(empty_sh); + for _ in 1..count { + let shdr = bytes.gread_with(&mut offset, ctx)?; + section_headers.push(shdr); + } + Ok(section_headers) + } + pub fn check_size(&self, size: usize) -> error::Result<()> { + if self.sh_type == SHT_NOBITS { + return Ok(()); + } + let (end, overflow) = self.sh_offset.overflowing_add(self.sh_size); + if overflow || end > size as u64 { + let message = format!("Section {} size ({}) + offset ({}) is out of bounds. Overflowed: {}", + self.sh_name, self.sh_offset, self.sh_size, overflow); + return Err(error::Error::Malformed(message)); + } + let (_, overflow) = self.sh_addr.overflowing_add(self.sh_size); + if overflow { + let message = format!("Section {} size ({}) + addr ({}) is out of bounds. Overflowed: {}", + self.sh_name, self.sh_addr, self.sh_size, overflow); + return Err(error::Error::Malformed(message)); + } + Ok(()) + } + pub fn is_relocation(&self) -> bool { + self.sh_type == SHT_RELA + } + pub fn is_executable(&self) -> bool { + self.is_alloc() && self.sh_flags as u32 & SHF_EXECINSTR == SHF_EXECINSTR + } + pub fn is_writable(&self) -> bool { + self.is_alloc() && self.sh_flags as u32 & SHF_WRITE == SHF_WRITE + } + pub fn is_alloc(&self) -> bool { + self.sh_flags as u32 & SHF_ALLOC == SHF_ALLOC + } + } + + impl fmt::Debug for SectionHeader { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SectionHeader") + .field("sh_name", &self.sh_name) + .field("sh_type", &sht_to_str(self.sh_type)) + .field("sh_flags", &format_args!("0x{:x}", self.sh_flags)) + .field("sh_addr", &format_args!("0x{:x}", self.sh_addr)) + .field("sh_offset", &format_args!("0x{:x}", self.sh_offset)) + .field("sh_size", &format_args!("0x{:x}", self.sh_size)) + .field("sh_link", &format_args!("0x{:x}", self.sh_link)) + .field("sh_info", &format_args!("0x{:x}", self.sh_info)) + .field("sh_addralign", &format_args!("0x{:x}", self.sh_addralign)) + .field("sh_entsize", &format_args!("0x{:x}", self.sh_entsize)) + .finish() + } + } + + impl ctx::SizeWith<Ctx> for SectionHeader { + fn size_with( &Ctx { container, .. }: &Ctx) -> usize { + match container { + Container::Little => { + section_header32::SIZEOF_SHDR + }, + Container::Big => { + section_header64::SIZEOF_SHDR + }, + } + } + } + + impl<'a> ctx::TryFromCtx<'a, Ctx> for SectionHeader { + type Error = crate::error::Error; + fn try_from_ctx(bytes: &'a [u8], Ctx {container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> { + use scroll::Pread; + let res = match container { + Container::Little => { + (bytes.pread_with::<section_header32::SectionHeader>(0, le)?.into(), section_header32::SIZEOF_SHDR) + }, + Container::Big => { + (bytes.pread_with::<section_header64::SectionHeader>(0, le)?.into(), section_header64::SIZEOF_SHDR) + } + }; + Ok(res) + } + } + + impl ctx::TryIntoCtx<Ctx> for SectionHeader { + type Error = crate::error::Error; + fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result<usize, Self::Error> { + use scroll::Pwrite; + match container { + Container::Little => { + let shdr: section_header32::SectionHeader = self.into(); + Ok(bytes.pwrite_with(shdr, 0, le)?) + }, + Container::Big => { + let shdr: section_header64::SectionHeader = self.into(); + Ok(bytes.pwrite_with(shdr, 0, le)?) + } + } + } + } + impl ctx::IntoCtx<Ctx> for SectionHeader { + fn into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) { + use scroll::Pwrite; + match container { + Container::Little => { + let shdr: section_header32::SectionHeader = self.into(); + bytes.pwrite_with(shdr, 0, le).unwrap(); + }, + Container::Big => { + let shdr: section_header64::SectionHeader = self.into(); + bytes.pwrite_with(shdr, 0, le).unwrap(); + } + } + } + } +} // end if_alloc |