2016-04-21 15:57:02 +03:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Net ;
using Xamarin.Bundler ;
namespace Xamarin
{
public class MachO
{
/* definitions from: /usr/include/mach-o/loader.h */
/* Constant for the magic field of the mach_header (32-bit architectures) */
internal const uint MH_MAGIC = 0xfeedface ; /* the mach magic number */
internal const uint MH_CIGAM = 0xcefaedfe ; /* NXSwapInt(MH_MAGIC) */
/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
internal const uint MH_MAGIC_64 = 0xfeedfacf ; /* the 64-bit mach magic number */
internal const uint MH_CIGAM_64 = 0xcffaedfe ; /* NXSwapInt(MH_MAGIC_64) */
/* definitions from: /usr/include/mach-o/fat.h */
internal const uint FAT_MAGIC = 0xcafebabe ;
internal const uint FAT_CIGAM = 0xbebafeca ; /* NXSwapLong(FAT_MAGIC) */
internal const uint MH_DYLIB = 0x6 ; /* dynamically bound shared library */
public enum Architectures {
None = 0 ,
i386 = 1 ,
ARMv6 = 2 ,
ARMv7 = 4 ,
ARMv7s = 8 ,
ARM64 = 16 ,
x86_64 = 32 ,
}
internal enum LoadCommands : uint
{
//#define LC_REQ_DYLD 0x80000000
ReqDyld = 0x80000000 ,
/ /
// /* Constants for the cmd field of all load commands, the type */
//#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
//#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
//#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
//#define LC_THREAD 0x4 /* thread */
//#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
//#define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */
//#define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */
//#define LC_IDENT 0x8 /* object identification info (obsolete) */
//#define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */
//#define LC_PREPAGE 0xa /* prepage command (internal use) */
//#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */
//#define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */
LoadDylib = 0xc ,
//#define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */
IdDylib = 0xd ,
//#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */
//#define LC_ID_DYLINKER 0xf /* dynamic linker identification */
//#define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */
// /* linked shared library */
//#define LC_ROUTINES 0x11 /* image routines */
//#define LC_SUB_FRAMEWORK 0x12 /* sub framework */
//#define LC_SUB_UMBRELLA 0x13 /* sub umbrella */
//#define LC_SUB_CLIENT 0x14 /* sub client */
//#define LC_SUB_LIBRARY 0x15 /* sub library */
//#define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */
//#define LC_PREBIND_CKSUM 0x17 /* prebind checksum */
/ /
// /*
// * load a dynamically linked shared library that is allowed to be missing
// * (all symbols are weak imported).
// */
//#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
LoadWeakDylib = 0x18 | ReqDyld ,
/ /
//#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be
// mapped */
//#define LC_ROUTINES_64 0x1a /* 64-bit image routines */
//#define LC_UUID 0x1b /* the uuid */
2016-06-28 21:35:05 +03:00
Uuid = 0x1b ,
2016-04-21 15:57:02 +03:00
//#define LC_RPATH (0x1c | LC_REQ_DYLD) /* runpath additions */
//#define LC_CODE_SIGNATURE 0x1d /* local of code signature */
//#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */
//#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
ReexportDylib = 0x1f | ReqDyld ,
//#define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */
//#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */
//#define LC_DYLD_INFO 0x22 /* compressed dyld information */
//#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
//#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
//#define LC_VERSION_MIN_MACOSX 0x24 /* build for MacOSX min OS version */
//#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */
//#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */
//#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat
// like environment variable */
//#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
//#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
//#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */
//#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
//#define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */
//#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
}
internal static uint FromBigEndian ( uint number )
{
return ( ( ( number > > 24 ) & 0xFF )
| ( ( number > > 08 ) & 0xFF00 )
| ( ( number < < 08 ) & 0xFF0000 )
| ( ( number < < 24 ) ) ) ;
}
internal static int FromBigEndian ( int number )
{
return ( ( ( number > > 24 ) & 0xFF )
| ( ( number > > 08 ) & 0xFF00 )
| ( ( number < < 08 ) & 0xFF0000 )
| ( ( number < < 24 ) ) ) ;
}
internal static uint ToBigEndian ( uint number )
{
return ( ( ( number > > 24 ) & 0xFF )
| ( ( number > > 08 ) & 0xFF00 )
| ( ( number < < 08 ) & 0xFF0000 )
| ( ( number < < 24 ) ) ) ;
}
internal static int ToBigEndian ( int number )
{
return ( ( ( number > > 24 ) & 0xFF )
| ( ( number > > 08 ) & 0xFF00 )
| ( ( number < < 08 ) & 0xFF0000 )
| ( ( number < < 24 ) ) ) ;
}
static object ReadFile ( BinaryReader reader , string filename )
{
var magic = reader . ReadUInt32 ( ) ;
reader . BaseStream . Position = 0 ;
switch ( magic ) {
case MH_MAGIC :
case MH_MAGIC_64 :
var mf = new MachOFile ( filename ) ;
mf . Read ( reader ) ;
return mf ;
case FAT_MAGIC : // little-endian fat binary
case FAT_CIGAM : // big-endian fat binary
{
var f = new FatFile ( filename ) ;
f . Read ( reader ) ;
return f ;
}
default :
if ( StaticLibrary . IsStaticLibrary ( reader ) ) {
var sl = new StaticLibrary ( ) ;
sl . Read ( reader ) ;
return sl ;
}
throw new Exception ( string . Format ( "File format not recognized: {0} (magic: 0x{1})" , filename , magic . ToString ( "X" ) ) ) ;
}
}
static object ReadFile ( string filename )
{
using ( var fs = new FileStream ( filename , FileMode . Open , FileAccess . Read , FileShare . Read ) ) {
using ( var reader = new BinaryReader ( fs ) ) {
return ReadFile ( reader , filename ) ;
}
}
}
public static IEnumerable < MachOFile > Read ( string filename )
{
var file = ReadFile ( filename ) ;
var fatfile = file as FatFile ;
if ( fatfile ! = null ) {
foreach ( var ff in fatfile . entries )
yield return ff . entry ;
} else {
var mf = file as MachOFile ;
if ( mf ! = null )
yield return mf ;
else
throw ErrorHelper . CreateError ( 1604 , "File of type {0} is not a MachO file ({1})." , file . GetType ( ) . Name , filename ) ;
}
}
#if MTOUCH
// Removes all architectures from the target file, except for those in 'architectures'.
// This method doesn't do anything if the target file is a thin mach-o file.
// Also it doesn't do anything if the result is an empty file (i.e. none of the
// selected architectures match any of the architectures in the file) - this is
// only because I haven't investigated what would be needed elsewhere in the link
// process when the entire file is removed. FIXME <--.
public static void SelectArchitectures ( string filename , ICollection < Abi > abis )
{
var architectures = GetArchitectures ( abis ) ;
var tmpfile = filename + ".tmp" ;
if ( abis . Count = = 0 )
return ;
using ( var fsr = new FileStream ( filename , FileMode . Open , FileAccess . Read ) ) {
using ( var reader = new BinaryReader ( fsr ) ) {
var file = ReadFile ( filename ) ;
if ( file is MachOFile ) {
Driver . Log ( 2 , "Skipping architecture selecting of '{0}', since it only contains 1 architecture." , filename ) ;
return ;
}
var fatfile = ( FatFile ) file ;
bool any_removed = false ;
// remove architectures we don't want
for ( int i = fatfile . entries . Count - 1 ; i > = 0 ; i - - ) {
var ff = fatfile . entries [ i ] ;
if ( ! architectures . Contains ( ff . entry . Architecture ) ) {
any_removed = true ;
fatfile . entries . RemoveAt ( i ) ;
fatfile . nfat_arch - - ;
Driver . Log ( 2 , "Removing architecture {0} from {1}" , ff . entry . Architecture , filename ) ;
}
}
if ( ! any_removed ) {
Driver . Log ( 2 , "Architecture selection of '{0}' didn't find any architectures to remove." , filename ) ;
return ;
}
if ( fatfile . nfat_arch = = 0 ) {
Driver . Log ( 2 , "Skipping architecture selection of '{0}', none of the selected architectures match any of the architectures in the archive." , filename , architectures [ 0 ] ) ;
return ;
}
if ( fatfile . nfat_arch = = 1 ) {
// Thin file
var entry = fatfile . entries [ 0 ] ;
using ( var fsw = new FileStream ( tmpfile , FileMode . Create , FileAccess . Write ) ) {
using ( var writer = new BinaryWriter ( fsw ) ) {
entry . WriteFile ( writer , reader , entry . offset ) ;
}
}
} else {
// Fat file
// Re-calculate header data
var read_offset = new List < uint > ( fatfile . entries . Count ) ;
read_offset . Add ( fatfile . entries [ 0 ] . offset ) ;
fatfile . entries [ 0 ] . offset = ( uint ) ( 1 < < ( int ) fatfile . entries [ 0 ] . align ) ;
for ( int i = 1 ; i < fatfile . entries . Count ; i + + ) {
read_offset . Add ( fatfile . entries [ i ] . offset ) ;
fatfile . entries [ i ] . offset = fatfile . entries [ i - 1 ] . offset + fatfile . entries [ i - 1 ] . size ;
var alignSize = ( 1 < < ( int ) fatfile . entries [ i ] . align ) ;
var align = ( int ) fatfile . entries [ i ] . offset % alignSize ;
if ( align ! = 0 )
fatfile . entries [ i ] . offset + = ( uint ) ( alignSize - align ) ;
}
// Write out the fat file
using ( var fsw = new FileStream ( tmpfile , FileMode . Create , FileAccess . Write ) ) {
using ( var writer = new BinaryWriter ( fsw ) ) {
// write headers
fatfile . WriteHeaders ( writer ) ;
// write data
for ( int i = 0 ; i < fatfile . entries . Count ; i + + ) {
fatfile . entries [ i ] . Write ( writer , reader , read_offset [ i ] ) ;
}
}
}
}
}
}
File . Delete ( filename ) ;
File . Move ( tmpfile , filename ) ;
}
#endif
static Dictionary < string , IEnumerable < string > > native_dependencies = new Dictionary < string , IEnumerable < string > > ( ) ;
public static IEnumerable < string > GetNativeDependencies ( string libraryName )
{
IEnumerable < string > result ;
lock ( native_dependencies ) {
if ( native_dependencies . TryGetValue ( libraryName , out result ) )
return result ;
}
var macho_files = Read ( libraryName ) ;
var dependencies = new HashSet < string > ( ) ;
foreach ( var macho_file in macho_files ) {
foreach ( var lc in macho_file . load_commands ) {
var dyld_lc = lc as Xamarin . DylibLoadCommand ;
if ( dyld_lc ! = null ) {
dependencies . Add ( dyld_lc . name ) ;
}
}
}
result = dependencies ;
lock ( native_dependencies )
native_dependencies . Add ( libraryName , result ) ;
return result ;
}
#if MTOUCH
public static List < Abi > GetArchitectures ( string file )
{
var result = new List < Abi > ( ) ;
// https://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
using ( var fs = File . OpenRead ( file ) ) {
using ( var reader = new BinaryReader ( fs ) ) {
int magic = reader . ReadInt32 ( ) ;
int architectures ;
switch ( ( uint ) magic ) {
case 0xCAFEBABE : // little-endian fat binary
architectures = reader . ReadInt32 ( ) ;
for ( int i = 0 ; i < architectures ; i + + ) {
result . Add ( GetArch ( reader . ReadInt32 ( ) , reader . ReadInt32 ( ) ) ) ;
// skip to next entry
reader . ReadInt32 ( ) ; // offset
reader . ReadInt32 ( ) ; // size
reader . ReadInt32 ( ) ; // align
}
break ;
case 0xBEBAFECA :
architectures = System . Net . IPAddress . NetworkToHostOrder ( reader . ReadInt32 ( ) ) ;
for ( int i = 0 ; i < architectures ; i + + ) {
result . Add ( GetArch ( System . Net . IPAddress . NetworkToHostOrder ( reader . ReadInt32 ( ) ) , System . Net . IPAddress . NetworkToHostOrder ( reader . ReadInt32 ( ) ) ) ) ;
// skip to next entry
reader . ReadInt32 ( ) ; // offset
reader . ReadInt32 ( ) ; // size
reader . ReadInt32 ( ) ; // align
}
break ;
case 0xFEEDFACE : // little-endian mach-o header
case 0xFEEDFACF : // little-endian 64-big mach-o header
result . Add ( GetArch ( reader . ReadInt32 ( ) , reader . ReadInt32 ( ) ) ) ;
break ;
case 0xCFFAEDFE :
case 0xCEFAEDFE :
result . Add ( GetArch ( System . Net . IPAddress . NetworkToHostOrder ( reader . ReadInt32 ( ) ) , System . Net . IPAddress . NetworkToHostOrder ( reader . ReadInt32 ( ) ) ) ) ;
break ;
default :
Console . WriteLine ( "File '{0}' is neither a Universal binary nor a Mach-O binary (magic: 0x{1})" , file , magic . ToString ( "x" ) ) ;
break ;
}
}
}
return result ;
}
public static List < Architectures > GetArchitectures ( ICollection < Abi > abi )
{
var rv = new List < Architectures > ( abi . Count ) ;
foreach ( var a in abi ) {
rv . Add ( ( Architectures ) ( a & Abi . ArchMask ) ) ;
}
return rv ;
}
static Abi GetArch ( int cputype , int cpusubtype )
{
switch ( cputype ) {
case 12 : // arm
switch ( cpusubtype ) {
case 6 :
return Abi . ARMv6 ;
case 9 :
return Abi . ARMv7 ;
case 11 :
return Abi . ARMv7s ;
2017-02-20 18:03:41 +03:00
case 12 :
return Abi . ARMv7k ;
2016-04-21 15:57:02 +03:00
default :
return Abi . None ;
}
case 12 | 0x01000000 :
return Abi . ARM64 ;
case 7 : // x86
return Abi . i386 ;
case 7 | 0x01000000 : // x64
return Abi . x86_64 ;
}
return Abi . None ;
}
public static bool IsDynamicFramework ( string filename )
{
var f = ReadFile ( filename ) ;
if ( f is StaticLibrary )
return false ;
else if ( f is MachOFile )
return ( ( MachOFile ) f ) . IsDynamicLibrary ;
var fat = f as FatFile ;
if ( fat = = null )
return false ;
foreach ( var entry in fat . entries )
if ( ! entry . IsDynamicLibrary )
return false ;
return true ;
}
#endif
}
public class StaticLibrary
{
internal void Read ( BinaryReader reader )
{
IsStaticLibrary ( reader , throw_if_error : true ) ;
}
public static bool IsStaticLibrary ( BinaryReader reader , bool throw_if_error = false )
{
var pos = reader . BaseStream . Position ;
var bytes = reader . ReadBytes ( 8 ) ;
var rv = bytes [ 0 ] = = '!' & & bytes [ 1 ] = = '<' & & bytes [ 2 ] = = 'a' & & bytes [ 3 ] = = 'r' & & bytes [ 4 ] = = 'c' & & bytes [ 5 ] = = 'h' & & bytes [ 6 ] = = '>' & & bytes [ 7 ] = = 0xa ;
reader . BaseStream . Position = pos ;
if ( throw_if_error & & ! rv )
throw ErrorHelper . CreateError ( 1601 , "Not a Mach-O dynamic library (unknown header '0x{0}'): {1}." , System . Text . Encoding . ASCII . GetString ( bytes , 0 , 7 ) ) ;
return rv ;
}
}
public class MachOFile
{
FatEntry fat_parent ;
string filename ;
public uint magic ;
int _cputype ;
int _cpusubtype ;
uint _filetype ;
uint _ncmds ;
uint _sizeofcmds ;
uint _flags ;
uint _reserved ;
bool is64bitheader ;
public int cputype { get { return is_big_endian ? MachO . ToBigEndian ( _cputype ) : _cputype ; } }
public int cpusubtype { get { return is_big_endian ? MachO . ToBigEndian ( _cpusubtype ) : _cpusubtype ; } }
public uint filetype { get { return is_big_endian ? MachO . ToBigEndian ( _filetype ) : _filetype ; } }
public uint ncmds { get { return is_big_endian ? MachO . ToBigEndian ( _ncmds ) : _ncmds ; } }
public uint sizeofcmds { get { return is_big_endian ? MachO . ToBigEndian ( _sizeofcmds ) : _sizeofcmds ; } }
public uint flags { get { return is_big_endian ? MachO . ToBigEndian ( _flags ) : _flags ; } }
public uint reserved { get { return is_big_endian ? MachO . ToBigEndian ( _reserved ) : _reserved ; } }
public List < LoadCommand > load_commands ;
public MachOFile ( FatEntry parent )
{
fat_parent = parent ;
}
public MachOFile ( string filename )
{
this . filename = filename ;
}
internal bool is_big_endian {
get {
return magic = = MachO . FAT_CIGAM | | magic = = MachO . MH_CIGAM_64 ;
}
}
internal void WriteHeader ( BinaryWriter writer )
{
writer . Write ( magic ) ;
writer . Write ( _cputype ) ;
writer . Write ( _cpusubtype ) ;
writer . Write ( _filetype ) ;
writer . Write ( _ncmds ) ;
writer . Write ( _sizeofcmds ) ;
writer . Write ( _flags ) ;
if ( is64bitheader )
writer . Write ( reserved ) ;
}
internal static bool IsMachOLibrary ( FatEntry fat_entry , BinaryReader reader , bool throw_if_error = false )
{
var pos = reader . BaseStream . Position ;
var magic = reader . ReadUInt32 ( ) ;
var rv = false ;
switch ( magic ) {
case MachO . MH_CIGAM :
case MachO . MH_MAGIC :
case MachO . MH_CIGAM_64 :
case MachO . MH_MAGIC_64 :
rv = true ;
break ;
default :
rv = false ;
break ;
}
reader . BaseStream . Position = pos ;
if ( throw_if_error & & ! rv )
throw ErrorHelper . CreateError ( 1600 , "Not a Mach-O dynamic library (unknown header '0x{0}'): {1}." , magic . ToString ( "x" ) , fat_entry . Parent . Filename ) ;
return rv ;
}
internal void Read ( BinaryReader reader )
{
/* definitions from: /usr/include/mach-o/loader.h */
/ *
* The 32 - bit mach header appears at the very beginning of the object file for
* 32 - bit architectures .
* /
// struct mach_header {
// uint32_t magic; /* mach magic number identifier */
// cpu_type_t cputype; /* cpu specifier */
// cpu_subtype_t cpusubtype; /* machine specifier */
// uint32_t filetype; /* type of file */
// uint32_t ncmds; /* number of load commands */
// uint32_t sizeofcmds; /* the size of all the load commands */
// uint32_t flags; /* flags */
// };
/ *
* The 64 - bit mach header appears at the very beginning of object files for
* 64 - bit architectures .
* /
// struct mach_header_64 {
// uint32_t magic; /* mach magic number identifier */
// cpu_type_t cputype; /* cpu specifier */
// cpu_subtype_t cpusubtype; /* machine specifier */
// uint32_t filetype; /* type of file */
// uint32_t ncmds; /* number of load commands */
// uint32_t sizeofcmds; /* the size of all the load commands */
// uint32_t flags; /* flags */
// uint32_t reserved; /* reserved */
// };
magic = reader . ReadUInt32 ( ) ;
switch ( magic ) {
case MachO . MH_CIGAM :
case MachO . MH_MAGIC :
is64bitheader = false ;
break ;
case MachO . MH_CIGAM_64 :
case MachO . MH_MAGIC_64 :
is64bitheader = true ;
break ;
default :
throw ErrorHelper . CreateError ( 1602 , "Not a Mach-O dynamic library (unknown header '0x{0}'): {1}." , magic . ToString ( "x" ) , fat_parent ! = null ? fat_parent . Parent . Filename : filename ) ;
}
_cputype = reader . ReadInt32 ( ) ;
_cpusubtype = reader . ReadInt32 ( ) ;
_filetype = reader . ReadUInt32 ( ) ;
_ncmds = reader . ReadUInt32 ( ) ;
_sizeofcmds = reader . ReadUInt32 ( ) ;
_flags = reader . ReadUInt32 ( ) ;
if ( is64bitheader )
_reserved = reader . ReadUInt32 ( ) ;
var cmds = new List < LoadCommand > ( ( int ) ncmds ) ;
for ( int i = 0 ; i < ncmds ; i + + ) {
var cmd = ( MachO . LoadCommands ) reader . ReadUInt32 ( ) ;
reader . BaseStream . Position - = 4 ;
LoadCommand lc ;
switch ( cmd ) {
case MachO . LoadCommands . LoadDylib :
case MachO . LoadCommands . LoadWeakDylib :
case MachO . LoadCommands . ReexportDylib :
var dlc = new DylibLoadCommand ( ) ;
dlc . cmd = reader . ReadUInt32 ( ) ;
dlc . cmdsize = reader . ReadUInt32 ( ) ;
/*var nameofs = */ reader . ReadUInt32 ( ) ;
dlc . timestamp = reader . ReadUInt32 ( ) ;
dlc . current_version = reader . ReadUInt32 ( ) ;
dlc . compatibility_version = reader . ReadUInt32 ( ) ;
var namelength = dlc . cmdsize - 6 * 4 ;
var namechars = reader . ReadBytes ( ( int ) namelength ) ;
// strip off any null characters at the end.
for ( int n = namechars . Length - 1 ; n > = 0 ; n - - ) {
if ( namechars [ n ] = = 0 )
namelength - - ;
else
break ;
}
dlc . name = System . Text . UTF8Encoding . UTF8 . GetString ( namechars , 0 , ( int ) namelength ) ;
lc = dlc ;
break ;
2016-06-28 21:35:05 +03:00
case MachO . LoadCommands . Uuid :
var uuidCmd = new UuidCommand ( ) ;
uuidCmd . cmd = reader . ReadUInt32 ( ) ;
uuidCmd . cmdsize = reader . ReadUInt32 ( ) ;
uuidCmd . uuid = reader . ReadBytes ( 16 ) ; // defined in the header as uint8_t uuid [16]
lc = uuidCmd ;
break ;
2016-04-21 15:57:02 +03:00
default :
lc = new LoadCommand ( ) ;
lc . cmd = reader . ReadUInt32 ( ) ;
lc . cmdsize = reader . ReadUInt32 ( ) ;
reader . BaseStream . Position + = lc . cmdsize - 8 ;
break ;
}
cmds . Add ( lc ) ;
}
load_commands = cmds ;
}
public MachO . Architectures Architecture {
get {
switch ( cputype ) {
case 12 : // arm
switch ( cpusubtype ) {
case 6 :
return MachO . Architectures . ARMv6 ;
case 9 :
return MachO . Architectures . ARMv7 ;
case 11 :
return MachO . Architectures . ARMv7s ;
default :
return MachO . Architectures . None ;
}
case 12 | 0x01000000 :
return MachO . Architectures . ARM64 ;
case 7 : // x86
return MachO . Architectures . i386 ;
case 7 | 0x01000000 : // x64
return MachO . Architectures . x86_64 ;
}
return MachO . Architectures . None ;
}
}
public bool IsDynamicLibrary {
get { return filetype = = MachO . MH_DYLIB ; }
}
}
public class FatFile {
public readonly string Filename ;
public uint magic ;
uint _nfat_arch ;
public FatFile ( string filename )
{
Filename = filename ;
}
public uint nfat_arch {
get { return is_big_endian ? MachO . ToBigEndian ( _nfat_arch ) : _nfat_arch ; }
set { _nfat_arch = is_big_endian ? MachO . FromBigEndian ( value ) : value ; }
}
public List < FatEntry > entries ;
internal bool is_big_endian {
get { return magic = = MachO . FAT_CIGAM ; }
}
internal void WriteHeader ( BinaryWriter writer )
{
writer . Write ( magic ) ;
writer . Write ( _nfat_arch ) ;
}
internal void WriteHeaders ( BinaryWriter writer )
{
WriteHeader ( writer ) ;
for ( int i = 0 ; i < entries . Count ; i + + ) {
entries [ i ] . WriteHeader ( writer ) ;
}
}
internal void Read ( BinaryReader reader )
{
magic = reader . ReadUInt32 ( ) ;
_nfat_arch = reader . ReadUInt32 ( ) ;
entries = new List < FatEntry > ( ( int ) nfat_arch ) ;
for ( int i = 0 ; i < ( int ) nfat_arch ; i + + ) {
var entry = new FatEntry ( ) ;
entry . Read ( this , reader ) ;
entries . Add ( entry ) ;
}
foreach ( var entry in entries )
entry . ReadEntry ( reader ) ;
}
}
public class FatEntry {
FatFile parent ;
public int cputype ;
public int cpusubtype ;
public uint offset ;
public uint size ;
public uint align ;
public MachOFile entry ;
public StaticLibrary static_library ;
public bool IsDynamicLibrary { get { return entry = = null ? false : entry . IsDynamicLibrary ; } }
public FatFile Parent { get { return parent ; } }
internal void WriteHeader ( BinaryWriter writer )
{
if ( parent . is_big_endian ) {
writer . Write ( MachO . ToBigEndian ( cputype ) ) ;
writer . Write ( MachO . ToBigEndian ( cpusubtype ) ) ;
writer . Write ( MachO . ToBigEndian ( offset ) ) ;
writer . Write ( MachO . ToBigEndian ( size ) ) ;
writer . Write ( MachO . ToBigEndian ( align ) ) ;
} else {
writer . Write ( cputype ) ;
writer . Write ( cpusubtype ) ;
writer . Write ( offset ) ;
writer . Write ( size ) ;
writer . Write ( align ) ;
}
}
internal void Write ( BinaryWriter writer , BinaryReader reader , uint reader_offset )
{
writer . BaseStream . Position = offset ;
// write data
WriteFile ( writer , reader , reader_offset ) ;
}
internal void WriteFile ( BinaryWriter writer , BinaryReader reader , uint reader_offset )
{
// write data
var ofs = writer . BaseStream . Position ;
reader . BaseStream . Position = reader_offset ;
var buffer = new byte [ 1 < < ( int ) align ] ;
var left = ( int ) size ;
while ( left > 0 ) {
var read = reader . Read ( buffer , 0 , Math . Min ( buffer . Length , left ) ) ;
writer . Write ( buffer , 0 , read ) ;
left - = read ;
}
writer . BaseStream . Position = ofs ; // restore to the post-header location.
}
internal void Read ( FatFile parent , BinaryReader reader )
{
this . parent = parent ;
cputype = reader . ReadInt32 ( ) ;
cpusubtype = reader . ReadInt32 ( ) ;
offset = reader . ReadUInt32 ( ) ;
size = reader . ReadUInt32 ( ) ;
align = reader . ReadUInt32 ( ) ;
if ( parent . is_big_endian ) {
cputype = MachO . FromBigEndian ( cputype ) ;
cpusubtype = MachO . FromBigEndian ( cpusubtype ) ;
offset = MachO . FromBigEndian ( offset ) ;
size = MachO . FromBigEndian ( size ) ;
align = MachO . FromBigEndian ( align ) ;
}
}
internal void ReadEntry ( BinaryReader reader )
{
reader . BaseStream . Position = offset ;
if ( MachOFile . IsMachOLibrary ( this , reader ) ) {
entry = new MachOFile ( this ) ;
entry . Read ( reader ) ;
} else if ( StaticLibrary . IsStaticLibrary ( reader ) ) {
static_library = new StaticLibrary ( ) ;
static_library . Read ( reader ) ;
} else {
throw ErrorHelper . CreateError ( 1603 , "Unknown format for fat entry at position {0} in {1}." , offset , parent . Filename ) ;
}
}
}
public class LoadCommand {
public uint cmd ;
public uint cmdsize ;
#if DEBUG
public virtual void Dump ( )
{
Console . WriteLine ( " cmd: {0}" , cmd ) ;
Console . WriteLine ( " cmdsize: {0}" , cmdsize ) ;
}
#endif
}
public class DylibLoadCommand : LoadCommand {
public string name ;
public uint timestamp ;
public uint current_version ;
public uint compatibility_version ;
#if DEBUG
public override void Dump ( )
{
base . Dump ( ) ;
Console . WriteLine ( " name: {0}" , name ) ;
Console . WriteLine ( " timestamp: {0}" , timestamp ) ;
Console . WriteLine ( " current_version: {0}" , current_version ) ;
Console . WriteLine ( " compatibility_version: {0}" , compatibility_version ) ;
}
2016-06-28 21:35:05 +03:00
#endif
}
public class UuidCommand : LoadCommand {
public byte [ ] uuid ;
#if DEBUG
public override void Dump ( )
{
base . Dump ( ) ;
Console . WriteLine ( " cmd: {0}" , cmd ) ;
Console . WriteLine ( " uuid: {0}" , uuid ) ;
}
2016-04-21 15:57:02 +03:00
#endif
}
}