diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md index f998d26c58..906d429189 100644 --- a/Library/Homebrew/vendor/README.md +++ b/Library/Homebrew/vendor/README.md @@ -3,7 +3,7 @@ Vendored Dependencies * [plist](https://github.com/bleything/plist), version 3.1.0 -* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 0.2.6 +* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 1.1.0 ## Licenses: @@ -33,7 +33,7 @@ Vendored Dependencies ### ruby-macho > The MIT License -> Copyright (c) 2015, 2016 William Woodruff +> Copyright (c) 2015, 2016, 2017 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 diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb index de1d4ff435..b7f20ea190 100644 --- a/Library/Homebrew/vendor/macho/macho.rb +++ b/Library/Homebrew/vendor/macho/macho.rb @@ -5,7 +5,6 @@ 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" @@ -13,5 +12,29 @@ require "#{File.dirname(__FILE__)}/macho/tools" # The primary namespace for ruby-macho. module MachO # release version - VERSION = "0.2.6".freeze + VERSION = "1.1.0".freeze + + # Opens the given filename as a MachOFile or FatFile, depending on its magic. + # @param filename [String] the file being opened + # @return [MachOFile] if the file is a Mach-O + # @return [FatFile] if the file is a Fat file + # @raise [ArgumentError] if the given file does not exist + # @raise [TruncatedFileError] if the file is too small to have a valid header + # @raise [MagicError] if the file's magic is not valid Mach-O magic + def self.open(filename) + raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) + raise TruncatedFileError unless File.stat(filename).size >= 4 + + magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first + + if Utils.fat_magic?(magic) + file = FatFile.new(filename) + elsif Utils.magic?(magic) + file = MachOFile.new(filename) + else + raise MagicError, magic + end + + file + end end diff --git a/Library/Homebrew/vendor/macho/macho/exceptions.rb b/Library/Homebrew/vendor/macho/macho/exceptions.rb index 262c195a35..14c2c22ae0 100644 --- a/Library/Homebrew/vendor/macho/macho/exceptions.rb +++ b/Library/Homebrew/vendor/macho/macho/exceptions.rb @@ -80,7 +80,8 @@ module MachO # @param cputype [Fixnum] the CPU type of the unknown pair # @param cpusubtype [Fixnum] the CPU sub-type of the unknown pair def initialize(cputype, cpusubtype) - super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype} (for CPU type: 0x#{"%08x" % cputype})" + super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype}" \ + " (for CPU type: 0x#{"%08x" % cputype})" end end @@ -108,13 +109,15 @@ module MachO end end - # Raised when the number of arguments used to create a load command manually is wrong. + # Raised when the number of arguments used to create a load command manually + # is wrong. class LoadCommandCreationArityError < MachOError # @param cmd_sym [Symbol] the load command's symbol # @param expected_arity [Fixnum] the number of arguments expected # @param actual_arity [Fixnum] the number of arguments received def initialize(cmd_sym, expected_arity, actual_arity) - super "Expected #{expected_arity} arguments for #{cmd_sym} creation, got #{actual_arity}" + super "Expected #{expected_arity} arguments for #{cmd_sym} creation," \ + " got #{actual_arity}" end end @@ -130,7 +133,8 @@ module MachO class LCStrMalformedError < MachOError # @param lc [MachO::LoadCommand] the load command containing the string def initialize(lc) - super "Load command #{lc.type} at offset #{lc.view.offset} contains a malformed string" + super "Load command #{lc.type} at offset #{lc.view.offset} contains a" \ + " malformed string" end end diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb index 9f29922e62..351be5ac68 100644 --- a/Library/Homebrew/vendor/macho/macho/fat_file.rb +++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb @@ -1,24 +1,50 @@ +require "forwardable" + 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 + # @see MachOFile class FatFile + extend Forwardable + # @return [String] the filename loaded from, or nil if loaded from a binary string attr_accessor :filename - # @return [MachO::FatHeader] the file's header + # @return [Headers::FatHeader] the file's header attr_reader :header - # @return [Array] an array of fat architectures + # @return [Array] an array of fat architectures attr_reader :fat_archs - # @return [Array] an array of Mach-O binaries + # @return [Array] an array of Mach-O binaries attr_reader :machos + # Creates a new FatFile from the given (single-arch) Mach-Os + # @param machos [Array] the machos to combine + # @return [FatFile] a new FatFile containing the give machos + def self.new_from_machos(*machos) + header = Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size) + offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize) + fat_archs = [] + machos.each do |macho| + fat_archs << Headers::FatArch.new(macho.header.cputype, + macho.header.cpusubtype, + offset, macho.serialize.bytesize, + macho.alignment) + offset += macho.serialize.bytesize + end + + bin = header.serialize + bin << fat_archs.map(&:serialize).join + bin << machos.map(&:serialize).join + + new_from_bin(bin) + end + # Creates a new FatFile instance from a binary string. # @param bin [String] a binary string containing raw Mach-O data - # @return [MachO::FatFile] a new FatFile + # @return [FatFile] a new FatFile def self.new_from_bin(bin) instance = allocate instance.initialize_from_bin(bin) @@ -38,7 +64,7 @@ module MachO end # Initializes a new FatFile instance from a binary string. - # @see MachO::FatFile.new_from_bin + # @see new_from_bin # @api private def initialize_from_bin(bin) @filename = nil @@ -52,70 +78,41 @@ module MachO @raw_data end - # @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise - def object? - machos.first.object? - end + # @!method object? + # @return (see MachO::MachOFile#object?) + # @!method executable? + # @return (see MachO::MachOFile#executable?) + # @!method fvmlib? + # @return (see MachO::MachOFile#fvmlib?) + # @!method core? + # @return (see MachO::MachOFile#core?) + # @!method preload? + # @return (see MachO::MachOFile#preload?) + # @!method dylib? + # @return (see MachO::MachOFile#dylib?) + # @!method dylinker? + # @return (see MachO::MachOFile#dylinker?) + # @!method bundle? + # @return (see MachO::MachOFile#bundle?) + # @!method dsym? + # @return (see MachO::MachOFile#dsym?) + # @!method kext? + # @return (see MachO::MachOFile#kext?) + # @!method filetype + # @return (see MachO::MachOFile#filetype) + # @!method dylib_id + # @return (see MachO::MachOFile#dylib_id) + def_delegators :canonical_macho, :object?, :executable?, :fvmlib?, + :core?, :preload?, :dylib?, :dylinker?, :bundle?, + :dsym?, :kext?, :filetype, :dylib_id - # @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 + # @!method magic + # @return (see MachO::Headers::FatHeader#magic) + def_delegators :header, :magic # @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 [Symbol] the filetype - def filetype - machos.first.filetype + Headers::MH_MAGICS[magic] end # Populate the instance's fields with the raw Fat Mach-O data. @@ -128,21 +125,13 @@ module MachO end # All load commands responsible for loading dylibs in the file's Mach-O's. - # @return [Array] an array of DylibCommands + # @return [Array] an array of DylibCommands def dylib_load_commands machos.map(&:dylib_load_commands).flatten 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 - # @see MachO::MachOFile#linked_dylibs - 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. + # Changes the file's dylib ID to `new_id`. If the file is not a dylib, + # does nothing. # @example # file.change_dylib_id('libFoo.dylib') # @param new_id [String] the new dylib ID @@ -151,7 +140,7 @@ module MachO # if false, fail only if all slices fail. # @return [void] # @raise [ArgumentError] if `new_id` is not a String - # @see MachO::MachOFile#linked_dylibs + # @see MachOFile#linked_dylibs def change_dylib_id(new_id, options = {}) raise ArgumentError, "argument must be a String" unless new_id.is_a?(String) return unless machos.all?(&:dylib?) @@ -167,7 +156,7 @@ module MachO # All shared libraries linked to the file's Mach-Os. # @return [Array] an array of all shared libraries - # @see MachO::MachOFile#linked_dylibs + # @see MachOFile#linked_dylibs def linked_dylibs # Individual architectures in a fat binary can link to different subsets # of libraries, but at this point we want to have the full picture, i.e. @@ -175,8 +164,9 @@ module MachO machos.map(&:linked_dylibs).flatten.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. + # 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 @@ -185,7 +175,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#change_install_name + # @see MachOFile#change_install_name def change_install_name(old_name, new_name, options = {}) each_macho(options) do |macho| macho.change_install_name(old_name, new_name, options) @@ -198,7 +188,7 @@ module MachO # All runtime paths associated with the file's Mach-Os. # @return [Array] an array of all runtime paths - # @see MachO::MachOFile#rpaths + # @see MachOFile#rpaths def rpaths # Can individual architectures have different runtime paths? machos.map(&:rpaths).flatten.uniq @@ -211,7 +201,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#change_rpath + # @see MachOFile#change_rpath def change_rpath(old_path, new_path, options = {}) each_macho(options) do |macho| macho.change_rpath(old_path, new_path, options) @@ -226,7 +216,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return [void] - # @see MachO::MachOFile#add_rpath + # @see MachOFile#add_rpath def add_rpath(path, options = {}) each_macho(options) do |macho| macho.add_rpath(path, options) @@ -241,7 +231,7 @@ module MachO # @option options [Boolean] :strict (true) if true, fail if one slice fails. # if false, fail only if all slices fail. # @return void - # @see MachO::MachOFile#delete_rpath + # @see MachOFile#delete_rpath def delete_rpath(path, options = {}) each_macho(options) do |macho| macho.delete_rpath(path, options) @@ -254,20 +244,21 @@ module MachO # @example # file.extract(:i386) # => MachO::MachOFile # @param cputype [Symbol] 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 + # @return [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 + # @return [void] 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. # @return [void] - # @raise [MachO::MachOError] if the instance was initialized without a file + # @raise [MachOError] if the instance was initialized without a file # @note Overwrites all data in the file! def write! if filename.nil? @@ -280,17 +271,18 @@ module MachO private # Obtain the fat header from raw file data. - # @return [MachO::FatHeader] the fat header - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid 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 - # @raise [MachO::JavaClassFileError] if the file is a Java classfile + # @return [Headers::FatHeader] the fat header + # @raise [TruncatedFileError] if the file is too small to have a + # valid header + # @raise [MagicError] if the magic is not valid Mach-O magic + # @raise [MachOBinaryError] if the magic is for a non-fat Mach-O file + # @raise [JavaClassFileError] if the file is a Java classfile # @api private def populate_fat_header # the smallest fat Mach-O header is 8 bytes raise TruncatedFileError if @raw_data.size < 8 - fh = FatHeader.new_from_bin(:big, @raw_data[0, FatHeader.bytesize]) + fh = Headers::FatHeader.new_from_bin(:big, @raw_data[0, Headers::FatHeader.bytesize]) raise MagicError, fh.magic unless Utils.magic?(fh.magic) raise MachOBinaryError unless Utils.fat_magic?(fh.magic) @@ -308,22 +300,22 @@ module MachO end # Obtain an array of fat architectures from raw file data. - # @return [Array] an array of fat architectures + # @return [Array] an array of fat architectures # @api private def populate_fat_archs archs = [] - fa_off = FatHeader.bytesize - fa_len = FatArch.bytesize + fa_off = Headers::FatHeader.bytesize + fa_len = Headers::FatArch.bytesize header.nfat_arch.times do |i| - archs << FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len]) + archs << Headers::FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len]) end archs end # Obtain an array of Mach-O blobs from raw file data. - # @return [Array] an array of Mach-Os + # @return [Array] an array of Mach-Os # @api private def populate_machos machos = [] @@ -351,7 +343,7 @@ module MachO # @option options [Boolean] :strict (true) whether or not to fail loudly # with an exception if at least one Mach-O raises an exception. If false, # only raises an exception if *all* Mach-Os raise exceptions. - # @raise [MachO::RecoverableModificationError] under the conditions of + # @raise [RecoverableModificationError] under the conditions of # the `:strict` option above. # @api private def each_macho(options = {}) @@ -373,5 +365,13 @@ module MachO # Non-strict mode: Raise first error if *all* Mach-O slices failed. raise errors.first if errors.size == machos.size end + + # Return a single-arch Mach-O that represents this fat Mach-O for purposes + # of delegation. + # @return [MachOFile] the Mach-O file + # @api private + def canonical_macho + machos.first + end end end diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb index 7272503af9..08a4b80c4c 100644 --- a/Library/Homebrew/vendor/macho/macho/headers.rb +++ b/Library/Homebrew/vendor/macho/macho/headers.rb @@ -1,587 +1,665 @@ module MachO - # big-endian fat magic - # @api private - FAT_MAGIC = 0xcafebabe - - # little-endian fat magic - # this is defined, but should never appear in ruby-macho code because - # fat headers are always big-endian and therefore always unpacked as such. - # @api private - FAT_CIGAM = 0xbebafeca - - # 32-bit big-endian magic - # @api private - MH_MAGIC = 0xfeedface - - # 32-bit little-endian magic - # @api private - MH_CIGAM = 0xcefaedfe - - # 64-bit big-endian magic - # @api private - MH_MAGIC_64 = 0xfeedfacf - - # 64-bit little-endian magic - # @api private - MH_CIGAM_64 = 0xcffaedfe - - # association of magic numbers to string representations - # @api private - MH_MAGICS = { - FAT_MAGIC => "FAT_MAGIC", - MH_MAGIC => "MH_MAGIC", - MH_CIGAM => "MH_CIGAM", - MH_MAGIC_64 => "MH_MAGIC_64", - MH_CIGAM_64 => "MH_CIGAM_64", - }.freeze - - # mask for CPUs with 64-bit architectures (when running a 64-bit ABI?) - # @api private - CPU_ARCH_ABI64 = 0x01000000 - - # any CPU (unused?) - # @api private - CPU_TYPE_ANY = -1 - - # m68k compatible CPUs - # @api private - CPU_TYPE_MC680X0 = 0x06 - - # i386 and later compatible CPUs - # @api private - CPU_TYPE_I386 = 0x07 - - # x86_64 (AMD64) compatible CPUs - # @api private - CPU_TYPE_X86_64 = (CPU_TYPE_I386 | CPU_ARCH_ABI64) - - # 32-bit ARM compatible CPUs - # @api private - CPU_TYPE_ARM = 0x0c - - # m88k compatible CPUs - # @api private - CPU_TYPE_MC88000 = 0xd - - # 64-bit ARM compatible CPUs - # @api private - CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64) - - # PowerPC compatible CPUs - # @api private - CPU_TYPE_POWERPC = 0x12 - - # PowerPC64 compatible CPUs - # @api private - CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) - - # association of cpu types to symbol representations - # @api private - CPU_TYPES = { - CPU_TYPE_ANY => :any, - CPU_TYPE_I386 => :i386, - CPU_TYPE_X86_64 => :x86_64, - CPU_TYPE_ARM => :arm, - CPU_TYPE_ARM64 => :arm64, - CPU_TYPE_POWERPC => :ppc, - CPU_TYPE_POWERPC64 => :ppc64, - }.freeze - - # mask for CPU subtype capabilities - # @api private - CPU_SUBTYPE_MASK = 0xff000000 - - # 64-bit libraries (undocumented!) - # @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html - # @api private - CPU_SUBTYPE_LIB64 = 0x80000000 - - # the lowest common sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_I386 = 3 - - # the i486 sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_486 = 4 - - # the i486SX sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_486SX = 132 - - # the i586 (P5, Pentium) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_586 = 5 - - # @see CPU_SUBTYPE_586 - # @api private - CPU_SUBTYPE_PENT = CPU_SUBTYPE_586 - - # the Pentium Pro (P6) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTPRO = 22 - - # the Pentium II (P6, M3?) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTII_M3 = 54 - - # the Pentium II (P6, M5?) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTII_M5 = 86 - - # the Pentium 4 (Netburst) sub-type for `CPU_TYPE_I386` - # @api private - CPU_SUBTYPE_PENTIUM_4 = 10 - - # the lowest common sub-type for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC680X0_ALL = 1 - - # @see CPU_SUBTYPE_MC680X0_ALL - # @api private - CPU_SUBTYPE_MC68030 = CPU_SUBTYPE_MC680X0_ALL - - # the 040 subtype for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC68040 = 2 - - # the 030 subtype for `CPU_TYPE_MC680X0` - # @api private - CPU_SUBTYPE_MC68030_ONLY = 3 - - # the lowest common sub-type for `CPU_TYPE_X86_64` - # @api private - CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386 - - # the Haskell sub-type for `CPU_TYPE_X86_64` - # @api private - CPU_SUBTYPE_X86_64_H = 8 - - # the lowest common sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_ALL = 0 - - # the v4t sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V4T = 5 - - # the v6 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V6 = 6 - - # the v5 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V5TEJ = 7 - - # the xscale (v5 family) sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_XSCALE = 8 - - # the v7 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7 = 9 - - # the v7f (Cortex A9) sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7F = 10 - - # the v7s ("Swift") sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7S = 11 - - # the v7k ("Kirkwood40") sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7K = 12 - - # the v6m sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V6M = 14 - - # the v7m sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7M = 15 - - # the v7em sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V7EM = 16 - - # the v8 sub-type for `CPU_TYPE_ARM` - # @api private - CPU_SUBTYPE_ARM_V8 = 13 - - # the lowest common sub-type for `CPU_TYPE_ARM64` - # @api private - CPU_SUBTYPE_ARM64_ALL = 0 - - # the v8 sub-type for `CPU_TYPE_ARM64` - # @api private - CPU_SUBTYPE_ARM64_V8 = 1 - - # the lowest common sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88000_ALL = 0 - - # @see CPU_SUBTYPE_MC88000_ALL - # @api private - CPU_SUBTYPE_MMAX_JPC = CPU_SUBTYPE_MC88000_ALL - - # the 100 sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88100 = 1 - - # the 110 sub-type for `CPU_TYPE_MC88000` - # @api private - CPU_SUBTYPE_MC88110 = 2 - - # the lowest common sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_ALL = 0 - - # the 601 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_601 = 1 - - # the 602 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_602 = 2 - - # the 603 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603 = 3 - - # the 603e (G2) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603E = 4 - - # the 603ev sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_603EV = 5 - - # the 604 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_604 = 6 - - # the 604e sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_604E = 7 - - # the 620 sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_620 = 8 - - # the 750 (G3) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_750 = 9 - - # the 7400 (G4) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_7400 = 10 - - # the 7450 (G4 "Voyager") sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_7450 = 11 - - # the 970 (G5) sub-type for `CPU_TYPE_POWERPC` - # @api private - CPU_SUBTYPE_POWERPC_970 = 100 - - # any CPU sub-type for CPU type `CPU_TYPE_POWERPC64` - # @api private - CPU_SUBTYPE_POWERPC64_ALL = CPU_SUBTYPE_POWERPC_ALL - - # association of CPU types/subtype pairs to symbol representations in - # (very) roughly descending order of commonness - # @see https://opensource.apple.com/source/cctools/cctools-877.8/libstuff/arch.c - # @api private - CPU_SUBTYPES = { - CPU_TYPE_I386 => { - CPU_SUBTYPE_I386 => :i386, - CPU_SUBTYPE_486 => :i486, - CPU_SUBTYPE_486SX => :i486SX, - CPU_SUBTYPE_586 => :i586, # also "pentium" in arch(3) - CPU_SUBTYPE_PENTPRO => :i686, # also "pentpro" in arch(3) - CPU_SUBTYPE_PENTII_M3 => :pentIIm3, - CPU_SUBTYPE_PENTII_M5 => :pentIIm5, - CPU_SUBTYPE_PENTIUM_4 => :pentium4, - }.freeze, - CPU_TYPE_X86_64 => { - CPU_SUBTYPE_X86_64_ALL => :x86_64, - CPU_SUBTYPE_X86_64_H => :x86_64h, - }.freeze, - CPU_TYPE_ARM => { - CPU_SUBTYPE_ARM_ALL => :arm, - CPU_SUBTYPE_ARM_V4T => :armv4t, - CPU_SUBTYPE_ARM_V6 => :armv6, - CPU_SUBTYPE_ARM_V5TEJ => :armv5, - CPU_SUBTYPE_ARM_XSCALE => :xscale, - CPU_SUBTYPE_ARM_V7 => :armv7, - CPU_SUBTYPE_ARM_V7F => :armv7f, - CPU_SUBTYPE_ARM_V7S => :armv7s, - CPU_SUBTYPE_ARM_V7K => :armv7k, - CPU_SUBTYPE_ARM_V6M => :armv6m, - CPU_SUBTYPE_ARM_V7M => :armv7m, - CPU_SUBTYPE_ARM_V7EM => :armv7em, - CPU_SUBTYPE_ARM_V8 => :armv8, - }.freeze, - CPU_TYPE_ARM64 => { - CPU_SUBTYPE_ARM64_ALL => :arm64, - CPU_SUBTYPE_ARM64_V8 => :arm64v8, - }.freeze, - CPU_TYPE_POWERPC => { - CPU_SUBTYPE_POWERPC_ALL => :ppc, - CPU_SUBTYPE_POWERPC_601 => :ppc601, - CPU_SUBTYPE_POWERPC_603 => :ppc603, - CPU_SUBTYPE_POWERPC_603E => :ppc603e, - CPU_SUBTYPE_POWERPC_603EV => :ppc603ev, - CPU_SUBTYPE_POWERPC_604 => :ppc604, - CPU_SUBTYPE_POWERPC_604E => :ppc604e, - CPU_SUBTYPE_POWERPC_750 => :ppc750, - CPU_SUBTYPE_POWERPC_7400 => :ppc7400, - CPU_SUBTYPE_POWERPC_7450 => :ppc7450, - CPU_SUBTYPE_POWERPC_970 => :ppc970, - }.freeze, - CPU_TYPE_POWERPC64 => { - CPU_SUBTYPE_POWERPC64_ALL => :ppc64, - # apparently the only exception to the naming scheme - CPU_SUBTYPE_POWERPC_970 => :ppc970_64, - }.freeze, - CPU_TYPE_MC680X0 => { - CPU_SUBTYPE_MC680X0_ALL => :m68k, - CPU_SUBTYPE_MC68030 => :mc68030, - CPU_SUBTYPE_MC68040 => :mc68040, - }, - CPU_TYPE_MC88000 => { - CPU_SUBTYPE_MC88000_ALL => :m88k, - }, - }.freeze - - # relocatable object file - # @api private - MH_OBJECT = 0x1 - - # demand paged executable file - # @api private - MH_EXECUTE = 0x2 - - # fixed VM shared library file - # @api private - MH_FVMLIB = 0x3 - - # core dump file - # @api private - MH_CORE = 0x4 - - # preloaded executable file - # @api private - MH_PRELOAD = 0x5 - - # dynamically bound shared library - # @api private - MH_DYLIB = 0x6 - - # dynamic link editor - # @api private - MH_DYLINKER = 0x7 - - # dynamically bound bundle file - # @api private - MH_BUNDLE = 0x8 - - # shared library stub for static linking only, no section contents - # @api private - MH_DYLIB_STUB = 0x9 - - # companion file with only debug sections - # @api private - MH_DSYM = 0xa - - # x86_64 kexts - # @api private - MH_KEXT_BUNDLE = 0xb - - # association of filetypes to Symbol representations - # @api private - MH_FILETYPES = { - MH_OBJECT => :object, - MH_EXECUTE => :execute, - MH_FVMLIB => :fvmlib, - MH_CORE => :core, - MH_PRELOAD => :preload, - MH_DYLIB => :dylib, - MH_DYLINKER => :dylinker, - MH_BUNDLE => :bundle, - MH_DYLIB_STUB => :dylib_stub, - MH_DSYM => :dsym, - MH_KEXT_BUNDLE => :kext_bundle, - }.freeze - - # 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, - }.freeze - - # 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 - - # always big-endian - # @see MachOStructure::FORMAT + # Classes and constants for parsing the headers of Mach-O binaries. + module Headers + # big-endian fat magic # @api private - FORMAT = "N2".freeze + FAT_MAGIC = 0xcafebabe - # @see MachOStructure::SIZEOF + # little-endian fat magic + # this is defined, but should never appear in ruby-macho code because + # fat headers are always big-endian and therefore always unpacked as such. # @api private - SIZEOF = 8 + FAT_CIGAM = 0xbebafeca + # 32-bit big-endian magic # @api private - def initialize(magic, nfat_arch) - @magic = magic - @nfat_arch = nfat_arch - end - end + MH_MAGIC = 0xfeedface - # 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 - - # always big-endian - # @see MachOStructure::FORMAT + # 32-bit little-endian magic # @api private - FORMAT = "N5".freeze + MH_CIGAM = 0xcefaedfe - # @see MachOStructure::SIZEOF + # 64-bit big-endian magic # @api private - SIZEOF = 20 + MH_MAGIC_64 = 0xfeedfacf + # 64-bit little-endian magic # @api private - def initialize(cputype, cpusubtype, offset, size, align) - @cputype = cputype - @cpusubtype = cpusubtype - @offset = offset - @size = size - @align = align - end - end + MH_CIGAM_64 = 0xcffaedfe - # 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 - - # @see MachOStructure::FORMAT + # association of magic numbers to string representations # @api private - FORMAT = "L=7".freeze + MH_MAGICS = { + FAT_MAGIC => "FAT_MAGIC", + MH_MAGIC => "MH_MAGIC", + MH_CIGAM => "MH_CIGAM", + MH_MAGIC_64 => "MH_MAGIC_64", + MH_CIGAM_64 => "MH_CIGAM_64", + }.freeze - # @see MachOStructure::SIZEOF + # mask for CPUs with 64-bit architectures (when running a 64-bit ABI?) # @api private - SIZEOF = 28 + CPU_ARCH_ABI64 = 0x01000000 + # any CPU (unused?) # @api private - def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, - flags) - @magic = magic - @cputype = cputype - # For now we're not interested in additional capability bits also to be - # found in the `cpusubtype` field. We only care about the CPU sub-type. - @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK - @filetype = filetype - @ncmds = ncmds - @sizeofcmds = sizeofcmds - @flags = flags + CPU_TYPE_ANY = -1 + + # m68k compatible CPUs + # @api private + CPU_TYPE_MC680X0 = 0x06 + + # i386 and later compatible CPUs + # @api private + CPU_TYPE_I386 = 0x07 + + # x86_64 (AMD64) compatible CPUs + # @api private + CPU_TYPE_X86_64 = (CPU_TYPE_I386 | CPU_ARCH_ABI64) + + # 32-bit ARM compatible CPUs + # @api private + CPU_TYPE_ARM = 0x0c + + # m88k compatible CPUs + # @api private + CPU_TYPE_MC88000 = 0xd + + # 64-bit ARM compatible CPUs + # @api private + CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64) + + # PowerPC compatible CPUs + # @api private + CPU_TYPE_POWERPC = 0x12 + + # PowerPC64 compatible CPUs + # @api private + CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) + + # association of cpu types to symbol representations + # @api private + CPU_TYPES = { + CPU_TYPE_ANY => :any, + CPU_TYPE_I386 => :i386, + CPU_TYPE_X86_64 => :x86_64, + CPU_TYPE_ARM => :arm, + CPU_TYPE_ARM64 => :arm64, + CPU_TYPE_POWERPC => :ppc, + CPU_TYPE_POWERPC64 => :ppc64, + }.freeze + + # mask for CPU subtype capabilities + # @api private + CPU_SUBTYPE_MASK = 0xff000000 + + # 64-bit libraries (undocumented!) + # @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html + # @api private + CPU_SUBTYPE_LIB64 = 0x80000000 + + # the lowest common sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_I386 = 3 + + # the i486 sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_486 = 4 + + # the i486SX sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_486SX = 132 + + # the i586 (P5, Pentium) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_586 = 5 + + # @see CPU_SUBTYPE_586 + # @api private + CPU_SUBTYPE_PENT = CPU_SUBTYPE_586 + + # the Pentium Pro (P6) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTPRO = 22 + + # the Pentium II (P6, M3?) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTII_M3 = 54 + + # the Pentium II (P6, M5?) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTII_M5 = 86 + + # the Pentium 4 (Netburst) sub-type for `CPU_TYPE_I386` + # @api private + CPU_SUBTYPE_PENTIUM_4 = 10 + + # the lowest common sub-type for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC680X0_ALL = 1 + + # @see CPU_SUBTYPE_MC680X0_ALL + # @api private + CPU_SUBTYPE_MC68030 = CPU_SUBTYPE_MC680X0_ALL + + # the 040 subtype for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC68040 = 2 + + # the 030 subtype for `CPU_TYPE_MC680X0` + # @api private + CPU_SUBTYPE_MC68030_ONLY = 3 + + # the lowest common sub-type for `CPU_TYPE_X86_64` + # @api private + CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386 + + # the Haskell sub-type for `CPU_TYPE_X86_64` + # @api private + CPU_SUBTYPE_X86_64_H = 8 + + # the lowest common sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_ALL = 0 + + # the v4t sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V4T = 5 + + # the v6 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V6 = 6 + + # the v5 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V5TEJ = 7 + + # the xscale (v5 family) sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_XSCALE = 8 + + # the v7 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7 = 9 + + # the v7f (Cortex A9) sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7F = 10 + + # the v7s ("Swift") sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7S = 11 + + # the v7k ("Kirkwood40") sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7K = 12 + + # the v6m sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V6M = 14 + + # the v7m sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7M = 15 + + # the v7em sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V7EM = 16 + + # the v8 sub-type for `CPU_TYPE_ARM` + # @api private + CPU_SUBTYPE_ARM_V8 = 13 + + # the lowest common sub-type for `CPU_TYPE_ARM64` + # @api private + CPU_SUBTYPE_ARM64_ALL = 0 + + # the v8 sub-type for `CPU_TYPE_ARM64` + # @api private + CPU_SUBTYPE_ARM64_V8 = 1 + + # the lowest common sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88000_ALL = 0 + + # @see CPU_SUBTYPE_MC88000_ALL + # @api private + CPU_SUBTYPE_MMAX_JPC = CPU_SUBTYPE_MC88000_ALL + + # the 100 sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88100 = 1 + + # the 110 sub-type for `CPU_TYPE_MC88000` + # @api private + CPU_SUBTYPE_MC88110 = 2 + + # the lowest common sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_ALL = 0 + + # the 601 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_601 = 1 + + # the 602 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_602 = 2 + + # the 603 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603 = 3 + + # the 603e (G2) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603E = 4 + + # the 603ev sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_603EV = 5 + + # the 604 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_604 = 6 + + # the 604e sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_604E = 7 + + # the 620 sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_620 = 8 + + # the 750 (G3) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_750 = 9 + + # the 7400 (G4) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_7400 = 10 + + # the 7450 (G4 "Voyager") sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_7450 = 11 + + # the 970 (G5) sub-type for `CPU_TYPE_POWERPC` + # @api private + CPU_SUBTYPE_POWERPC_970 = 100 + + # any CPU sub-type for CPU type `CPU_TYPE_POWERPC64` + # @api private + CPU_SUBTYPE_POWERPC64_ALL = CPU_SUBTYPE_POWERPC_ALL + + # association of CPU types/subtype pairs to symbol representations in + # (very) roughly descending order of commonness + # @see https://opensource.apple.com/source/cctools/cctools-877.8/libstuff/arch.c + # @api private + CPU_SUBTYPES = { + CPU_TYPE_I386 => { + CPU_SUBTYPE_I386 => :i386, + CPU_SUBTYPE_486 => :i486, + CPU_SUBTYPE_486SX => :i486SX, + CPU_SUBTYPE_586 => :i586, # also "pentium" in arch(3) + CPU_SUBTYPE_PENTPRO => :i686, # also "pentpro" in arch(3) + CPU_SUBTYPE_PENTII_M3 => :pentIIm3, + CPU_SUBTYPE_PENTII_M5 => :pentIIm5, + CPU_SUBTYPE_PENTIUM_4 => :pentium4, + }.freeze, + CPU_TYPE_X86_64 => { + CPU_SUBTYPE_X86_64_ALL => :x86_64, + CPU_SUBTYPE_X86_64_H => :x86_64h, + }.freeze, + CPU_TYPE_ARM => { + CPU_SUBTYPE_ARM_ALL => :arm, + CPU_SUBTYPE_ARM_V4T => :armv4t, + CPU_SUBTYPE_ARM_V6 => :armv6, + CPU_SUBTYPE_ARM_V5TEJ => :armv5, + CPU_SUBTYPE_ARM_XSCALE => :xscale, + CPU_SUBTYPE_ARM_V7 => :armv7, + CPU_SUBTYPE_ARM_V7F => :armv7f, + CPU_SUBTYPE_ARM_V7S => :armv7s, + CPU_SUBTYPE_ARM_V7K => :armv7k, + CPU_SUBTYPE_ARM_V6M => :armv6m, + CPU_SUBTYPE_ARM_V7M => :armv7m, + CPU_SUBTYPE_ARM_V7EM => :armv7em, + CPU_SUBTYPE_ARM_V8 => :armv8, + }.freeze, + CPU_TYPE_ARM64 => { + CPU_SUBTYPE_ARM64_ALL => :arm64, + CPU_SUBTYPE_ARM64_V8 => :arm64v8, + }.freeze, + CPU_TYPE_POWERPC => { + CPU_SUBTYPE_POWERPC_ALL => :ppc, + CPU_SUBTYPE_POWERPC_601 => :ppc601, + CPU_SUBTYPE_POWERPC_603 => :ppc603, + CPU_SUBTYPE_POWERPC_603E => :ppc603e, + CPU_SUBTYPE_POWERPC_603EV => :ppc603ev, + CPU_SUBTYPE_POWERPC_604 => :ppc604, + CPU_SUBTYPE_POWERPC_604E => :ppc604e, + CPU_SUBTYPE_POWERPC_750 => :ppc750, + CPU_SUBTYPE_POWERPC_7400 => :ppc7400, + CPU_SUBTYPE_POWERPC_7450 => :ppc7450, + CPU_SUBTYPE_POWERPC_970 => :ppc970, + }.freeze, + CPU_TYPE_POWERPC64 => { + CPU_SUBTYPE_POWERPC64_ALL => :ppc64, + # apparently the only exception to the naming scheme + CPU_SUBTYPE_POWERPC_970 => :ppc970_64, + }.freeze, + CPU_TYPE_MC680X0 => { + CPU_SUBTYPE_MC680X0_ALL => :m68k, + CPU_SUBTYPE_MC68030 => :mc68030, + CPU_SUBTYPE_MC68040 => :mc68040, + }, + CPU_TYPE_MC88000 => { + CPU_SUBTYPE_MC88000_ALL => :m88k, + }, + }.freeze + + # relocatable object file + # @api private + MH_OBJECT = 0x1 + + # demand paged executable file + # @api private + MH_EXECUTE = 0x2 + + # fixed VM shared library file + # @api private + MH_FVMLIB = 0x3 + + # core dump file + # @api private + MH_CORE = 0x4 + + # preloaded executable file + # @api private + MH_PRELOAD = 0x5 + + # dynamically bound shared library + # @api private + MH_DYLIB = 0x6 + + # dynamic link editor + # @api private + MH_DYLINKER = 0x7 + + # dynamically bound bundle file + # @api private + MH_BUNDLE = 0x8 + + # shared library stub for static linking only, no section contents + # @api private + MH_DYLIB_STUB = 0x9 + + # companion file with only debug sections + # @api private + MH_DSYM = 0xa + + # x86_64 kexts + # @api private + MH_KEXT_BUNDLE = 0xb + + # association of filetypes to Symbol representations + # @api private + MH_FILETYPES = { + MH_OBJECT => :object, + MH_EXECUTE => :execute, + MH_FVMLIB => :fvmlib, + MH_CORE => :core, + MH_PRELOAD => :preload, + MH_DYLIB => :dylib, + MH_DYLINKER => :dylinker, + MH_BUNDLE => :bundle, + MH_DYLIB_STUB => :dylib_stub, + MH_DSYM => :dsym, + MH_KEXT_BUNDLE => :kext_bundle, + }.freeze + + # 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, + }.freeze + + # 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 + + # always big-endian + # @see MachOStructure::FORMAT + # @api private + FORMAT = "N2".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + + # @api private + def initialize(magic, nfat_arch) + @magic = magic + @nfat_arch = nfat_arch + end + + # @return [String] the serialized fields of the fat header + def serialize + [magic, nfat_arch].pack(FORMAT) + end 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 + # Fat binary header architecture structure. A Fat binary has one or more of + # these, representing one or more internal Mach-O blobs. + # @see MachO::Headers::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 + + # always big-endian + # @see MachOStructure::FORMAT + # @api private + FORMAT = "N5".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 + + # @api private + def initialize(cputype, cpusubtype, offset, size, align) + @cputype = cputype + @cpusubtype = cpusubtype + @offset = offset + @size = size + @align = align + end + + # @return [String] the serialized fields of the fat arch + def serialize + [cputype, cpusubtype, offset, size, align].pack(FORMAT) + end end - end - # 64-bit Mach-O file header structure - class MachHeader64 < MachHeader - # @return [void] - attr_reader :reserved + # 32-bit Mach-O file header structure + class MachHeader < MachOStructure + # @return [Fixnum] the magic number + attr_reader :magic - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=8".freeze + # @return [Fixnum] the CPU type of the Mach-O + attr_reader :cputype - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 32 + # @return [Fixnum] the CPU subtype of the Mach-O + attr_reader :cpusubtype - # @api private - def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, - flags, reserved) - super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) - @reserved = reserved + # @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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=7".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 28 + + # @api private + def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, + flags) + @magic = magic + @cputype = cputype + # For now we're not interested in additional capability bits also to be + # found in the `cpusubtype` field. We only care about the CPU sub-type. + @cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK + @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 + + # @return [Boolean] whether or not the file is of type `MH_OBJECT` + def object? + filetype == Headers::MH_OBJECT + end + + # @return [Boolean] whether or not the file is of type `MH_EXECUTE` + def executable? + filetype == Headers::MH_EXECUTE + end + + # @return [Boolean] whether or not the file is of type `MH_FVMLIB` + def fvmlib? + filetype == Headers::MH_FVMLIB + end + + # @return [Boolean] whether or not the file is of type `MH_CORE` + def core? + filetype == Headers::MH_CORE + end + + # @return [Boolean] whether or not the file is of type `MH_PRELOAD` + def preload? + filetype == Headers::MH_PRELOAD + end + + # @return [Boolean] whether or not the file is of type `MH_DYLIB` + def dylib? + filetype == Headers::MH_DYLIB + end + + # @return [Boolean] whether or not the file is of type `MH_DYLINKER` + def dylinker? + filetype == Headers::MH_DYLINKER + end + + # @return [Boolean] whether or not the file is of type `MH_BUNDLE` + def bundle? + filetype == Headers::MH_BUNDLE + end + + # @return [Boolean] whether or not the file is of type `MH_DSYM` + def dsym? + filetype == Headers::MH_DSYM + end + + # @return [Boolean] whether or not the file is of type `MH_KEXT_BUNDLE` + def kext? + filetype == Headers::MH_KEXT_BUNDLE + end + + # @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise + def magic32? + Utils.magic32?(magic) + end + + # @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise + def magic64? + Utils.magic64?(magic) + end + + # @return [Fixnum] the file's internal alignment + def alignment + magic32? ? 4 : 8 + end + end + + # 64-bit Mach-O file header structure + class MachHeader64 < MachHeader + # @return [void] + attr_reader :reserved + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=8".freeze + + # @see MachOStructure::SIZEOF + # @api private + 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 end diff --git a/Library/Homebrew/vendor/macho/macho/load_commands.rb b/Library/Homebrew/vendor/macho/macho/load_commands.rb index 205110801f..be4319ee2d 100644 --- a/Library/Homebrew/vendor/macho/macho/load_commands.rb +++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb @@ -1,1314 +1,1350 @@ 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) - # @api private - 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, - 0x2f => :LC_VERSION_MIN_TVOS, - 0x30 => :LC_VERSION_MIN_WATCHOS, - }.freeze - - # association of symbol representations to load command constants - # @api private - LOAD_COMMAND_CONSTANTS = LOAD_COMMANDS.invert.freeze - - # 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 - - # load commands that can be created manually via {LoadCommand.create} - # @api private - CREATABLE_LOAD_COMMANDS = DYLIB_LOAD_COMMANDS + [ - :LC_ID_DYLIB, - :LC_RPATH, - :LC_LOAD_DYLINKER, - ].freeze - - # association of load command symbols to string representations of classes - # @api private - LC_STRUCTURES = { - :LC_SEGMENT => "SegmentCommand", - :LC_SYMTAB => "SymtabCommand", - :LC_SYMSEG => "SymsegCommand", # obsolete - :LC_THREAD => "ThreadCommand", # seems obsolete, but not documented as such - :LC_UNIXTHREAD => "ThreadCommand", - :LC_LOADFVMLIB => "FvmlibCommand", # obsolete - :LC_IDFVMLIB => "FvmlibCommand", # obsolete - :LC_IDENT => "IdentCommand", # obsolete - :LC_FVMFILE => "FvmfileCommand", # reserved for internal use only - :LC_PREPAGE => "LoadCommand", # reserved for internal use only, no public struct - :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", - :LC_VERSION_MIN_TVOS => "VersionMinCommand", - :LC_VERSION_MIN_WATCHOS => "VersionMinCommand", - }.freeze - - # 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", - }.freeze - - # 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, - }.freeze - - # 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 [MachO::MachOView] the raw view associated with the load command - attr_reader :view - - # @return [Fixnum] the load command's identifying number - attr_reader :cmd - - # @return [Fixnum] the size of the load command, in bytes - attr_reader :cmdsize - - # @see MachOStructure::FORMAT + # Classes and constants for parsing load commands in Mach-O binaries. + module LoadCommands + # load commands added after OS X 10.1 need to be bitwise ORed with + # LC_REQ_DYLD to be recognized by the dynamic linker (dyld) # @api private - FORMAT = "L=2".freeze + LC_REQ_DYLD = 0x80000000 - # @see MachOStructure::SIZEOF + # association of load commands to symbol representations # @api private - SIZEOF = 8 + 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, + 0x2f => :LC_VERSION_MIN_TVOS, + 0x30 => :LC_VERSION_MIN_WATCHOS, + }.freeze - # Instantiates a new LoadCommand given a view into its origin Mach-O - # @param view [MachO::MachOView] the load command's raw view - # @return [MachO::LoadCommand] the new load command + # association of symbol representations to load command constants # @api private - def self.new_from_bin(view) - bin = view.raw_data.slice(view.offset, bytesize) - format = Utils.specialize_format(self::FORMAT, view.endianness) + LOAD_COMMAND_CONSTANTS = LOAD_COMMANDS.invert.freeze - new(view, *bin.unpack(format)) - end - - # Creates a new (viewless) command corresponding to the symbol provided - # @param cmd_sym [Symbol] the symbol of the load command being created - # @param args [Array] the arguments for the load command being created - def self.create(cmd_sym, *args) - raise LoadCommandNotCreatableError, cmd_sym unless CREATABLE_LOAD_COMMANDS.include?(cmd_sym) - - klass = MachO.const_get LC_STRUCTURES[cmd_sym] - cmd = LOAD_COMMAND_CONSTANTS[cmd_sym] - - # cmd will be filled in, view and cmdsize will be left unpopulated - klass_arity = klass.instance_method(:initialize).arity - 3 - - raise LoadCommandCreationArityError.new(cmd_sym, klass_arity, args.size) if klass_arity != args.size - - klass.new(nil, cmd, nil, *args) - end - - # @param view [MachO::MachOView] the load command's raw view - # @param cmd [Fixnum] the load command's identifying number - # @param cmdsize [Fixnum] the size of the load command in bytes + # load commands responsible for loading dylibs # @api private - def initialize(view, cmd, cmdsize) - @view = view - @cmd = cmd - @cmdsize = cmdsize - end + DYLIB_LOAD_COMMANDS = [ + :LC_LOAD_DYLIB, + :LC_LOAD_WEAK_DYLIB, + :LC_REEXPORT_DYLIB, + :LC_LAZY_LOAD_DYLIB, + :LC_LOAD_UPWARD_DYLIB, + ].freeze - # @return [Boolean] true if the load command can be serialized, false otherwise - def serializable? - CREATABLE_LOAD_COMMANDS.include?(LOAD_COMMANDS[cmd]) - end - - # @param context [MachO::LoadCommand::SerializationContext] the context - # to serialize into - # @return [String, nil] the serialized fields of the load command, or nil - # if the load command can't be serialized + # load commands that can be created manually via {LoadCommand.create} # @api private - def serialize(context) - raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable? - format = Utils.specialize_format(FORMAT, context.endianness) - [cmd, SIZEOF].pack(format) - end + CREATABLE_LOAD_COMMANDS = DYLIB_LOAD_COMMANDS + [ + :LC_ID_DYLIB, + :LC_RPATH, + :LC_LOAD_DYLINKER, + ].freeze - # @return [Fixnum] the load command's offset in the source file - # @deprecated use {#view} instead - def offset - view.offset - end + # association of load command symbols to string representations of classes + # @api private + LC_STRUCTURES = { + :LC_SEGMENT => "SegmentCommand", + :LC_SYMTAB => "SymtabCommand", + # "obsolete" + :LC_SYMSEG => "SymsegCommand", + # seems obsolete, but not documented as such + :LC_THREAD => "ThreadCommand", + :LC_UNIXTHREAD => "ThreadCommand", + # "obsolete" + :LC_LOADFVMLIB => "FvmlibCommand", + # "obsolete" + :LC_IDFVMLIB => "FvmlibCommand", + # "obsolete" + :LC_IDENT => "IdentCommand", + # "reserved for internal use only" + :LC_FVMFILE => "FvmfileCommand", + # "reserved for internal use only", no public struct + :LC_PREPAGE => "LoadCommand", + :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", + :LC_VERSION_MIN_TVOS => "VersionMinCommand", + :LC_VERSION_MIN_WATCHOS => "VersionMinCommand", + }.freeze - # @return [Symbol] a symbol representation of the load command's identifying number - def type - LOAD_COMMANDS[cmd] - end + # 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", + }.freeze - alias to_sym type + # 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, + }.freeze - # @return [String] a string representation of the load command's identifying number - def to_s - type.to_s - end + # 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 [MachO::MachOView] the raw view associated with the load command + attr_reader :view - # 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 lc [MachO::LoadCommand] the load command - # @param lc_str [Fixnum, String] the offset to the beginning of the string, - # or the string itself if not being initialized with a view. - # @raise [MachO::LCStrMalformedError] if the string is malformed - # @todo devise a solution such that the `lc_str` parameter is not - # interpreted differently depending on `lc.view`. The current behavior - # is a hack to allow viewless load command creation. + # @return [Fixnum] the load command's identifying number + attr_reader :cmd + + # @return [Fixnum] the size of the load command, in bytes + attr_reader :cmdsize + + # @see MachOStructure::FORMAT # @api private - def initialize(lc, lc_str) - view = lc.view + FORMAT = "L=2".freeze - if view - lc_str_abs = view.offset + lc_str - lc_end = view.offset + lc.cmdsize - 1 - raw_string = view.raw_data.slice(lc_str_abs..lc_end) - @string, null_byte, _padding = raw_string.partition("\x00") - raise LCStrMalformedError, lc if null_byte.empty? - @string_offset = lc_str - else - @string = lc_str - @string_offset = 0 - end + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + + # Instantiates a new LoadCommand given a view into its origin Mach-O + # @param view [MachO::MachOView] the load command's raw view + # @return [LoadCommand] the new load command + # @api private + def self.new_from_bin(view) + bin = view.raw_data.slice(view.offset, bytesize) + format = Utils.specialize_format(self::FORMAT, view.endianness) + + new(view, *bin.unpack(format)) end - # @return [String] a string representation of the LCStr + # Creates a new (viewless) command corresponding to the symbol provided + # @param cmd_sym [Symbol] the symbol of the load command being created + # @param args [Array] the arguments for the load command being created + def self.create(cmd_sym, *args) + raise LoadCommandNotCreatableError, cmd_sym unless CREATABLE_LOAD_COMMANDS.include?(cmd_sym) + + klass = LoadCommands.const_get LC_STRUCTURES[cmd_sym] + cmd = LOAD_COMMAND_CONSTANTS[cmd_sym] + + # cmd will be filled in, view and cmdsize will be left unpopulated + klass_arity = klass.instance_method(:initialize).arity - 3 + + raise LoadCommandCreationArityError.new(cmd_sym, klass_arity, args.size) if klass_arity != args.size + + klass.new(nil, cmd, nil, *args) + end + + # @param view [MachO::MachOView] the load command's raw view + # @param cmd [Fixnum] the load command's identifying number + # @param cmdsize [Fixnum] the size of the load command in bytes + # @api private + def initialize(view, cmd, cmdsize) + @view = view + @cmd = cmd + @cmdsize = cmdsize + end + + # @return [Boolean] whether the load command can be serialized + def serializable? + CREATABLE_LOAD_COMMANDS.include?(LOAD_COMMANDS[cmd]) + end + + # @param context [SerializationContext] the context + # to serialize into + # @return [String, nil] the serialized fields of the load command, or nil + # if the load command can't be serialized + # @api private + def serialize(context) + raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable? + format = Utils.specialize_format(FORMAT, context.endianness) + [cmd, SIZEOF].pack(format) + end + + # @return [Fixnum] the load command's offset in the source file + # @deprecated use {#view} instead + def offset + view.offset + 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 - @string + type.to_s end - # @return [Fixnum] the offset to the beginning of the string in the load command - def to_i - @string_offset - end - end - - # Represents the contextual information needed by a load command to - # serialize itself correctly into a binary string. - class SerializationContext - # @return [Symbol] the endianness of the serialized load command - attr_reader :endianness - - # @return [Fixnum] the constant alignment value used to pad the serialized load command - attr_reader :alignment - - # @param macho [MachO::MachOFile] the file to contextualize - # @return [MachO::LoadCommand::SerializationContext] the resulting context - def self.context_for(macho) - new(macho.endianness, macho.alignment) - end - - # @param endianness [Symbol] the endianness of the context - # @param alignment [Fixnum] the alignment of the context - # @api private - def initialize(endianness, alignment) - @endianness = endianness - @alignment = alignment - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 - - # @api private - def initialize(view, cmd, cmdsize, uuid) - super(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16L=4l=2L=2".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 56 - - # @api private - def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, - filesize, maxprot, initprot, nsects, flags) - super(view, cmd, cmdsize) - @segname = segname.delete("\x00") - @vmaddr = vmaddr - @vmsize = vmsize - @fileoff = fileoff - @filesize = filesize - @maxprot = maxprot - @initprot = initprot - @nsects = nsects - @flags = flags - end - - # All sections referenced within this segment. - # @return [Array] if the Mach-O is 32-bit - # @return [Array] if the Mach-O is 64-bit - def sections - klass = case self - when MachO::SegmentCommand64 - MachO::Section64 - when MachO::SegmentCommand - MachO::Section - end - - bins = view.raw_data[view.offset + self.class.bytesize, nsects * klass.bytesize] - bins.unpack("a#{klass.bytesize}" * nsects).map do |bin| - klass.new_from_bin(view.endianness, bin) - end - 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 < SegmentCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2a16Q=4l=2L=2".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 72 - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 - - # @api private - def initialize(view, cmd, cmdsize, name, timestamp, current_version, compatibility_version) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @timestamp = timestamp - @current_version = current_version - @compatibility_version = compatibility_version - end - - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :name => name.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:name], timestamp, current_version, - compatibility_version].pack(format) + string_payload - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, name) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - end - - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :name => name.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 - - # @api private - def initialize(view, cmd, cmdsize, name, nmodules, linked_modules) - super(view, cmd, cmdsize) - @name = LCStr.new(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 - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=10".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 40 - - # @api private - def initialize(view, cmd, cmdsize, init_address, init_module, reserved1, - reserved2, reserved3, reserved4, reserved5, reserved6) - super(view, 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 < RoutinesCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=8".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 72 - 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, umbrella) - super(view, cmd, cmdsize) - @umbrella = LCStr.new(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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, sub_umbrella) - super(view, cmd, cmdsize) - @sub_umbrella = LCStr.new(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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, sub_library) - super(view, cmd, cmdsize) - @sub_library = LCStr.new(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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, sub_client) - super(view, cmd, cmdsize) - @sub_client = LCStr.new(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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 - - # @api private - def initialize(view, cmd, cmdsize, symoff, nsyms, stroff, strsize) - super(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=20".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 80 - - # ugh - # @api private - def initialize(view, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, - nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, - nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, - nindirectsyms, extreloff, nextrel, locreloff, nlocrel) - super(view, 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 - - # @return [MachO::TwolevelHintsCommand::TwolevelHintTable] the hint table - attr_reader :table - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 - - # @api private - def initialize(view, cmd, cmdsize, htoffset, nhints) - super(view, cmd, cmdsize) - @htoffset = htoffset - @nhints = nhints - @table = TwolevelHintsTable.new(view, htoffset, nhints) - end - - # A representation of the two-level namespace lookup hints table exposed - # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`). - class TwolevelHintsTable - # @return [Array] all hints in the table - attr_reader :hints - - # @param view [MachO::MachOView] the view into the current Mach-O - # @param htoffset [Fixnum] the offset of the hints table - # @param nhints [Fixnum] the number of two-level hints in the table - # @api private - def initialize(view, htoffset, nhints) - format = Utils.specialize_format("L=#{nhints}", view.endianness) - raw_table = view.raw_data[htoffset, nhints * 4] - blobs = raw_table.unpack(format) - - @hints = blobs.map { |b| TwolevelHint.new(b) } - end - - # An individual two-level namespace lookup hint. - class TwolevelHint - # @return [Fixnum] the index into the sub-images - attr_reader :isub_image - - # @return [Fixnum] the index into the table of contents - attr_reader :itoc - - # @param blob [Fixnum] the 32-bit number containing the lookup hint + # 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 lc [LoadCommand] the load command + # @param lc_str [Fixnum, String] the offset to the beginning of the + # string, or the string itself if not being initialized with a view. + # @raise [MachO::LCStrMalformedError] if the string is malformed + # @todo devise a solution such that the `lc_str` parameter is not + # interpreted differently depending on `lc.view`. The current behavior + # is a hack to allow viewless load command creation. # @api private - def initialize(blob) - @isub_image = blob >> 24 - @itoc = blob & 0x00FFFFFF + def initialize(lc, lc_str) + view = lc.view + + if view + lc_str_abs = view.offset + lc_str + lc_end = view.offset + lc.cmdsize - 1 + raw_string = view.raw_data.slice(lc_str_abs..lc_end) + @string, null_byte, _padding = raw_string.partition("\x00") + raise LCStrMalformedError, lc if null_byte.empty? + @string_offset = lc_str + else + @string = lc_str + @string_offset = 0 + end + end + + # @return [String] a string representation of the LCStr + def to_s + @string + end + + # @return [Fixnum] the offset to the beginning of the string in the + # load command + def to_i + @string_offset + end + end + + # Represents the contextual information needed by a load command to + # serialize itself correctly into a binary string. + class SerializationContext + # @return [Symbol] the endianness of the serialized load command + attr_reader :endianness + + # @return [Fixnum] the constant alignment value used to pad the + # serialized load command + attr_reader :alignment + + # @param macho [MachO::MachOFile] the file to contextualize + # @return [SerializationContext] the + # resulting context + def self.context_for(macho) + new(macho.endianness, macho.alignment) + end + + # @param endianness [Symbol] the endianness of the context + # @param alignment [Fixnum] the alignment of the context + # @api private + def initialize(endianness, alignment) + @endianness = endianness + @alignment = alignment end end 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 + # 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 - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @api private - def initialize(view, cmd, cmdsize, cksum) - super(view, cmd, cmdsize) - @cksum = cksum - end - end + # @api private + def initialize(view, cmd, cmdsize, uuid) + super(view, cmd, cmdsize) + @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array + 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 + # @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 + ] - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, path) - super(view, cmd, cmdsize) - @path = LCStr.new(self, path) + segs.join("-") + end end - # @param context [MachO::LoadCcommand::SerializationContext] the context - # @return [String] the serialized fields of the load command - # @api private - def serialize(context) - format = Utils.specialize_format(FORMAT, context.endianness) - string_payload, string_offsets = Utils.pack_strings(SIZEOF, context.alignment, :path => path.to_s) - cmdsize = SIZEOF + string_payload.bytesize - [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload - 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 - # 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] the memory address of the segment + attr_reader :vmaddr - # @return [Fixnum] size of the data in the __LINKEDIT segment - attr_reader :datasize + # @return [Fixnum] the memory size of the segment + attr_reader :vmsize - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @return [Fixnum] the file offset of the segment + attr_reader :fileoff - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @return [Fixnum] the amount to map from the file + attr_reader :filesize - # @api private - def initialize(view, cmd, cmdsize, dataoff, datasize) - super(view, cmd, cmdsize) - @dataoff = dataoff - @datasize = datasize - end - end + # @return [Fixnum] the maximum VM protection + attr_reader :maxprot - # 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 initial VM protection + attr_reader :initprot - # @return [Fixnum] the size of the encrypted segment - attr_reader :cryptsize + # @return [Fixnum] the number of sections in the segment + attr_reader :nsects - # @return [Fixnum] the encryption system, or 0 if not encrypted yet - attr_reader :cryptid + # @return [Fixnum] any flags associated with the segment + attr_reader :flags - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16L=4l=2L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 56 - # @api private - def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid) - super(view, cmd, cmdsize) - @cryptoff = cryptoff - @cryptsize = cryptsize - @cryptid = cryptid - end - end + # @api private + def initialize(view, cmd, cmdsize, segname, vmaddr, vmsize, fileoff, + filesize, maxprot, initprot, nsects, flags) + super(view, cmd, cmdsize) + @segname = segname.delete("\x00") + @vmaddr = vmaddr + @vmsize = vmsize + @fileoff = fileoff + @filesize = filesize + @maxprot = maxprot + @initprot = initprot + @nsects = nsects + @flags = flags + 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 + # All sections referenced within this segment. + # @return [Array] if the Mach-O is 32-bit + # @return [Array] if the Mach-O is 64-bit + def sections + klass = case self + when SegmentCommand64 + MachO::Sections::Section64 + when SegmentCommand + MachO::Sections::Section + end - # @return [Fixnum] the size of the encrypted segment - attr_reader :cryptsize + offset = view.offset + self.class.bytesize + length = nsects * klass.bytesize - # @return [Fixnum] the encryption system, or 0 if not encrypted yet - attr_reader :cryptid + bins = view.raw_data[offset, length] + bins.unpack("a#{klass.bytesize}" * nsects).map do |bin| + klass.new_from_bin(view.endianness, bin) + end + end - # @return [Fixnum] 64-bit padding value - attr_reader :pad - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=6".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 - - # @api private - def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad) - super(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 - - # @api private - def initialize(view, cmd, cmdsize, version, sdk) - super(view, cmd, cmdsize) - @version = version - @sdk = sdk + # @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 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) } + # 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 < SegmentCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2a16Q=4l=2L=2".freeze - segs.join(".") + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 72 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) } + # 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 [LCStr] the library's path + # name as an LCStr + attr_reader :name - segs.join(".") - end - end + # @return [Fixnum] the library's build time stamp + attr_reader :timestamp - # 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 library's current version number + attr_reader :current_version - # @return [Fixnum] the size of the rebase information - attr_reader :rebase_size + # @return [Fixnum] the library's compatibility version number + attr_reader :compatibility_version - # @return [Fixnum] the file offset to the binding information - attr_reader :bind_off + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze - # @return [Fixnum] the size of the binding information - attr_reader :bind_size + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 - # @return [Fixnum] the file offset to the weak binding information - attr_reader :weak_bind_off + # @api private + def initialize(view, cmd, cmdsize, name, timestamp, current_version, + compatibility_version) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @timestamp = timestamp + @current_version = current_version + @compatibility_version = compatibility_version + end - # @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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=12".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 48 - - # @api private - def initialize(view, 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(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=3".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 12 - - # @api private - def initialize(view, cmd, cmdsize, count) - super(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=2".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 24 - - # @api private - def initialize(view, cmd, cmdsize, entryoff, stacksize) - super(view, 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 - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2Q=1".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 - - # @api private - def initialize(view, cmd, cmdsize, version) - super(view, cmd, cmdsize) - @version = version + # @param context [SerializationContext] + # the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :name => name.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:name], timestamp, current_version, + compatibility_version].pack(format) + string_payload + end 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) } + # 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 [LCStr] the dynamic linker's + # path name as an LCStr + attr_reader :name - segs.join(".") + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, name) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + end + + # @param context [SerializationContext] + # the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :name => name.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload + end end - end - # An obsolete load command containing the offset and size of the (GNU style) - # symbol table information. Corresponds to LC_SYMSEG. - class SymsegCommand < LoadCommand - # @return [Fixnum] the offset to the symbol segment - attr_reader :offset + # A load command used to indicate dynamic libraries used in prebinding. + # Corresponds to LC_PREBOUND_DYLIB. + class PreboundDylibCommand < LoadCommand + # @return [LCStr] the library's path + # name as an LCStr + attr_reader :name - # @return [Fixnum] the size of the symbol segment in bytes - attr_reader :size + # @return [Fixnum] the number of modules in the library + attr_reader :nmodules - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze + # @return [Fixnum] a bit vector of linked modules + attr_reader :linked_modules - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze - # @api private - def initialize(view, cmd, cmdsize, offset, size) - super(view, cmd, cmdsize) - @offset = offset - @size = size + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 + + # @api private + def initialize(view, cmd, cmdsize, name, nmodules, linked_modules) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @nmodules = nmodules + @linked_modules = linked_modules + end end - end - # An obsolete load command containing a free format string table. Each string - # is null-terminated and the command is zero-padded to a multiple of 4. - # Corresponds to LC_IDENT. - class IdentCommand < LoadCommand - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=2".freeze + # A load command used to represent threads. + # @note cctools-870 and onwards have all fields of thread_command commented + # out except the common ones (cmd, cmdsize) + class ThreadCommand < LoadCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2".freeze - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 8 - end - - # An obsolete load command containing the path to a file to be loaded into - # memory. Corresponds to LC_FVMFILE. - class FvmfileCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the pathname of the file being loaded - attr_reader :name - - # @return [Fixnum] the virtual address being loaded at - attr_reader :header_addr - - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=4".freeze - - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 16 - - def initialize(view, cmd, cmdsize, name, header_addr) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @header_addr = header_addr + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 end - end - # An obsolete load command containing the path to a library to be loaded into - # memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB. - class FvmlibCommand < LoadCommand - # @return [MachO::LoadCommand::LCStr] the library's target pathname - attr_reader :name + # 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 library's minor version number - attr_reader :minor_version + # @return [Fixnum] the index into the module table that the init routine + # is defined in + attr_reader :init_module - # @return [Fixnum] the library's header address - attr_reader :header_addr + # @return [void] + attr_reader :reserved1 - # @see MachOStructure::FORMAT - # @api private - FORMAT = "L=5".freeze + # @return [void] + attr_reader :reserved2 - # @see MachOStructure::SIZEOF - # @api private - SIZEOF = 20 + # @return [void] + attr_reader :reserved3 - def initialize(view, cmd, cmdsize, name, minor_version, header_addr) - super(view, cmd, cmdsize) - @name = LCStr.new(self, name) - @minor_version = minor_version - @header_addr = header_addr + # @return [void] + attr_reader :reserved4 + + # @return [void] + attr_reader :reserved5 + + # @return [void] + attr_reader :reserved6 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=10".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 40 + + # @api private + def initialize(view, cmd, cmdsize, init_address, init_module, reserved1, + reserved2, reserved3, reserved4, reserved5, reserved6) + super(view, 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 < RoutinesCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=8".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 72 + 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 [LCStr] the umbrella framework name as an LCStr + attr_reader :umbrella + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, umbrella) + super(view, cmd, cmdsize) + @umbrella = LCStr.new(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 [LCStr] the subumbrella framework name as an LCStr + attr_reader :sub_umbrella + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, sub_umbrella) + super(view, cmd, cmdsize) + @sub_umbrella = LCStr.new(self, sub_umbrella) + end + end + + # A load command signifying a sublibrary of a shared library. Corresponds + # to LC_SUB_LIBRARY. + class SubLibraryCommand < LoadCommand + # @return [LCStr] the sublibrary name as an LCStr + attr_reader :sub_library + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, sub_library) + super(view, cmd, cmdsize) + @sub_library = LCStr.new(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 [LCStr] the subclient name as an LCStr + attr_reader :sub_client + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, sub_client) + super(view, cmd, cmdsize) + @sub_client = LCStr.new(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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 + + # @api private + def initialize(view, cmd, cmdsize, symoff, nsyms, stroff, strsize) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=20".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 80 + + # ugh + # @api private + def initialize(view, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, + nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, + nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, + nindirectsyms, extreloff, nextrel, locreloff, nlocrel) + super(view, 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 + + # @return [TwolevelHintsTable] + # the hint table + attr_reader :table + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + # @api private + def initialize(view, cmd, cmdsize, htoffset, nhints) + super(view, cmd, cmdsize) + @htoffset = htoffset + @nhints = nhints + @table = TwolevelHintsTable.new(view, htoffset, nhints) + end + + # A representation of the two-level namespace lookup hints table exposed + # by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`). + class TwolevelHintsTable + # @return [Array] all hints in the table + attr_reader :hints + + # @param view [MachO::MachOView] the view into the current Mach-O + # @param htoffset [Fixnum] the offset of the hints table + # @param nhints [Fixnum] the number of two-level hints in the table + # @api private + def initialize(view, htoffset, nhints) + format = Utils.specialize_format("L=#{nhints}", view.endianness) + raw_table = view.raw_data[htoffset, nhints * 4] + blobs = raw_table.unpack(format) + + @hints = blobs.map { |b| TwolevelHint.new(b) } + end + + # An individual two-level namespace lookup hint. + class TwolevelHint + # @return [Fixnum] the index into the sub-images + attr_reader :isub_image + + # @return [Fixnum] the index into the table of contents + attr_reader :itoc + + # @param blob [Fixnum] the 32-bit number containing the lookup hint + # @api private + def initialize(blob) + @isub_image = blob >> 24 + @itoc = blob & 0x00FFFFFF + end + end + 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, cksum) + super(view, 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 [LCStr] the path to add to the run path as an LCStr + attr_reader :path + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, path) + super(view, cmd, cmdsize) + @path = LCStr.new(self, path) + end + + # @param context [SerializationContext] the context + # @return [String] the serialized fields of the load command + # @api private + def serialize(context) + format = Utils.specialize_format(FORMAT, context.endianness) + string_payload, string_offsets = Utils.pack_strings(SIZEOF, + context.alignment, + :path => path.to_s) + cmdsize = SIZEOF + string_payload.bytesize + [cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload + 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + # @api private + def initialize(view, cmd, cmdsize, dataoff, datasize) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 + + # @api private + def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=6".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 + + # @api private + def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + # @api private + def initialize(view, cmd, cmdsize, version, sdk) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=12".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 48 + + # @api private + def initialize(view, 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(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=3".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 12 + + # @api private + def initialize(view, cmd, cmdsize, count) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=2".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 24 + + # @api private + def initialize(view, cmd, cmdsize, entryoff, stacksize) + super(view, 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 + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2Q=1".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + # @api private + def initialize(view, cmd, cmdsize, version) + super(view, 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 + + # An obsolete load command containing the offset and size of the (GNU style) + # symbol table information. Corresponds to LC_SYMSEG. + class SymsegCommand < LoadCommand + # @return [Fixnum] the offset to the symbol segment + attr_reader :offset + + # @return [Fixnum] the size of the symbol segment in bytes + attr_reader :size + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + # @api private + def initialize(view, cmd, cmdsize, offset, size) + super(view, cmd, cmdsize) + @offset = offset + @size = size + end + end + + # An obsolete load command containing a free format string table. Each + # string is null-terminated and the command is zero-padded to a multiple of + # 4. Corresponds to LC_IDENT. + class IdentCommand < LoadCommand + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=2".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 8 + end + + # An obsolete load command containing the path to a file to be loaded into + # memory. Corresponds to LC_FVMFILE. + class FvmfileCommand < LoadCommand + # @return [LCStr] the pathname of the file being loaded + attr_reader :name + + # @return [Fixnum] the virtual address being loaded at + attr_reader :header_addr + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=4".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 16 + + def initialize(view, cmd, cmdsize, name, header_addr) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @header_addr = header_addr + end + end + + # An obsolete load command containing the path to a library to be loaded + # into memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB. + class FvmlibCommand < LoadCommand + # @return [LCStr] the library's target pathname + attr_reader :name + + # @return [Fixnum] the library's minor version number + attr_reader :minor_version + + # @return [Fixnum] the library's header address + attr_reader :header_addr + + # @see MachOStructure::FORMAT + # @api private + FORMAT = "L=5".freeze + + # @see MachOStructure::SIZEOF + # @api private + SIZEOF = 20 + + def initialize(view, cmd, cmdsize, name, minor_version, header_addr) + super(view, cmd, cmdsize) + @name = LCStr.new(self, name) + @minor_version = minor_version + @header_addr = header_addr + end end end end diff --git a/Library/Homebrew/vendor/macho/macho/macho_file.rb b/Library/Homebrew/vendor/macho/macho/macho_file.rb index 8ece626721..7693ab0dd3 100644 --- a/Library/Homebrew/vendor/macho/macho/macho_file.rb +++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb @@ -1,27 +1,33 @@ +require "forwardable" + 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 + # @see FatFile class MachOFile - # @return [String] the filename loaded from, or nil if loaded from a binary string + extend Forwardable + + # @return [String] the filename loaded from, or nil if loaded from a binary + # string attr_accessor :filename # @return [Symbol] the endianness of the file, :big or :little attr_reader :endianness - # @return [MachO::MachHeader] if the Mach-O is 32-bit - # @return [MachO::MachHeader64] if the Mach-O is 64-bit + # @return [Headers::MachHeader] if the Mach-O is 32-bit + # @return [Headers::MachHeader64] if the Mach-O is 64-bit attr_reader :header - # @return [Array] an array of the file's load commands + # @return [Array] an array of the file's load + # commands # @note load commands are provided in order of ascending offset. 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 + # @return [MachOFile] a new MachOFile def self.new_from_bin(bin) instance = allocate instance.initialize_from_bin(bin) @@ -55,109 +61,63 @@ module MachO @raw_data end - # @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise - def magic32? - Utils.magic32?(header.magic) - end - - # @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise - def magic64? - Utils.magic64?(header.magic) - end - - # @return [Fixnum] the file's internal alignment - def alignment - magic32? ? 4 : 8 - 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 + # @!method magic + # @return (see MachO::Headers::MachHeader#magic) + # @!method ncmds + # @return (see MachO::Headers::MachHeader#ncmds) + # @!method sizeofcmds + # @return (see MachO::Headers::MachHeader#sizeofcmds) + # @!method flags + # @return (see MachO::Headers::MachHeader#flags) + # @!method object? + # @return (see MachO::Headers::MachHeader#object?) + # @!method executable? + # @return (see MachO::Headers::MachHeader#executable?) + # @!method fvmlib? + # @return (see MachO::Headers::MachHeader#fvmlib?) + # @!method core? + # @return (see MachO::Headers::MachHeader#core?) + # @!method preload? + # @return (see MachO::Headers::MachHeader#preload?) + # @!method dylib? + # @return (see MachO::Headers::MachHeader#dylib?) + # @!method dylinker? + # @return (see MachO::Headers::MachHeader#dylinker?) + # @!method bundle? + # @return (see MachO::Headers::MachHeader#bundle?) + # @!method dsym? + # @return (see MachO::Headers::MachHeader#dsym?) + # @!method kext? + # @return (see MachO::Headers::MachHeader#kext?) + # @!method magic32? + # @return (see MachO::Headers::MachHeader#magic32?) + # @!method magic64? + # @return (see MachO::Headers::MachHeader#magic64?) + # @!method alignment + # @return (see MachO::Headers::MachHeader#alignment) + def_delegators :header, :magic, :ncmds, :sizeofcmds, :flags, :object?, + :executable?, :fvmlib?, :core?, :preload?, :dylib?, + :dylinker?, :bundle?, :dsym?, :kext?, :magic32?, :magic64?, + :alignment # @return [String] a string representation of the file's magic number def magic_string - MH_MAGICS[magic] + Headers::MH_MAGICS[magic] end # @return [Symbol] a string representation of the Mach-O's filetype def filetype - MH_FILETYPES[header.filetype] + Headers::MH_FILETYPES[header.filetype] end # @return [Symbol] a symbol representation of the Mach-O's CPU type def cputype - CPU_TYPES[header.cputype] + Headers::CPU_TYPES[header.cputype] end # @return [Symbol] a symbol representation of the Mach-O's CPU subtype def cpusubtype - CPU_SUBTYPES[header.cputype][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 + Headers::CPU_SUBTYPES[header.cputype][header.cpusubtype] end # All load commands of a given name. @@ -165,7 +125,8 @@ module MachO # 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` + # @return [Array] an array of load commands + # corresponding to `name` def command(name) load_commands.select { |lc| lc.type == name.to_sym } end @@ -174,16 +135,16 @@ module MachO # Inserts a load command at the given offset. # @param offset [Fixnum] the offset to insert at - # @param lc [MachO::LoadCommand] the load command to insert + # @param lc [LoadCommands::LoadCommand] the load command to insert # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields - # @raise [MachO::OffsetInsertionError] if the offset is not in the load command region - # @raise [MachO::HeaderPadError] if the new command exceeds the header pad buffer + # @raise [OffsetInsertionError] if the offset is not in the load command region + # @raise [HeaderPadError] if the new command exceeds the header pad buffer # @note Calling this method with an arbitrary offset in the load command # region **will leave the object in an inconsistent state**. def insert_command(offset, lc, options = {}) - context = LoadCommand::SerializationContext.context_for(self) + context = LoadCommands::LoadCommand::SerializationContext.context_for(self) cmd_raw = lc.serialize(context) if offset < header.class.bytesize || offset + cmd_raw.bytesize > low_fileoff @@ -207,14 +168,14 @@ module MachO end # Replace a load command with another command in the Mach-O, preserving location. - # @param old_lc [MachO::LoadCommand] the load command being replaced - # @param new_lc [MachO::LoadCommand] the load command being added + # @param old_lc [LoadCommands::LoadCommand] the load command being replaced + # @param new_lc [LoadCommands::LoadCommand] the load command being added # @return [void] - # @raise [MachO::HeaderPadError] if the new command exceeds the header pad buffer - # @see {#insert_command} + # @raise [HeaderPadError] if the new command exceeds the header pad buffer + # @see #insert_command # @note This is public, but methods like {#dylib_id=} should be preferred. def replace_command(old_lc, new_lc) - context = LoadCommand::SerializationContext.context_for(self) + context = LoadCommands::LoadCommand::SerializationContext.context_for(self) cmd_raw = new_lc.serialize(context) new_sizeofcmds = sizeofcmds + cmd_raw.bytesize - old_lc.cmdsize if header.class.bytesize + new_sizeofcmds > low_fileoff @@ -226,12 +187,12 @@ module MachO end # Appends a new load command to the Mach-O. - # @param lc [MachO::LoadCommand] the load command being added + # @param lc [LoadCommands::LoadCommand] the load command being added # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields # @return [void] - # @see {#insert_command} + # @see #insert_command # @note This is public, but methods like {#add_rpath} should be preferred. # Setting `repopulate` to false **will leave the instance in an # inconsistent state** unless {#populate_fields} is called **immediately** @@ -241,7 +202,7 @@ module MachO end # Delete a load command from the Mach-O. - # @param lc [MachO::LoadCommand] the load command being deleted + # @param lc [LoadCommands::LoadCommand] the load command being deleted # @param options [Hash] # @option options [Boolean] :repopulate (true) whether or not to repopulate # the instance fields @@ -275,14 +236,14 @@ module MachO end # All load commands responsible for loading dylibs. - # @return [Array] an array of DylibCommands + # @return [Array] an array of DylibCommands def dylib_load_commands - load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) } + load_commands.select { |lc| LoadCommands::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 + # @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) @@ -319,10 +280,10 @@ module MachO old_lc = command(:LC_ID_DYLIB).first raise DylibIdMissingError unless old_lc - new_lc = LoadCommand.create(:LC_ID_DYLIB, new_id, - old_lc.timestamp, - old_lc.current_version, - old_lc.compatibility_version) + new_lc = LoadCommands::LoadCommand.create(:LC_ID_DYLIB, new_id, + old_lc.timestamp, + old_lc.current_version, + old_lc.compatibility_version) replace_command(old_lc, new_lc) end @@ -341,22 +302,22 @@ module MachO # Changes the shared library `old_name` to `new_name` # @example - # file.change_install_name("/usr/lib/libWhatever.dylib", "/usr/local/lib/libWhatever2.dylib") + # file.change_install_name("abc.dylib", "def.dylib") # @param old_name [String] the shared library's old name # @param new_name [String] the shared library's new name # @param _options [Hash] # @return [void] - # @raise [MachO::DylibUnknownError] if no shared library has the old name + # @raise [DylibUnknownError] if no shared library has the old name # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#change_install_name} def change_install_name(old_name, new_name, _options = {}) old_lc = dylib_load_commands.find { |d| d.name.to_s == old_name } raise DylibUnknownError, old_name if old_lc.nil? - new_lc = LoadCommand.create(old_lc.type, new_name, - old_lc.timestamp, - old_lc.current_version, - old_lc.compatibility_version) + new_lc = LoadCommands::LoadCommand.create(old_lc.type, new_name, + old_lc.timestamp, + old_lc.current_version, + old_lc.compatibility_version) replace_command(old_lc, new_lc) end @@ -376,8 +337,8 @@ module MachO # @param new_path [String] the new runtime path # @param _options [Hash] # @return [void] - # @raise [MachO::RpathUnknownError] if no such old runtime path exists - # @raise [MachO::RpathExistsError] if the new runtime path already exists + # @raise [RpathUnknownError] if no such old runtime path exists + # @raise [RpathExistsError] if the new runtime path already exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#change_rpath} def change_rpath(old_path, new_path, _options = {}) @@ -385,7 +346,7 @@ module MachO raise RpathUnknownError, old_path if old_lc.nil? raise RpathExistsError, new_path if rpaths.include?(new_path) - new_lc = LoadCommand.create(:LC_RPATH, new_path) + new_lc = LoadCommands::LoadCommand.create(:LC_RPATH, new_path) delete_rpath(old_path) insert_command(old_lc.view.offset, new_lc) @@ -399,13 +360,13 @@ module MachO # @param path [String] the new runtime path # @param _options [Hash] # @return [void] - # @raise [MachO::RpathExistsError] if the runtime path already exists + # @raise [RpathExistsError] if the runtime path already exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#add_rpath} def add_rpath(path, _options = {}) raise RpathExistsError, path if rpaths.include?(path) - rpath_cmd = LoadCommand.create(:LC_RPATH, path) + rpath_cmd = LoadCommands::LoadCommand.create(:LC_RPATH, path) add_command(rpath_cmd) end @@ -417,7 +378,7 @@ module MachO # @param path [String] the runtime path to delete # @param _options [Hash] # @return void - # @raise [MachO::RpathUnknownError] if no such runtime path exists + # @raise [RpathUnknownError] if no such runtime path exists # @note `_options` is currently unused and is provided for signature # compatibility with {MachO::FatFile#delete_rpath} def delete_rpath(path, _options = {}) @@ -431,15 +392,6 @@ module MachO populate_fields 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 - # @deprecated use {MachO::SegmentCommand#sections} instead - def sections(segment) - segment.sections - end - # Write all Mach-O data to the given filename. # @param filename [String] the file to write to # @return [void] @@ -449,7 +401,7 @@ module MachO # Write all Mach-O data to the file used to initialize the instance. # @return [void] - # @raise [MachO::MachOError] if the instance was initialized without a file + # @raise [MachOError] if the instance was initialized without a file # @note Overwrites all data in the file! def write! if @filename.nil? @@ -462,16 +414,16 @@ module MachO 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 - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid header + # @return [Headers::MachHeader] if the Mach-O is 32-bit + # @return [Headers::MachHeader64] if the Mach-O is 64-bit + # @raise [TruncatedFileError] if the file is too small to have a valid header # @api private def populate_mach_header # the smallest Mach-O header is 28 bytes raise TruncatedFileError if @raw_data.size < 28 magic = populate_and_check_magic - mh_klass = Utils.magic32?(magic) ? MachHeader : MachHeader64 + mh_klass = Utils.magic32?(magic) ? Headers::MachHeader : Headers::MachHeader64 mh = mh_klass.new_from_bin(endianness, @raw_data[0, mh_klass.bytesize]) check_cputype(mh.cputype) @@ -483,8 +435,8 @@ module MachO # Read just the file's magic number and check its validity. # @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 + # @raise [MagicError] if the magic is not valid Mach-O magic + # @raise [FatBinaryError] if the magic is for a Fat file # @api private def populate_and_check_magic magic = @raw_data[0..3].unpack("N").first @@ -499,32 +451,32 @@ module MachO # Check the file's CPU type. # @param cputype [Fixnum] the CPU type - # @raise [MachO::CPUTypeError] if the CPU type is unknown + # @raise [CPUTypeError] if the CPU type is unknown # @api private def check_cputype(cputype) - raise CPUTypeError, cputype unless CPU_TYPES.key?(cputype) + raise CPUTypeError, cputype unless Headers::CPU_TYPES.key?(cputype) end # Check the file's CPU type/subtype pair. # @param cpusubtype [Fixnum] the CPU subtype - # @raise [MachO::CPUSubtypeError] if the CPU sub-type is unknown + # @raise [CPUSubtypeError] if the CPU sub-type is unknown # @api private def check_cpusubtype(cputype, cpusubtype) # Only check sub-type w/o capability bits (see `populate_mach_header`). - raise CPUSubtypeError.new(cputype, cpusubtype) unless CPU_SUBTYPES[cputype].key?(cpusubtype) + raise CPUSubtypeError.new(cputype, cpusubtype) unless Headers::CPU_SUBTYPES[cputype].key?(cpusubtype) end # Check the file's type. # @param filetype [Fixnum] the file type - # @raise [MachO::FiletypeError] if the file type is unknown + # @raise [FiletypeError] if the file type is unknown # @api private def check_filetype(filetype) - raise FiletypeError, filetype unless MH_FILETYPES.key?(filetype) + raise FiletypeError, filetype unless Headers::MH_FILETYPES.key?(filetype) end # All load commands in the file. - # @return [Array] an array of load commands - # @raise [MachO::LoadCommandError] if an unknown load command is encountered + # @return [Array] an array of load commands + # @raise [LoadCommandError] if an unknown load command is encountered # @api private def populate_load_commands offset = header.class.bytesize @@ -533,13 +485,13 @@ module MachO header.ncmds.times do fmt = Utils.specialize_format("L=", endianness) cmd = @raw_data.slice(offset, 4).unpack(fmt).first - cmd_sym = LOAD_COMMANDS[cmd] + cmd_sym = LoadCommands::LOAD_COMMANDS[cmd] raise LoadCommandError, 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] + klass = LoadCommands.const_get LoadCommands::LC_STRUCTURES[cmd_sym] view = MachOView.new(@raw_data, endianness, offset) command = klass.new_from_bin(view) diff --git a/Library/Homebrew/vendor/macho/macho/open.rb b/Library/Homebrew/vendor/macho/macho/open.rb deleted file mode 100644 index 103f617410..0000000000 --- a/Library/Homebrew/vendor/macho/macho/open.rb +++ /dev/null @@ -1,25 +0,0 @@ -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 - # @raise [ArgumentError] if the given file does not exist - # @raise [MachO::TruncatedFileError] if the file is too small to have a valid header - # @raise [MachO::MagicError] if the file's magic is not valid Mach-O magic - def self.open(filename) - raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) - raise TruncatedFileError unless File.stat(filename).size >= 4 - - magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first - - if Utils.fat_magic?(magic) - file = FatFile.new(filename) - elsif Utils.magic?(magic) - file = MachOFile.new(filename) - else - raise MagicError, magic - end - - file - end -end diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb index bd6218bb5a..1e69e0b61c 100644 --- a/Library/Homebrew/vendor/macho/macho/sections.rb +++ b/Library/Homebrew/vendor/macho/macho/sections.rb @@ -1,170 +1,176 @@ module MachO - # type mask - SECTION_TYPE = 0x000000ff + # Classes and constants for parsing sections in Mach-O binaries. + module Sections + # type mask + SECTION_TYPE = 0x000000ff - # attributes mask - SECTION_ATTRIBUTES = 0xffffff00 + # attributes mask + SECTION_ATTRIBUTES = 0xffffff00 - # user settable attributes mask - SECTION_ATTRIBUTES_USR = 0xff000000 + # 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, - }.freeze - - # 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", - }.freeze - - # 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 attributes 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 - - # @see MachOStructure::FORMAT - FORMAT = "a16a16L=9".freeze - - # @see MachOStructure::SIZEOF - SIZEOF = 68 + # system settable attributes mask + SECTION_ATTRIBUTES_SYS = 0x00ffff00 + # association of section flag symbols to values # @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 - - # @return [Boolean] true if the section has no contents (i.e, `size` is 0) - def empty? - size.zero? - 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 - - # @see MachOStructure::FORMAT - FORMAT = "a16a16Q=2L=8".freeze - - # @see MachOStructure::SIZEOF - SIZEOF = 80 + 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, + }.freeze + # association of section name symbols to names # @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 + 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", + }.freeze + + # 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 attributes 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 + + # @see MachOStructure::FORMAT + FORMAT = "a16a16L=9".freeze + + # @see MachOStructure::SIZEOF + 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 + + # @return [Boolean] whether the section is empty (i.e, {size} is 0) + def empty? + size.zero? + end + + # @example + # puts "this section is regular" if sect.flag?(:S_REGULAR) + # @param flag [Symbol] a section flag symbol + # @return [Boolean] whether the flag is present in the section's {flags} + 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 + + # @see MachOStructure::FORMAT + FORMAT = "a16a16Q=2L=8".freeze + + # @see MachOStructure::SIZEOF + 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 end diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb index 43f7bc84ed..bac5342d4f 100644 --- a/Library/Homebrew/vendor/macho/macho/structure.rb +++ b/Library/Homebrew/vendor/macho/macho/structure.rb @@ -19,7 +19,7 @@ module MachO # @param endianness [Symbol] either `:big` or `:little` # @param bin [String] the string to be unpacked into the new structure - # @return [MachO::MachOStructure] a new MachOStructure initialized with `bin` + # @return [MachO::MachOStructure] the resulting structure # @api private def self.new_from_bin(endianness, bin) format = Utils.specialize_format(self::FORMAT, endianness) diff --git a/Library/Homebrew/vendor/macho/macho/tools.rb b/Library/Homebrew/vendor/macho/macho/tools.rb index f34d75dc14..b49626d9d2 100644 --- a/Library/Homebrew/vendor/macho/macho/tools.rb +++ b/Library/Homebrew/vendor/macho/macho/tools.rb @@ -1,5 +1,6 @@ module MachO - # A collection of convenient methods for common operations on Mach-O and Fat binaries. + # 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 @@ -9,7 +10,8 @@ module MachO file.linked_dylibs end - # Changes the dylib ID of a Mach-O or Fat binary, overwriting the source file. + # 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 # @param options [Hash] @@ -23,7 +25,8 @@ module MachO file.write! end - # Changes a shared library install name in a Mach-O or Fat binary, overwriting the source file. + # 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 @@ -38,7 +41,8 @@ module MachO file.write! end - # Changes a runtime path in a Mach-O or Fat binary, overwriting the source file. + # 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 @@ -67,7 +71,8 @@ module MachO file.write! end - # Delete a runtime path from a Mach-O or Fat binary, overwriting the source file. + # 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 # @param options [Hash] @@ -80,5 +85,24 @@ module MachO file.delete_rpath(old_path, options) file.write! end + + # Merge multiple Mach-Os into one universal (Fat) binary. + # @param filename [String] the fat binary to create + # @param files [Array] the files to merge + # @return [void] + def self.merge_machos(filename, *files) + machos = files.map do |file| + macho = MachO.open(file) + case macho + when MachO::MachOFile + macho + else + macho.machos + end + end.flatten + + fat_macho = MachO::FatFile.new_from_machos(*machos) + fat_macho.write(filename) + end end end diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb index cedd0bc1cf..ecfc8390be 100644 --- a/Library/Homebrew/vendor/macho/macho/utils.rb +++ b/Library/Homebrew/vendor/macho/macho/utils.rb @@ -5,7 +5,7 @@ module MachO # @param value [Fixnum] the number being rounded # @param round [Fixnum] the number being rounded with # @return [Fixnum] the rounded value - # @see https://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c + # @see http://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c def self.round(value, round) round -= 1 value += round @@ -13,7 +13,8 @@ module MachO value end - # Returns the number of bytes needed to pad the given size to the given alignment. + # Returns the number of bytes needed to pad the given size to the given + # alignment. # @param size [Fixnum] the unpadded size # @param alignment [Fixnum] the number to alignment the size with # @return [Fixnum] the number of pad bytes required @@ -21,7 +22,8 @@ module MachO round(size, alignment) - size end - # Converts an abstract (native-endian) String#unpack format to big or little. + # Converts an abstract (native-endian) String#unpack format to big or + # little. # @param format [String] the format string being converted # @param endianness [Symbol] either `:big` or `:little` # @return [String] the converted string @@ -31,7 +33,8 @@ module MachO end # Packs tagged strings into an aligned payload. - # @param fixed_offset [Fixnum] the baseline offset for the first packed string + # @param fixed_offset [Fixnum] the baseline offset for the first packed + # string # @param alignment [Fixnum] the alignment value to use for packing # @param strings [Hash] the labeled strings to pack # @return [Array] the packed string and labeled offsets @@ -53,44 +56,44 @@ module MachO # Compares the given number to valid Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid Mach-O magic number, false otherwise + # @return [Boolean] whether `num` is a valid Mach-O magic number def self.magic?(num) - MH_MAGICS.key?(num) + Headers::MH_MAGICS.key?(num) end # Compares the given number to valid Fat magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid Fat magic number, false otherwise + # @return [Boolean] whether `num` is a valid Fat magic number def self.fat_magic?(num) - num == FAT_MAGIC + num == Headers::FAT_MAGIC end # Compares the given number to valid 32-bit Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid 32-bit magic number, false otherwise + # @return [Boolean] whether `num` is a valid 32-bit magic number def self.magic32?(num) - num == MH_MAGIC || num == MH_CIGAM + num == Headers::MH_MAGIC || num == Headers::MH_CIGAM end # Compares the given number to valid 64-bit Mach-O magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid 64-bit magic number, false otherwise + # @return [Boolean] whether `num` is a valid 64-bit magic number def self.magic64?(num) - num == MH_MAGIC_64 || num == MH_CIGAM_64 + num == Headers::MH_MAGIC_64 || num == Headers::MH_CIGAM_64 end # Compares the given number to valid little-endian magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid little-endian magic number, false otherwise + # @return [Boolean] whether `num` is a valid little-endian magic number def self.little_magic?(num) - num == MH_CIGAM || num == MH_CIGAM_64 + num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64 end # Compares the given number to valid big-endian magic numbers. # @param num [Fixnum] the number being checked - # @return [Boolean] true if `num` is a valid big-endian magic number, false otherwise + # @return [Boolean] whether `num` is a valid big-endian magic number def self.big_magic?(num) - num == MH_CIGAM || num == MH_CIGAM_64 + num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64 end end end