Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/hello_world_fat64
Binary file not shown.
103 changes: 103 additions & 0 deletions src/mach/fat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use scroll::{Pread, Pwrite, SizeWith};

pub const FAT_MAGIC: u32 = 0xcafe_babe;
pub const FAT_CIGAM: u32 = 0xbeba_feca;
/// 64-bit fat magic number (for archives with 64-bit offsets/sizes)
pub const FAT_MAGIC_64: u32 = 0xcafe_babf;
pub const FAT_CIGAM_64: u32 = 0xbfba_feca;
Comment on lines +17 to +18
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are good changes, though I don't think I've ever seen this magic in the wild, though I haven't poked at macho binaries in a while


#[repr(C)]
#[derive(Clone, Copy, Default, Pread, Pwrite, SizeWith)]
Expand Down Expand Up @@ -130,3 +133,103 @@ impl FatArch {
Ok(arch)
}
}

#[repr(C)]
#[derive(Clone, Copy, Default, Pread, Pwrite, SizeWith)]
/// 64-bit version of `FatArch` for fat binaries with large offsets/sizes
/// Uses u64 for offset and size fields
pub struct FatArch64 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so in mod.rs we implement a FatArhcIterator, and SingleArchIterator that returns these FatArch64's; since this patch doesn't add those features, I don't see where this would be used by a client?

Again I've never seen this struct in the wild before, can you upload a binary that has it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m4b thanks for the review. I added a fat64 binary to the assets and a test case. Also happy to remove all of that (could not find a single one on my system to send you, but was able to build one).

/// What kind of CPU this binary is
pub cputype: u32,
pub cpusubtype: u32,
/// Where in the fat binary it starts (64-bit)
pub offset: u64,
/// How big the binary is (64-bit)
pub size: u64,
pub align: u32,
/// Reserved for future use
pub reserved: u32,
}

pub const SIZEOF_FAT_ARCH_64: usize = 32;

impl fmt::Debug for FatArch64 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FatArch64")
.field("cputype", &self.cputype())
.field("cpusubtype", &self.cpusubtype())
.field("offset", &format_args!("{:#x}", &self.offset))
.field("size", &self.size)
.field("align", &self.align)
.finish()
}
}

impl FatArch64 {
/// Get the slice of bytes this header describes from `bytes`
pub fn slice<'a>(&self, bytes: &'a [u8]) -> &'a [u8] {
let start = self.offset as usize;
match start
.checked_add(self.size as usize)
.and_then(|end| bytes.get(start..end))
{
Some(slice) => slice,
None => {
log::warn!("invalid `FatArch64` offset");
&[]
}
}
}

/// 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
}

/// Whether this fat architecture header describes a 64-bit binary
pub fn is_64(&self) -> bool {
(self.cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64
}

/// Parse a `FatArch64` header from `bytes` at `offset`
pub fn parse(bytes: &[u8], offset: usize) -> error::Result<Self> {
let arch = bytes.pread_with::<FatArch64>(offset, scroll::BE)?;
Ok(arch)
}
}

impl From<FatArch64> for FatArch {
/// Convert to a 32-bit FatArch (may lose precision for large values)
fn from(arch: FatArch64) -> Self {
FatArch {
cputype: arch.cputype,
cpusubtype: arch.cpusubtype,
offset: arch.offset as u32,
size: arch.size as u32,
align: arch.align,
}
}
}

impl From<FatArch> for FatArch64 {
fn from(arch: FatArch) -> Self {
FatArch64 {
cputype: arch.cputype,
cpusubtype: arch.cpusubtype,
offset: arch.offset as u64,
size: arch.size as u64,
align: arch.align,
reserved: 0,
}
}
}
62 changes: 61 additions & 1 deletion src/mach/load_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ pub struct DylibCommand {
pub dylib: Dylib,
}

pub const SIZEOF_DYLIB_COMMAND: usize = 20;
pub const SIZEOF_DYLIB_COMMAND: usize = 24;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to understand why this changed (and perhaps why a test didn't fail when it did?)


/// A dynamically linked shared library may be a subframework of an umbrella
/// framework. If so it will be linked with "-umbrella umbrella_name" where
Expand Down Expand Up @@ -1858,3 +1858,63 @@ impl LoadCommand {
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use core::mem::size_of;

/// Validate that our SIZEOF_* constants match the actual struct sizes.
/// These sizes must match Apple's loader.h definitions exactly for
/// correct binary parsing and generation.
/// Reference: https://github.com/apple/darwin-xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
#[test]
fn test_load_command_struct_sizes() {
// Basic structures
assert_eq!(size_of::<LoadCommandHeader>(), SIZEOF_LOAD_COMMAND);
assert_eq!(size_of::<Section32>(), SIZEOF_SECTION_32);
assert_eq!(size_of::<Section64>(), SIZEOF_SECTION_64);
assert_eq!(size_of::<SegmentCommand32>(), SIZEOF_SEGMENT_COMMAND_32);
assert_eq!(size_of::<SegmentCommand64>(), SIZEOF_SEGMENT_COMMAND_64);

// Dylib structures
// struct dylib: lc_str (4) + timestamp (4) + current_version (4) + compat_version (4) = 16
assert_eq!(size_of::<Dylib>(), SIZEOF_DYLIB);
// struct dylib_command: cmd (4) + cmdsize (4) + dylib (16) = 24
assert_eq!(size_of::<DylibCommand>(), SIZEOF_DYLIB_COMMAND);

// Other load commands
assert_eq!(size_of::<Fvmlib>(), SIZEOF_FVMLIB);
assert_eq!(size_of::<FvmlibCommand>(), SIZEOF_FVMLIB_COMMAND);
assert_eq!(
size_of::<SubFrameworkCommand>(),
SIZEOF_SUB_FRAMEWORK_COMMAND
);
assert_eq!(size_of::<SubClientCommand>(), SIZEOF_SUB_CLIENT_COMMAND);
assert_eq!(size_of::<SubUmbrellaCommand>(), SIZEOF_SUB_UMBRELLA_COMMAND);
assert_eq!(size_of::<SubLibraryCommand>(), SIZEOF_SUB_LIBRARY_COMMAND);
assert_eq!(
size_of::<PreboundDylibCommand>(),
SIZEOF_PREBOUND_DYLIB_COMMAND
);
assert_eq!(size_of::<DylinkerCommand>(), SIZEOF_DYLINKER_COMMAND);
assert_eq!(size_of::<RpathCommand>(), SIZEOF_RPATH_COMMAND);
assert_eq!(
size_of::<LinkeditDataCommand>(),
SIZEOF_LINKEDIT_DATA_COMMAND
);
assert_eq!(size_of::<SymtabCommand>(), SIZEOF_SYMTAB_COMMAND);
assert_eq!(size_of::<DysymtabCommand>(), SIZEOF_DYSYMTAB_COMMAND);
assert_eq!(size_of::<EntryPointCommand>(), SIZEOF_ENTRY_POINT_COMMAND);
assert_eq!(
size_of::<EncryptionInfoCommand32>(),
SIZEOF_ENCRYPTION_INFO_COMMAND_32
);
assert_eq!(
size_of::<EncryptionInfoCommand64>(),
SIZEOF_ENCRYPTION_INFO_COMMAND_64
);
assert_eq!(size_of::<VersionMinCommand>(), SIZEOF_VERSION_MIN_COMMAND);
assert_eq!(size_of::<DyldInfoCommand>(), SIZEOF_DYLIB_INFO_COMMAND);
}
}
Loading