brew vendor-gems: commit updates.

This commit is contained in:
BrewTestBot 2023-07-25 18:25:53 +00:00
parent 051f3e719f
commit e91b64d138
No known key found for this signature in database
GPG Key ID: 82D7D104050B0F0F
13 changed files with 585 additions and 895 deletions

View File

@ -96,7 +96,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rails-2.19.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rspec-2.20.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-sorbet-0.7.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-macho-3.0.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-macho-4.0.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-22/#{Gem.extension_api_version}/ruby-prof-1.4.3")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-prof-1.4.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/simplecov-html-0.12.3/lib")

View File

@ -1,42 +0,0 @@
# frozen_string_literal: true
module MachO
# A general purpose pseudo-structure.
# @abstract
class MachOStructure
# The String#unpack format of the data structure.
# @return [String] the unpacking format
# @api private
FORMAT = ""
# The size of the data structure, in bytes.
# @return [Integer] the size, in bytes
# @api private
SIZEOF = 0
# @return [Integer] the size, in bytes, of the represented structure.
def self.bytesize
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] the resulting structure
# @api private
def self.new_from_bin(endianness, bin)
format = Utils.specialize_format(self::FORMAT, endianness)
new(*bin.unpack(format))
end
# @return [Hash] a hash representation of this {MachOStructure}.
def to_h
{
"structure" => {
"format" => self.class::FORMAT,
"bytesize" => self.class.bytesize,
},
}
end
end
end

View File

@ -2,6 +2,7 @@
require "open3"
require_relative "macho/utils"
require_relative "macho/structure"
require_relative "macho/view"
require_relative "macho/headers"
@ -10,13 +11,12 @@ require_relative "macho/sections"
require_relative "macho/macho_file"
require_relative "macho/fat_file"
require_relative "macho/exceptions"
require_relative "macho/utils"
require_relative "macho/tools"
# The primary namespace for ruby-macho.
module MachO
# release version
VERSION = "3.0.0"
VERSION = "4.0.0"
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
# @param filename [String] the file being opened

View File

@ -56,6 +56,28 @@ module MachO
end
end
# Raised when a a fat Mach-O file has zero architectures
class ZeroArchitectureError < NotAMachOError
def initialize
super "Fat file has zero internal architectures"
end
end
# Raised when there is a mismatch between the fat arch
# and internal slice cputype or cpusubtype.
class CPUTypeMismatchError < NotAMachOError
def initialize(fat_cputype, fat_cpusubtype, macho_cputype, macho_cpusubtype)
# @param cputype_fat [Integer] the CPU type in the fat header
# @param cpusubtype_fat [Integer] the CPU subtype in the fat header
# @param cputype_macho [Integer] the CPU type in the macho header
# @param cpusubtype_macho [Integer] the CPU subtype in the macho header
super ("Mismatch between cputypes >> 0x%08<fat_cputype>x and 0x%08<macho_cputype>x\n" \
"and/or cpusubtypes >> 0x%08<fat_cpusubtype>x and 0x%08<macho_cpusubtype>x" %
{ :fat_cputype => fat_cputype, :macho_cputype => macho_cputype,
:fat_cpusubtype => fat_cpusubtype, :macho_cpusubtype => macho_cpusubtype })
end
end
# Raised when a fat binary is loaded with MachOFile.
class FatBinaryError < MachOError
def initialize
@ -83,8 +105,8 @@ module MachO
# @param cputype [Integer] the CPU type of the unknown pair
# @param cpusubtype [Integer] the CPU sub-type of the unknown pair
def initialize(cputype, cpusubtype)
super "Unrecognized CPU sub-type: 0x%08<cpusubtype>x" \
" (for CPU type: 0x%08<cputype>x" % { :cputype => cputype, :cpusubtype => cpusubtype }
super "Unrecognized CPU sub-type: 0x%08<cpusubtype>x " \
"(for CPU type: 0x%08<cputype>x" % { :cputype => cputype, :cpusubtype => cpusubtype }
end
end
@ -119,8 +141,8 @@ module MachO
# @param expected_arity [Integer] the number of arguments expected
# @param actual_arity [Integer] the number of arguments received
def initialize(cmd_sym, expected_arity, actual_arity)
super "Expected #{expected_arity} arguments for #{cmd_sym} creation," \
" got #{actual_arity}"
super "Expected #{expected_arity} arguments for #{cmd_sym} creation, " \
"got #{actual_arity}"
end
end
@ -136,8 +158,8 @@ module MachO
class LCStrMalformedError < MachOError
# @param lc [MachO::LoadCommand] the load command containing the string
def initialize(lc)
super "Load command #{lc.type} at offset #{lc.view.offset} contains a" \
" malformed string"
super "Load command #{lc.type} at offset #{lc.view.offset} contains a " \
"malformed string"
end
end
@ -203,8 +225,8 @@ module MachO
class FatArchOffsetOverflowError < MachOError
# @param offset [Integer] the offending offset
def initialize(offset)
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset." \
" Consider merging with `fat64: true`"
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset. " \
"Consider merging with `fat64: true`"
end
end

