-
Notifications
You must be signed in to change notification settings - Fork 188
feat: install name tool style macho writing #509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c580304
09adbca
386deda
9bb1120
7cf34a6
6d706e8
01392b1
b8a22ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
||
| #[repr(C)] | ||
| #[derive(Clone, Copy, Default, Pread, Pwrite, SizeWith)] | ||
|
|
@@ -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 { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
m4b marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -224,7 +224,7 @@ pub struct DylibCommand { | |
| pub dylib: Dylib, | ||
| } | ||
|
|
||
| pub const SIZEOF_DYLIB_COMMAND: usize = 20; | ||
| pub const SIZEOF_DYLIB_COMMAND: usize = 24; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
@@ -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); | ||
m4b marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // 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); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
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