Class: MachO::FatFile

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/macho/fat_file.rb

Overview

Represents a "Fat" file, which contains a header, a listing of available architectures, and one or more Mach-O binaries.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename) ⇒ FatFile

Creates a new FatFile from the given filename.

Parameters:

  • filename (String)

    the fat file to load from

Raises:

  • (ArgumentError)

    if the given file does not exist



74
75
76
77
78
79
80
# File 'lib/macho/fat_file.rb', line 74

def initialize(filename)
  raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)

  @filename = filename
  @raw_data = File.open(@filename, "rb", &:read)
  populate_fields
end

Instance Attribute Details

#fat_archsArray<Headers::FatArch> (readonly)

Returns an array of fat architectures.

Returns:



18
19
20
# File 'lib/macho/fat_file.rb', line 18

def fat_archs
  @fat_archs
end

#filenameString

Returns the filename loaded from, or nil if loaded from a binary string.

Returns:

  • (String)

    the filename loaded from, or nil if loaded from a binary string



12
13
14
# File 'lib/macho/fat_file.rb', line 12

def filename
  @filename
end

#headerHeaders::FatHeader (readonly)

Returns the file's header.

Returns:



15
16
17
# File 'lib/macho/fat_file.rb', line 15

def header
  @header
end

#machosArray<MachOFile> (readonly)

Returns an array of Mach-O binaries.

Returns:

  • (Array<MachOFile>)

    an array of Mach-O binaries



21
22
23
# File 'lib/macho/fat_file.rb', line 21

def machos
  @machos
end

Class Method Details

.new_from_bin(bin) ⇒ FatFile

Creates a new FatFile instance from a binary string.

Parameters:

  • bin (String)

    a binary string containing raw Mach-O data

Returns:



64
65
66
67
68
69
# File 'lib/macho/fat_file.rb', line 64

def self.new_from_bin(bin)
  instance = allocate
  instance.initialize_from_bin(bin)

  instance
end

.new_from_machos(*machos) ⇒ FatFile

Creates a new FatFile from the given (single-arch) Mach-Os

Parameters:

  • machos (Array<MachOFile>)

    the machos to combine

Returns:

  • (FatFile)

    a new FatFile containing the give machos

Raises:

  • (ArgumentError)

    if less than one Mach-O is given



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/macho/fat_file.rb', line 27

def self.new_from_machos(*machos)
  raise ArgumentError, "expected at least one Mach-O" if machos.empty?

  # put the smaller alignments further forwards in fat macho, so that we do less padding
  machos = machos.sort_by(&:segment_alignment)

  bin = +""

  bin << Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size).serialize
  offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize)

  macho_pads = {}
  macho_bins = {}

  machos.each do |macho|
    macho_offset      = Utils.round(offset, 2**macho.segment_alignment)
    macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
    macho_bins[macho] = macho.serialize

    bin << Headers::FatArch.new(macho.header.cputype, macho.header.cpusubtype,
                                macho_offset, macho_bins[macho].bytesize,
                                macho.segment_alignment).serialize

    offset += (macho_bins[macho].bytesize + macho_pads[macho])
  end

  machos.each do |macho|
    bin << Utils.nullpad(macho_pads[macho])
    bin << macho_bins[macho]
  end

  new_from_bin(bin)
end

Instance Method Details

#add_rpath(path, options = {}) ⇒ void

This method returns an undefined value.

Add the given runtime path to the file's Mach-Os.

Parameters:

  • path (String)

    the new runtime path

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :strict (Boolean) — default: true

    if true, fail if one slice fails. if false, fail only if all slices fail.

See Also:



236
237
238
239
240
241
242
# File 'lib/macho/fat_file.rb', line 236

def add_rpath(path, options = {})
  each_macho(options) do |macho|
    macho.add_rpath(path, options)
  end

  repopulate_raw_machos
end

#bundle?Boolean

Returns whether or not the file is of type MH_BUNDLE.

Returns:

  • (Boolean)

    whether or not the file is of type MH_BUNDLE



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#change_dylib_id(new_id, options = {}) ⇒ void Also known as: dylib_id=

This method returns an undefined value.

Changes the file's dylib ID to new_id. If the file is not a dylib, does nothing.

Examples:

file.change_dylib_id('libFoo.dylib')