View File

@ -327,6 +327,8 @@ module MachO
# @raise [MagicError] if the magic is not valid Mach-O magic
# @raise [MachOBinaryError] if the magic is for a non-fat Mach-O file
# @raise [JavaClassFileError] if the file is a Java classfile
# @raise [ZeroArchitectureError] if the file has no internal slices
# (i.e., nfat_arch == 0) and the permissive option is not set
# @api private
def populate_fat_header
# the smallest fat Mach-O header is 8 bytes
@ -346,6 +348,9 @@ module MachO
# formats.
raise JavaClassFileError if fh.nfat_arch > 30
# Rationale: return an error if the file has no internal slices.
raise ZeroArchitectureError if fh.nfat_arch.zero?
fh
end
@ -374,6 +379,13 @@ module MachO
fat_archs.each do |arch|
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size], **options)
# Make sure that each fat_arch and internal slice.
# contain matching cputypes and cpusubtypes
next if machos.last.header.cputype == arch.cputype &&
machos.last.header.cpusubtype == arch.cpusubtype
raise CPUTypeMismatchError.new(arch.cputype, arch.cpusubtype, machos.last.header.cputype, machos.last.header.cpusubtype)
end
machos

View File

@ -505,30 +505,14 @@ module MachO
# @see MachO::FatArch
class FatHeader < MachOStructure
# @return [Integer] the magic number of the header (and file)
attr_reader :magic
field :magic, :uint32, :endian => :big
# @return [Integer] the number of fat architecture structures following the header
attr_reader :nfat_arch
# always big-endian
# @see MachOStructure::FORMAT
# @api private
FORMAT = "N2"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 8
# @api private
def initialize(magic, nfat_arch)
super()
@magic = magic
@nfat_arch = nfat_arch
end
field :nfat_arch, :uint32, :endian => :big
# @return [String] the serialized fields of the fat header
def serialize
[magic, nfat_arch].pack(FORMAT)
[magic, nfat_arch].pack(self.class.format)
end
# @return [Hash] a hash representation of this {FatHeader}
@ -548,42 +532,23 @@ module MachO
# @see MachO::Headers::FatHeader
class FatArch < MachOStructure
# @return [Integer] the CPU type of the Mach-O
attr_reader :cputype
field :cputype, :uint32, :endian => :big
# @return [Integer] the CPU subtype of the Mach-O
attr_reader :cpusubtype
field :cpusubtype, :uint32, :endian => :big, :mask => CPU_SUBTYPE_MASK
# @return [Integer] the file offset to the beginning of the Mach-O data
attr_reader :offset
field :offset, :uint32, :endian => :big
# @return [Integer] the size, in bytes, of the Mach-O data
attr_reader :size
field :size, :uint32, :endian => :big
# @return [Integer] the alignment, as a power of 2
attr_reader :align
# @note Always big endian.
# @see MachOStructure::FORMAT
# @api private
FORMAT = "L>5"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 20
# @api private
def initialize(cputype, cpusubtype, offset, size, align)
super()
@cputype = cputype
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
@offset = offset
@size = size
@align = align
end
field :align, :uint32, :endian => :big
# @return [String] the serialized fields of the fat arch
def serialize
[cputype, cpusubtype, offset, size, align].pack(FORMAT)
[cputype, cpusubtype, offset, size, align].pack(self.class.format)
end
# @return [Hash] a hash representation of this {FatArch}
@ -606,27 +571,18 @@ module MachO
# Mach-Os that it points to necessarily *are* 64-bit.
# @see MachO::Headers::FatHeader
class FatArch64 < FatArch
# @return [Integer] the file offset to the beginning of the Mach-O data
field :offset, :uint64, :endian => :big
# @return [Integer] the size, in bytes, of the Mach-O data
field :size, :uint64, :endian => :big
# @return [void]
attr_reader :reserved
# @note Always big endian.
# @see MachOStructure::FORMAT
# @api private
FORMAT = "L>2Q>2L>2"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 32
# @api private
def initialize(cputype, cpusubtype, offset, size, align, reserved = 0)
super(cputype, cpusubtype, offset, size, align)
@reserved = reserved
end
field :reserved, :uint32, :endian => :big, :default => 0
# @return [String] the serialized fields of the fat arch
def serialize
[cputype, cpusubtype, offset, size, align, reserved].pack(FORMAT)
[cputype, cpusubtype, offset, size, align, reserved].pack(self.class.format)
end
# @return [Hash] a hash representation of this {FatArch64}
@ -640,48 +596,25 @@ module MachO
# 32-bit Mach-O file header structure
class MachHeader < MachOStructure
# @return [Integer] the magic number
attr_reader :magic
field :magic, :uint32
# @return [Integer] the CPU type of the Mach-O
attr_reader :cputype
field :cputype, :uint32
# @return [Integer] the CPU subtype of the Mach-O
attr_reader :cpusubtype
field :cpusubtype, :uint32, :mask => CPU_SUBTYPE_MASK
# @return [Integer] the file type of the Mach-O
attr_reader :filetype
field :filetype, :uint32
# @return [Integer] the number of load commands in the Mach-O
attr_reader :ncmds
field :ncmds, :uint32
# @return [Integer] the size of all load commands, in bytes, in the Mach-O
attr_reader :sizeofcmds
field :sizeofcmds, :uint32
# @return [Integer] the header flags associated with the Mach-O
attr_reader :flags
# @see MachOStructure::FORMAT
# @api private
FORMAT = "L=7"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 28
# @api private
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
flags)
super()
@magic = magic
@cputype = cputype
# For now we're not interested in additional capability bits also to be
# found in the `cpusubtype` field. We only care about the CPU sub-type.
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
@filetype = filetype
@ncmds = ncmds
@sizeofcmds = sizeofcmds
@flags = flags
end
field :flags, :uint32
# @example
# puts "this mach-o has position-independent execution" if header.flag?(:MH_PIE)
@ -787,22 +720,7 @@ module MachO
# 64-bit Mach-O file header structure
class MachHeader64 < MachHeader
# @return [void]
attr_reader :reserved
# @see MachOStructure::FORMAT
# @api private
FORMAT = "L=8"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 32
# @api private
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
flags, reserved)
super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags)
@reserved = reserved
end
field :reserved, :uint32
# @return [Hash] a hash representation of this {MachHeader64}
def to_h
@ -815,54 +733,31 @@ module MachO
# Prelinked kernel/"kernelcache" header structure
class PrelinkedKernelHeader < MachOStructure
# @return [Integer] the magic number for a compressed header ({COMPRESSED_MAGIC})
attr_reader :signature
field :signature, :uint32, :endian => :big
# @return [Integer] the type of compression used
attr_reader :compress_type
field :compress_type, :uint32, :endian => :big
# @return [Integer] a checksum for the uncompressed data
attr_reader :adler32
field :adler32, :uint32, :endian => :big
# @return [Integer] the size of the uncompressed data, in bytes
attr_reader :uncompressed_size
field :uncompressed_size, :uint32, :endian => :big
# @return [Integer] the size of the compressed data, in bytes
attr_reader :compressed_size
field :compressed_size, :uint32, :endian => :big
# @return [Integer] the version of the prelink format
attr_reader :prelink_version
field :prelink_version, :uint32, :endian => :big
# @return [void]
attr_reader :reserved
field :reserved, :string, :size => 40, :unpack => "L>10"
# @return [void]
attr_reader :platform_name
field :platform_name, :string, :size => 64
# @return [void]
attr_reader :root_path
# @see MachOStructure::FORMAT
# @api private
FORMAT = "L>6a40a64a256"
# @see MachOStructure::SIZEOF
# @api private
SIZEOF = 384
# @api private
def initialize(signature, compress_type, adler32, uncompressed_size, compressed_size, prelink_version, reserved, platform_name, root_path)
super()
@signature = signature
@compress_type = compress_type
@adler32 = adler32
@uncompressed_size = uncompressed_size
@compressed_size = compressed_size
@prelink_version = prelink_version
@reserved = reserved.unpack("L>10")
@platform_name = platform_name
@root_path = root_path
end
field :root_path, :string, :size => 256
# @return [Boolean] whether this prelinked kernel supports KASLR
def kaslr?

