vendor: Update vendored ruby-macho to 0.2.4.

This commit is contained in:
William Woodruff 2016-06-17 20:37:39 -04:00 committed by Martin Afanasjew
parent b05a596d57
commit 27e489e4c0
11 changed files with 376 additions and 106 deletions

View File

@ -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.2-39-ge2fbedc9.
* [ruby-macho](https://github.com/Homebrew/ruby-macho), version 0.2.4
## Licenses:
@ -32,7 +32,7 @@ Vendored Dependencies
### ruby-macho
> The MIT License
> Copyright (c) 2015 William Woodruff <william @ tuffbizz.com>
> Copyright (c) 2015, 2016 William Woodruff <william @ tuffbizz.com>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal

View File

@ -12,5 +12,5 @@ require "#{File.dirname(__FILE__)}/macho/tools"
# The primary namespace for ruby-macho.
module MachO
# release version
VERSION = "0.2.2".freeze
VERSION = "0.2.4".freeze
end

View File

@ -49,17 +49,18 @@ module MachO
# Raised when the CPU type is unknown.
class CPUTypeError < MachOError
# @param num [Fixnum] the unknown number
def initialize(num)
super "Unrecognized CPU type: 0x#{"%02x" % num}"
# @param cputype [Fixnum] the unknown CPU type
def initialize(cputype)
super "Unrecognized CPU type: 0x#{"%08x" % cputype}"
end
end
# Raised when the CPU subtype is unknown.
# Raised when the CPU type/sub-type pair is unknown.
class CPUSubtypeError < MachOError
# @param num [Fixnum] the unknown number
def initialize(num)
super "Unrecognized CPU sub-type: 0x#{"%02x" % num}"
# @param cputype [Fixnum] the CPU type of the unknown pair
# @param cpusubtype [Fixnum] the CPU sub-type of the unknown pair
def initialize(cputype, cpusubtype)
super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype} (for CPU type: 0x#{"%08x" % cputype})"
end
end

View File

@ -4,6 +4,9 @@ module MachO
# @see https://en.wikipedia.org/wiki/Mach-O#Multi-architecture_binaries
# @see MachO::MachOFile
class FatFile
# @return [String] the filename loaded from, or nil if loaded from a binary string
attr_accessor :filename
# @return [MachO::FatHeader] the file's header
attr_reader :header
@ -25,12 +28,12 @@ module MachO
# Creates a new FatFile from the given filename.
# @param filename [String] the fat file to load from
# @raise [ArgumentError] if the given filename does not exist
# @raise [ArgumentError] if the given file does not exist
def initialize(filename)
raise ArgumentError.new("#{filetype}: no such file") unless File.exist?(filename)
raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
@filename = filename
@raw_data = open(@filename, "rb") { |f| f.read }
@raw_data = File.open(@filename, "rb") { |f| f.read }
@header = get_fat_header
@fat_archs = get_fat_archs
@machos = get_machos
@ -175,8 +178,8 @@ module MachO
# Extract a Mach-O with the given CPU type from the file.
# @example
# file.extract("CPU_TYPE_I386") # => MachO::MachOFile
# @param cputype [String] the CPU type of the Mach-O being extracted
# file.extract(:i386) # => MachO::MachOFile
# @param cputype [Symbol] the CPU type of the Mach-O being extracted
# @return [MachO::MachOFile, nil] the extracted Mach-O or nil if no Mach-O has the given CPU type
def extract(cputype)
machos.select { |macho| macho.cputype == cputype }.first
@ -213,7 +216,7 @@ module MachO
# the smallest fat Mach-O header is 8 bytes
raise TruncatedFileError.new if @raw_data.size < 8
fh = FatHeader.new_from_bin(@raw_data[0, FatHeader.bytesize])
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)
@ -239,7 +242,7 @@ module MachO
fa_off = FatHeader.bytesize
fa_len = FatArch.bytesize
header.nfat_arch.times do |i|
archs << FatArch.new_from_bin(@raw_data[fa_off + (fa_len * i), fa_len])
archs << FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len])
end
archs