Parameters:

  • new_id (String)

    the new dylib ID

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :strict (Boolean) — default: true

    if true, fail if one slice fails. if false, fail only if all slices fail.

Raises:

  • (ArgumentError)

    if new_id is not a String

See Also:



160
161
162
163
164
165
166
167
168
169
# File 'lib/macho/fat_file.rb', line 160

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?)

  each_macho(options) do |macho|
    macho.change_dylib_id(new_id, options)
  end

  repopulate_raw_machos
end

#change_install_name(old_name, new_name, options = {}) ⇒ void Also known as: change_dylib

This method returns an undefined value.

Changes all dependent shared library install names from old_name to new_name. In a fat file, this changes install names in all internal Mach-Os.

Examples:

file.change_install_name('/usr/lib/libFoo.dylib', '/usr/lib/libBar.dylib')

Parameters:

  • old_name (String)

    the shared library name being changed

  • new_name (String)

    the new name

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :strict (Boolean) — default: true

    if true, fail if one slice fails. if false, fail only if all slices fail.

See Also:



195
196
197
198
199
200
201
# File 'lib/macho/fat_file.rb', line 195

def change_install_name(old_name, new_name, options = {})
  each_macho(options) do |macho|
    macho.change_install_name(old_name, new_name, options)
  end

  repopulate_raw_machos
end

#change_rpath(old_path, new_path, options = {}) ⇒ void

This method returns an undefined value.

Change the runtime path old_path to new_path in the file's Mach-Os.

Parameters:

  • old_path (String)

    the old runtime path

  • new_path (String)

    the new runtime path

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :strict (Boolean) — default: true

    if true, fail if one slice fails. if false, fail only if all slices fail.

See Also:



221
222
223
224
225
226
227
# File 'lib/macho/fat_file.rb', line 221

def change_rpath(old_path, new_path, options = {})
  each_macho(options) do |macho|
    macho.change_rpath(old_path, new_path, options)
  end

  repopulate_raw_machos
end

#core?Boolean

Returns whether or not the file is of type MH_CORE.

Returns:

  • (Boolean)

    whether or not the file is of type MH_CORE



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#delete_rpath(path, options = {}) ⇒ Object

Delete the given runtime path from the file's Mach-Os.

Parameters:

  • path (String)

    the runtime path to delete

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :strict (Boolean) — default: true

    if true, fail if one slice fails. if false, fail only if all slices fail.

Returns:

  • void

See Also:



251
252
253
254
255
256
257
# File 'lib/macho/fat_file.rb', line 251

def delete_rpath(path, options = {})
  each_macho(options) do |macho|
    macho.delete_rpath(path, options)
  end

  repopulate_raw_machos
end

#dsym?Boolean

Returns whether or not the file is of type MH_DSYM.

Returns:

  • (Boolean)

    whether or not the file is of type MH_DSYM



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#dylib?Boolean

Returns whether or not the file is of type MH_DYLIB.

Returns:

  • (Boolean)

    whether or not the file is of type MH_DYLIB



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#dylib_idString?

Returns the Mach-O's dylib ID.

Returns:

  • (String, nil)

    the Mach-O's dylib ID



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#dylib_load_commandsArray<LoadCommands::DylibCommand>

All load commands responsible for loading dylibs in the file's Mach-O's.

Returns:



145
146
147
# File 'lib/macho/fat_file.rb', line 145

def dylib_load_commands
  machos.map(&:dylib_load_commands).flatten
end

#dylinker?Boolean

Returns whether or not the file is of type MH_DYLINKER.

Returns:

  • (Boolean)

    whether or not the file is of type MH_DYLINKER



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#executable?Boolean

Returns whether or not the file is of type MH_EXECUTE.

Returns:

  • (Boolean)

    whether or not the file is of type MH_EXECUTE



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#extract(cputype) ⇒ MachOFile?

Extract a Mach-O with the given CPU type from the file.

Examples:

file.extract(:i386) # => MachO::MachOFile

Parameters:

  • cputype (Symbol)

    the CPU type of the Mach-O being extracted

Returns:

  • (MachOFile, nil)

    the extracted Mach-O or nil if no Mach-O has the given CPU type



264
265
266
# File 'lib/macho/fat_file.rb', line 264

def extract(cputype)
  machos.select { |macho| macho.cputype == cputype }.first