View File

@ -381,11 +381,9 @@ module MachO
# rpaths simultaneously.
# @return [void]
# @raise [RpathUnknownError] if no such old runtime path exists
# @raise [RpathExistsError] if the new runtime path already exists
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 = LoadCommands::LoadCommand.create(:LC_RPATH, new_path)
@ -420,15 +418,24 @@ module MachO
# @param options [Hash]
# @option options [Boolean] :uniq (false) if true, also delete
# duplicates of the requested path. If false, delete the first
# instance (by offset) of the requested path.
# instance (by offset) of the requested path, unless :last is true.
# Incompatible with :last.
# @option options [Boolean] :last (false) if true, delete the last
# instance (by offset) of the requested path. Incompatible with :uniq.
# @return void
# @raise [RpathUnknownError] if no such runtime path exists
# @raise [ArgumentError] if both :uniq and :last are true
def delete_rpath(path, options = {})
uniq = options.fetch(:uniq, false)
search_method = uniq ? :select : :find
last = options.fetch(:last, false)
raise ArgumentError, "Cannot set both :uniq and :last to true" if uniq && last
search_method = uniq || last ? :select : :find
rpath_cmds = command(:LC_RPATH).public_send(search_method) { |r| r.path.to_s == path }
rpath_cmds = rpath_cmds.last if last
# Cast rpath_cmds into an Array so we can handle the uniq and non-uniq cases the same way
rpath_cmds = Array(command(:LC_RPATH).method(search_method).call { |r| r.path.to_s == path })
rpath_cmds = Array(rpath_cmds)
raise RpathUnknownError, path if rpath_cmds.empty?
# delete the commands in reverse order, offset descending.
@ -592,7 +599,7 @@ module MachO
LoadCommands::LoadCommand
end
view = MachOView.new(@raw_data, endianness, offset)
view = MachOView.new(self, @raw_data, endianness, offset)
command = klass.new_from_bin(view)
load_commands << command