View File

@ -3,6 +3,8 @@ module MachO
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.
FAT_CIGAM = 0xbebafeca
# 32-bit big-endian magic
@ -20,7 +22,6 @@ module MachO
# association of magic numbers to string representations
MH_MAGICS = {
FAT_MAGIC => "FAT_MAGIC",
FAT_CIGAM => "FAT_CIGAM",
MH_MAGIC => "MH_MAGIC",
MH_CIGAM => "MH_CIGAM",
MH_MAGIC_64 => "MH_MAGIC_64",
@ -33,25 +34,39 @@ module MachO
# any CPU (unused?)
CPU_TYPE_ANY = -1
# m68k compatible CPUs
CPU_TYPE_MC680X0 = 0x06
# i386 and later compatible CPUs
CPU_TYPE_I386 = 0x07
# x86_64 (AMD64) compatible CPUs
CPU_TYPE_X86_64 = (CPU_TYPE_I386 | CPU_ARCH_ABI64)
# PowerPC compatible CPUs (7400 series?)
# 32-bit ARM compatible CPUs
CPU_TYPE_ARM = 0x0c
# m88k compatible CPUs
CPU_TYPE_MC88000 = 0xd
# 64-bit ARM compatible CPUs
CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64)
# PowerPC compatible CPUs
CPU_TYPE_POWERPC = 0x12
# PowerPC64 compatible CPUs (970 series?)
# PowerPC64 compatible CPUs
CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64)
# association of cpu types to string representations
# association of cpu types to symbol representations
CPU_TYPES = {
CPU_TYPE_ANY => "CPU_TYPE_ANY",
CPU_TYPE_I386 => "CPU_TYPE_I386",
CPU_TYPE_X86_64 => "CPU_TYPE_X86_64",
CPU_TYPE_POWERPC => "CPU_TYPE_POWERPC",
CPU_TYPE_POWERPC64 => "CPU_TYPE_POWERPC64"
CPU_TYPE_ANY => :any,
CPU_TYPE_I386 => :i386,
CPU_TYPE_X86_64 => :x86_64,
CPU_TYPE_ARM => :arm,
CPU_TYPE_ARM64 => :arm64,
CPU_TYPE_POWERPC => :ppc,
CPU_TYPE_POWERPC64 => :ppc64,
}
# mask for CPU subtype capabilities
@ -61,17 +76,208 @@ module MachO
# @see http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html
CPU_SUBTYPE_LIB64 = 0x80000000
# all x86-type CPUs
CPU_SUBTYPE_X86_ALL = 3
# the lowest common sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_I386 = 3
# all x86-type CPUs (what makes this different from CPU_SUBTYPE_X86_ALL?)
CPU_SUBTYPE_X86_ARCH1 = 4
# the i486 sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_486 = 4
# association of cpu subtypes to string representations
# the i486SX sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_486SX = 132
# the i586 (P5, Pentium) sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_586 = 5
CPU_SUBTYPE_PENT = CPU_SUBTYPE_586
# the Pentium Pro (P6) sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_PENTPRO = 22
# the Pentium II (P6, M3?) sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_PENTII_M3 = 54
# the Pentium II (P6, M5?) sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_PENTII_M5 = 86
# the Pentium 4 (Netburst) sub-type for `CPU_TYPE_I386`
CPU_SUBTYPE_PENTIUM_4 = 10
# the lowest common sub-type for `CPU_TYPE_MC680X0`
CPU_SUBTYPE_MC680X0_ALL = 1
CPU_SUBTYPE_MC68030 = CPU_SUBTYPE_MC680X0_ALL
# the 040 subtype for `CPU_TYPE_MC680X0`
CPU_SUBTYPE_MC68040 = 2
# the 030 subtype for `CPU_TYPE_MC680X0`
CPU_SUBTYPE_MC68030_ONLY = 3
# the lowest common sub-type for `CPU_TYPE_X86_64`
CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386
# the Haskell sub-type for `CPU_TYPE_X86_64`
CPU_SUBTYPE_X86_64_H = 8
# the lowest common sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_ALL = 0
# the v4t sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V4T = 5
# the v6 sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V6 = 6
# the v5 sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V5TEJ = 7
# the xscale (v5 family) sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_XSCALE = 8
# the v7 sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7 = 9
# the v7f (Cortex A9) sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7F = 10
# the v7s ("Swift") sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7S = 11
# the v7k ("Kirkwood40") sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7K = 12
# the v6m sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V6M = 14
# the v7m sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7M = 15
# the v7em sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V7EM = 16
# the v8 sub-type for `CPU_TYPE_ARM`
CPU_SUBTYPE_ARM_V8 = 13
# the lowest common sub-type for `CPU_TYPE_ARM64`
CPU_SUBTYPE_ARM64_ALL = 0
# the v8 sub-type for `CPU_TYPE_ARM64`
CPU_SUBTYPE_ARM64_V8 = 1
# the lowest common sub-type for `CPU_TYPE_MC88000`
CPU_SUBTYPE_MC88000_ALL = 0
CPU_SUBTYPE_MMAX_JPC = CPU_SUBTYPE_MC88000_ALL
# the 100 sub-type for `CPU_TYPE_MC88000`
CPU_SUBTYPE_MC88100 = 1
# the 110 sub-type for `CPU_TYPE_MC88000`
CPU_SUBTYPE_MC88110 = 2
# the lowest common sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_ALL = 0
# the 601 sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_601 = 1
# the 602 sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_602 = 2
# the 603 sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_603 = 3
# the 603e (G2) sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_603E = 4
# the 603ev sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_603EV = 5
# the 604 sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_604 = 6
# the 604e sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_604E = 7
# the 620 sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_620 = 8
# the 750 (G3) sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_750 = 9
# the 7400 (G4) sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_7400 = 10
# the 7450 (G4 "Voyager") sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_7450 = 11
# the 970 (G5) sub-type for `CPU_TYPE_POWERPC`
CPU_SUBTYPE_POWERPC_970 = 100
# any CPU sub-type for CPU type `CPU_TYPE_POWERPC64`
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
CPU_SUBTYPES = {
CPU_SUBTYPE_X86_ALL => "CPU_SUBTYPE_X86_ALL",
CPU_SUBTYPE_X86_ARCH1 => "CPU_SUBTYPE_X86_ARCH1"
}
CPU_TYPE_I386 => {
CPU_SUBTYPE_I386 => :i386,
CPU_SUBTYPE_486 => :i486,
CPU_SUBTYPE_486SX => :i486SX,
CPU_SUBTYPE_586 => :i586, # also "pentium" in arch(3)
CPU_SUBTYPE_PENTPRO => :i686, # also "pentpro" in arch(3)
CPU_SUBTYPE_PENTII_M3 => :pentIIm3,
CPU_SUBTYPE_PENTII_M5 => :pentIIm5,
CPU_SUBTYPE_PENTIUM_4 => :pentium4,
}.freeze,
CPU_TYPE_X86_64 => {
CPU_SUBTYPE_X86_64_ALL => :x86_64,
CPU_SUBTYPE_X86_64_H => :x86_64h,
}.freeze,
CPU_TYPE_ARM => {
CPU_SUBTYPE_ARM_ALL => :arm,
CPU_SUBTYPE_ARM_V4T => :armv4t,
CPU_SUBTYPE_ARM_V6 => :armv6,
CPU_SUBTYPE_ARM_V5TEJ => :armv5,
CPU_SUBTYPE_ARM_XSCALE => :xscale,
CPU_SUBTYPE_ARM_V7 => :armv7,
CPU_SUBTYPE_ARM_V7F => :armv7f,
CPU_SUBTYPE_ARM_V7S => :armv7s,
CPU_SUBTYPE_ARM_V7K => :armv7k,
CPU_SUBTYPE_ARM_V6M => :armv6m,
CPU_SUBTYPE_ARM_V7M => :armv7m,
CPU_SUBTYPE_ARM_V7EM => :armv7em,
CPU_SUBTYPE_ARM_V8 => :armv8,
}.freeze,
CPU_TYPE_ARM64 => {
CPU_SUBTYPE_ARM64_ALL => :arm64,
CPU_SUBTYPE_ARM64_V8 => :arm64v8,
}.freeze,
CPU_TYPE_POWERPC => {
CPU_SUBTYPE_POWERPC_ALL => :ppc,
CPU_SUBTYPE_POWERPC_601 => :ppc601,
CPU_SUBTYPE_POWERPC_603 => :ppc603,
CPU_SUBTYPE_POWERPC_603E => :ppc603e,
CPU_SUBTYPE_POWERPC_603EV => :ppc603ev,
CPU_SUBTYPE_POWERPC_604 => :ppc604,
CPU_SUBTYPE_POWERPC_604E => :ppc604e,
CPU_SUBTYPE_POWERPC_750 => :ppc750,
CPU_SUBTYPE_POWERPC_7400 => :ppc7400,
CPU_SUBTYPE_POWERPC_7450 => :ppc7450,
CPU_SUBTYPE_POWERPC_970 => :ppc970,
}.freeze,
CPU_TYPE_POWERPC64 => {
CPU_SUBTYPE_POWERPC64_ALL => :ppc64,
# apparently the only exception to the naming scheme
CPU_SUBTYPE_POWERPC_970 => :ppc970_64,
}.freeze,
CPU_TYPE_MC680X0 => {
CPU_SUBTYPE_MC680X0_ALL => :m68k,
CPU_SUBTYPE_MC68030 => :mc68030,
CPU_SUBTYPE_MC68040 => :mc68040,
},
CPU_TYPE_MC88000 => {
CPU_SUBTYPE_MC88000_ALL => :m88k,
},
}.freeze
# relocatable object file
MH_OBJECT = 0x1
@ -162,7 +368,8 @@ module MachO
# @return [Fixnum] the number of fat architecture structures following the header
attr_reader :nfat_arch
FORMAT = "N2" # always big-endian
# always big-endian
FORMAT = "N2"
SIZEOF = 8
# @api private
@ -191,7 +398,8 @@ module MachO
# @return [Fixnum] the alignment, as a power of 2
attr_reader :align
FORMAT = "N5" # always big-endian
# always big-endian
FORMAT = "N5"
SIZEOF = 20
# @api private
@ -227,7 +435,7 @@ module MachO
# @return [Fixnum] the header flags associated with the Mach-O
attr_reader :flags
FORMAT = "VVVVVVV"
FORMAT = "L=7"
SIZEOF = 28
# @api private
@ -260,7 +468,7 @@ module MachO
# @return [void]
attr_reader :reserved
FORMAT = "VVVVVVVV"
FORMAT = "L=8"
SIZEOF = 32
# @api private

