aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/vendor/README.md2
-rw-r--r--Library/Homebrew/vendor/macho/macho.rb3
-rw-r--r--Library/Homebrew/vendor/macho/macho/exceptions.rb89
-rw-r--r--Library/Homebrew/vendor/macho/macho/fat_file.rb169
-rw-r--r--Library/Homebrew/vendor/macho/macho/headers.rb156
-rw-r--r--Library/Homebrew/vendor/macho/macho/load_commands.rb695
-rw-r--r--Library/Homebrew/vendor/macho/macho/macho_file.rb426
-rw-r--r--Library/Homebrew/vendor/macho/macho/open.rb10
-rw-r--r--Library/Homebrew/vendor/macho/macho/sections.rb29
-rw-r--r--Library/Homebrew/vendor/macho/macho/structure.rb24
-rw-r--r--Library/Homebrew/vendor/macho/macho/tools.rb49
-rw-r--r--Library/Homebrew/vendor/macho/macho/utils.rb126
-rw-r--r--Library/Homebrew/vendor/macho/macho/view.rb23
13 files changed, 1296 insertions, 505 deletions
diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md
index c48571895..848ce703f 100644
--- a/Library/Homebrew/vendor/README.md
+++ b/Library/Homebrew/vendor/README.md
@@ -3,7 +3,7 @@ Vendored Dependencies
* [okjson](https://github.com/kr/okjson), version 43.
-* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 0.2.4
+* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 0.2.5
## Licenses:
diff --git a/Library/Homebrew/vendor/macho/macho.rb b/Library/Homebrew/vendor/macho/macho.rb
index c94eec77b..5b0974b3f 100644
--- a/Library/Homebrew/vendor/macho/macho.rb
+++ b/Library/Homebrew/vendor/macho/macho.rb
@@ -1,4 +1,5 @@
require "#{File.dirname(__FILE__)}/macho/structure"
+require "#{File.dirname(__FILE__)}/macho/view"
require "#{File.dirname(__FILE__)}/macho/headers"
require "#{File.dirname(__FILE__)}/macho/load_commands"
require "#{File.dirname(__FILE__)}/macho/sections"
@@ -12,5 +13,5 @@ require "#{File.dirname(__FILE__)}/macho/tools"
# The primary namespace for ruby-macho.
module MachO
# release version
- VERSION = "0.2.4".freeze
+ VERSION = "0.2.5".freeze
end
diff --git a/Library/Homebrew/vendor/macho/macho/exceptions.rb b/Library/Homebrew/vendor/macho/macho/exceptions.rb
index 6840060e8..262c195a3 100644
--- a/Library/Homebrew/vendor/macho/macho/exceptions.rb
+++ b/Library/Homebrew/vendor/macho/macho/exceptions.rb
@@ -3,6 +3,26 @@ module MachO
class MachOError < RuntimeError
end
+ # Raised when a Mach-O file modification fails.
+ class ModificationError < MachOError
+ end
+
+ # Raised when a Mach-O file modification fails but can be recovered when
+ # operating on multiple Mach-O slices of a fat binary in non-strict mode.
+ class RecoverableModificationError < ModificationError
+ # @return [Fixnum, nil] The index of the Mach-O slice of a fat binary for
+ # which modification failed or `nil` if not a fat binary. This is used to
+ # make the error message more useful.
+ attr_accessor :macho_slice
+
+ # @return [String] The exception message.
+ def to_s
+ s = super.to_s
+ s = "While modifying Mach-O slice #{@macho_slice}: #{s}" if @macho_slice
+ s
+ end
+ end
+
# Raised when a file is not a Mach-O.
class NotAMachOError < MachOError
# @param error [String] the error in question
@@ -80,32 +100,89 @@ module MachO
end
end
+ # Raised when a load command can't be created manually.
+ class LoadCommandNotCreatableError < MachOError
+ # @param cmd_sym [Symbol] the uncreatable load command's symbol
+ def initialize(cmd_sym)
+ super "Load commands of type #{cmd_sym} cannot be created manually"
+ end
+ end
+
+ # 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}"
+ end
+ end
+
+ # Raised when a load command can't be serialized.
+ class LoadCommandNotSerializableError < MachOError
+ # @param cmd_sym [Symbol] the load command's symbol
+ def initialize(cmd_sym)
+ super "Load commands of type #{cmd_sym} cannot be serialized"
+ end
+ end
+
+ # Raised when a load command string is malformed in some way.
+ 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"
+ end
+ end
+
+ # Raised when a change at an offset is not valid.
+ class OffsetInsertionError < ModificationError
+ # @param offset [Fixnum] the invalid offset
+ def initialize(offset)
+ super "Insertion at offset #{offset} is not valid"
+ end
+ end
+
# Raised when load commands are too large to fit in the current file.
- class HeaderPadError < MachOError
+ class HeaderPadError < ModificationError
# @param filename [String] the filename
def initialize(filename)
- super "Updated load commands do not fit in the header of " +
- "#{filename}. #{filename} needs to be relinked, possibly with " +
- "-headerpad or -headerpad_max_install_names"
+ super "Updated load commands do not fit in the header of " \
+ "#{filename}. #{filename} needs to be relinked, possibly with " \
+ "-headerpad or -headerpad_max_install_names"
end
end
# Raised when attempting to change a dylib name that doesn't exist.
- class DylibUnknownError < MachOError
+ class DylibUnknownError < RecoverableModificationError
# @param dylib [String] the unknown shared library name
def initialize(dylib)
super "No such dylib name: #{dylib}"
end
end
+ # Raised when a dylib is missing an ID
+ class DylibIdMissingError < RecoverableModificationError
+ def initialize
+ super "Dylib is missing a dylib ID"
+ end
+ end
+
# Raised when attempting to change an rpath that doesn't exist.
- class RpathUnknownError < MachOError
+ class RpathUnknownError < RecoverableModificationError
# @param path [String] the unknown runtime path
def initialize(path)
super "No such runtime path: #{path}"
end
end
+ # Raised when attempting to add an rpath that already exists.
+ class RpathExistsError < RecoverableModificationError
+ # @param path [String] the extant path
+ def initialize(path)
+ super "#{path} already exists"
+ end
+ end
+
# Raised whenever unfinished code is called.
class UnimplementedError < MachOError
# @param thing [String] the thing that is unimplemented
diff --git a/Library/Homebrew/vendor/macho/macho/fat_file.rb b/Library/Homebrew/vendor/macho/macho/fat_file.rb
index dd24fa10f..792473648 100644
--- a/Library/Homebrew/vendor/macho/macho/fat_file.rb
+++ b/Library/Homebrew/vendor/macho/macho/fat_file.rb
@@ -30,22 +30,24 @@ module MachO
# @param filename [String] the fat file to load from
# @raise [ArgumentError] if the given file does not exist
def initialize(filename)
- raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
+ raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
@filename = filename
- @raw_data = File.open(@filename, "rb") { |f| f.read }
- @header = get_fat_header
- @fat_archs = get_fat_archs
- @machos = get_machos
+ @raw_data = File.open(@filename, "rb", &:read)
+ @header = populate_fat_header
+ @fat_archs = populate_fat_archs
+ @machos = populate_machos
end
+ # Initializes a new FatFile instance from a binary string.
+ # @see MachO::FatFile.new_from_bin
# @api private
def initialize_from_bin(bin)
@filename = nil
@raw_data = bin
- @header = get_fat_header
- @fat_archs = get_fat_archs
- @machos = get_machos
+ @header = populate_fat_header
+ @fat_archs = populate_fat_archs
+ @machos = populate_machos
end
# The file's raw fat data.
@@ -115,7 +117,7 @@ module MachO
end
# The file's type. Assumed to be the same for every Mach-O within.
- # @return [String] the filetype
+ # @return [Symbol] the filetype
def filetype
machos.first.filetype
end
@@ -124,34 +126,37 @@ module MachO
# @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.
# @example
- # file.dylib_id = 'libFoo.dylib'
+ # file.change_dylib_id('libFoo.dylib')
# @param new_id [String] the new dylib ID
+ # @param options [Hash]
+ # @option options [Boolean] :strict (true) if true, fail if one slice fails.
+ # if false, fail only if all slices fail.
# @return [void]
# @raise [ArgumentError] if `new_id` is not a String
- def dylib_id=(new_id)
- if !new_id.is_a?(String)
- raise ArgumentError.new("argument must be a String")
- end
-
- if !machos.all?(&:dylib?)
- return nil
- end
+ # @see MachO::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?)
- machos.each do |macho|
- macho.dylib_id = new_id
+ each_macho(options) do |macho|
+ macho.change_dylib_id(new_id, options)
end
synchronize_raw_data
end
+ alias dylib_id= change_dylib_id
+
# All shared libraries linked to the file's Mach-Os.
# @return [Array<String>] an array of all shared libraries
+ # @see MachO::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.
@@ -165,16 +170,74 @@ module MachO
# file.change_install_name('/usr/lib/libFoo.dylib', '/usr/lib/libBar.dylib')
# @param old_name [String] the shared library name being changed
# @param new_name [String] the new name
- # @todo incomplete
- def change_install_name(old_name, new_name)
- machos.each do |macho|
- macho.change_install_name(old_name, new_name)
+ # @param options [Hash]
+ # @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
+ def change_install_name(old_name, new_name, options = {})
+ each_macho(options) do |macho|
+ macho.change_install_name(old_name, new_name, options)
+ end
+
+ synchronize_raw_data
+ end
+
+ alias change_dylib change_install_name
+
+ # All runtime paths associated with the file's Mach-Os.
+ # @return [Array<String>] an array of all runtime paths
+ # @see MachO::MachOFile#rpaths
+ def rpaths
+ # Can individual architectures have different runtime paths?
+ machos.map(&:rpaths).flatten.uniq
+ end
+
+ # Change the runtime path `old_path` to `new_path` in the file's Mach-Os.
+ # @param old_path [String] the old runtime path
+ # @param new_path [String] the new runtime path
+ # @param options [Hash]
+ # @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
+ def change_rpath(old_path, new_path, options = {})
+ each_macho(options) do |macho|
+ macho.change_rpath(old_path, new_path, options)
+ end
+
+ synchronize_raw_data
+ end
+
+ # Add the given runtime path to the file's Mach-Os.
+ # @param path [String] the new runtime path
+ # @param options [Hash]
+ # @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
+ def add_rpath(path, options = {})
+ each_macho(options) do |macho|
+ macho.add_rpath(path, options)
end
synchronize_raw_data
end
- alias :change_dylib :change_install_name
+ # Delete the given runtime path from the file's Mach-Os.
+ # @param path [String] the runtime path to delete
+ # @param options [Hash]
+ # @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
+ def delete_rpath(path, options = {})
+ each_macho(options) do |macho|
+ macho.delete_rpath(path, options)
+ end
+
+ synchronize_raw_data
+ end
# Extract a Mach-O with the given CPU type from the file.
# @example
@@ -197,7 +260,7 @@ module MachO
# @note Overwrites all data in the file!
def write!
if filename.nil?
- raise MachOError.new("cannot write to a default file when initialized from a binary string")
+ raise MachOError, "cannot write to a default file when initialized from a binary string"
else
File.open(@filename, "wb") { |f| f.write(@raw_data) }
end
@@ -211,15 +274,15 @@ module MachO
# @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
- # @private
- def get_fat_header
+ # @api private
+ def populate_fat_header
# the smallest fat Mach-O header is 8 bytes
- raise TruncatedFileError.new if @raw_data.size < 8
+ raise TruncatedFileError if @raw_data.size < 8
fh = FatHeader.new_from_bin(:big, @raw_data[0, FatHeader.bytesize])
- raise MagicError.new(fh.magic) unless MachO.magic?(fh.magic)
- raise MachOBinaryError.new unless MachO.fat_magic?(fh.magic)
+ raise MagicError, fh.magic unless Utils.magic?(fh.magic)
+ raise MachOBinaryError unless Utils.fat_magic?(fh.magic)
# Rationale: Java classfiles have the same magic as big-endian fat
# Mach-Os. Classfiles encode their version at the same offset as
@@ -228,15 +291,15 @@ module MachO
# technically possible for a fat Mach-O to have over 30 architectures,
# but this is extremely unlikely and in practice distinguishes the two
# formats.
- raise JavaClassFileError.new if fh.nfat_arch > 30
+ raise JavaClassFileError if fh.nfat_arch > 30
fh
end
# Obtain an array of fat architectures from raw file data.
# @return [Array<MachO::FatArch>] an array of fat architectures
- # @private
- def get_fat_archs
+ # @api private
+ def populate_fat_archs
archs = []
fa_off = FatHeader.bytesize
@@ -250,8 +313,8 @@ module MachO
# Obtain an array of Mach-O blobs from raw file data.
# @return [Array<MachO::MachOFile>] an array of Mach-Os
- # @private
- def get_machos
+ # @api private
+ def populate_machos
machos = []
fat_archs.each do |arch|
@@ -261,9 +324,37 @@ module MachO
machos
end
- # @todo this needs to be redesigned. arch[:offset] and arch[:size] are
- # already out-of-date, and the header needs to be synchronized as well.
- # @private
+ # Yield each Mach-O object in the file, rescuing and accumulating errors.
+ # @param options [Hash]
+ # @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
+ # the `:strict` option above.
+ # @api private
+ def each_macho(options = {})
+ strict = options.fetch(:strict, true)
+ errors = []
+
+ machos.each_with_index do |macho, index|
+ begin
+ yield macho
+ rescue RecoverableModificationError => error
+ error.macho_slice = index
+
+ # Strict mode: Immediately re-raise. Otherwise: Retain, check later.
+ raise error if strict
+ errors << error
+ end
+ end
+
+ # Non-strict mode: Raise first error if *all* Mach-O slices failed.
+ raise errors.first if errors.size == machos.size
+ end
+
+ # Synchronize the raw file data with each internal Mach-O object.
+ # @return [void]
+ # @api private
def synchronize_raw_data
machos.each_with_index do |macho, i|
arch = fat_archs[i]
diff --git a/Library/Homebrew/vendor/macho/macho/headers.rb b/Library/Homebrew/vendor/macho/macho/headers.rb
index 79b5a1802..7272503af 100644
--- a/Library/Homebrew/vendor/macho/macho/headers.rb
+++ b/Library/Homebrew/vendor/macho/macho/headers.rb
@@ -1,64 +1,82 @@
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"
- }
+ 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,
@@ -67,156 +85,213 @@ module MachO
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
- CPU_SUBTYPE_PENT = CPU_SUBTYPE_586
+
+ # @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,
@@ -280,53 +355,64 @@ module MachO
}.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 string representations
+ # association of filetypes to Symbol representations
# @api private
MH_FILETYPES = {
- MH_OBJECT => "MH_OBJECT",
- MH_EXECUTE => "MH_EXECUTE",
- MH_FVMLIB => "MH_FVMLIB",
- MH_CORE => "MH_CORE",
- MH_PRELOAD => "MH_PRELOAD",
- MH_DYLIB => "MH_DYLIB",
- MH_DYLINKER => "MH_DYLINKER",
- MH_BUNDLE => "MH_BUNDLE",
- MH_DYLIB_STUB => "MH_DYLIB_STUB",
- MH_DSYM => "MH_DSYM",
- MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE"
- }
+ 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
@@ -356,8 +442,8 @@ module MachO
:MH_DEAD_STRIPPABLE_DYLIB => 0x400000,
:MH_HAS_TLV_DESCRIPTORS => 0x800000,
:MH_NO_HEAP_EXECUTION => 0x1000000,
- :MH_APP_EXTENSION_SAFE => 0x02000000
- }
+ :MH_APP_EXTENSION_SAFE => 0x02000000,
+ }.freeze
# Fat binary header structure
# @see MachO::FatArch
@@ -369,7 +455,12 @@ module MachO
attr_reader :nfat_arch
# always big-endian
- FORMAT = "N2"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "N2".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 8
# @api private
@@ -399,7 +490,12 @@ module MachO
attr_reader :align
# always big-endian
- FORMAT = "N5"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "N5".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 20
# @api private
@@ -435,12 +531,17 @@ module MachO
# @return [Fixnum] the header flags associated with the Mach-O
attr_reader :flags
- FORMAT = "L=7"
+ # @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)
+ flags)
@magic = magic
@cputype = cputype
# For now we're not interested in additional capability bits also to be
@@ -468,12 +569,17 @@ module MachO
# @return [void]
attr_reader :reserved
- FORMAT = "L=8"
+ # @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)
+ flags, reserved)
super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags)
@reserved = reserved
end
diff --git a/Library/Homebrew/vendor/macho/macho/load_commands.rb b/Library/Homebrew/vendor/macho/macho/load_commands.rb
index 0e7695ee9..3eb130bb8 100644
--- a/Library/Homebrew/vendor/macho/macho/load_commands.rb
+++ b/Library/Homebrew/vendor/macho/macho/load_commands.rb
@@ -1,6 +1,7 @@
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
@@ -57,6 +58,10 @@ module MachO
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 = [
@@ -67,19 +72,27 @@ module MachO
: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 => "LoadCommand", # obsolete
+ :LC_SYMSEG => "SymsegCommand", # obsolete
:LC_THREAD => "ThreadCommand", # seems obsolete, but not documented as such
:LC_UNIXTHREAD => "ThreadCommand",
- :LC_LOADFVMLIB => "LoadCommand", # obsolete
- :LC_IDFVMLIB => "LoadCommand", # obsolete
- :LC_IDENT => "LoadCommand", # obsolete
- :LC_FVMFILE => "LoadCommand", # reserved for internal use only
- :LC_PREPAGE => "LoadCommand", # reserved for internal use only
+ :LC_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",
@@ -148,8 +161,8 @@ module MachO
# represented, and no actual data. Used when a more specific class
# isn't available/implemented.
class LoadCommand < MachOStructure
- # @return [Fixnum] the offset in the file the command was created from
- attr_reader :offset
+ # @return [MachO::MachOView] the raw view associated with the load command
+ attr_reader :view
# @return [Fixnum] the load command's identifying number
attr_reader :cmd
@@ -157,40 +170,80 @@ module MachO
# @return [Fixnum] the size of the load command, in bytes
attr_reader :cmdsize
- FORMAT = "L=2"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 8
- # Creates a new LoadCommand given an offset and binary string
- # @param raw_data [String] the raw Mach-O data
- # @param endianness [Symbol] the endianness of the command (:big or :little)
- # @param offset [Fixnum] the offset to initialize with
- # @param bin [String] the binary string to initialize with
+ # 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
# @api private
- def self.new_from_bin(raw_data, endianness, offset, bin)
- format = specialize_format(self::FORMAT, endianness)
+ 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
+
+ # 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]
- self.new(raw_data, offset, *bin.unpack(format))
+ # 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 raw_data [String] the raw Mach-O data
- # @param offset [Fixnum] the offset to initialize with
+ # @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(raw_data, offset, cmd, cmdsize)
- @raw_data = raw_data
- @offset = offset
+ def initialize(view, cmd, cmdsize)
+ @view = view
@cmd = cmd
@cmdsize = cmdsize
end
+ # @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
+ # @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
+ alias to_sym type
# @return [String] a string representation of the load command's identifying number
def to_s
@@ -202,25 +255,62 @@ module MachO
# pretend that strings stored in LCs are immediately available without
# explicit operations on the raw Mach-O data.
class LCStr
- # @param raw_data [String] the raw Mach-O data.
# @param lc [MachO::LoadCommand] the load command
- # @param lc_str [Fixnum] the offset to the beginning of the string
+ # @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(raw_data, lc, lc_str)
- @raw_data = raw_data
- @lc = lc
- @lc_str = lc_str
- @str = @raw_data.slice(@lc.offset + @lc_str...@lc.offset + @lc.cmdsize).delete("\x00")
+ 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
- @str
+ @string
end
# @return [Fixnum] the offset to the beginning of the string in the load command
def to_i
- @lc_str
+ @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
@@ -231,12 +321,17 @@ module MachO
# @return [Array<Fixnum>] the UUID
attr_reader :uuid
- FORMAT = "L=2a16"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2a16".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 24
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, uuid)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, uuid)
+ super(view, cmd, cmdsize)
@uuid = uuid.unpack("C16") # re-unpack for the actual UUID array
end
@@ -282,13 +377,18 @@ module MachO
# @return [Fixnum] any flags associated with the segment
attr_reader :flags
- FORMAT = "L=2a16L=4l=2L=2"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2a16L=4l=2L=2".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 56
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
- filesize, maxprot, initprot, nsects, flags)
- super(raw_data, offset, cmd, cmdsize)
+ 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
@@ -300,6 +400,23 @@ module MachO
@flags = flags
end
+ # All sections referenced within this segment.
+ # @return [Array<MachO::Section>] if the Mach-O is 32-bit
+ # @return [Array<MachO::Section64>] 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
@@ -313,61 +430,14 @@ module MachO
# A load command indicating that part of this file is to be mapped into
# the task's address space. Corresponds to LC_SEGMENT_64.
- class SegmentCommand64 < LoadCommand
- # @return [String] the name of the segment
- attr_reader :segname
-
- # @return [Fixnum] the memory address of the segment
- attr_reader :vmaddr
-
- # @return [Fixnum] the memory size of the segment
- attr_reader :vmsize
-
- # @return [Fixnum] the file offset of the segment
- attr_reader :fileoff
-
- # @return [Fixnum] the amount to map from the file
- attr_reader :filesize
-
- # @return [Fixnum] the maximum VM protection
- attr_reader :maxprot
-
- # @return [Fixnum] the initial VM protection
- attr_reader :initprot
-
- # @return [Fixnum] the number of sections in the segment
- attr_reader :nsects
-
- # @return [Fixnum] any flags associated with the segment
- attr_reader :flags
-
- FORMAT = "L=2a16Q=4l=2L=2"
- SIZEOF = 72
-
+ class SegmentCommand64 < SegmentCommand
+ # @see MachOStructure::FORMAT
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
- filesize, maxprot, initprot, nsects, flags)
- super(raw_data, offset, cmd, cmdsize)
- @segname = segname.delete("\x00")
- @vmaddr = vmaddr
- @vmsize = vmsize
- @fileoff = fileoff
- @filesize = filesize
- @maxprot = maxprot
- @initprot = initprot
- @nsects = nsects
- @flags = flags
- end
+ FORMAT = "L=2a16Q=4l=2L=2".freeze
- # @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
+ # @see MachOStructure::SIZEOF
+ # @api private
+ SIZEOF = 72
end
# A load command representing some aspect of shared libraries, depending
@@ -386,18 +456,33 @@ module MachO
# @return [Fixnum] the library's compatibility version number
attr_reader :compatibility_version
- FORMAT = "L=6"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=6".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 24
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, name, timestamp, current_version,
- compatibility_version)
- super(raw_data, offset, cmd, cmdsize)
- @name = LCStr.new(raw_data, self, name)
+ 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
@@ -407,13 +492,28 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr
attr_reader :name
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, name)
- super(raw_data, offset, cmd, cmdsize)
- @name = LCStr.new(raw_data, self, name)
+ 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
@@ -429,13 +529,18 @@ module MachO
# @return [Fixnum] a bit vector of linked modules
attr_reader :linked_modules
- FORMAT = "L=5"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=5".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 20
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, name, nmodules, linked_modules)
- super(raw_data, offset, cmd, cmdsize)
- @name = LCStr.new(raw_data, self, name)
+ 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
@@ -444,7 +549,12 @@ module MachO
# 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
- FORMAT = "L=2"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 8
end
@@ -476,14 +586,18 @@ module MachO
# @return [void]
attr_reader :reserved6
- FORMAT = "L=10"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=10".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 40
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
- reserved1, reserved2, reserved3, reserved4, reserved5,
- reserved6)
- super(raw_data, offset, cmd, cmdsize)
+ 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
@@ -523,14 +637,18 @@ module MachO
# @return [void]
attr_reader :reserved6
- FORMAT = "L=2Q=8"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2Q=8".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 72
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
- reserved1, reserved2, reserved3, reserved4, reserved5,
- reserved6)
- super(raw_data, offset, cmd, cmdsize)
+ 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
@@ -548,13 +666,18 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr
attr_reader :umbrella
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, umbrella)
- super(raw_data, offset, cmd, cmdsize)
- @umbrella = LCStr.new(raw_data, self, umbrella)
+ def initialize(view, cmd, cmdsize, umbrella)
+ super(view, cmd, cmdsize)
+ @umbrella = LCStr.new(self, umbrella)
end
end
@@ -564,13 +687,18 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr
attr_reader :sub_umbrella
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, sub_umbrella)
- super(raw_data, offset, cmd, cmdsize)
- @sub_umbrella = LCStr.new(raw_data, self, sub_umbrella)
+ def initialize(view, cmd, cmdsize, sub_umbrella)
+ super(view, cmd, cmdsize)
+ @sub_umbrella = LCStr.new(self, sub_umbrella)
end
end
@@ -580,13 +708,18 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr
attr_reader :sub_library
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, sub_library)
- super(raw_data, offset, cmd, cmdsize)
- @sub_library = LCStr.new(raw_data, self, sub_library)
+ def initialize(view, cmd, cmdsize, sub_library)
+ super(view, cmd, cmdsize)
+ @sub_library = LCStr.new(self, sub_library)
end
end
@@ -596,13 +729,18 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr
attr_reader :sub_client
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, sub_client)
- super(raw_data, offset, cmd, cmdsize)
- @sub_client = LCStr.new(raw_data, self, sub_client)
+ def initialize(view, cmd, cmdsize, sub_client)
+ super(view, cmd, cmdsize)
+ @sub_client = LCStr.new(self, sub_client)
end
end
@@ -621,12 +759,17 @@ module MachO
# @return the string table size in bytes
attr_reader :strsize
- FORMAT = "L=6"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=6".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 24
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, symoff, nsyms, stroff, strsize)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, symoff, nsyms, stroff, strsize)
+ super(view, cmd, cmdsize)
@symoff = symoff
@nsyms = nsyms
@stroff = stroff
@@ -691,17 +834,21 @@ module MachO
# @return [Fixnum] the number of local relocation entries
attr_reader :nlocrel
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=20".freeze
- FORMAT = "L=20"
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 80
# ugh
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym,
- nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff,
- nmodtab, extrefsymoff, nextrefsyms, indirectsymoff,
- nindirectsyms, extreloff, nextrel, locreloff, nlocrel)
- super(raw_data, offset, cmd, cmdsize)
+ 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
@@ -732,14 +879,58 @@ module MachO
# @return [Fixnum] the number of hints in the hint table
attr_reader :nhints
- FORMAT = "L=4"
+ # @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(raw_data, offset, cmd, cmdsize, htoffset, nhints)
- super(raw_data, offset, cmd, cmdsize)
+ 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<MachO::TwoLevelHintsTable::TwoLevelHint>] 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
@@ -749,12 +940,17 @@ module MachO
# @return [Fixnum] the checksum or 0
attr_reader :cksum
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, cksum)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, cksum)
+ super(view, cmd, cmdsize)
@cksum = cksum
end
end
@@ -766,13 +962,28 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr
attr_reader :path
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, path)
- super(raw_data, offset, cmd, cmdsize)
- @path = LCStr.new(raw_data, self, path)
+ def initialize(view, cmd, cmdsize, path)
+ super(view, cmd, cmdsize)
+ @path = LCStr.new(self, path)
+ 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
@@ -786,12 +997,17 @@ module MachO
# @return [Fixnum] size of the data in the __LINKEDIT segment
attr_reader :datasize
- FORMAT = "L=4"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=4".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 16
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, dataoff, datasize)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, dataoff, datasize)
+ super(view, cmd, cmdsize)
@dataoff = dataoff
@datasize = datasize
end
@@ -809,12 +1025,17 @@ module MachO
# @return [Fixnum] the encryption system, or 0 if not encrypted yet
attr_reader :cryptid
- FORMAT = "L=5"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=5".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 20
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid)
+ super(view, cmd, cmdsize)
@cryptoff = cryptoff
@cryptsize = cryptsize
@cryptid = cryptid
@@ -836,12 +1057,17 @@ module MachO
# @return [Fixnum] 64-bit padding value
attr_reader :pad
- FORMAT = "L=6"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=6".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 24
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad)
+ super(view, cmd, cmdsize)
@cryptoff = cryptoff
@cryptsize = cryptsize
@cryptid = cryptid
@@ -858,12 +1084,17 @@ module MachO
# @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8
attr_reader :sdk
- FORMAT = "L=4"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=4".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 16
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, version, sdk)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, version, sdk)
+ super(view, cmd, cmdsize)
@version = version
@sdk = sdk
end
@@ -925,14 +1156,19 @@ module MachO
# @return [Fixnum] the size of the export information
attr_reader :export_size
- FORMAT = "L=12"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=12".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 48
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, rebase_off, rebase_size, bind_off,
- bind_size, weak_bind_off, weak_bind_size, lazy_bind_off,
- lazy_bind_size, export_off, export_size)
- super(raw_data, offset, cmd, cmdsize)
+ 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
@@ -952,12 +1188,17 @@ module MachO
# @return [Fixnum] the number of strings
attr_reader :count
- FORMAT = "L=3"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=3".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 12
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, count)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, count)
+ super(view, cmd, cmdsize)
@count = count
end
end
@@ -970,12 +1211,17 @@ module MachO
# @return [Fixnum] if not 0, the initial stack size.
attr_reader :stacksize
- FORMAT = "L=2Q=2"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2Q=2".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 24
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, entryoff, stacksize)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, entryoff, stacksize)
+ super(view, cmd, cmdsize)
@entryoff = entryoff
@stacksize = stacksize
end
@@ -987,12 +1233,17 @@ module MachO
# @return [Fixnum] the version packed as a24.b10.c10.d10.e10
attr_reader :version
- FORMAT = "L=2Q=1"
+ # @see MachOStructure::FORMAT
+ # @api private
+ FORMAT = "L=2Q=1".freeze
+
+ # @see MachOStructure::SIZEOF
+ # @api private
SIZEOF = 16
# @api private
- def initialize(raw_data, offset, cmd, cmdsize, version)
- super(raw_data, offset, cmd, cmdsize)
+ def initialize(view, cmd, cmdsize, version)
+ super(view, cmd, cmdsize)
@version = version
end
@@ -1008,4 +1259,94 @@ module MachO
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 [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
+ 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
+
+ # @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
diff --git a/Library/Homebrew/vendor/macho/macho/macho_file.rb b/Library/Homebrew/vendor/macho/macho/macho_file.rb
index dc8d6fc25..8ece62672 100644
--- a/Library/Homebrew/vendor/macho/macho/macho_file.rb
+++ b/Library/Homebrew/vendor/macho/macho/macho_file.rb
@@ -16,6 +16,7 @@ module MachO
attr_reader :header
# @return [Array<MachO::LoadCommand>] 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.
@@ -32,20 +33,20 @@ module MachO
# @param filename [String] the Mach-O file to load from
# @raise [ArgumentError] if the given file does not exist
def initialize(filename)
- raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
+ raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
@filename = filename
- @raw_data = File.open(@filename, "rb") { |f| f.read }
- @header = get_mach_header
- @load_commands = get_load_commands
+ @raw_data = File.open(@filename, "rb", &:read)
+ populate_fields
end
+ # Initializes a new MachOFile instance from a binary string.
+ # @see MachO::MachOFile.new_from_bin
# @api private
def initialize_from_bin(bin)
@filename = nil
@raw_data = bin
- @header = get_mach_header
- @load_commands = get_load_commands
+ populate_fields
end
# The file's raw Mach-O data.
@@ -56,12 +57,17 @@ module MachO
# @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise
def magic32?
- MachO.magic32?(header.magic)
+ Utils.magic32?(header.magic)
end
# @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise
def magic64?
- MachO.magic64?(header.magic)
+ 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
@@ -124,7 +130,7 @@ module MachO
MH_MAGICS[magic]
end
- # @return [String] a string representation of the Mach-O's filetype
+ # @return [Symbol] a string representation of the Mach-O's filetype
def filetype
MH_FILETYPES[header.filetype]
end
@@ -164,7 +170,109 @@ module MachO
load_commands.select { |lc| lc.type == name.to_sym }
end
- alias :[] :command
+ alias [] command
+
+ # 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 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
+ # @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)
+ cmd_raw = lc.serialize(context)
+
+ if offset < header.class.bytesize || offset + cmd_raw.bytesize > low_fileoff
+ raise OffsetInsertionError, offset
+ end
+
+ new_sizeofcmds = sizeofcmds + cmd_raw.bytesize
+
+ if header.class.bytesize + new_sizeofcmds > low_fileoff
+ raise HeaderPadError, @filename
+ end
+
+ # update Mach-O header fields to account for inserted load command
+ update_ncmds(ncmds + 1)
+ update_sizeofcmds(new_sizeofcmds)
+
+ @raw_data.insert(offset, cmd_raw)
+ @raw_data.slice!(header.class.bytesize + new_sizeofcmds, cmd_raw.bytesize)
+
+ populate_fields if options.fetch(:repopulate, true)
+ 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
+ # @return [void]
+ # @raise [MachO::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)
+ cmd_raw = new_lc.serialize(context)
+ new_sizeofcmds = sizeofcmds + cmd_raw.bytesize - old_lc.cmdsize
+ if header.class.bytesize + new_sizeofcmds > low_fileoff
+ raise HeaderPadError, @filename
+ end
+
+ delete_command(old_lc)
+ insert_command(old_lc.view.offset, new_lc)
+ end
+
+ # Appends a new load command to the Mach-O.
+ # @param lc [MachO::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}
+ # @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**
+ # afterwards.
+ def add_command(lc, options = {})
+ insert_command(header.class.bytesize + sizeofcmds, lc, options)
+ end
+
+ # Delete a load command from the Mach-O.
+ # @param lc [MachO::LoadCommand] the load command being deleted
+ # @param options [Hash]
+ # @option options [Boolean] :repopulate (true) whether or not to repopulate
+ # the instance fields
+ # @return [void]
+ # @note This is public, but methods like {#delete_rpath} should be preferred.
+ # Setting `repopulate` to false **will leave the instance in an
+ # inconsistent state** unless {#populate_fields} is called **immediately**
+ # afterwards.
+ def delete_command(lc, options = {})
+ @raw_data.slice!(lc.view.offset, lc.cmdsize)
+
+ # update Mach-O header fields to account for deleted load command
+ update_ncmds(ncmds - 1)
+ update_sizeofcmds(sizeofcmds - lc.cmdsize)
+
+ # pad the space after the load commands to preserve offsets
+ null_pad = "\x00" * lc.cmdsize
+ @raw_data.insert(header.class.bytesize + sizeofcmds - lc.cmdsize, null_pad)
+
+ populate_fields if options.fetch(:repopulate, true)
+ end
+
+ # Populate the instance's fields with the raw Mach-O data.
+ # @return [void]
+ # @note This method is public, but should (almost) never need to be called.
+ # The exception to this rule is when methods like {#add_command} and
+ # {#delete_command} have been called with `repopulate = false`.
+ def populate_fields
+ @header = populate_mach_header
+ @load_commands = populate_load_commands
+ end
# All load commands responsible for loading dylibs.
# @return [Array<MachO::DylibCommand>] an array of DylibCommands
@@ -188,9 +296,7 @@ module MachO
# file.dylib_id # => 'libBar.dylib'
# @return [String, nil] the Mach-O's dylib ID
def dylib_id
- if !dylib?
- return nil
- end
+ return unless dylib?
dylib_id_cmd = command(:LC_ID_DYLIB).first
@@ -199,25 +305,30 @@ module MachO
# Changes the Mach-O's dylib ID to `new_id`. Does nothing if not a dylib.
# @example
- # file.dylib_id = "libFoo.dylib"
+ # file.change_dylib_id("libFoo.dylib")
# @param new_id [String] the dylib's new ID
+ # @param _options [Hash]
# @return [void]
# @raise [ArgumentError] if `new_id` is not a String
- def dylib_id=(new_id)
- if !new_id.is_a?(String)
- raise ArgumentError.new("argument must be a String")
- end
+ # @note `_options` is currently unused and is provided for signature
+ # compatibility with {MachO::FatFile#change_dylib_id}
+ def change_dylib_id(new_id, _options = {})
+ raise ArgumentError, "new ID must be a String" unless new_id.is_a?(String)
+ return unless dylib?
- if !dylib?
- return nil
- end
+ old_lc = command(:LC_ID_DYLIB).first
+ raise DylibIdMissingError unless old_lc
- dylib_cmd = command(:LC_ID_DYLIB).first
- old_id = dylib_id
+ new_lc = LoadCommand.create(:LC_ID_DYLIB, new_id,
+ old_lc.timestamp,
+ old_lc.current_version,
+ old_lc.compatibility_version)
- set_name_in_dylib(dylib_cmd, old_id, new_id)
+ replace_command(old_lc, new_lc)
end
+ alias dylib_id= change_dylib_id
+
# All shared libraries linked to the Mach-O.
# @return [Array<String>] an array of all shared libraries
def linked_dylibs
@@ -233,16 +344,24 @@ module MachO
# file.change_install_name("/usr/lib/libWhatever.dylib", "/usr/local/lib/libWhatever2.dylib")
# @param old_name [String] the shared library's old name
# @param new_name [String] the shared library's new name
+ # @param _options [Hash]
# @return [void]
# @raise [MachO::DylibUnknownError] if no shared library has the old name
- def change_install_name(old_name, new_name)
- dylib_cmd = dylib_load_commands.find { |d| d.name.to_s == old_name }
- raise DylibUnknownError.new(old_name) if dylib_cmd.nil?
+ # @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?
- set_name_in_dylib(dylib_cmd, old_name, new_name)
+ new_lc = 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
- alias :change_dylib :change_install_name
+ alias change_dylib change_install_name
# All runtime paths searched by the dynamic linker for the Mach-O.
# @return [Array<String>] an array of all runtime paths
@@ -255,44 +374,70 @@ module MachO
# file.change_rpath("/usr/lib", "/usr/local/lib")
# @param old_path [String] the old runtime path
# @param new_path [String] the new runtime path
+ # @param _options [Hash]
# @return [void]
# @raise [MachO::RpathUnknownError] if no such old runtime path exists
- # @api private
- def change_rpath(old_path, new_path)
- rpath_cmd = command(:LC_RPATH).find { |r| r.path.to_s == old_path }
- raise RpathUnknownError.new(old_path) if rpath_cmd.nil?
+ # @raise [MachO::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 = {})
+ old_lc = command(:LC_RPATH).find { |r| r.path.to_s == old_path }
+ 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)
+
+ delete_rpath(old_path)
+ insert_command(old_lc.view.offset, new_lc)
+ end
- set_path_in_rpath(rpath_cmd, old_path, new_path)
+ # Add the given runtime path to the Mach-O.
+ # @example
+ # file.rpaths # => ["/lib"]
+ # file.add_rpath("/usr/lib")
+ # file.rpaths # => ["/lib", "/usr/lib"]
+ # @param path [String] the new runtime path
+ # @param _options [Hash]
+ # @return [void]
+ # @raise [MachO::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)
+ add_command(rpath_cmd)
+ end
+
+ # Delete the given runtime path from the Mach-O.
+ # @example
+ # file.rpaths # => ["/lib"]
+ # file.delete_rpath("/lib")
+ # file.rpaths # => []
+ # @param path [String] the runtime path to delete
+ # @param _options [Hash]
+ # @return void
+ # @raise [MachO::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 = {})
+ rpath_cmds = command(:LC_RPATH).select { |r| r.path.to_s == path }
+ raise RpathUnknownError, path if rpath_cmds.empty?
+
+ # delete the commands in reverse order, offset descending. this
+ # allows us to defer (expensive) field population until the very end
+ rpath_cmds.reverse_each { |cmd| delete_command(cmd, :repopulate => false) }
+
+ populate_fields
end
# All sections of the segment `segment`.
# @param segment [MachO::SegmentCommand, MachO::SegmentCommand64] the segment being inspected
# @return [Array<MachO::Section>] if the Mach-O is 32-bit
# @return [Array<MachO::Section64>] if the Mach-O is 64-bit
+ # @deprecated use {MachO::SegmentCommand#sections} instead
def sections(segment)
- sections = []
-
- if !segment.is_a?(SegmentCommand) && !segment.is_a?(SegmentCommand64)
- raise ArgumentError.new("not a valid segment")
- end
-
- if segment.nsects.zero?
- return sections
- end
-
- offset = segment.offset + segment.class.bytesize
-
- segment.nsects.times do
- if segment.is_a? SegmentCommand
- sections << Section.new_from_bin(endianness, @raw_data.slice(offset, Section.bytesize))
- offset += Section.bytesize
- else
- sections << Section64.new_from_bin(endianness, @raw_data.slice(offset, Section64.bytesize))
- offset += Section64.bytesize
- end
- end
-
- sections
+ segment.sections
end
# Write all Mach-O data to the given filename.
@@ -308,7 +453,7 @@ module MachO
# @note Overwrites all data in the file!
def write!
if @filename.nil?
- raise MachOError.new("cannot write to a default file when initialized from a binary string")
+ raise MachOError, "cannot write to a default file when initialized from a binary string"
else
File.open(@filename, "wb") { |f| f.write(@raw_data) }
end
@@ -320,13 +465,13 @@ module MachO
# @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
- # @private
- def get_mach_header
+ # @api private
+ def populate_mach_header
# the smallest Mach-O header is 28 bytes
- raise TruncatedFileError.new if @raw_data.size < 28
+ raise TruncatedFileError if @raw_data.size < 28
- magic = get_and_check_magic
- mh_klass = MachO.magic32?(magic) ? MachHeader : MachHeader64
+ magic = populate_and_check_magic
+ mh_klass = Utils.magic32?(magic) ? MachHeader : MachHeader64
mh = mh_klass.new_from_bin(endianness, @raw_data[0, mh_klass.bytesize])
check_cputype(mh.cputype)
@@ -340,14 +485,14 @@ module MachO
# @return [Fixnum] the magic
# @raise [MachO::MagicError] if the magic is not valid Mach-O magic
# @raise [MachO::FatBinaryError] if the magic is for a Fat file
- # @private
- def get_and_check_magic
+ # @api private
+ def populate_and_check_magic
magic = @raw_data[0..3].unpack("N").first
- raise MagicError.new(magic) unless MachO.magic?(magic)
- raise FatBinaryError.new if MachO.fat_magic?(magic)
+ raise MagicError, magic unless Utils.magic?(magic)
+ raise FatBinaryError if Utils.fat_magic?(magic)
- @endianness = MachO.little_magic?(magic) ? :little : :big
+ @endianness = Utils.little_magic?(magic) ? :little : :big
magic
end
@@ -355,47 +500,48 @@ module MachO
# Check the file's CPU type.
# @param cputype [Fixnum] the CPU type
# @raise [MachO::CPUTypeError] if the CPU type is unknown
- # @private
+ # @api private
def check_cputype(cputype)
- raise CPUTypeError.new(cputype) unless CPU_TYPES.key?(cputype)
+ raise CPUTypeError, cputype unless 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
- # @private
+ # @api private
def check_cpusubtype(cputype, cpusubtype)
- # Only check sub-type w/o capability bits (see `get_mach_header`).
+ # Only check sub-type w/o capability bits (see `populate_mach_header`).
raise CPUSubtypeError.new(cputype, cpusubtype) unless 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
- # @private
+ # @api private
def check_filetype(filetype)
- raise FiletypeError.new(filetype) unless MH_FILETYPES.key?(filetype)
+ raise FiletypeError, filetype unless MH_FILETYPES.key?(filetype)
end
# All load commands in the file.
# @return [Array<MachO::LoadCommand>] an array of load commands
# @raise [MachO::LoadCommandError] if an unknown load command is encountered
- # @private
- def get_load_commands
+ # @api private
+ def populate_load_commands
offset = header.class.bytesize
load_commands = []
header.ncmds.times do
- fmt = (endianness == :little) ? "L<" : "L>"
+ fmt = Utils.specialize_format("L=", endianness)
cmd = @raw_data.slice(offset, 4).unpack(fmt).first
cmd_sym = LOAD_COMMANDS[cmd]
- raise LoadCommandError.new(cmd) if cmd_sym.nil?
+ 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]}"
- command = klass.new_from_bin(@raw_data, endianness, offset, @raw_data.slice(offset, klass.bytesize))
+ klass = MachO.const_get LC_STRUCTURES[cmd_sym]
+ view = MachOView.new(@raw_data, endianness, offset)
+ command = klass.new_from_bin(view)
load_commands << command
offset += command.cmdsize
@@ -404,108 +550,44 @@ module MachO
load_commands
end
- # Updates the size of all load commands in the raw data.
- # @param size [Fixnum] the new size, in bytes
- # @return [void]
- # @private
- def set_sizeofcmds(size)
- fmt = (endianness == :little) ? "L<" : "L>"
- new_size = [size].pack(fmt)
- @raw_data[20..23] = new_size
- end
-
- # Updates the `name` field in a DylibCommand.
- # @param dylib_cmd [MachO::DylibCommand] the dylib command
- # @param old_name [String] the old dylib name
- # @param new_name [String] the new dylib name
- # @return [void]
- # @private
- def set_name_in_dylib(dylib_cmd, old_name, new_name)
- set_lc_str_in_cmd(dylib_cmd, dylib_cmd.name, old_name, new_name)
- end
-
- # Updates the `path` field in an RpathCommand.
- # @param rpath_cmd [MachO::RpathCommand] the rpath command
- # @param old_path [String] the old runtime name
- # @param new_path [String] the new runtime name
- # @return [void]
- # @private
- def set_path_in_rpath(rpath_cmd, old_path, new_path)
- set_lc_str_in_cmd(rpath_cmd, rpath_cmd.path, old_path, new_path)
- end
-
- # Updates a generic LCStr field in any LoadCommand.
- # @param cmd [MachO::LoadCommand] the load command
- # @param lc_str [MachO::LoadCommand::LCStr] the load command string
- # @param old_str [String] the old string
- # @param new_str [String] the new string
- # @raise [MachO::HeaderPadError] if the new name exceeds the header pad buffer
- # @private
- def set_lc_str_in_cmd(cmd, lc_str, old_str, new_str)
- if magic32?
- cmd_round = 4
- else
- cmd_round = 8
- end
-
- new_sizeofcmds = header.sizeofcmds
- old_str = old_str.dup
- new_str = new_str.dup
-
- old_pad = MachO.round(old_str.size + 1, cmd_round) - old_str.size
- new_pad = MachO.round(new_str.size + 1, cmd_round) - new_str.size
-
- # pad the old and new IDs with null bytes to meet command bounds
- old_str << "\x00" * old_pad
- new_str << "\x00" * new_pad
-
- # calculate the new size of the cmd and sizeofcmds in MH
- new_size = cmd.class.bytesize + new_str.size
- new_sizeofcmds += new_size - cmd.cmdsize
-
- low_fileoff = @raw_data.size
+ # The low file offset (offset to first section data).
+ # @return [Fixnum] the offset
+ # @api private
+ def low_fileoff
+ offset = @raw_data.size
- # calculate the low file offset (offset to first section data)
segments.each do |seg|
- sections(seg).each do |sect|
- next if sect.size == 0
+ seg.sections.each do |sect|
+ next if sect.empty?
next if sect.flag?(:S_ZEROFILL)
next if sect.flag?(:S_THREAD_LOCAL_ZEROFILL)
- next unless sect.offset < low_fileoff
+ next unless sect.offset < offset
- low_fileoff = sect.offset
+ offset = sect.offset
end
end
- if new_sizeofcmds + header.class.bytesize > low_fileoff
- raise HeaderPadError.new(@filename)
- end
-
- # update sizeofcmds in mach_header
- set_sizeofcmds(new_sizeofcmds)
-
- # update cmdsize in the cmd
- fmt = (endianness == :little) ? "L<" : "L>"
- @raw_data[cmd.offset + 4, 4] = [new_size].pack(fmt)
-
- # delete the old str
- @raw_data.slice!(cmd.offset + lc_str.to_i...cmd.offset + cmd.class.bytesize + old_str.size)
-
- # insert the new str
- @raw_data.insert(cmd.offset + lc_str.to_i, new_str)
-
- # pad/unpad after new_sizeofcmds until offsets are corrected
- null_pad = old_str.size - new_str.size
+ offset
+ end
- if null_pad < 0
- @raw_data.slice!(new_sizeofcmds + header.class.bytesize, null_pad.abs)
- else
- @raw_data.insert(new_sizeofcmds + header.class.bytesize, "\x00" * null_pad)
- end
+ # Updates the number of load commands in the raw data.
+ # @param ncmds [Fixnum] the new number of commands
+ # @return [void]
+ # @api private
+ def update_ncmds(ncmds)
+ fmt = Utils.specialize_format("L=", endianness)
+ ncmds_raw = [ncmds].pack(fmt)
+ @raw_data[16..19] = ncmds_raw
+ end
- # synchronize fields with the raw data
- @header = get_mach_header
- @load_commands = get_load_commands
+ # Updates the size of all load commands in the raw data.
+ # @param size [Fixnum] the new size, in bytes
+ # @return [void]
+ # @api private
+ def update_sizeofcmds(size)
+ fmt = Utils.specialize_format("L=", endianness)
+ size_raw = [size].pack(fmt)
+ @raw_data[20..23] = size_raw
end
end
end
diff --git a/Library/Homebrew/vendor/macho/macho/open.rb b/Library/Homebrew/vendor/macho/macho/open.rb
index ad1b02bb2..103f61741 100644
--- a/Library/Homebrew/vendor/macho/macho/open.rb
+++ b/Library/Homebrew/vendor/macho/macho/open.rb
@@ -7,17 +7,17 @@ module MachO
# @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.new("#{filename}: no such file") unless File.file?(filename)
- raise TruncatedFileError.new unless File.stat(filename).size >= 4
+ 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 MachO.fat_magic?(magic)
+ if Utils.fat_magic?(magic)
file = FatFile.new(filename)
- elsif MachO.magic?(magic)
+ elsif Utils.magic?(magic)
file = MachOFile.new(filename)
else
- raise MagicError.new(magic)
+ raise MagicError, magic
end
file
diff --git a/Library/Homebrew/vendor/macho/macho/sections.rb b/Library/Homebrew/vendor/macho/macho/sections.rb
index 3ea1e3fa4..bd6218bb5 100644
--- a/Library/Homebrew/vendor/macho/macho/sections.rb
+++ b/Library/Homebrew/vendor/macho/macho/sections.rb
@@ -45,7 +45,7 @@ module MachO
:S_ATTR_DEBUG => 0x02000000,
:S_ATTR_SOME_INSTRUCTIONS => 0x00000400,
:S_ATTR_EXT_RELOC => 0x00000200,
- :S_ATTR_LOC_RELOC => 0x00000100
+ :S_ATTR_LOC_RELOC => 0x00000100,
}.freeze
# association of section name symbols to names
@@ -62,7 +62,7 @@ module MachO
:SECT_OBJC_STRINGS => "__selector_strs",
:SECT_OBJC_REFS => "__selector_refs",
:SECT_ICON_HEADER => "__header",
- :SECT_ICON_TIFF => "__tiff"
+ :SECT_ICON_TIFF => "__tiff",
}.freeze
# Represents a section of a segment for 32-bit architectures.
@@ -91,7 +91,7 @@ module MachO
# @return [Fixnum] the number of relocation entries
attr_reader :nreloc
- # @return [Fixnum] flags for type and addrributes of the section
+ # @return [Fixnum] flags for type and attributes of the section
attr_reader :flags
# @return [void] reserved (for offset or index)
@@ -100,12 +100,15 @@ module MachO
# @return [void] reserved (for count or sizeof)
attr_reader :reserved2
- FORMAT = "a16a16L=9"
+ # @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)
+ nreloc, flags, reserved1, reserved2)
@sectname = sectname
@segname = segname
@addr = addr
@@ -121,12 +124,17 @@ module MachO
# @return [String] the section's name, with any trailing NULL characters removed
def section_name
- @sectname.delete("\x00")
+ sectname.delete("\x00")
end
# @return [String] the parent segment's name, with any trailing NULL characters removed
def segment_name
- @segname.delete("\x00")
+ segname.delete("\x00")
+ end
+
+ # @return [Boolean] true if the section has no contents (i.e, `size` is 0)
+ def empty?
+ size.zero?
end
# @example
@@ -145,12 +153,15 @@ module MachO
# @return [void] reserved
attr_reader :reserved3
- FORMAT = "a16a16Q=2L=8"
+ # @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)
+ nreloc, flags, reserved1, reserved2, reserved3)
super(sectname, segname, addr, size, offset, align, reloff,
nreloc, flags, reserved1, reserved2)
@reserved3 = reserved3
diff --git a/Library/Homebrew/vendor/macho/macho/structure.rb b/Library/Homebrew/vendor/macho/macho/structure.rb
index aaa2ada27..43f7bc84e 100644
--- a/Library/Homebrew/vendor/macho/macho/structure.rb
+++ b/Library/Homebrew/vendor/macho/macho/structure.rb
@@ -3,9 +3,13 @@ module MachO
# @abstract
class MachOStructure
# The String#unpack format of the data structure.
- FORMAT = ""
+ # @return [String] the unpacking format
+ # @api private
+ FORMAT = "".freeze
# The size of the data structure, in bytes.
+ # @return [Fixnum] the size, in bytes
+ # @api private
SIZEOF = 0
# @return [Fixnum] the size, in bytes, of the represented structure.
@@ -13,26 +17,14 @@ module MachO
self::SIZEOF
end
- # @param endianness [Symbol] either :big or :little
+ # @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`
# @api private
def self.new_from_bin(endianness, bin)
- format = specialize_format(self::FORMAT, endianness)
-
- self.new(*bin.unpack(format))
- end
-
- private
+ format = Utils.specialize_format(self::FORMAT, endianness)
- # Convert 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
- # @api private
- def self.specialize_format(format, endianness)
- modifier = (endianness == :big) ? ">" : "<"
- format.tr("=", modifier)
+ new(*bin.unpack(format))
end
end
end
diff --git a/Library/Homebrew/vendor/macho/macho/tools.rb b/Library/Homebrew/vendor/macho/macho/tools.rb
index fe3da455b..f34d75dc1 100644
--- a/Library/Homebrew/vendor/macho/macho/tools.rb
+++ b/Library/Homebrew/vendor/macho/macho/tools.rb
@@ -12,12 +12,14 @@ module MachO
# 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]
+ # @option options [Boolean] :strict (true) whether or not to fail loudly
+ # with an exception if the change cannot be performed
# @return [void]
- # @todo unstub for fat files
- def self.change_dylib_id(filename, new_id)
+ def self.change_dylib_id(filename, new_id, options = {})
file = MachO.open(filename)
- file.dylib_id = new_id
+ file.change_dylib_id(new_id, options)
file.write!
end
@@ -25,12 +27,14 @@ module MachO
# @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
+ # @param options [Hash]
+ # @option options [Boolean] :strict (true) whether or not to fail loudly
+ # with an exception if the change cannot be performed
# @return [void]
- # @todo unstub for fat files
- def self.change_install_name(filename, old_name, new_name)
+ def self.change_install_name(filename, old_name, new_name, options = {})
file = MachO.open(filename)
- file.change_install_name(old_name, new_name)
+ file.change_install_name(old_name, new_name, options)
file.write!
end
@@ -38,28 +42,43 @@ module MachO
# @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
+ # @param options [Hash]
+ # @option options [Boolean] :strict (true) whether or not to fail loudly
+ # with an exception if the change cannot be performed
# @return [void]
- # @todo unstub
- def self.change_rpath(filename, old_path, new_path)
- raise UnimplementedError.new("changing rpaths in a Mach-O")
+ def self.change_rpath(filename, old_path, new_path, options = {})
+ file = MachO.open(filename)
+
+ file.change_rpath(old_path, new_path, options)
+ file.write!
end
# Add a runtime path to a Mach-O or Fat binary, overwriting the source file.
# @param filename [String] the Mach-O or Fat binary being modified
# @param new_path [String] the new runtime path
+ # @param options [Hash]
+ # @option options [Boolean] :strict (true) whether or not to fail loudly
+ # with an exception if the change cannot be performed
# @return [void]
- # @todo unstub
- def self.add_rpath(filename, new_path)
- raise UnimplementedError.new("adding rpaths to a Mach-O")
+ def self.add_rpath(filename, new_path, options = {})
+ file = MachO.open(filename)
+
+ file.add_rpath(new_path, options)
+ file.write!
end
# Delete a runtime path from a Mach-O or Fat binary, overwriting the source file.
# @param filename [String] the Mach-O or Fat binary being modified
# @param old_path [String] the old runtime path
+ # @param options [Hash]
+ # @option options [Boolean] :strict (true) whether or not to fail loudly
+ # with an exception if the change cannot be performed
# @return [void]
- # @todo unstub
- def self.delete_rpath(filename, old_path)
- raise UnimplementedError.new("removing rpaths from a Mach-O")
+ def self.delete_rpath(filename, old_path, options = {})
+ file = MachO.open(filename)
+
+ file.delete_rpath(old_path, options)
+ file.write!
end
end
end
diff --git a/Library/Homebrew/vendor/macho/macho/utils.rb b/Library/Homebrew/vendor/macho/macho/utils.rb
index 102f1e57d..deb4c9c66 100644
--- a/Library/Homebrew/vendor/macho/macho/utils.rb
+++ b/Library/Homebrew/vendor/macho/macho/utils.rb
@@ -1,48 +1,96 @@
module MachO
- # @param value [Fixnum] the number being rounded
- # @param round [Fixnum] the number being rounded with
- # @return [Fixnum] the next number >= `value` such that `round` is its divisor
- # @see http://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c
- def self.round(value, round)
- round -= 1
- value += round
- value &= ~round
- value
- end
+ # A collection of utility functions used throughout ruby-macho.
+ module Utils
+ # Rounds a value to the next multiple of the given round.
+ # @param value [Fixnum] the number being rounded
+ # @param round [Fixnum] the number being rounded with
+ # @return [Fixnum] the rounded value
+ # @see http://www.opensource.apple.com/source/cctools/cctools-870/libstuff/rnd.c
+ def self.round(value, round)
+ round -= 1
+ value += round
+ value &= ~round
+ value
+ end
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid Mach-O magic number, false otherwise
- def self.magic?(num)
- MH_MAGICS.has_key?(num)
- end
+ # 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
+ def self.padding_for(size, alignment)
+ round(size, alignment) - size
+ end
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid Fat magic number, false otherwise
- def self.fat_magic?(num)
- num == FAT_MAGIC
- end
+ # 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
+ def self.specialize_format(format, endianness)
+ modifier = endianness == :big ? ">" : "<"
+ format.tr("=", modifier)
+ end
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid 32-bit magic number, false otherwise
- def self.magic32?(num)
- num == MH_MAGIC || num == MH_CIGAM
- end
+ # Packs tagged strings into an aligned payload.
+ # @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<String, Hash>] the packed string and labeled offsets
+ def self.pack_strings(fixed_offset, alignment, strings = {})
+ offsets = {}
+ next_offset = fixed_offset
+ payload = ""
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid 64-bit magic number, false otherwise
- def self.magic64?(num)
- num == MH_MAGIC_64 || num == MH_CIGAM_64
- end
+ strings.each do |key, string|
+ offsets[key] = next_offset
+ payload << string
+ payload << "\x00"
+ next_offset += string.bytesize + 1
+ end
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid little-endian magic number, false otherwise
- def self.little_magic?(num)
- num == MH_CIGAM || num == MH_CIGAM_64
- end
+ payload << "\x00" * padding_for(fixed_offset + payload.bytesize, alignment)
+ [payload, offsets]
+ end
+
+ # 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
+ def self.magic?(num)
+ 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
+ def self.fat_magic?(num)
+ num == 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
+ def self.magic32?(num)
+ num == MH_MAGIC || num == 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
+ def self.magic64?(num)
+ num == MH_MAGIC_64 || num == 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
+ def self.little_magic?(num)
+ num == MH_CIGAM || num == MH_CIGAM_64
+ end
- # @param num [Fixnum] the number being checked
- # @return [Boolean] true if `num` is a valid big-endian magic number, false otherwise
- def self.big_magic?(num)
- num == MH_CIGAM || num == MH_CIGAM_64
+ # 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
+ def self.big_magic?(num)
+ num == MH_CIGAM || num == MH_CIGAM_64
+ end
end
end
diff --git a/Library/Homebrew/vendor/macho/macho/view.rb b/Library/Homebrew/vendor/macho/macho/view.rb
new file mode 100644
index 000000000..9586e73be
--- /dev/null
+++ b/Library/Homebrew/vendor/macho/macho/view.rb
@@ -0,0 +1,23 @@
+module MachO
+ # A representation of some unspecified Mach-O data.
+ class MachOView
+ # @return [String] the raw Mach-O data
+ attr_reader :raw_data
+
+ # @return [Symbol] the endianness of the data (`:big` or `:little`)
+ attr_reader :endianness
+
+ # @return [Fixnum] the offset of the relevant data (in {#raw_data})
+ attr_reader :offset
+
+ # Creates a new MachOView.
+ # @param raw_data [String] the raw Mach-O data
+ # @param endianness [Symbol] the endianness of the data
+ # @param offset [Fixnum] the offset of the relevant data
+ def initialize(raw_data, endianness, offset)
+ @raw_data = raw_data
+ @endianness = endianness
+ @offset = offset
+ end
+ end
+end