View File

@ -89,61 +89,38 @@ module MachO
# Represents a section of a segment for 32-bit architectures.
class Section < MachOStructure
# @return [String] the name of the section, including null pad bytes
attr_reader :sectname
field :sectname, :string, :padding => :null, :size => 16
# @return [String] the name of the segment's section, including null
# pad bytes
attr_reader :segname
field :segname, :string, :padding => :null, :size => 16
# @return [Integer] the memory address of the section
attr_reader :addr
field :addr, :uint32
# @return [Integer] the size, in bytes, of the section
attr_reader :size
field :size, :uint32
# @return [Integer] the file offset of the section
attr_reader :offset
field :offset, :uint32
# @return [Integer] the section alignment (power of 2) of the section
attr_reader :align
field :align, :uint32
# @return [Integer] the file offset of the section's relocation entries
attr_reader :reloff
field :reloff, :uint32
# @return [Integer] the number of relocation entries
attr_reader :nreloc
field :nreloc, :uint32
# @return [Integer] flags for type and attributes of the section
attr_reader :flags
field :flags, :uint32
# @return [void] reserved (for offset or index)
attr_reader :reserved1
field :reserved1, :uint32
# @return [void] reserved (for count or sizeof)
attr_reader :reserved2
# @see MachOStructure::FORMAT
FORMAT = "Z16Z16L=9"
# @see MachOStructure::SIZEOF
SIZEOF = 68
# @api private
def initialize(sectname, segname, addr, size, offset, align, reloff,
nreloc, flags, reserved1, reserved2)
super()
@sectname = sectname
@segname = segname
@addr = addr
@size = size
@offset = offset
@align = align
@reloff = reloff
@nreloc = nreloc
@flags = flags
@reserved1 = reserved1
@reserved2 = reserved2
end
field :reserved2, :uint32
# @return [String] the section's name
def section_name
@ -219,22 +196,14 @@ module MachO
# Represents a section of a segment for 64-bit architectures.
class Section64 < Section
# @return [Integer] the memory address of the section
field :addr, :uint64
# @return [Integer] the size, in bytes, of the section
field :size, :uint64
# @return [void] reserved
attr_reader :reserved3
# @see MachOStructure::FORMAT
FORMAT = "Z16Z16Q=2L=8"
# @see MachOStructure::SIZEOF
SIZEOF = 80
# @api private
def initialize(sectname, segname, addr, size, offset, align, reloff,
nreloc, flags, reserved1, reserved2, reserved3)
super(sectname, segname, addr, size, offset, align, reloff,
nreloc, flags, reserved1, reserved2)
@reserved3 = reserved3
end
field :reserved3, :uint32
# @return [Hash] a hash representation of this {Section64}
def to_h