View File

@ -52,8 +52,10 @@ module MachO
0x2b => :LC_DYLIB_CODE_SIGN_DRS,
0x2c => :LC_ENCRYPTION_INFO_64,
0x2d => :LC_LINKER_OPTION,
0x2e => :LC_LINKER_OPTIMIZATION_HINT
}
0x2e => :LC_LINKER_OPTIMIZATION_HINT,
0x2f => :LC_VERSION_MIN_TVOS,
0x30 => :LC_VERSION_MIN_WATCHOS,
}.freeze
# load commands responsible for loading dylibs
# @api private
@ -71,7 +73,7 @@ module MachO
:LC_SEGMENT => "SegmentCommand",
:LC_SYMTAB => "SymtabCommand",
:LC_SYMSEG => "LoadCommand", # obsolete
:LC_THREAD => "ThreadCommand",
:LC_THREAD => "ThreadCommand", # seems obsolete, but not documented as such
:LC_UNIXTHREAD => "ThreadCommand",
:LC_LOADFVMLIB => "LoadCommand", # obsolete
:LC_IDFVMLIB => "LoadCommand", # obsolete
@ -114,8 +116,10 @@ module MachO
:LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand",
:LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64",
:LC_LINKER_OPTION => "LinkerOptionCommand",
:LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand"
}
:LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand",
:LC_VERSION_MIN_TVOS => "VersionMinCommand",
:LC_VERSION_MIN_WATCHOS => "VersionMinCommand",
}.freeze
# association of segment name symbols to names
# @api private
@ -127,8 +131,8 @@ module MachO
:SEG_ICON => "__ICON",
:SEG_LINKEDIT => "__LINKEDIT",
:SEG_UNIXSTACK => "__UNIXSTACK",
:SEG_IMPORT => "__IMPORT"
}
:SEG_IMPORT => "__IMPORT",
}.freeze
# association of segment flag symbols to values
# @api private
@ -136,8 +140,8 @@ module MachO
:SG_HIGHVM => 0x1,
:SG_FVMLIB => 0x2,
:SG_NORELOC => 0x4,
:SG_PROTECTED_VERSION_1 => 0x8
}
:SG_PROTECTED_VERSION_1 => 0x8,
}.freeze
# Mach-O load command structure
# This is the most generic load command - only cmd ID and size are
@ -153,18 +157,23 @@ module MachO
# @return [Fixnum] the size of the load command, in bytes
attr_reader :cmdsize
FORMAT = "VV"
FORMAT = "L=2"
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
# @return [MachO::LoadCommand] the new load command
# @api private
def self.new_from_bin(raw_data, offset, bin)
self.new(raw_data, offset, *bin.unpack(self::FORMAT))
def self.new_from_bin(raw_data, endianness, offset, bin)
format = specialize_format(self::FORMAT, endianness)
self.new(raw_data, offset, *bin.unpack(format))
end
# @param raw_data [String] the raw Mach-O data
# @param offset [Fixnum] the offset to initialize with
# @param cmd [Fixnum] the load command's identifying number
# @param cmdsize [Fixnum] the size of the load command in bytes
@ -222,7 +231,7 @@ module MachO
# @return [Array<Fixnum>] the UUID
attr_reader :uuid
FORMAT = "VVa16"
FORMAT = "L=2a16"
SIZEOF = 24
# @api private
@ -273,7 +282,7 @@ module MachO
# @return [Fixnum] any flags associated with the segment
attr_reader :flags
FORMAT = "VVa16VVVVVVVV"
FORMAT = "L=2a16L=4l=2L=2"
SIZEOF = 56
# @api private
@ -332,7 +341,7 @@ module MachO
# @return [Fixnum] any flags associated with the segment
attr_reader :flags
FORMAT = "VVa16QQQQVVVV"
FORMAT = "L=2a16Q=4l=2L=2"
SIZEOF = 72
# @api private
@ -377,7 +386,7 @@ module MachO
# @return [Fixnum] the library's compatibility version number
attr_reader :compatibility_version
FORMAT = "VVVVVV"
FORMAT = "L=6"
SIZEOF = 24
# @api private
@ -398,7 +407,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr
attr_reader :name
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -420,7 +429,7 @@ module MachO
# @return [Fixnum] a bit vector of linked modules
attr_reader :linked_modules
FORMAT = "VVVVV"
FORMAT = "L=5"
SIZEOF = 20
# @api private
@ -435,7 +444,8 @@ 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"
SIZEOF = 8
end
# A load command containing the address of the dynamic shared library
@ -466,7 +476,7 @@ module MachO
# @return [void]
attr_reader :reserved6
FORMAT = "VVVVVVVVVV"
FORMAT = "L=10"
SIZEOF = 40
# @api private
@ -513,7 +523,7 @@ module MachO
# @return [void]
attr_reader :reserved6
FORMAT = "VVQQQQQQQQ"
FORMAT = "L=2Q=8"
SIZEOF = 72
# @api private
@ -538,7 +548,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr
attr_reader :umbrella
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -554,7 +564,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr
attr_reader :sub_umbrella
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -570,7 +580,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr
attr_reader :sub_library
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -586,7 +596,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr
attr_reader :sub_client
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -611,7 +621,7 @@ module MachO
# @return the string table size in bytes
attr_reader :strsize
FORMAT = "VVVVVV"
FORMAT = "L=6"
SIZEOF = 24
# @api private
@ -682,7 +692,7 @@ module MachO
attr_reader :nlocrel
FORMAT = "VVVVVVVVVVVVVVVVVVVV"
FORMAT = "L=20"
SIZEOF = 80
# ugh
@ -722,7 +732,7 @@ module MachO
# @return [Fixnum] the number of hints in the hint table
attr_reader :nhints
FORMAT = "VVVV"
FORMAT = "L=4"
SIZEOF = 16
# @api private
@ -739,7 +749,7 @@ module MachO
# @return [Fixnum] the checksum or 0
attr_reader :cksum
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -756,7 +766,7 @@ module MachO
# @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr
attr_reader :path
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -776,7 +786,7 @@ module MachO
# @return [Fixnum] size of the data in the __LINKEDIT segment
attr_reader :datasize
FORMAT = "VVVV"
FORMAT = "L=4"
SIZEOF = 16
# @api private
@ -799,7 +809,7 @@ module MachO
# @return [Fixnum] the encryption system, or 0 if not encrypted yet
attr_reader :cryptid
FORMAT = "VVVVV"
FORMAT = "L=5"
SIZEOF = 20
# @api private
@ -826,7 +836,7 @@ module MachO
# @return [Fixnum] 64-bit padding value
attr_reader :pad
FORMAT = "VVVVVV"
FORMAT = "L=6"
SIZEOF = 24
# @api private
@ -848,7 +858,7 @@ module MachO
# @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8
attr_reader :sdk
FORMAT = "VVVV"
FORMAT = "L=4"
SIZEOF = 16
# @api private
@ -915,7 +925,7 @@ module MachO
# @return [Fixnum] the size of the export information
attr_reader :export_size
FORMAT = "VVVVVVVVVVVV"
FORMAT = "L=12"
SIZEOF = 48
# @api private
@ -942,7 +952,7 @@ module MachO
# @return [Fixnum] the number of strings
attr_reader :count
FORMAT = "VVV"
FORMAT = "L=3"
SIZEOF = 12
# @api private
@ -960,7 +970,7 @@ module MachO
# @return [Fixnum] if not 0, the initial stack size.
attr_reader :stacksize
FORMAT = "VVQQ"
FORMAT = "L=2Q=2"
SIZEOF = 24
# @api private
@ -977,7 +987,7 @@ module MachO
# @return [Fixnum] the version packed as a24.b10.c10.d10.e10
attr_reader :version
FORMAT = "VVQ"
FORMAT = "L=2Q=1"
SIZEOF = 16
# @api private

