summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/mach/header.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/goblin/src/mach/header.rs
parentInitial commit. (diff)
downloadfirefox-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.rs438
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>());
+ }
+}