View File

@ -0,0 +1,284 @@
# frozen_string_literal: true
module MachO
# A general purpose pseudo-structure. Described in detail in docs/machostructure-dsl.md.
# @abstract
class MachOStructure
# Constants used for parsing MachOStructure fields
module Fields
# 1. All fields with empty strings and zeros aren't used
# to calculate the format and sizeof variables.
# 2. All fields with nil should provide those values manually
# via the :size parameter.
# association of field types to byte size
# @api private
BYTE_SIZE = {
# Binary slices
:string => nil,
:null_padded_string => nil,
:int32 => 4,
:uint32 => 4,
:uint64 => 8,
# Classes
:view => 0,
:lcstr => 4,
:two_level_hints_table => 0,
:tool_entries => 4,
}.freeze
# association of field types with ruby format codes
# Binary format codes can be found here:
# https://docs.ruby-lang.org/en/2.6.0/String.html#method-i-unpack
#
# The equals sign is used to manually change endianness using
# the Utils#specialize_format() method.
# @api private
FORMAT_CODE = {
# Binary slices
:string => "a",
:null_padded_string => "Z",
:int32 => "l=",
:uint32 => "L=",
:uint64 => "Q=",
# Classes
:view => "",
:lcstr => "L=",
:two_level_hints_table => "",
:tool_entries => "L=",
}.freeze
# A list of classes that must get initialized
# To add a new class append it here and add the init method to the def_class_reader method
# @api private
CLASSES_TO_INIT = %i[lcstr tool_entries two_level_hints_table].freeze
# A list of fields that don't require arguments in the initializer
# Used to calculate MachOStructure#min_args
# @api private
NO_ARG_REQUIRED = %i[two_level_hints_table].freeze
end
# map of field names to indices
@field_idxs = {}
# array of fields sizes
@size_list = []
# array of field format codes
@fmt_list = []
# minimum number of required arguments
@min_args = 0
# @param args [Array[Value]] list of field parameters
def initialize(*args)
raise ArgumentError, "Invalid number of arguments" if args.size < self.class.min_args
@values = args
end
# @return [Hash] a hash representation of this {MachOStructure}.
def to_h
{
"structure" => {
"format" => self.class.format,
"bytesize" => self.class.bytesize,
},
}
end
class << self
attr_reader :min_args
# @param endianness [Symbol] either `:big` or `:little`
# @param bin [String] the string to be unpacked into the new structure
# @return [MachO::MachOStructure] the resulting structure
# @api private
def new_from_bin(endianness, bin)
format = Utils.specialize_format(self.format, endianness)
new(*bin.unpack(format))
end
def format
@format ||= @fmt_list.join
end
def bytesize
@bytesize ||= @size_list.sum
end
private
# @param subclass [Class] subclass type
# @api private
def inherited(subclass) # rubocop:disable Lint/MissingSuper
# Clone all class instance variables
field_idxs = @field_idxs.dup
size_list = @size_list.dup
fmt_list = @fmt_list.dup
min_args = @min_args.dup
# Add those values to the inheriting class
subclass.class_eval do
@field_idxs = field_idxs
@size_list = size_list
@fmt_list = fmt_list
@min_args = min_args
end
end
# @param name [Symbol] name of internal field
# @param type [Symbol] type of field in terms of binary size
# @param options [Hash] set of additonal options
# Expected options
# :size [Integer] size in bytes
# :mask [Integer] bitmask
# :unpack [String] string format
# :default [Value] default value
# :to_s [Boolean] flag for generating #to_s
# :endian [Symbol] optionally specify :big or :little endian
# :padding [Symbol] optionally specify :null padding
# @api private
def field(name, type, **options)
raise ArgumentError, "Invalid field type #{type}" unless Fields::FORMAT_CODE.key?(type)
# Get field idx for size_list and fmt_list
idx = if @field_idxs.key?(name)
@field_idxs[name]
else
@min_args += 1 unless options.key?(:default) || Fields::NO_ARG_REQUIRED.include?(type)
@field_idxs[name] = @field_idxs.size
@size_list << nil
@fmt_list << nil
@field_idxs.size - 1
end
# Update string type if padding is specified
type = :null_padded_string if type == :string && options[:padding] == :null
# Add to size_list and fmt_list
@size_list[idx] = Fields::BYTE_SIZE[type] || options[:size]
@fmt_list[idx] = if options[:endian]
Utils.specialize_format(Fields::FORMAT_CODE[type], options[:endian])
else
Fields::FORMAT_CODE[type]
end
@fmt_list[idx] += options[:size].to_s if options.key?(:size)
# Generate methods
if Fields::CLASSES_TO_INIT.include?(type)
def_class_reader(name, type, idx)
elsif options.key?(:mask)
def_mask_reader(name, idx, options[:mask])
elsif options.key?(:unpack)
def_unpack_reader(name, idx, options[:unpack])
elsif options.key?(:default)
def_default_reader(name, idx, options[:default])
else
def_reader(name, idx)
end
def_to_s(name) if options[:to_s]
end
#
# Method Generators
#
# Generates a reader method for classes that need to be initialized.
# These classes are defined in the Fields::CLASSES_TO_INIT array.
# @param name [Symbol] name of internal field
# @param type [Symbol] type of field in terms of binary size
# @param idx [Integer] the index of the field value in the @values array
# @api private
def def_class_reader(name, type, idx)
case type
when :lcstr
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", LoadCommands::LoadCommand::LCStr.new(self, @values[idx]))
instance_variable_get("@#{name}")
end
when :two_level_hints_table
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", LoadCommands::TwolevelHintsCommand::TwolevelHintsTable.new(view, htoffset, nhints))
instance_variable_get("@#{name}")
end
when :tool_entries
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", LoadCommands::BuildVersionCommand::ToolEntries.new(view, @values[idx]))
instance_variable_get("@#{name}")
end
end
end
# Generates a reader method for fields that need to be bitmasked.
# @param name [Symbol] name of internal field
# @param idx [Integer] the index of the field value in the @values array
# @param mask [Integer] the bitmask
# @api private
def def_mask_reader(name, idx, mask)
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", @values[idx] & ~mask)
instance_variable_get("@#{name}")
end
end
# Generates a reader method for fields that need further unpacking.
# @param name [Symbol] name of internal field
# @param idx [Integer] the index of the field value in the @values array
# @param unpack [String] the format code used for futher binary unpacking
# @api private
def def_unpack_reader(name, idx, unpack)
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", @values[idx].unpack(unpack))
instance_variable_get("@#{name}")
end
end
# Generates a reader method for fields that have default values.
# @param name [Symbol] name of internal field
# @param idx [Integer] the index of the field value in the @values array
# @param default [Value] the default value
# @api private
def def_default_reader(name, idx, default)
define_method(name) do
instance_variable_defined?("@#{name}") ||
instance_variable_set("@#{name}", @values.size > idx ? @values[idx] : default)
instance_variable_get("@#{name}")
end
end
# Generates an attr_reader like method for a field.
# @param name [Symbol] name of internal field
# @param idx [Integer] the index of the field value in the @values array
# @api private
def def_reader(name, idx)
define_method(name) do
@values[idx]
end
end
# Generates the to_s method based on the named field.
# @param name [Symbol] name of the field
# @api private
def def_to_s(name)
define_method(:to_s) do
send(name).to_s
end
end
end
end
end

View File

@ -3,6 +3,9 @@
module MachO
# A representation of some unspecified Mach-O data.
class MachOView
# @return [MachOFile] that this view belongs to
attr_reader :macho_file
# @return [String] the raw Mach-O data
attr_reader :raw_data
@ -13,10 +16,12 @@ module MachO
attr_reader :offset
# Creates a new MachOView.
# @param macho_file [MachOFile] the file this view slice is from
# @param raw_data [String] the raw Mach-O data
# @param endianness [Symbol] the endianness of the data
# @param offset [Integer] the offset of the relevant data
def initialize(raw_data, endianness, offset)
def initialize(macho_file, raw_data, endianness, offset)
@macho_file = macho_file
@raw_data = raw_data
@endianness = endianness
@offset = offset
@ -29,5 +34,9 @@ module MachO
"offset" => offset,
}
end
def inspect
"#<#{self.class}:0x#{(object_id << 1).to_s(16)} @endianness=#{@endianness.inspect}, @offset=#{@offset.inspect}, length=#{@raw_data.length}>"
end
end
end