end

#filetypeSymbol

Returns a string representation of the Mach-O's filetype.

Returns:

  • (Symbol)

    a string representation of the Mach-O's filetype



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#fvmlib?Boolean

Returns whether or not the file is of type MH_FVMLIB.

Returns:

  • (Boolean)

    whether or not the file is of type MH_FVMLIB



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#initialize_from_bin(bin) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Initializes a new FatFile instance from a binary string.

See Also:



85
86
87
88
89
# File 'lib/macho/fat_file.rb', line 85

def initialize_from_bin(bin)
  @filename = nil
  @raw_data = bin
  populate_fields
end

#kext?Boolean

Returns whether or not the file is of type MH_KEXT_BUNDLE.

Returns:

  • (Boolean)

    whether or not the file is of type MH_KEXT_BUNDLE



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#linked_dylibsArray<String>

All shared libraries linked to the file's Mach-Os.

Returns:

  • (Array<String>)

    an array of all shared libraries

See Also:



176
177
178
179
180
181
# File 'lib/macho/fat_file.rb', line 176

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.
  # the union of all libraries used by all architectures.
  machos.map(&:linked_dylibs).flatten.uniq
end

#magicInteger

Returns the magic number of the header (and file).

Returns:

  • (Integer)

    the magic number of the header (and file)



127
# File 'lib/macho/fat_file.rb', line 127

def_delegators :header, :magic

#magic_stringString

Returns a string representation of the file's magic number.

Returns:

  • (String)

    a string representation of the file's magic number



130
131
132
# File 'lib/macho/fat_file.rb', line 130

def magic_string
  Headers::MH_MAGICS[magic]
end

#object?Boolean

Returns whether or not the file is of type MH_OBJECT.

Returns:

  • (Boolean)

    whether or not the file is of type MH_OBJECT



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#populate_fieldsvoid

Note:

This method is public, but should (almost) never need to be called.

This method returns an undefined value.

Populate the instance's fields with the raw Fat Mach-O data.



137
138
139
140
141
# File 'lib/macho/fat_file.rb', line 137

def populate_fields
  @header = populate_fat_header
  @fat_archs = populate_fat_archs
  @machos = populate_machos
end

#preload?Boolean

Returns whether or not the file is of type MH_PRELOAD.

Returns:

  • (Boolean)

    whether or not the file is of type MH_PRELOAD



121
122
123
# File 'lib/macho/fat_file.rb', line 121

def_delegators :canonical_macho, :object?, :executable?, :fvmlib?,
:core?, :preload?, :dylib?, :dylinker?, :bundle?,
:dsym?, :kext?, :filetype, :dylib_id

#rpathsArray<String>

All runtime paths associated with the file's Mach-Os.

Returns:

  • (Array<String>)

    an array of all runtime paths

See Also:



208
209
210
211
# File 'lib/macho/fat_file.rb', line 208

def rpaths
  # Can individual architectures have different runtime paths?
  machos.map(&:rpaths).flatten.uniq
end

#serializeString

The file's raw fat data.

Returns:

  • (String)

    the raw fat data



93
94
95
# File 'lib/macho/fat_file.rb', line 93

def serialize
  @raw_data
end

#to_hHash

Returns a hash representation of this MachO::FatFile.

Returns:



285
286
287
288
289
290
291
# File 'lib/macho/fat_file.rb', line 285

def to_h
  {
    "header" => header.to_h,
    "fat_archs" => fat_archs.map(&:to_h),
    "machos" => machos.map(&:to_h),
  }
end

#write(filename) ⇒ void

This method returns an undefined value.

Write all (fat) data to the given filename.

Parameters:

  • filename (String)

    the file to write to



271
272
273
# File 'lib/macho/fat_file.rb', line 271

def write(filename)
  File.open(filename, "wb") { |f| f.write(@raw_data) }
end

#write!void

Note:

Overwrites all data in the file!

This method returns an undefined value.

Write all (fat) data to the file used to initialize the instance.

Raises:

  • (MachOError)

    if the instance was initialized without a file



279
280
281
282
# File 'lib/macho/fat_file.rb', line 279

def write!
  raise MachOError, "no initial file to write to" if filename.nil?
  File.open(@filename, "wb") { |f| f.write(@raw_data) }
end