View File

@ -5,6 +5,12 @@ module MachO
# @see https://en.wikipedia.org/wiki/Mach-O
# @see MachO::FatFile
class MachOFile
# @return [String] the filename loaded from, or nil if loaded from a binary string
attr_accessor :filename
# @return [Symbol] the endianness of the file, :big or :little
attr_reader :endianness
# @return [MachO::MachHeader] if the Mach-O is 32-bit
# @return [MachO::MachHeader64] if the Mach-O is 64-bit
attr_reader :header
@ -24,12 +30,12 @@ module MachO
# Creates a new FatFile from the given filename.
# @param filename [String] the Mach-O file to load from
# @raise [ArgumentError] if the given filename does not exist
# @raise [ArgumentError] if the given file does not exist
def initialize(filename)
raise ArgumentError.new("#{filetype}: no such file") unless File.exist?(filename)
raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
@filename = filename
@raw_data = open(@filename, "rb") { |f| f.read }
@raw_data = File.open(@filename, "rb") { |f| f.read }
@header = get_mach_header
@load_commands = get_load_commands
end
@ -123,14 +129,14 @@ module MachO
MH_FILETYPES[header.filetype]
end
# @return [String] a string representation of the Mach-O's CPU type
# @return [Symbol] a symbol representation of the Mach-O's CPU type
def cputype
CPU_TYPES[header.cputype]
end
# @return [String] a string representation of the Mach-O's CPU subtype
# @return [Symbol] a symbol representation of the Mach-O's CPU subtype
def cpusubtype
CPU_SUBTYPES[header.cpusubtype]
CPU_SUBTYPES[header.cputype][header.cpusubtype]
end
# @return [Fixnum] the number of load commands in the Mach-O's header
@ -278,10 +284,10 @@ module MachO
segment.nsects.times do
if segment.is_a? SegmentCommand
sections << Section.new_from_bin(@raw_data.slice(offset, Section.bytesize))
sections << Section.new_from_bin(endianness, @raw_data.slice(offset, Section.bytesize))
offset += Section.bytesize
else
sections << Section64.new_from_bin(@raw_data.slice(offset, Section64.bytesize))
sections << Section64.new_from_bin(endianness, @raw_data.slice(offset, Section64.bytesize))
offset += Section64.bytesize
end
end
@ -321,10 +327,10 @@ module MachO
magic = get_and_check_magic
mh_klass = MachO.magic32?(magic) ? MachHeader : MachHeader64
mh = mh_klass.new_from_bin(@raw_data[0, mh_klass.bytesize])
mh = mh_klass.new_from_bin(endianness, @raw_data[0, mh_klass.bytesize])
check_cputype(mh.cputype)
check_cpusubtype(mh.cpusubtype)
check_cpusubtype(mh.cputype, mh.cpusubtype)
check_filetype(mh.filetype)
mh
@ -341,6 +347,8 @@ module MachO
raise MagicError.new(magic) unless MachO.magic?(magic)
raise FatBinaryError.new if MachO.fat_magic?(magic)
@endianness = MachO.little_magic?(magic) ? :little : :big
magic
end
@ -352,13 +360,13 @@ module MachO
raise CPUTypeError.new(cputype) unless CPU_TYPES.key?(cputype)
end
# Check the file's CPU sub-type.
# 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
def check_cpusubtype(cpusubtype)
def check_cpusubtype(cputype, cpusubtype)
# Only check sub-type w/o capability bits (see `get_mach_header`).
raise CPUSubtypeError.new(cpusubtype) unless CPU_SUBTYPES.key?(cpusubtype)
raise CPUSubtypeError.new(cputype, cpusubtype) unless CPU_SUBTYPES[cputype].key?(cpusubtype)
end
# Check the file's type.
@ -378,7 +386,8 @@ module MachO
load_commands = []
header.ncmds.times do
cmd = @raw_data.slice(offset, 4).unpack("V").first
fmt = (endianness == :little) ? "L<" : "L>"
cmd = @raw_data.slice(offset, 4).unpack(fmt).first
cmd_sym = LOAD_COMMANDS[cmd]
raise LoadCommandError.new(cmd) if cmd_sym.nil?
@ -386,7 +395,7 @@ module MachO
# why do I do this? i don't like declaring constants below
# classes, and i need them to resolve...
klass = MachO.const_get "#{LC_STRUCTURES[cmd_sym]}"
command = klass.new_from_bin(@raw_data, offset, @raw_data.slice(offset, klass.bytesize))
command = klass.new_from_bin(@raw_data, endianness, offset, @raw_data.slice(offset, klass.bytesize))
load_commands << command
offset += command.cmdsize
@ -400,7 +409,8 @@ module MachO
# @return [void]
# @private
def set_sizeofcmds(size)
new_size = [size].pack("V")
fmt = (endianness == :little) ? "L<" : "L>"
new_size = [size].pack(fmt)
@raw_data[20..23] = new_size
end
@ -475,7 +485,8 @@ module MachO
set_sizeofcmds(new_sizeofcmds)
# update cmdsize in the cmd
@raw_data[cmd.offset + 4, 4] = [new_size].pack("V")
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)

