diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md new file mode 100644 index 0000000000..325bab28d2 --- /dev/null +++ b/Library/Homebrew/vendor/README.md @@ -0,0 +1,53 @@ +Vendored Dependencies +===================== + +* [okjson](https://github.com/kr/okjson), version 43. + +* [ruby-macho](https://github.com/woodruffw/ruby-macho), version 0.2.2. + +## Licenses: + +### okjson + +> Copyright 2011, 2012 Keith Rarick +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +### ruby-macho + +> The MIT License +> Copyright (c) 2015 William Woodruff +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb new file mode 100644 index 0000000000..8cfc5521cb --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho.rb @@ -0,0 +1,16 @@ +require "#{File.dirname(__FILE__)}/macho/structure" +require "#{File.dirname(__FILE__)}/macho/headers" +require "#{File.dirname(__FILE__)}/macho/load_commands" +require "#{File.dirname(__FILE__)}/macho/sections" +require "#{File.dirname(__FILE__)}/macho/macho_file" +require "#{File.dirname(__FILE__)}/macho/fat_file" +require "#{File.dirname(__FILE__)}/macho/open" +require "#{File.dirname(__FILE__)}/macho/exceptions" +require "#{File.dirname(__FILE__)}/macho/utils" +require "#{File.dirname(__FILE__)}/macho/tools" + +# The primary namespace for ruby-macho. +module MachO + # release version + VERSION = "0.2.2".freeze +end diff --git a/Library/Homebrew/vendor/macho/macho/exceptions.rb b/Library/Homebrew/vendor/macho/macho/exceptions.rb new file mode 100644 index 0000000000..1295176b2d --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/exceptions.rb @@ -0,0 +1,85 @@ +module MachO + # A generic Mach-O error in execution. + class MachOError < RuntimeError + end + + # Raised when a file's magic bytes are not valid Mach-O magic. + class MagicError < MachOError + # @param num [Fixnum] the unknown number + def initialize(num) + super "Unrecognized Mach-O magic: 0x#{"%02x" % num}" + end + end + + # Raised when a fat binary is loaded with MachOFile. + class FatBinaryError < MachOError + def initialize + super "Fat binaries must be loaded with MachO::FatFile" + end + end + + # Raised when a Mach-O is loaded with FatFile. + class MachOBinaryError < MachOError + def initialize + super "Normal binaries must be loaded with MachO::MachOFile" + end + end + + # Raised when the CPU type is unknown. + class CPUTypeError < MachOError + # @param num [Fixnum] the unknown number + def initialize(num) + super "Unrecognized CPU type: 0x#{"%02x" % num}" + end + end + + # Raised when the CPU subtype is unknown. + class CPUSubtypeError < MachOError + # @param num [Fixnum] the unknown number + def initialize(num) + super "Unrecognized CPU sub-type: 0x#{"%02x" % num}" + end + end + + # Raised when a mach-o file's filetype field is unknown. + class FiletypeError < MachOError + # @param num [Fixnum] the unknown number + def initialize(num) + super "Unrecognized Mach-O filetype code: 0x#{"%02x" % num}" + end + end + + # Raised when an unknown load command is encountered. + class LoadCommandError < MachOError + # @param num [Fixnum] the unknown number + def initialize(num) + super "Unrecognized Mach-O load command: 0x#{"%02x" % num}" + end + end + + # Raised when load commands are too large to fit in the current file. + class HeaderPadError < MachOError + # @param filename [String] the filename + def initialize(filename) + super "Updated load commands do not fit in the header of " + + "#{filename}. #{filename} needs to be relinked, possibly with " + + "-headerpad or -headerpad_max_install_names" + end + end + + # Raised when attempting to change a dylib name that doesn't exist. + class DylibUnknownError < MachOError + # @param dylib [String] the unknown shared library name + def initialize(dylib) + super "No such dylib name: #{dylib}" + end + end + + # Raised when attempting to change an rpath that doesn't exist. + class RpathUnknownError < MachOError + # @param path [String] the unknown runtime path + def initialize(path) + super "No such runtime path: #{path}" + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb new file mode 100644 index 0000000000..829ee83f0e --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb @@ -0,0 +1,230 @@ +module MachO + # Represents a "Fat" file, which contains a header, a listing of available + # architectures, and one or more Mach-O binaries. + # @see https://en.wikipedia.org/wiki/Mach-O#Multi-architecture_binaries + # @see MachO::MachOFile + class FatFile + # @return [MachO::FatHeader] the file's header + attr_reader :header + + # @return [Array] an array of fat architectures + attr_reader :fat_archs + + # @return [Array] an array of Mach-O binaries + attr_reader :machos + + # Creates a new FatFile from the given filename. + # @param filename [String] the fat file to load from + # @raise [ArgumentError] if the given filename does not exist + def initialize(filename) + raise ArgumentError.new("#{filetype}: no such file") unless File.exist?(filename) + + @filename = filename + @raw_data = open(@filename, "rb") { |f| f.read } + @header = get_fat_header + @fat_archs = get_fat_archs + @machos = get_machos + end + + # The file's raw fat data. + # @return [String] the raw fat data + def serialize + @raw_data + end + + # @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise + def object? + machos.first.object? + end + + # @return [Boolean] true if the file is of type `MH_EXECUTE`, false otherwise + def executable? + machos.first.executable? + end + + # @return [Boolean] true if the file is of type `MH_FVMLIB`, false otherwise + def fvmlib? + machos.first.fvmlib? + end + + # @return [Boolean] true if the file is of type `MH_CORE`, false otherwise + def core? + machos.first.core? + end + + # @return [Boolean] true if the file is of type `MH_PRELOAD`, false otherwise + def preload? + machos.first.preload? + end + + # @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise + def dylib? + machos.first.dylib? + end + + # @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise + def dylinker? + machos.first.dylinker? + end + + # @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise + def bundle? + machos.first.bundle? + end + + # @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise + def dsym? + machos.first.dsym? + end + + # @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise + def kext? + machos.first.kext? + end + + # @return [Fixnum] the file's magic number + def magic + header.magic + end + + # @return [String] a string representation of the file's magic number + def magic_string + MH_MAGICS[magic] + end + + # The file's type. Assumed to be the same for every Mach-O within. + # @return [String] the filetype + def filetype + machos.first.filetype + end + + # The file's dylib ID. If the file is not a dylib, returns `nil`. + # @example + # file.dylib_id # => 'libBar.dylib' + # @return [String, nil] the file's dylib ID + def dylib_id + machos.first.dylib_id + end + + # Changes the file's dylib ID to `new_id`. If the file is not a dylib, does nothing. + # @example + # file.dylib_id = 'libFoo.dylib' + # @param new_id [String] the new dylib ID + # @return [void] + # @raise [ArgumentError] if `new_id` is not a String + def dylib_id=(new_id) + if !new_id.is_a?(String) + raise ArgumentError.new("argument must be a String") + end + + if !machos.all?(&:dylib?) + return nil + end + + machos.each do |macho| + macho.dylib_id = new_id + end + + synchronize_raw_data + end + + # All shared libraries linked to the file's Mach-Os. + # @return [Array] an array of all shared libraries + def linked_dylibs + # can machos inside fat binaries have different dylibs? + machos.flat_map(&:linked_dylibs).uniq + end + + # Changes all dependent shared library install names from `old_name` to `new_name`. + # In a fat file, this changes install names in all internal Mach-Os. + # @example + # file.change_install_name('/usr/lib/libFoo.dylib', '/usr/lib/libBar.dylib') + # @param old_name [String] the shared library name being changed + # @param new_name [String] the new name + # @todo incomplete + def change_install_name(old_name, new_name) + machos.each do |macho| + macho.change_install_name(old_name, new_name) + end + + synchronize_raw_data + end + + alias :change_dylib :change_install_name + + # Extract a Mach-O with the given CPU type from the file. + # @example + # file.extract("CPU_TYPE_I386") # => MachO::MachOFile + # @param cputype [String] the CPU type of the Mach-O being extracted + # @return [MachO::MachOFile, nil] the extracted Mach-O or nil if no Mach-O has the given CPU type + def extract(cputype) + machos.select { |macho| macho.cputype == cputype }.first + end + + # Write all (fat) data to the given filename. + # @param filename [String] the file to write to + def write(filename) + File.open(filename, "wb") { |f| f.write(@raw_data) } + end + + # Write all (fat) data to the file used to initialize the instance. + # @note Overwrites all data in the file! + def write! + File.open(@filename, "wb") { |f| f.write(@raw_data) } + end + + private + + # Obtain the fat header from raw file data. + # @return [MachO::FatHeader] the fat header + # @raise [MachO::MagicError] if the magic is not valid Mach-O magic + # @raise [MachO::MachOBinaryError] if the magic is for a non-fat Mach-O file + # @private + def get_fat_header + magic, nfat_arch = @raw_data[0..7].unpack("N2") + + raise MagicError.new(magic) unless MachO.magic?(magic) + raise MachOBinaryError.new unless MachO.fat_magic?(magic) + + FatHeader.new(magic, nfat_arch) + end + + # Obtain an array of fat architectures from raw file data. + # @return [Array] an array of fat architectures + # @private + def get_fat_archs + archs = [] + + header.nfat_arch.times do |i| + fields = @raw_data[8 + (FatArch.bytesize * i), FatArch.bytesize].unpack("N5") + archs << FatArch.new(*fields) + end + + archs + end + + # Obtain an array of Mach-O blobs from raw file data. + # @return [Array] an array of Mach-Os + # @private + def get_machos + machos = [] + + fat_archs.each do |arch| + machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size]) + end + + machos + end + + # @todo this needs to be redesigned. arch[:offset] and arch[:size] are + # already out-of-date, and the header needs to be synchronized as well. + # @private + def synchronize_raw_data + machos.each_with_index do |macho, i| + arch = fat_archs[i] + + @raw_data[arch.offset, arch.size] = macho.serialize + end + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb new file mode 100644 index 0000000000..7e4d126a37 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/headers.rb @@ -0,0 +1,275 @@ +module MachO + # big-endian fat magic + FAT_MAGIC = 0xcafebabe + + # little-endian fat magic + FAT_CIGAM = 0xbebafeca + + # 32-bit big-endian magic + MH_MAGIC = 0xfeedface + + # 32-bit little-endian magic + MH_CIGAM = 0xcefaedfe + + # 64-bit big-endian magic + MH_MAGIC_64 = 0xfeedfacf + + # 64-bit little-endian magic + MH_CIGAM_64 = 0xcffaedfe + + # association of magic numbers to string representations + MH_MAGICS = { + FAT_MAGIC => "FAT_MAGIC", + FAT_CIGAM => "FAT_CIGAM", + MH_MAGIC => "MH_MAGIC", + MH_CIGAM => "MH_CIGAM", + MH_MAGIC_64 => "MH_MAGIC_64", + MH_CIGAM_64 => "MH_CIGAM_64" + } + + # mask for CPUs with 64-bit architectures (when running a 64-bit ABI?) + CPU_ARCH_ABI64 = 0x01000000 + + # any CPU (unused?) + CPU_TYPE_ANY = -1 + + # x86 compatible CPUs + CPU_TYPE_X86 = 0x07 + + # i386 and later compatible CPUs + CPU_TYPE_I386 = CPU_TYPE_X86 + + # x86_64 (AMD64) compatible CPUs + CPU_TYPE_X86_64 = (CPU_TYPE_X86 | CPU_ARCH_ABI64) + + # PowerPC compatible CPUs (7400 series?) + CPU_TYPE_POWERPC = 0x12 + + # PowerPC64 compatible CPUs (970 series?) + CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) + + # association of cpu types to string representations + CPU_TYPES = { + CPU_TYPE_ANY => "CPU_TYPE_ANY", + CPU_TYPE_X86 => "CPU_TYPE_X86", + CPU_TYPE_I386 => "CPU_TYPE_I386", + CPU_TYPE_X86_64 => "CPU_TYPE_X86_64", + CPU_TYPE_POWERPC => "CPU_TYPE_POWERPC", + CPU_TYPE_POWERPC64 => "CPU_TYPE_POWERPC64" + } + + # mask for CPU subtype capabilities + CPU_SUBTYPE_MASK = 0xff000000 + + # 64-bit libraries (undocumented!) + # @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html + CPU_SUBTYPE_LIB64 = 0x80000000 + + # all x86-type CPUs + CPU_SUBTYPE_X86_ALL = 3 + + # all x86-type CPUs (what makes this different from CPU_SUBTYPE_X86_ALL?) + CPU_SUBTYPE_X86_ARCH1 = 4 + + # association of cpu subtypes to string representations + CPU_SUBTYPES = { + CPU_SUBTYPE_X86_ALL => "CPU_SUBTYPE_X86_ALL", + CPU_SUBTYPE_X86_ARCH1 => "CPU_SUBTYPE_X86_ARCH1" + } + + # relocatable object file + MH_OBJECT = 0x1 + + # demand paged executable file + MH_EXECUTE = 0x2 + + # fixed VM shared library file + MH_FVMLIB = 0x3 + + # core dump file + MH_CORE = 0x4 + + # preloaded executable file + MH_PRELOAD = 0x5 + + # dynamically bound shared library + MH_DYLIB = 0x6 + + # dynamic link editor + MH_DYLINKER = 0x7 + + # dynamically bound bundle file + MH_BUNDLE = 0x8 + + # shared library stub for static linking only, no section contents + MH_DYLIB_STUB = 0x9 + + # companion file with only debug sections + MH_DSYM = 0xa + + # x86_64 kexts + MH_KEXT_BUNDLE = 0xb + + # association of filetypes to string representations + # @api private + MH_FILETYPES = { + MH_OBJECT => "MH_OBJECT", + MH_EXECUTE => "MH_EXECUTE", + MH_FVMLIB => "MH_FVMLIB", + MH_CORE => "MH_CORE", + MH_PRELOAD => "MH_PRELOAD", + MH_DYLIB => "MH_DYLIB", + MH_DYLINKER => "MH_DYLINKER", + MH_BUNDLE => "MH_BUNDLE", + MH_DYLIB_STUB => "MH_DYLIB_STUB", + MH_DSYM => "MH_DSYM", + MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE" + } + + # association of mach header flag symbols to values + # @api private + MH_FLAGS = { + :MH_NOUNDEFS => 0x1, + :MH_INCRLINK => 0x2, + :MH_DYLDLINK => 0x4, + :MH_BINDATLOAD => 0x8, + :MH_PREBOUND => 0x10, + :MH_SPLIT_SEGS => 0x20, + :MH_LAZY_INIT => 0x40, + :MH_TWOLEVEL => 0x80, + :MH_FORCE_FLAT => 0x100, + :MH_NOMULTIDEFS => 0x200, + :MH_NOPREFIXBINDING => 0x400, + :MH_PREBINDABLE => 0x800, + :MH_ALLMODSBOUND => 0x1000, + :MH_SUBSECTIONS_VIA_SYMBOLS => 0x2000, + :MH_CANONICAL => 0x4000, + :MH_WEAK_DEFINES => 0x8000, + :MH_BINDS_TO_WEAK => 0x10000, + :MH_ALLOW_STACK_EXECUTION => 0x20000, + :MH_ROOT_SAFE => 0x40000, + :MH_SETUID_SAFE => 0x80000, + :MH_NO_REEXPORTED_DYLIBS => 0x100000, + :MH_PIE => 0x200000, + :MH_DEAD_STRIPPABLE_DYLIB => 0x400000, + :MH_HAS_TLV_DESCRIPTORS => 0x800000, + :MH_NO_HEAP_EXECUTION => 0x1000000, + :MH_APP_EXTENSION_SAFE => 0x02000000 + } + + # Fat binary header structure + # @see MachO::FatArch + class FatHeader < MachOStructure + # @return [Fixnum] the magic number of the header (and file) + attr_reader :magic + + # @return [Fixnum] the number of fat architecture structures following the header + attr_reader :nfat_arch + + FORMAT = "VV" + SIZEOF = 8 + + # @api private + def initialize(magic, nfat_arch) + @magic = magic + @nfat_arch = nfat_arch + end + end + + # Fat binary header architecture structure. A Fat binary has one or more of + # these, representing one or more internal Mach-O blobs. + # @see MachO::FatHeader + class FatArch < MachOStructure + # @return [Fixnum] the CPU type of the Mach-O + attr_reader :cputype + + # @return [Fixnum] the CPU subtype of the Mach-O + attr_reader :cpusubtype + + # @return [Fixnum] the file offset to the beginning of the Mach-O data + attr_reader :offset + + # @return [Fixnum] the size, in bytes, of the Mach-O data + attr_reader :size + + # @return [Fixnum] the alignment, as a power of 2 + attr_reader :align + + FORMAT = "VVVVV" + SIZEOF = 20 + + # @api private + def initialize(cputype, cpusubtype, offset, size, align) + @cputype = cputype + @cpusubtype = cpusubtype + @offset = offset + @size = size + @align = align + end + end + + # 32-bit Mach-O file header structure + class MachHeader < MachOStructure + # @return [Fixnum] the magic number + attr_reader :magic + + # @return [Fixnum] the CPU type of the Mach-O + attr_reader :cputype + + # @return [Fixnum] the CPU subtype of the Mach-O + attr_reader :cpusubtype + + # @return [Fixnum] the file type of the Mach-O + attr_reader :filetype + + # @return [Fixnum] the number of load commands in the Mach-O + attr_reader :ncmds + + # @return [Fixnum] the size of all load commands, in bytes, in the Mach-O + attr_reader :sizeofcmds + + # @return [Fixnum] the header flags associated with the Mach-O + attr_reader :flags + + FORMAT = "VVVVVVV" + SIZEOF = 28 + + # @api private + def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, + flags) + @magic = magic + @cputype = cputype + @cpusubtype = cpusubtype + @filetype = filetype + @ncmds = ncmds + @sizeofcmds = sizeofcmds + @flags = flags + end + + # @example + # puts "this mach-o has position-independent execution" if header.flag?(:MH_PIE) + # @param flag [Symbol] a mach header flag symbol + # @return [Boolean] true if `flag` is present in the header's flag section + def flag?(flag) + flag = MH_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end + end + + # 64-bit Mach-O file header structure + class MachHeader64 < MachHeader + # @return [void] + attr_reader :reserved + + FORMAT = "VVVVVVVV" + SIZEOF = 32 + + # @api private + def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, + flags, reserved) + super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) + @reserved = reserved + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/load_commands.rb b/Library/Homebrew/vendor/macho/macho/load_commands.rb new file mode 100644 index 0000000000..e5c71b1608 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb @@ -0,0 +1,1001 @@ +module MachO + # load commands added after OS X 10.1 need to be bitwise ORed with + # LC_REQ_DYLD to be recognized by the dynamic linder (dyld) + LC_REQ_DYLD = 0x80000000 + + # association of load commands to symbol representations + # @api private + LOAD_COMMANDS = { + 0x1 => :LC_SEGMENT, + 0x2 => :LC_SYMTAB, + 0x3 => :LC_SYMSEG, + 0x4 => :LC_THREAD, + 0x5 => :LC_UNIXTHREAD, + 0x6 => :LC_LOADFVMLIB, + 0x7 => :LC_IDFVMLIB, + 0x8 => :LC_IDENT, + 0x9 => :LC_FVMFILE, + 0xa => :LC_PREPAGE, + 0xb => :LC_DYSYMTAB, + 0xc => :LC_LOAD_DYLIB, + 0xd => :LC_ID_DYLIB, + 0xe => :LC_LOAD_DYLINKER, + 0xf => :LC_ID_DYLINKER, + 0x10 => :LC_PREBOUND_DYLIB, + 0x11 => :LC_ROUTINES, + 0x12 => :LC_SUB_FRAMEWORK, + 0x13 => :LC_SUB_UMBRELLA, + 0x14 => :LC_SUB_CLIENT, + 0x15 => :LC_SUB_LIBRARY, + 0x16 => :LC_TWOLEVEL_HINTS, + 0x17 => :LC_PREBIND_CKSUM, + (0x18 | LC_REQ_DYLD) => :LC_LOAD_WEAK_DYLIB, + 0x19 => :LC_SEGMENT_64, + 0x1a => :LC_ROUTINES_64, + 0x1b => :LC_UUID, + (0x1c | LC_REQ_DYLD) => :LC_RPATH, + 0x1d => :LC_CODE_SIGNATURE, + 0x1e => :LC_SEGMENT_SPLIT_INFO, + (0x1f | LC_REQ_DYLD) => :LC_REEXPORT_DYLIB, + 0x20 => :LC_LAZY_LOAD_DYLIB, + 0x21 => :LC_ENCRYPTION_INFO, + 0x22 => :LC_DYLD_INFO, + (0x22 | LC_REQ_DYLD) => :LC_DYLD_INFO_ONLY, + (0x23 | LC_REQ_DYLD) => :LC_LOAD_UPWARD_DYLIB, + 0x24 => :LC_VERSION_MIN_MACOSX, + 0x25 => :LC_VERSION_MIN_IPHONEOS, + 0x26 => :LC_FUNCTION_STARTS, + 0x27 => :LC_DYLD_ENVIRONMENT, + (0x28 | LC_REQ_DYLD) => :LC_MAIN, + 0x29 => :LC_DATA_IN_CODE, + 0x2a => :LC_SOURCE_VERSION, + 0x2b => :LC_DYLIB_CODE_SIGN_DRS, + 0x2c => :LC_ENCRYPTION_INFO_64, + 0x2d => :LC_LINKER_OPTION, + 0x2e => :LC_LINKER_OPTIMIZATION_HINT + } + + # load commands responsible for loading dylibs + # @api private + DYLIB_LOAD_COMMANDS = [ + :LC_LOAD_DYLIB, + :LC_LOAD_WEAK_DYLIB, + :LC_REEXPORT_DYLIB, + :LC_LAZY_LOAD_DYLIB, + :LC_LOAD_UPWARD_DYLIB, + ].freeze + + # association of load command symbols to string representations of classes + # @api private + LC_STRUCTURES = { + :LC_SEGMENT => "SegmentCommand", + :LC_SYMTAB => "SymtabCommand", + :LC_SYMSEG => "LoadCommand", # obsolete + :LC_THREAD => "ThreadCommand", + :LC_UNIXTHREAD => "ThreadCommand", + :LC_LOADFVMLIB => "LoadCommand", # obsolete + :LC_IDFVMLIB => "LoadCommand", # obsolete + :LC_IDENT => "LoadCommand", # obsolete + :LC_FVMFILE => "LoadCommand", # reserved for internal use only + :LC_PREPAGE => "LoadCommand", # reserved for internal use only + :LC_DYSYMTAB => "DysymtabCommand", + :LC_LOAD_DYLIB => "DylibCommand", + :LC_ID_DYLIB => "DylibCommand", + :LC_LOAD_DYLINKER => "DylinkerCommand", + :LC_ID_DYLINKER => "DylinkerCommand", + :LC_PREBOUND_DYLIB => "PreboundDylibCommand", + :LC_ROUTINES => "RoutinesCommand", + :LC_SUB_FRAMEWORK => "SubFrameworkCommand", + :LC_SUB_UMBRELLA => "SubUmbrellaCommand", + :LC_SUB_CLIENT => "SubClientCommand", + :LC_SUB_LIBRARY => "SubLibraryCommand", + :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand", + :LC_PREBIND_CKSUM => "PrebindCksumCommand", + :LC_LOAD_WEAK_DYLIB => "DylibCommand", + :LC_SEGMENT_64 => "SegmentCommand64", + :LC_ROUTINES_64 => "RoutinesCommand64", + :LC_UUID => "UUIDCommand", + :LC_RPATH => "RpathCommand", + :LC_CODE_SIGNATURE => "LinkeditDataCommand", + :LC_SEGMENT_SPLIT_INFO => "LinkeditDataCommand", + :LC_REEXPORT_DYLIB => "DylibCommand", + :LC_LAZY_LOAD_DYLIB => "DylibCommand", + :LC_ENCRYPTION_INFO => "EncryptionInfoCommand", + :LC_DYLD_INFO => "DyldInfoCommand", + :LC_DYLD_INFO_ONLY => "DyldInfoCommand", + :LC_LOAD_UPWARD_DYLIB => "DylibCommand", + :LC_VERSION_MIN_MACOSX => "VersionMinCommand", + :LC_VERSION_MIN_IPHONEOS => "VersionMinCommand", + :LC_FUNCTION_STARTS => "LinkeditDataCommand", + :LC_DYLD_ENVIRONMENT => "DylinkerCommand", + :LC_MAIN => "EntryPointCommand", + :LC_DATA_IN_CODE => "LinkeditDataCommand", + :LC_SOURCE_VERSION => "SourceVersionCommand", + :LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand", + :LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64", + :LC_LINKER_OPTION => "LinkerOptionCommand", + :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand" + } + + # association of segment name symbols to names + # @api private + SEGMENT_NAMES = { + :SEG_PAGEZERO => "__PAGEZERO", + :SEG_TEXT => "__TEXT", + :SEG_DATA => "__DATA", + :SEG_OBJC => "__OBJC", + :SEG_ICON => "__ICON", + :SEG_LINKEDIT => "__LINKEDIT", + :SEG_UNIXSTACK => "__UNIXSTACK", + :SEG_IMPORT => "__IMPORT" + } + + # association of segment flag symbols to values + # @api private + SEGMENT_FLAGS = { + :SG_HIGHVM => 0x1, + :SG_FVMLIB => 0x2, + :SG_NORELOC => 0x4, + :SG_PROTECTED_VERSION_1 => 0x8 + } + + # Mach-O load command structure + # This is the most generic load command - only cmd ID and size are + # represented, and no actual data. Used when a more specific class + # isn't available/implemented. + class LoadCommand < MachOStructure + # @return [Fixnum] the offset in the file the command was created from + attr_reader :offset + + # @return [Fixnum] the load command's identifying number + attr_reader :cmd + + # @return [Fixnum] the size of the load command, in bytes + attr_reader :cmdsize + + FORMAT = "VV" + SIZEOF = 8 + + # Creates a new LoadCommand given an offset and binary string + # @param offset [Fixnum] the offset to initialize with + # @param bin [String] the binary string to initialize with + # @return [MachO::LoadCommand] the new load command + # @api private + def self.new_from_bin(raw_data, offset, bin) + self.new(raw_data, offset, *bin.unpack(self::FORMAT)) + end + + # @param offset [Fixnum] the offset to initialize with + # @param cmd [Fixnum] the load command's identifying number + # @param cmdsize [Fixnum] the size of the load command in bytes + # @api private + def initialize(raw_data, offset, cmd, cmdsize) + @raw_data = raw_data + @offset = offset + @cmd = cmd + @cmdsize = cmdsize + end + + # @return [Symbol] a symbol representation of the load command's identifying number + def type + LOAD_COMMANDS[cmd] + end + + alias :to_sym :type + + # @return [String] a string representation of the load command's identifying number + def to_s + type.to_s + end + + # Represents a Load Command string. A rough analogue to the lc_str + # struct used internally by OS X. This class allows ruby-macho to + # pretend that strings stored in LCs are immediately available without + # explicit operations on the raw Mach-O data. + class LCStr + # @param raw_data [String] the raw Mach-O data. + # @param lc [MachO::LoadCommand] the load command + # @param lc_str [Fixnum] the offset to the beginning of the string + # @api private + def initialize(raw_data, lc, lc_str) + @raw_data = raw_data + @lc = lc + @lc_str = lc_str + @str = @raw_data.slice(@lc.offset + @lc_str...@lc.offset + @lc.cmdsize).delete("\x00") + end + + # @return [String] a string representation of the LCStr + def to_s + @str + end + + # @return [Fixnum] the offset to the beginning of the string in the load command + def to_i + @lc_str + end + end + end + + # A load command containing a single 128-bit unique random number identifying + # an object produced by static link editor. Corresponds to LC_UUID. + class UUIDCommand < LoadCommand + # @return [Array] the UUID + attr_reader :uuid + + FORMAT = "VVa16" + SIZEOF = 24 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, uuid) + super(raw_data, offset, cmd, cmdsize) + @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array + end + + # @return [String] a string representation of the UUID + def uuid_string + hexes = uuid.map { |e| "%02x" % e } + segs = [ + hexes[0..3].join, hexes[4..5].join, hexes[6..7].join, + hexes[8..9].join, hexes[10..15].join + ] + + segs.join("-") + end + end + + # A load command indicating that part of this file is to be mapped into + # the task's address space. Corresponds to LC_SEGMENT. + class SegmentCommand < LoadCommand + # @return [String] the name of the segment + attr_reader :segname + + # @return [Fixnum] the memory address of the segment + attr_reader :vmaddr + + # @return [Fixnum] the memory size of the segment + attr_reader :vmsize + + # @return [Fixnum] the file offset of the segment + attr_reader :fileoff + + # @return [Fixnum] the amount to map from the file + attr_reader :filesize + + # @return [Fixnum] the maximum VM protection + attr_reader :maxprot + + # @return [Fixnum] the initial VM protection + attr_reader :initprot + + # @return [Fixnum] the number of sections in the segment + attr_reader :nsects + + # @return [Fixnum] any flags associated with the segment + attr_reader :flags + + FORMAT = "VVa16VVVVVVVV" + SIZEOF = 56 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, + filesize, maxprot, initprot, nsects, flags) + super(raw_data, offset, cmd, cmdsize) + @segname = segname.delete("\x00") + @vmaddr = vmaddr + @vmsize = vmsize + @fileoff = fileoff + @filesize = filesize + @maxprot = maxprot + @initprot = initprot + @nsects = nsects + @flags = flags + end + + # @example + # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC) + # @param flag [Symbol] a segment flag symbol + # @return [Boolean] true if `flag` is present in the segment's flag field + def flag?(flag) + flag = SEGMENT_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end + end + + # A load command indicating that part of this file is to be mapped into + # the task's address space. Corresponds to LC_SEGMENT_64. + class SegmentCommand64 < LoadCommand + # @return [String] the name of the segment + attr_reader :segname + + # @return [Fixnum] the memory address of the segment + attr_reader :vmaddr + + # @return [Fixnum] the memory size of the segment + attr_reader :vmsize + + # @return [Fixnum] the file offset of the segment + attr_reader :fileoff + + # @return [Fixnum] the amount to map from the file + attr_reader :filesize + + # @return [Fixnum] the maximum VM protection + attr_reader :maxprot + + # @return [Fixnum] the initial VM protection + attr_reader :initprot + + # @return [Fixnum] the number of sections in the segment + attr_reader :nsects + + # @return [Fixnum] any flags associated with the segment + attr_reader :flags + + FORMAT = "VVa16QQQQVVVV" + SIZEOF = 72 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, + filesize, maxprot, initprot, nsects, flags) + super(raw_data, offset, cmd, cmdsize) + @segname = segname.delete("\x00") + @vmaddr = vmaddr + @vmsize = vmsize + @fileoff = fileoff + @filesize = filesize + @maxprot = maxprot + @initprot = initprot + @nsects = nsects + @flags = flags + end + + # @example + # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC) + # @param flag [Symbol] a segment flag symbol + # @return [Boolean] true if `flag` is present in the segment's flag field + def flag?(flag) + flag = SEGMENT_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end + end + + # A load command representing some aspect of shared libraries, depending + # on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, + # and LC_REEXPORT_DYLIB. + class DylibCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr + attr_reader :name + + # @return [Fixnum] the library's build time stamp + attr_reader :timestamp + + # @return [Fixnum] the library's current version number + attr_reader :current_version + + # @return [Fixnum] the library's compatibility version number + attr_reader :compatibility_version + + FORMAT = "VVVVVV" + SIZEOF = 24 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, name, timestamp, current_version, + compatibility_version) + super(raw_data, offset, cmd, cmdsize) + @name = LCStr.new(raw_data, self, name) + @timestamp = timestamp + @current_version = current_version + @compatibility_version = compatibility_version + end + end + + # A load command representing some aspect of the dynamic linker, depending + # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and + # LC_DYLD_ENVIRONMENT. + class DylinkerCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr + attr_reader :name + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, name) + super(raw_data, offset, cmd, cmdsize) + @name = LCStr.new(raw_data, self, name) + end + end + + # A load command used to indicate dynamic libraries used in prebinding. + # Corresponds to LC_PREBOUND_DYLIB. + class PreboundDylibCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr + attr_reader :name + + # @return [Fixnum] the number of modules in the library + attr_reader :nmodules + + # @return [Fixnum] a bit vector of linked modules + attr_reader :linked_modules + + FORMAT = "VVVVV" + SIZEOF = 20 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, name, nmodules, linked_modules) + super(raw_data, offset, cmd, cmdsize) + @name = LCStr.new(raw_data, self, name) + @nmodules = nmodules + @linked_modules = linked_modules + end + end + + # A load command used to represent threads. + # @note cctools-870 has all fields of thread_command commented out except common ones (cmd, cmdsize) + class ThreadCommand < LoadCommand + + end + + # A load command containing the address of the dynamic shared library + # initialization routine and an index into the module table for the module + # that defines the routine. Corresponds to LC_ROUTINES. + class RoutinesCommand < LoadCommand + # @return [Fixnum] the address of the initialization routine + attr_reader :init_address + + # @return [Fixnum] the index into the module table that the init routine is defined in + attr_reader :init_module + + # @return [void] + attr_reader :reserved1 + + # @return [void] + attr_reader :reserved2 + + # @return [void] + attr_reader :reserved3 + + # @return [void] + attr_reader :reserved4 + + # @return [void] + attr_reader :reserved5 + + # @return [void] + attr_reader :reserved6 + + FORMAT = "VVVVVVVVVV" + SIZEOF = 40 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module, + reserved1, reserved2, reserved3, reserved4, reserved5, + reserved6) + super(raw_data, offset, cmd, cmdsize) + @init_address = init_address + @init_module = init_module + @reserved1 = reserved1 + @reserved2 = reserved2 + @reserved3 = reserved3 + @reserved4 = reserved4 + @reserved5 = reserved5 + @reserved6 = reserved6 + end + end + + # A load command containing the address of the dynamic shared library + # initialization routine and an index into the module table for the module + # that defines the routine. Corresponds to LC_ROUTINES_64. + class RoutinesCommand64 < LoadCommand + # @return [Fixnum] the address of the initialization routine + attr_reader :init_address + + # @return [Fixnum] the index into the module table that the init routine is defined in + attr_reader :init_module + + # @return [void] + attr_reader :reserved1 + + # @return [void] + attr_reader :reserved2 + + # @return [void] + attr_reader :reserved3 + + # @return [void] + attr_reader :reserved4 + + # @return [void] + attr_reader :reserved5 + + # @return [void] + attr_reader :reserved6 + + FORMAT = "VVQQQQQQQQ" + SIZEOF = 72 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module, + reserved1, reserved2, reserved3, reserved4, reserved5, + reserved6) + super(raw_data, offset, cmd, cmdsize) + @init_address = init_address + @init_module = init_module + @reserved1 = reserved1 + @reserved2 = reserved2 + @reserved3 = reserved3 + @reserved4 = reserved4 + @reserved5 = reserved5 + @reserved6 = reserved6 + end + end + + # A load command signifying membership of a subframework containing the name + # of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK. + class SubFrameworkCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr + attr_reader :umbrella + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, umbrella) + super(raw_data, offset, cmd, cmdsize) + @umbrella = LCStr.new(raw_data, self, umbrella) + end + end + + # A load command signifying membership of a subumbrella containing the name + # of an umbrella framework. Corresponds to LC_SUB_UMBRELLA. + class SubUmbrellaCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr + attr_reader :sub_umbrella + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, sub_umbrella) + super(raw_data, offset, cmd, cmdsize) + @sub_umbrella = LCStr.new(raw_data, self, sub_umbrella) + end + end + + # A load command signifying a sublibrary of a shared library. Corresponds + # to LC_SUB_LIBRARY. + class SubLibraryCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr + attr_reader :sub_library + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, sub_library) + super(raw_data, offset, cmd, cmdsize) + @sub_library = LCStr.new(raw_data, self, sub_library) + end + end + + # A load command signifying a shared library that is a subframework of + # an umbrella framework. Corresponds to LC_SUB_CLIENT. + class SubClientCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr + attr_reader :sub_client + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, sub_client) + super(raw_data, offset, cmd, cmdsize) + @sub_client = LCStr.new(raw_data, self, sub_client) + end + end + + # A load command containing the offsets and sizes of the link-edit 4.3BSD + # "stab" style symbol table information. Corresponds to LC_SYMTAB. + class SymtabCommand < LoadCommand + # @return [Fixnum] the symbol table's offset + attr_reader :symoff + + # @return [Fixnum] the number of symbol table entries + attr_reader :nsyms + + # @return the string table's offset + attr_reader :stroff + + # @return the string table size in bytes + attr_reader :strsize + + FORMAT = "VVVVVV" + SIZEOF = 24 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, symoff, nsyms, stroff, strsize) + super(raw_data, offset, cmd, cmdsize) + @symoff = symoff + @nsyms = nsyms + @stroff = stroff + @strsize = strsize + end + end + + # A load command containing symbolic information needed to support data + # structures used by the dynamic link editor. Corresponds to LC_DYSYMTAB. + class DysymtabCommand < LoadCommand + # @return [Fixnum] the index to local symbols + attr_reader :ilocalsym + + # @return [Fixnum] the number of local symbols + attr_reader :nlocalsym + + # @return [Fixnum] the index to externally defined symbols + attr_reader :iextdefsym + + # @return [Fixnum] the number of externally defined symbols + attr_reader :nextdefsym + + # @return [Fixnum] the index to undefined symbols + attr_reader :iundefsym + + # @return [Fixnum] the number of undefined symbols + attr_reader :nundefsym + + # @return [Fixnum] the file offset to the table of contents + attr_reader :tocoff + + # @return [Fixnum] the number of entries in the table of contents + attr_reader :ntoc + + # @return [Fixnum] the file offset to the module table + attr_reader :modtaboff + + # @return [Fixnum] the number of entries in the module table + attr_reader :nmodtab + + # @return [Fixnum] the file offset to the referenced symbol table + attr_reader :extrefsymoff + + # @return [Fixnum] the number of entries in the referenced symbol table + attr_reader :nextrefsyms + + # @return [Fixnum] the file offset to the indirect symbol table + attr_reader :indirectsymoff + + # @return [Fixnum] the number of entries in the indirect symbol table + attr_reader :nindirectsyms + + # @return [Fixnum] the file offset to the external relocation entries + attr_reader :extreloff + + # @return [Fixnum] the number of external relocation entries + attr_reader :nextrel + + # @return [Fixnum] the file offset to the local relocation entries + attr_reader :locreloff + + # @return [Fixnum] the number of local relocation entries + attr_reader :nlocrel + + + FORMAT = "VVVVVVVVVVVVVVVVVVVV" + SIZEOF = 80 + + # ugh + # @api private + def initialize(raw_data, offset, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, + nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, + nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, + nindirectsyms, extreloff, nextrel, locreloff, nlocrel) + super(raw_data, offset, cmd, cmdsize) + @ilocalsym = ilocalsym + @nlocalsym = nlocalsym + @iextdefsym = iextdefsym + @nextdefsym = nextdefsym + @iundefsym = iundefsym + @nundefsym = nundefsym + @tocoff = tocoff + @ntoc = ntoc + @modtaboff = modtaboff + @nmodtab = nmodtab + @extrefsymoff = extrefsymoff + @nextrefsyms = nextrefsyms + @indirectsymoff = indirectsymoff + @nindirectsyms = nindirectsyms + @extreloff = extreloff + @nextrel = nextrel + @locreloff = locreloff + @nlocrel = nlocrel + end + end + + # A load command containing the offset and number of hints in the two-level + # namespace lookup hints table. Corresponds to LC_TWOLEVEL_HINTS. + class TwolevelHintsCommand < LoadCommand + # @return [Fixnum] the offset to the hint table + attr_reader :htoffset + + # @return [Fixnum] the number of hints in the hint table + attr_reader :nhints + + FORMAT = "VVVV" + SIZEOF = 16 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, htoffset, nhints) + super(raw_data, offset, cmd, cmdsize) + @htoffset = htoffset + @nhints = nhints + end + end + + # A load command containing the value of the original checksum for prebound + # files, or zero. Corresponds to LC_PREBIND_CKSUM. + class PrebindCksumCommand < LoadCommand + # @return [Fixnum] the checksum or 0 + attr_reader :cksum + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, cksum) + super(raw_data, offset, cmd, cmdsize) + @cksum = cksum + end + end + + # A load command representing an rpath, which specifies a path that should + # be added to the current run path used to find @rpath prefixed dylibs. + # Corresponds to LC_RPATH. + class RpathCommand < LoadCommand + # @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr + attr_reader :path + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, path) + super(raw_data, offset, cmd, cmdsize) + @path = LCStr.new(raw_data, self, path) + end + end + + # A load command representing the offsets and sizes of a blob of data in + # the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, + # LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT. + class LinkeditDataCommand < LoadCommand + # @return [Fixnum] offset to the data in the __LINKEDIT segment + attr_reader :dataoff + + # @return [Fixnum] size of the data in the __LINKEDIT segment + attr_reader :datasize + + FORMAT = "VVVV" + SIZEOF = 16 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, dataoff, datasize) + super(raw_data, offset, cmd, cmdsize) + @dataoff = dataoff + @datasize = datasize + end + end + + # A load command representing the offset to and size of an encrypted + # segment. Corresponds to LC_ENCRYPTION_INFO. + class EncryptionInfoCommand < LoadCommand + # @return [Fixnum] the offset to the encrypted segment + attr_reader :cryptoff + + # @return [Fixnum] the size of the encrypted segment + attr_reader :cryptsize + + # @return [Fixnum] the encryption system, or 0 if not encrypted yet + attr_reader :cryptid + + FORMAT = "VVVVV" + SIZEOF = 20 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid) + super(raw_data, offset, cmd, cmdsize) + @cryptoff = cryptoff + @cryptsize = cryptsize + @cryptid = cryptid + end + end + + # A load command representing the offset to and size of an encrypted + # segment. Corresponds to LC_ENCRYPTION_INFO_64. + class EncryptionInfoCommand64 < LoadCommand + # @return [Fixnum] the offset to the encrypted segment + attr_reader :cryptoff + + # @return [Fixnum] the size of the encrypted segment + attr_reader :cryptsize + + # @return [Fixnum] the encryption system, or 0 if not encrypted yet + attr_reader :cryptid + + # @return [Fixnum] 64-bit padding value + attr_reader :pad + + FORMAT = "VVVVVV" + SIZEOF = 24 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid) + super(raw_data, offset, cmd, cmdsize) + @cryptoff = cryptoff + @cryptsize = cryptsize + @cryptid = cryptid + @pad = pad + end + end + + # A load command containing the minimum OS version on which the binary + # was built to run. Corresponds to LC_VERSION_MIN_MACOSX and LC_VERSION_MIN_IPHONEOS. + class VersionMinCommand < LoadCommand + # @return [Fixnum] the version X.Y.Z packed as x16.y8.z8 + attr_reader :version + + # @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8 + attr_reader :sdk + + FORMAT = "VVVV" + SIZEOF = 16 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, version, sdk) + super(raw_data, offset, cmd, cmdsize) + @version = version + @sdk = sdk + end + + # A string representation of the binary's minimum OS version. + # @return [String] a string representing the minimum OS version. + def version_string + binary = "%032b" % version + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } + + segs.join(".") + end + + # A string representation of the binary's SDK version. + # @return [String] a string representing the SDK version. + def sdk_string + binary = "%032b" % sdk + segs = [ + binary[0..15], binary[16..23], binary[24..31] + ].map { |s| s.to_i(2) } + + segs.join(".") + end + end + + # A load command containing the file offsets and sizes of the new + # compressed form of the information dyld needs to load the image. + # Corresponds to LC_DYLD_INFO and LC_DYLD_INFO_ONLY. + class DyldInfoCommand < LoadCommand + # @return [Fixnum] the file offset to the rebase information + attr_reader :rebase_off + + # @return [Fixnum] the size of the rebase information + attr_reader :rebase_size + + # @return [Fixnum] the file offset to the binding information + attr_reader :bind_off + + # @return [Fixnum] the size of the binding information + attr_reader :bind_size + + # @return [Fixnum] the file offset to the weak binding information + attr_reader :weak_bind_off + + # @return [Fixnum] the size of the weak binding information + attr_reader :weak_bind_size + + # @return [Fixnum] the file offset to the lazy binding information + attr_reader :lazy_bind_off + + # @return [Fixnum] the size of the lazy binding information + attr_reader :lazy_bind_size + + # @return [Fixnum] the file offset to the export information + attr_reader :export_off + + # @return [Fixnum] the size of the export information + attr_reader :export_size + + FORMAT = "VVVVVVVVVVVV" + SIZEOF = 48 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, rebase_off, rebase_size, bind_off, + bind_size, weak_bind_off, weak_bind_size, lazy_bind_off, + lazy_bind_size, export_off, export_size) + super(raw_data, offset, cmd, cmdsize) + @rebase_off = rebase_off + @rebase_size = rebase_size + @bind_off = bind_off + @bind_size = bind_size + @weak_bind_off = weak_bind_off + @weak_bind_size = weak_bind_size + @lazy_bind_off = lazy_bind_off + @lazy_bind_size = lazy_bind_size + @export_off = export_off + @export_size = export_size + end + end + + # A load command containing linker options embedded in object files. + # Corresponds to LC_LINKER_OPTION. + class LinkerOptionCommand < LoadCommand + # @return [Fixnum] the number of strings + attr_reader :count + + FORMAT = "VVV" + SIZEOF = 12 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, count) + super(raw_data, offset, cmd, cmdsize) + @count = count + end + end + + # A load command specifying the offset of main(). Corresponds to LC_MAIN. + class EntryPointCommand < LoadCommand + # @return [Fixnum] the file (__TEXT) offset of main() + attr_reader :entryoff + + # @return [Fixnum] if not 0, the initial stack size. + attr_reader :stacksize + + FORMAT = "VVQQ" + SIZEOF = 24 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, entryoff, stacksize) + super(raw_data, offset, cmd, cmdsize) + @entryoff = entryoff + @stacksize = stacksize + end + end + + # A load command specifying the version of the sources used to build the + # binary. Corresponds to LC_SOURCE_VERSION. + class SourceVersionCommand < LoadCommand + # @return [Fixnum] the version packed as a24.b10.c10.d10.e10 + attr_reader :version + + FORMAT = "VVQ" + SIZEOF = 16 + + # @api private + def initialize(raw_data, offset, cmd, cmdsize, version) + super(raw_data, offset, cmd, cmdsize) + @version = version + end + + # A string representation of the sources used to build the binary. + # @return [String] a string representation of the version + def version_string + binary = "%064b" % version + segs = [ + binary[0..23], binary[24..33], binary[34..43], binary[44..53], + binary[54..63] + ].map { |s| s.to_i(2) } + + segs.join(".") + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/macho_file.rb b/Library/Homebrew/vendor/macho/macho/macho_file.rb new file mode 100644 index 0000000000..39d584c8e9 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb @@ -0,0 +1,531 @@ +module MachO + # Represents a Mach-O file, which contains a header and load commands + # as well as binary executable instructions. Mach-O binaries are + # architecture specific. + # @see https://en.wikipedia.org/wiki/Mach-O + # @see MachO::FatFile + class MachOFile + # @return [MachO::MachHeader] if the Mach-O is 32-bit + # @return [MachO::MachHeader64] if the Mach-O is 64-bit + attr_reader :header + + # @return [Array] an array of the file's load commands + attr_reader :load_commands + + # Creates a new MachOFile instance from a binary string. + # @param bin [String] a binary string containing raw Mach-O data + # @return [MachO::MachOFile] a new MachOFile + def self.new_from_bin(bin) + instance = allocate + instance.initialize_from_bin(bin) + + instance + end + + # Creates a new FatFile from the given filename. + # @param filename [String] the Mach-O file to load from + # @raise [ArgumentError] if the given filename does not exist + def initialize(filename) + raise ArgumentError.new("#{filetype}: no such file") unless File.exist?(filename) + + @filename = filename + @raw_data = open(@filename, "rb") { |f| f.read } + @header = get_mach_header + @load_commands = get_load_commands + end + + # @api private + def initialize_from_bin(bin) + @filename = nil + @raw_data = bin + @header = get_mach_header + @load_commands = get_load_commands + end + + # The file's raw Mach-O data. + # @return [String] the raw Mach-O data + def serialize + @raw_data + end + + # @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise + def magic32? + MachO.magic32?(header.magic) + end + + # @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise + def magic64? + MachO.magic64?(header.magic) + end + + # @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise + def object? + header.filetype == MH_OBJECT + end + + # @return [Boolean] true if the file is of type `MH_EXECUTE`, false otherwise + def executable? + header.filetype == MH_EXECUTE + end + + # @return [Boolean] true if the file is of type `MH_FVMLIB`, false otherwise + def fvmlib? + header.filetype == MH_FVMLIB + end + + # @return [Boolean] true if the file is of type `MH_CORE`, false otherwise + def core? + header.filetype == MH_CORE + end + + # @return [Boolean] true if the file is of type `MH_PRELOAD`, false otherwise + def preload? + header.filetype == MH_PRELOAD + end + + # @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise + def dylib? + header.filetype == MH_DYLIB + end + + # @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise + def dylinker? + header.filetype == MH_DYLINKER + end + + # @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise + def bundle? + header.filetype == MH_BUNDLE + end + + # @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise + def dsym? + header.filetype == MH_DSYM + end + + # @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise + def kext? + header.filetype == MH_KEXT_BUNDLE + end + + # @return [Fixnum] the file's magic number + def magic + header.magic + end + + # @return [String] a string representation of the file's magic number + def magic_string + MH_MAGICS[magic] + end + + # @return [String] a string representation of the Mach-O's filetype + def filetype + MH_FILETYPES[header.filetype] + end + + # @return [String] a string representation of the Mach-O's CPU type + def cputype + CPU_TYPES[header.cputype] + end + + # @return [String] a string representation of the Mach-O's CPU subtype + def cpusubtype + CPU_SUBTYPES[header.cpusubtype] + end + + # @return [Fixnum] the number of load commands in the Mach-O's header + def ncmds + header.ncmds + end + + # @return [Fixnum] the size of all load commands, in bytes + def sizeofcmds + header.sizeofcmds + end + + # @return [Fixnum] execution flags set by the linker + def flags + header.flags + end + + # All load commands of a given name. + # @example + # file.command("LC_LOAD_DYLIB") + # file[:LC_LOAD_DYLIB] + # @param [String, Symbol] name the load command ID + # @return [Array] an array of LoadCommands corresponding to `name` + def command(name) + load_commands.select { |lc| lc.type == name.to_sym } + end + + alias :[] :command + + # All load commands responsible for loading dylibs. + # @return [Array] an array of DylibCommands + def dylib_load_commands + load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) } + end + + # All segment load commands in the Mach-O. + # @return [Array] if the Mach-O is 32-bit + # @return [Array] if the Mach-O is 64-bit + def segments + if magic32? + command(:LC_SEGMENT) + else + command(:LC_SEGMENT_64) + end + end + + # The Mach-O's dylib ID, or `nil` if not a dylib. + # @example + # file.dylib_id # => 'libBar.dylib' + # @return [String, nil] the Mach-O's dylib ID + def dylib_id + if !dylib? + return nil + end + + dylib_id_cmd = command(:LC_ID_DYLIB).first + + dylib_id_cmd.name.to_s + end + + # Changes the Mach-O's dylib ID to `new_id`. Does nothing if not a dylib. + # @example + # file.dylib_id = "libFoo.dylib" + # @param new_id [String] the dylib's new ID + # @return [void] + # @raise [ArgumentError] if `new_id` is not a String + def dylib_id=(new_id) + if !new_id.is_a?(String) + raise ArgumentError.new("argument must be a String") + end + + if !dylib? + return nil + end + + dylib_cmd = command(:LC_ID_DYLIB).first + old_id = dylib_id + + set_name_in_dylib(dylib_cmd, old_id, new_id) + end + + # All shared libraries linked to the Mach-O. + # @return [Array] an array of all shared libraries + def linked_dylibs + dylib_load_commands.map(&:name).map(&:to_s) + end + + # Changes the shared library `old_name` to `new_name` + # @example + # file.change_install_name("/usr/lib/libWhatever.dylib", "/usr/local/lib/libWhatever2.dylib") + # @param old_name [String] the shared library's old name + # @param new_name [String] the shared library's new name + # @return [void] + # @raise [MachO::DylibUnknownError] if no shared library has the old name + def change_install_name(old_name, new_name) + dylib_cmd = dylib_load_commands.find { |d| d.name.to_s == old_name } + raise DylibUnknownError.new(old_name) if dylib_cmd.nil? + + set_name_in_dylib(dylib_cmd, old_name, new_name) + end + + alias :change_dylib :change_install_name + + # All runtime paths searched by the dynamic linker for the Mach-O. + # @return [Array] an array of all runtime paths + def rpaths + command(:LC_RPATH).map(&:path).map(&:to_s) + end + + # Changes the runtime path `old_path` to `new_path` + # @example + # file.change_rpath("/usr/lib", "/usr/local/lib") + # @param old_path [String] the old runtime path + # @param new_path [String] the new runtime path + # @return [void] + # @raise [MachO::RpathUnknownError] if no such old runtime path exists + # @api private + def change_rpath(old_path, new_path) + rpath_cmd = command(:LC_RPATH).find { |r| r.path.to_s == old_path } + raise RpathUnknownError.new(old_path) if rpath_cmd.nil? + + set_path_in_rpath(rpath_cmd, old_path, new_path) + end + + # All sections of the segment `segment`. + # @param segment [MachO::SegmentCommand, MachO::SegmentCommand64] the segment being inspected + # @return [Array] if the Mach-O is 32-bit + # @return [Array] if the Mach-O is 64-bit + def sections(segment) + sections = [] + + if !segment.is_a?(SegmentCommand) && !segment.is_a?(SegmentCommand64) + raise ArgumentError.new("not a valid segment") + end + + if segment.nsects.zero? + return sections + end + + offset = segment.offset + segment.class.bytesize + + segment.nsects.times do + if segment.is_a? SegmentCommand + sections << Section.new_from_bin(@raw_data.slice(offset, Section.bytesize)) + offset += Section.bytesize + else + sections << Section64.new_from_bin(@raw_data.slice(offset, Section64.bytesize)) + offset += Section64.bytesize + end + end + + sections + end + + # Write all Mach-O data to the given filename. + # @param filename [String] the file to write to + # @return [void] + def write(filename) + File.open(filename, "wb") { |f| f.write(@raw_data) } + end + + # Write all Mach-O data to the file used to initialize the instance. + # @raise [MachOError] if the instance was created from a binary string + # @return [void] + # @raise [MachO::MachOError] if the instance was initialized without a file + # @note Overwrites all data in the file! + def write! + if @filename.nil? + raise MachOError.new("cannot write to a default file when initialized from a binary string") + else + File.open(@filename, "wb") { |f| f.write(@raw_data) } + end + end + + private + + # The file's Mach-O header structure. + # @return [MachO::MachHeader] if the Mach-O is 32-bit + # @return [MachO::MachHeader64] if the Mach-O is 64-bit + # @private + def get_mach_header + magic = get_magic + cputype = get_cputype + cpusubtype = get_cpusubtype + filetype = get_filetype + ncmds = get_ncmds + sizeofcmds = get_sizeofcmds + flags = get_flags + + if MachO.magic32?(magic) + MachHeader.new(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) + else + # the reserved field is...reserved, so just fill it with 0 + MachHeader64.new(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags, 0) + end + end + + # The file's magic number. + # @return [Fixnum] the magic + # @raise [MachO::MagicError] if the magic is not valid Mach-O magic + # @raise [MachO::FatBinaryError] if the magic is for a Fat file + # @private + def get_magic + magic = @raw_data[0..3].unpack("N").first + + raise MagicError.new(magic) unless MachO.magic?(magic) + raise FatBinaryError.new if MachO.fat_magic?(magic) + + magic + end + + # The file's CPU type. + # @return [Fixnum] the CPU type + # @raise [MachO::CPUTypeError] if the CPU type is unknown + # @private + def get_cputype + cputype = @raw_data[4..7].unpack("V").first + + raise CPUTypeError.new(cputype) unless CPU_TYPES.key?(cputype) + + cputype + end + + # The file's CPU subtype. + # @return [Fixnum] the CPU subtype + # @raise [MachO::CPUSubtypeError] if the CPU subtype is unknown + # @private + def get_cpusubtype + cpusubtype = @raw_data[8..11].unpack("V").first + cpusubtype &= ~CPU_SUBTYPE_LIB64 # this mask isn't documented! + + raise CPUSubtypeError.new(cpusubtype) unless CPU_SUBTYPES.key?(cpusubtype) + + cpusubtype + end + + # The file's type. + # @return [Fixnum] the file type + # @raise [MachO::FiletypeError] if the file type is unknown + # @private + def get_filetype + filetype = @raw_data[12..15].unpack("V").first + + raise FiletypeError.new(filetype) unless MH_FILETYPES.key?(filetype) + + filetype + end + + # The number of load commands in the file. + # @return [Fixnum] the number of load commands + # @private + def get_ncmds + @raw_data[16..19].unpack("V").first + end + + # The size of all load commands, in bytes. + # return [Fixnum] the size of all load commands + # @private + def get_sizeofcmds + @raw_data[20..23].unpack("V").first + end + + # The Mach-O header's flags. + # @return [Fixnum] the flags + # @private + def get_flags + @raw_data[24..27].unpack("V").first + end + + # All load commands in the file. + # @return [Array] an array of load commands + # @raise [MachO::LoadCommandError] if an unknown load command is encountered + # @private + def get_load_commands + offset = header.class.bytesize + load_commands = [] + + header.ncmds.times do + cmd = @raw_data.slice(offset, 4).unpack("V").first + cmd_sym = LOAD_COMMANDS[cmd] + + raise LoadCommandError.new(cmd) if cmd_sym.nil? + + # why do I do this? i don't like declaring constants below + # classes, and i need them to resolve... + klass = MachO.const_get "#{LC_STRUCTURES[cmd_sym]}" + command = klass.new_from_bin(@raw_data, offset, @raw_data.slice(offset, klass.bytesize)) + + load_commands << command + offset += command.cmdsize + end + + load_commands + end + + # Updates the size of all load commands in the raw data. + # @param size [Fixnum] the new size, in bytes + # @return [void] + # @private + def set_sizeofcmds(size) + new_size = [size].pack("V") + @raw_data[20..23] = new_size + end + + # Updates the `name` field in a DylibCommand. + # @param dylib_cmd [MachO::DylibCommand] the dylib command + # @param old_name [String] the old dylib name + # @param new_name [String] the new dylib name + # @return [void] + # @private + def set_name_in_dylib(dylib_cmd, old_name, new_name) + set_lc_str_in_cmd(dylib_cmd, dylib_cmd.name, old_name, new_name) + end + + # Updates the `path` field in an RpathCommand. + # @param rpath_cmd [MachO::RpathCommand] the rpath command + # @param old_path [String] the old runtime name + # @param new_path [String] the new runtime name + # @return [void] + # @private + def set_path_in_rpath(rpath_cmd, old_path, new_path) + set_lc_str_in_cmd(rpath_cmd, rpath_cmd.path, old_path, new_path) + end + + # Updates a generic LCStr field in any LoadCommand. + # @param cmd [MachO::LoadCommand] the load command + # @param lc_str [MachO::LoadCommand::LCStr] the load command string + # @param old_str [String] the old string + # @param new_str [String] the new string + # @raise [MachO::HeaderPadError] if the new name exceeds the header pad buffer + # @private + def set_lc_str_in_cmd(cmd, lc_str, old_str, new_str) + if magic32? + cmd_round = 4 + else + cmd_round = 8 + end + + new_sizeofcmds = header.sizeofcmds + old_str = old_str.dup + new_str = new_str.dup + + old_pad = MachO.round(old_str.size + 1, cmd_round) - old_str.size + new_pad = MachO.round(new_str.size + 1, cmd_round) - new_str.size + + # pad the old and new IDs with null bytes to meet command bounds + old_str << "\x00" * old_pad + new_str << "\x00" * new_pad + + # calculate the new size of the cmd and sizeofcmds in MH + new_size = cmd.class.bytesize + new_str.size + new_sizeofcmds += new_size - cmd.cmdsize + + low_fileoff = 2**64 # ULLONGMAX + + # calculate the low file offset (offset to first section data) + segments.each do |seg| + sections(seg).each do |sect| + if sect.size != 0 && !sect.flag?(:S_ZEROFILL) && + !sect.flag?(:S_THREAD_LOCAL_ZEROFILL) && + sect.offset < low_fileoff + + low_fileoff = sect.offset + end + end + end + + if new_sizeofcmds + header.class.bytesize > low_fileoff + raise HeaderPadError.new(@filename) + end + + # update sizeofcmds in mach_header + set_sizeofcmds(new_sizeofcmds) + + # update cmdsize in the cmd + @raw_data[cmd.offset + 4, 4] = [new_size].pack("V") + + # delete the old str + @raw_data.slice!(cmd.offset + lc_str.to_i...cmd.offset + cmd.class.bytesize + old_str.size) + + # insert the new str + @raw_data.insert(cmd.offset + lc_str.to_i, new_str) + + # pad/unpad after new_sizeofcmds until offsets are corrected + null_pad = old_str.size - new_str.size + + if null_pad < 0 + @raw_data.slice!(new_sizeofcmds + header.class.bytesize, null_pad.abs) + else + @raw_data.insert(new_sizeofcmds + header.class.bytesize, "\x00" * null_pad) + end + + # synchronize fields with the raw data + @header = get_mach_header + @load_commands = get_load_commands + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/open.rb b/Library/Homebrew/vendor/macho/macho/open.rb new file mode 100644 index 0000000000..2c5fc04692 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/open.rb @@ -0,0 +1,16 @@ +module MachO + # Opens the given filename as a MachOFile or FatFile, depending on its magic. + # @param filename [String] the file being opened + # @return [MachO::MachOFile] if the file is a Mach-O + # @return [MachO::FatFile] if the file is a Fat file + def self.open(filename) + # open file and test magic instead of using exceptions for control? + begin + file = MachOFile.new(filename) + rescue FatBinaryError + file = FatFile.new(filename) + end + + file + end +end \ No newline at end of file diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb new file mode 100644 index 0000000000..8a3cbac024 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/sections.rb @@ -0,0 +1,159 @@ +module MachO + # type mask + SECTION_TYPE = 0x000000ff + + # attributes mask + SECTION_ATTRIBUTES = 0xffffff00 + + # user settable attributes mask + SECTION_ATTRIBUTES_USR = 0xff000000 + + # system settable attributes mask + SECTION_ATTRIBUTES_SYS = 0x00ffff00 + + # association of section flag symbols to values + # @api private + SECTION_FLAGS = { + :S_REGULAR => 0x0, + :S_ZEROFILL => 0x1, + :S_CSTRING_LITERALS => 0x2, + :S_4BYTE_LITERALS => 0x3, + :S_8BYTE_LITERALS => 0x4, + :S_LITERAL_POINTERS => 0x5, + :S_NON_LAZY_SYMBOL_POINTERS => 0x6, + :S_LAZY_SYMBOL_POINTERS => 0x7, + :S_SYMBOL_STUBS => 0x8, + :S_MOD_INIT_FUNC_POINTERS => 0x9, + :S_MOD_TERM_FUNC_POINTERS => 0xa, + :S_COALESCED => 0xb, + :S_GB_ZEROFILE => 0xc, + :S_INTERPOSING => 0xd, + :S_16BYTE_LITERALS => 0xe, + :S_DTRACE_DOF => 0xf, + :S_LAZY_DYLIB_SYMBOL_POINTERS => 0x10, + :S_THREAD_LOCAL_REGULAR => 0x11, + :S_THREAD_LOCAL_ZEROFILL => 0x12, + :S_THREAD_LOCAL_VARIABLES => 0x13, + :S_THREAD_LOCAL_VARIABLE_POINTERS => 0x14, + :S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => 0x15, + :S_ATTR_PURE_INSTRUCTIONS => 0x80000000, + :S_ATTR_NO_TOC => 0x40000000, + :S_ATTR_STRIP_STATIC_SYMS => 0x20000000, + :S_ATTR_NO_DEAD_STRIP => 0x10000000, + :S_ATTR_LIVE_SUPPORT => 0x08000000, + :S_ATTR_SELF_MODIFYING_CODE => 0x04000000, + :S_ATTR_DEBUG => 0x02000000, + :S_ATTR_SOME_INSTRUCTIONS => 0x00000400, + :S_ATTR_EXT_RELOC => 0x00000200, + :S_ATTR_LOC_RELOC => 0x00000100 + } + + # association of section name symbols to names + # @api private + SECTION_NAMES = { + :SECT_TEXT => "__text", + :SECT_FVMLIB_INIT0 => "__fvmlib_init0", + :SECT_FVMLIB_INIT1 => "__fvmlib_init1", + :SECT_DATA => "__data", + :SECT_BSS => "__bss", + :SECT_COMMON => "__common", + :SECT_OBJC_SYMBOLS => "__symbol_table", + :SECT_OBJC_MODULES => "__module_info", + :SECT_OBJC_STRINGS => "__selector_strs", + :SECT_OBJC_REFS => "__selector_refs", + :SECT_ICON_HEADER => "__header", + :SECT_ICON_TIFF => "__tiff" + } + + # Represents a section of a segment for 32-bit architectures. + class Section < MachOStructure + # @return [String] the name of the section, including null pad bytes + attr_reader :sectname + + # @return [String] the name of the segment's section, including null pad bytes + attr_reader :segname + + # @return [Fixnum] the memory address of the section + attr_reader :addr + + # @return [Fixnum] the size, in bytes, of the section + attr_reader :size + + # @return [Fixnum] the file offset of the section + attr_reader :offset + + # @return [Fixnum] the section alignment (power of 2) of the section + attr_reader :align + + # @return [Fixnum] the file offset of the section's relocation entries + attr_reader :reloff + + # @return [Fixnum] the number of relocation entries + attr_reader :nreloc + + # @return [Fixnum] flags for type and addrributes of the section + attr_reader :flags + + # @return [void] reserved (for offset or index) + attr_reader :reserved1 + + # @return [void] reserved (for count or sizeof) + attr_reader :reserved2 + + FORMAT = "a16a16VVVVVVVVV" + SIZEOF = 68 + + # @api private + def initialize(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2) + @sectname = sectname + @segname = segname + @addr = addr + @size = size + @offset = offset + @align = align + @reloff = reloff + @nreloc = nreloc + @flags = flags + @reserved1 = reserved1 + @reserved2 = reserved2 + end + + # @return [String] the section's name, with any trailing NULL characters removed + def section_name + @sectname.delete("\x00") + end + + # @return [String] the parent segment's name, with any trailing NULL characters removed + def segment_name + @segname.delete("\x00") + end + + # @example + # puts "this section is regular" if sect.flag?(:S_REGULAR) + # @param flag [Symbol] a section flag symbol + # @return [Boolean] true if `flag` is present in the section's flag field + def flag?(flag) + flag = SECTION_FLAGS[flag] + return false if flag.nil? + flags & flag == flag + end + end + + # Represents a section of a segment for 64-bit architectures. + class Section64 < Section + # @return [void] reserved + attr_reader :reserved3 + + FORMAT = "a16a16QQVVVVVVVV" + SIZEOF = 80 + + # @api private + def initialize(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2, reserved3) + super(sectname, segname, addr, size, offset, align, reloff, + nreloc, flags, reserved1, reserved2) + @reserved3 = reserved3 + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb new file mode 100644 index 0000000000..77aa18852d --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/structure.rb @@ -0,0 +1,22 @@ +module MachO + # A general purpose pseudo-structure. + # @abstract + class MachOStructure + # The format of the data structure, in String#unpack format. + FORMAT = "" + + # The size of the data structure, in bytes. + SIZEOF = 0 + + # @return [Fixnum] the size, in bytes, of the represented structure. + def self.bytesize + self::SIZEOF + end + + # @return [MachO::MachOStructure] a new MachOStructure initialized with `bin` + # @api private + def self.new_from_bin(bin) + self.new(*bin.unpack(self::FORMAT)) + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/tools.rb b/Library/Homebrew/vendor/macho/macho/tools.rb new file mode 100644 index 0000000000..18f20fb802 --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/tools.rb @@ -0,0 +1,65 @@ +module MachO + # A collection of convenient methods for common operations on Mach-O and Fat binaries. + module Tools + # @param filename [String] the Mach-O or Fat binary being read + # @return [Array] an array of all dylibs linked to the binary + def self.dylibs(filename) + file = MachO.open(filename) + + file.linked_dylibs + end + + # Changes the dylib ID of a Mach-O or Fat binary, overwriting the source file. + # @param filename [String] the Mach-O or Fat binary being modified + # @param new_id [String] the new dylib ID for the binary + # @return [void] + # @todo unstub for fat files + def self.change_dylib_id(filename, new_id) + file = MachO.open(filename) + + file.dylib_id = new_id + file.write! + end + + # Changes a shared library install name in a Mach-O or Fat binary, overwriting the source file. + # @param filename [String] the Mach-O or Fat binary being modified + # @param old_name [String] the old shared library name + # @param new_name [String] the new shared library name + # @return [void] + # @todo unstub for fat files + def self.change_install_name(filename, old_name, new_name) + file = MachO.open(filename) + + file.change_install_name(old_name, new_name) + file.write! + end + + # Changes a runtime path in a Mach-O or Fat binary, overwriting the source file. + # @param filename [String] the Mach-O or Fat binary being modified + # @param old_path [String] the old runtime path + # @param new_path [String] the new runtime path + # @return [void] + # @todo unstub + def self.change_rpath(filename, old_path, new_path) + raise "stub" + end + + # Add a runtime path to a Mach-O or Fat binary, overwriting the source file. + # @param filename [String] the Mach-O or Fat binary being modified + # @param new_path [String] the new runtime path + # @return [void] + # @todo unstub + def self.add_rpath(filename, new_path) + raise "stub" + end + + # Delete a runtime path from a Mach-O or Fat binary, overwriting the source file. + # @param filename [String] the Mach-O or Fat binary being modified + # @param old_path [String] the old runtime path + # @return [void] + # @todo unstub + def self.delete_rpath(filename, old_path) + raise "stub" + end + end +end diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb new file mode 100644 index 0000000000..3b06cc125e --- /dev/null +++ b/Library/Homebrew/vendor/macho/macho/utils.rb @@ -0,0 +1,36 @@ +module MachO + # @param value [Fixnum] the number being rounded + # @param round [Fixnum] the number being rounded with + # @return [Fixnum] the next number >= `value` such that `round` is its divisor + # @see http://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c + def self.round(value, round) + round -= 1 + value += round + value &= ~round + value + end + + # @param num [Fixnum] the number being checked + # @return [Boolean] true if `num` is a valid Mach-O magic number, false otherwise + def self.magic?(num) + MH_MAGICS.has_key?(num) + end + + # @param num [Fixnum] the number being checked + # @return [Boolean] true if `num` is a valid Fat magic number, false otherwise + def self.fat_magic?(num) + num == FAT_MAGIC || num == FAT_CIGAM + end + + # @param num [Fixnum] the number being checked + # @return [Boolean] true if `num` is a valid 32-bit magic number, false otherwise + def self.magic32?(num) + num == MH_MAGIC || num == MH_CIGAM + end + + # @param num [Fixnum] the number being checked + # @return [Boolean] true if `num` is a valid 64-bit magic number, false otherwise + def self.magic64?(num) + num == MH_MAGIC_64 || num == MH_CIGAM_64 + end +end