diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/goblin/src/mach/header.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/goblin/src/mach/header.rs')
-rw-r--r-- | third_party/rust/goblin/src/mach/header.rs | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/mach/header.rs b/third_party/rust/goblin/src/mach/header.rs new file mode 100644 index 0000000000..df8731f111 --- /dev/null +++ b/third_party/rust/goblin/src/mach/header.rs @@ -0,0 +1,438 @@ +//! A header contains minimal architecture information, the binary kind, the number of load commands, as well as an endianness hint + +use core::fmt; +use plain::Plain; +use scroll::ctx; +use scroll::ctx::SizeWith; +use scroll::{Pread, Pwrite, SizeWith}; + +use crate::container::{self, Container}; +use crate::error; +use crate::mach::constants::cputype::{CpuSubType, CpuType, CPU_SUBTYPE_MASK}; + +// Constants for the flags field of the mach_header +/// the object file has no undefined references +pub const MH_NOUNDEFS: u32 = 0x1; +/// the object file is the output of an incremental link against a base file and can't be +/// link edited again +pub const MH_INCRLINK: u32 = 0x2; +/// the object file is input for the dynamic linker and can't be staticly link edited again +pub const MH_DYLDLINK: u32 = 0x4; +/// the object file's undefined references are bound by the dynamic linker when loaded. +pub const MH_BINDATLOAD: u32 = 0x8; +/// the file has its dynamic undefined references prebound. +pub const MH_PREBOUND: u32 = 0x10; +/// the file has its read-only and read-write segments split +pub const MH_SPLIT_SEGS: u32 = 0x20; +/// the shared library init routine is to be run lazily via catching memory faults to its writeable +/// segments (obsolete) +pub const MH_LAZY_INIT: u32 = 0x40; +/// the image is using two-level name space bindings +pub const MH_TWOLEVEL: u32 = 0x80; +/// the executable is forcing all images to use flat name space bindings +pub const MH_FORCE_FLAT: u32 = 0x100; +/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the +/// two-level namespace hints can always be used. +pub const MH_NOMULTIDEFS: u32 = 0x200; +/// do not have dyld notify the prebinding agent about this executable +pub const MH_NOFIXPREBINDING: u32 = 0x400; +/// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. +pub const MH_PREBINDABLE: u32 = 0x800; +/// indicates that this binary binds to all two-level namespace modules of its dependent libraries. +/// Only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. +pub const MH_ALLMODSBOUND: u32 = 0x1000; +/// safe to divide up the sections into sub-sections via symbols for dead code stripping +pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000; +/// the binary has been canonicalized via the unprebind operation +pub const MH_CANONICAL: u32 = 0x4000; +/// the final linked image contains external weak symbols +pub const MH_WEAK_DEFINES: u32 = 0x8000; +/// the final linked image uses weak symbols +pub const MH_BINDS_TO_WEAK: u32 = 0x10000; +/// When this bit is set, all stacks in the task will be given stack execution privilege. +/// Only used in MH_EXECUTE filetypes. +pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000; +/// When this bit is set, the binary declares it is safe for use in processes with uid zero +pub const MH_ROOT_SAFE: u32 = 0x40000; +/// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true +pub const MH_SETUID_SAFE: u32 = 0x80000; +/// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to +/// see if any are re-exported +pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x0010_0000; +/// When this bit is set, the OS will load the main executable at a random address. +/// Only used in MH_EXECUTE filetypes. +pub const MH_PIE: u32 = 0x0020_0000; +/// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker +/// will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being +/// referenced from the dylib. +pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x0040_0000; +/// Contains a section of type S_THREAD_LOCAL_VARIABLES +pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x0080_0000; +/// When this bit is set, the OS will run the main executable with a non-executable heap even on +/// platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. +pub const MH_NO_HEAP_EXECUTION: u32 = 0x0100_0000; + +// TODO: verify this number is correct, it was previously 0x02000000 which could indicate a typo/data entry error +/// The code was linked for use in an application extension. +pub const MH_APP_EXTENSION_SAFE: u32 = 0x0200_0000; + +#[inline(always)] +pub fn flag_to_str(flag: u32) -> &'static str { + match flag { + MH_NOUNDEFS => "MH_NOUNDEFS", + MH_INCRLINK => "MH_INCRLINK", + MH_DYLDLINK => "MH_DYLDLINK", + MH_BINDATLOAD => "MH_BINDATLOAD", + MH_PREBOUND => "MH_PREBOUND", + MH_SPLIT_SEGS => "MH_SPLIT_SEGS", + MH_LAZY_INIT => "MH_LAZY_INIT", + MH_TWOLEVEL => "MH_TWOLEVEL", + MH_FORCE_FLAT => "MH_FORCE_FLAT", + MH_NOMULTIDEFS => "MH_NOMULTIDEFS", + MH_NOFIXPREBINDING => "MH_NOFIXPREBINDING", + MH_PREBINDABLE => "MH_PREBINDABLE ", + MH_ALLMODSBOUND => "MH_ALLMODSBOUND", + MH_SUBSECTIONS_VIA_SYMBOLS => "MH_SUBSECTIONS_VIA_SYMBOLS", + MH_CANONICAL => "MH_CANONICAL", + MH_WEAK_DEFINES => "MH_WEAK_DEFINES", + MH_BINDS_TO_WEAK => "MH_BINDS_TO_WEAK", + MH_ALLOW_STACK_EXECUTION => "MH_ALLOW_STACK_EXECUTION", + MH_ROOT_SAFE => "MH_ROOT_SAFE", + MH_SETUID_SAFE => "MH_SETUID_SAFE", + MH_NO_REEXPORTED_DYLIBS => "MH_NO_REEXPORTED_DYLIBS", + MH_PIE => "MH_PIE", + MH_DEAD_STRIPPABLE_DYLIB => "MH_DEAD_STRIPPABLE_DYLIB", + MH_HAS_TLV_DESCRIPTORS => "MH_HAS_TLV_DESCRIPTORS", + MH_NO_HEAP_EXECUTION => "MH_NO_HEAP_EXECUTION", + MH_APP_EXTENSION_SAFE => "MH_APP_EXTENSION_SAFE", + _ => "UNKNOWN FLAG", + } +} + +/// Mach Header magic constant +pub const MH_MAGIC: u32 = 0xfeed_face; +pub const MH_CIGAM: u32 = 0xcefa_edfe; +/// Mach Header magic constant for 64-bit +pub const MH_MAGIC_64: u32 = 0xfeed_facf; +pub const MH_CIGAM_64: u32 = 0xcffa_edfe; + +// Constants for the filetype field of the mach_header +/// relocatable object file +pub const MH_OBJECT: u32 = 0x1; +/// demand paged executable file +pub const MH_EXECUTE: u32 = 0x2; +/// fixed VM shared library file +pub const MH_FVMLIB: u32 = 0x3; +/// core file +pub const MH_CORE: u32 = 0x4; +/// preloaded executable file +pub const MH_PRELOAD: u32 = 0x5; +/// dynamically bound shared library +pub const MH_DYLIB: u32 = 0x6; +/// dynamic link editor +pub const MH_DYLINKER: u32 = 0x7; +/// dynamically bound bundle file +pub const MH_BUNDLE: u32 = 0x8; +/// shared library stub for static linking only, no section contents +pub const MH_DYLIB_STUB: u32 = 0x9; +/// companion file with only debug sections +pub const MH_DSYM: u32 = 0xa; +/// x86_64 kexts +pub const MH_KEXT_BUNDLE: u32 = 0xb; +/// set of mach-o's +pub const MH_FILESET: u32 = 0xc; + +pub fn filetype_to_str(filetype: u32) -> &'static str { + match filetype { + MH_OBJECT => "OBJECT", + MH_EXECUTE => "EXECUTE", + MH_FVMLIB => "FVMLIB", + MH_CORE => "CORE", + MH_PRELOAD => "PRELOAD", + MH_DYLIB => "DYLIB", + MH_DYLINKER => "DYLINKER", + MH_BUNDLE => "BUNDLE", + MH_DYLIB_STUB => "DYLIB_STUB", + MH_DSYM => "DSYM", + MH_KEXT_BUNDLE => "KEXT_BUNDLE", + MH_FILESET => "FILESET", + _ => "UNKNOWN FILETYPE", + } +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)] +/// A 32-bit Mach-o header +pub struct Header32 { + /// mach magic number identifier + pub magic: u32, + /// cpu specifier + pub cputype: u32, + /// machine specifier + pub cpusubtype: u32, + /// type of file + pub filetype: u32, + /// number of load commands + pub ncmds: u32, + /// the size of all the load commands + pub sizeofcmds: u32, + /// flags + pub flags: u32, +} + +pub const SIZEOF_HEADER_32: usize = 0x1c; + +unsafe impl Plain for Header32 {} + +impl Header32 { + /// Transmutes the given byte array into the corresponding 32-bit Mach-o header + pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_32]) -> &Self { + plain::from_bytes(bytes).unwrap() + } + pub fn size(&self) -> usize { + SIZEOF_HEADER_32 + } +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug, Pread, Pwrite, SizeWith)] +/// A 64-bit Mach-o header +pub struct Header64 { + /// mach magic number identifier + pub magic: u32, + /// cpu specifier + pub cputype: u32, + /// machine specifier + pub cpusubtype: u32, + /// type of file + pub filetype: u32, + /// number of load commands + pub ncmds: u32, + /// the size of all the load commands + pub sizeofcmds: u32, + /// flags + pub flags: u32, + pub reserved: u32, +} + +unsafe impl Plain for Header64 {} + +pub const SIZEOF_HEADER_64: usize = 32; + +impl Header64 { + /// Transmutes the given byte array into the corresponding 64-bit Mach-o header + pub fn from_bytes(bytes: &[u8; SIZEOF_HEADER_64]) -> &Self { + plain::from_bytes(bytes).unwrap() + } + pub fn size(&self) -> usize { + SIZEOF_HEADER_64 + } +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +/// Generic sized header +pub struct Header { + pub magic: u32, + pub cputype: u32, + pub cpusubtype: u32, + /// type of file + pub filetype: u32, + /// number of load commands + pub ncmds: usize, + /// the size of all the load commands + pub sizeofcmds: u32, + /// flags + pub flags: u32, + pub reserved: u32, +} + +impl fmt::Debug for Header { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Header") + .field("magic", &format_args!("0x{:x}", self.magic)) + .field("cputype", &self.cputype()) + .field("cpusubtype", &format_args!("0x{:x}", self.cpusubtype())) + .field("filetype", &filetype_to_str(self.filetype)) + .field("ncmds", &self.ncmds) + .field("sizeofcmds", &self.sizeofcmds) + .field("flags", &format_args!("0x{:x}", self.flags)) + .field("reserved", &format_args!("0x{:x}", self.reserved)) + .finish() + } +} + +impl From<Header32> for Header { + fn from(header: Header32) -> Self { + Header { + magic: header.magic, + cputype: header.cputype, + cpusubtype: header.cpusubtype, + filetype: header.filetype, + ncmds: header.ncmds as usize, + sizeofcmds: header.sizeofcmds, + flags: header.flags, + reserved: 0, + } + } +} + +impl From<Header> for Header32 { + fn from(header: Header) -> Self { + Header32 { + magic: header.magic, + cputype: header.cputype, + cpusubtype: header.cpusubtype, + filetype: header.filetype, + ncmds: header.ncmds as u32, + sizeofcmds: header.sizeofcmds, + flags: header.flags, + } + } +} + +impl From<Header64> for Header { + fn from(header: Header64) -> Self { + Header { + magic: header.magic, + cputype: header.cputype, + cpusubtype: header.cpusubtype, + filetype: header.filetype, + ncmds: header.ncmds as usize, + sizeofcmds: header.sizeofcmds, + flags: header.flags, + reserved: header.reserved, + } + } +} + +impl From<Header> for Header64 { + fn from(header: Header) -> Self { + Header64 { + magic: header.magic, + cputype: header.cputype, + cpusubtype: header.cpusubtype, + filetype: header.filetype, + ncmds: header.ncmds as u32, + sizeofcmds: header.sizeofcmds, + flags: header.flags, + reserved: header.reserved, + } + } +} + +impl Header { + pub fn new(ctx: container::Ctx) -> Self { + let mut header = Header::default(); + header.magic = if ctx.is_big() { MH_MAGIC_64 } else { MH_MAGIC }; + header + } + /// Returns the cpu type + pub fn cputype(&self) -> CpuType { + self.cputype + } + /// Returns the cpu subtype with the capabilities removed + pub fn cpusubtype(&self) -> CpuSubType { + self.cpusubtype & !CPU_SUBTYPE_MASK + } + /// Returns the capabilities of the CPU + pub fn cpu_caps(&self) -> u32 { + (self.cpusubtype & CPU_SUBTYPE_MASK) >> 24 + } +} + +impl ctx::SizeWith<container::Ctx> for Header { + fn size_with(container: &container::Ctx) -> usize { + match container.container { + Container::Little => SIZEOF_HEADER_32, + Container::Big => SIZEOF_HEADER_64, + } + } +} + +impl ctx::SizeWith<Container> for Header { + fn size_with(container: &Container) -> usize { + match container { + Container::Little => SIZEOF_HEADER_32, + Container::Big => SIZEOF_HEADER_64, + } + } +} + +impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Header { + type Error = crate::error::Error; + fn try_from_ctx( + bytes: &'a [u8], + container::Ctx { le, container }: container::Ctx, + ) -> error::Result<(Self, usize)> { + let size = bytes.len(); + if size < SIZEOF_HEADER_32 || size < SIZEOF_HEADER_64 { + let error = + error::Error::Malformed("bytes size is smaller than a Mach-o header".into()); + Err(error) + } else { + match container { + Container::Little => { + let header = bytes.pread_with::<Header32>(0, le)?; + Ok((Header::from(header), SIZEOF_HEADER_32)) + } + Container::Big => { + let header = bytes.pread_with::<Header64>(0, le)?; + Ok((Header::from(header), SIZEOF_HEADER_64)) + } + } + } + } +} + +impl ctx::TryIntoCtx<container::Ctx> for Header { + type Error = crate::error::Error; + fn try_into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) -> error::Result<usize> { + match ctx.container { + Container::Little => { + bytes.pwrite_with(Header32::from(self), 0, ctx.le)?; + } + Container::Big => { + bytes.pwrite_with(Header64::from(self), 0, ctx.le)?; + } + }; + Ok(Header::size_with(&ctx)) + } +} + +impl ctx::IntoCtx<container::Ctx> for Header { + fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) { + bytes.pwrite_with(self, 0, ctx).unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem::size_of; + + #[test] + fn test_parse_armv7_header() { + use crate::mach::constants::cputype::CPU_TYPE_ARM; + const CPU_SUBTYPE_ARM_V7: u32 = 9; + use super::Header; + use crate::container::{Container, Ctx, Endian}; + use scroll::Pread; + let bytes = b"\xce\xfa\xed\xfe\x0c\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x06\x00\x00\x00\x8c\r\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x18\x00\x00\x00\xe0\xf7B\xbb\x1c\xf50w\xa6\xf7u\xa3\xba("; + let header: Header = bytes + .pread_with(0, Ctx::new(Container::Little, Endian::Little)) + .unwrap(); + assert_eq!(header.cputype, CPU_TYPE_ARM); + assert_eq!(header.cpusubtype, CPU_SUBTYPE_ARM_V7); + } + + #[test] + fn sizeof_header32() { + assert_eq!(SIZEOF_HEADER_32, size_of::<Header32>()); + } + + #[test] + fn sizeof_header64() { + assert_eq!(SIZEOF_HEADER_64, size_of::<Header64>()); + } +} |