View File

@ -3,14 +3,23 @@ module MachO
# @param filename [String] the file being opened
# @return [MachO::MachOFile] if the file is a Mach-O
# @return [MachO::FatFile] if the file is a Fat file
# @raise [ArgumentError] if the given file does not exist
# @raise [MachO::TruncatedFileError] if the file is too small to have a valid header
# @raise [MachO::MagicError] if the file's magic is not valid Mach-O magic
def self.open(filename)
# open file and test magic instead of using exceptions for control?
begin
file = MachOFile.new(filename)
rescue FatBinaryError
raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
raise TruncatedFileError.new unless File.stat(filename).size >= 4
magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first
if MachO.fat_magic?(magic)
file = FatFile.new(filename)
elsif MachO.magic?(magic)
file = MachOFile.new(filename)
else
raise MagicError.new(magic)
end
file
end
end
end

View File

@ -46,7 +46,7 @@ module MachO
:S_ATTR_SOME_INSTRUCTIONS => 0x00000400,
:S_ATTR_EXT_RELOC => 0x00000200,
:S_ATTR_LOC_RELOC => 0x00000100
}
}.freeze
# association of section name symbols to names
# @api private
@ -63,7 +63,7 @@ module MachO
:SECT_OBJC_REFS => "__selector_refs",
:SECT_ICON_HEADER => "__header",
:SECT_ICON_TIFF => "__tiff"
}
}.freeze
# Represents a section of a segment for 32-bit architectures.
class Section < MachOStructure
@ -100,7 +100,7 @@ module MachO
# @return [void] reserved (for count or sizeof)
attr_reader :reserved2
FORMAT = "a16a16VVVVVVVVV"
FORMAT = "a16a16L=9"
SIZEOF = 68
# @api private
@ -145,7 +145,7 @@ module MachO
# @return [void] reserved
attr_reader :reserved3
FORMAT = "a16a16QQVVVVVVVV"
FORMAT = "a16a16Q=2L=8"
SIZEOF = 80
# @api private

View File

@ -2,7 +2,7 @@ module MachO
# A general purpose pseudo-structure.
# @abstract
class MachOStructure
# The format of the data structure, in String#unpack format.
# The String#unpack format of the data structure.
FORMAT = ""
# The size of the data structure, in bytes.
@ -13,10 +13,26 @@ module MachO
self::SIZEOF
end
# @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(bin)
self.new(*bin.unpack(self::FORMAT))
def self.new_from_bin(endianness, bin)
format = specialize_format(self::FORMAT, endianness)
self.new(*bin.unpack(format))
end
private
# 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)
end
end
end

View File

@ -19,7 +19,7 @@ module MachO
# @param num [Fixnum] the number being checked
# @return [Boolean] true if `num` is a valid Fat magic number, false otherwise
def self.fat_magic?(num)
num == FAT_MAGIC || num == FAT_CIGAM
num == FAT_MAGIC
end
# @param num [Fixnum] the number being checked
@ -33,4 +33,16 @@ module MachO
def self.magic64?(num)
num == MH_MAGIC_64 || num == MH_CIGAM_64
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
# @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