Class: MachO::MachOFile
- Inherits:
-
Object
- Object
- MachO::MachOFile
- Defined in:
- lib/macho/macho_file.rb
Overview
Represents a Mach-O file, which contains a header and load commands as well as binary executable instructions. Mach-O binaries are architecture specific.
Instance Attribute Summary collapse
-
#endianness ⇒ Symbol
readonly
The endianness of the file, :big or :little.
-
#filename ⇒ String
The filename loaded from, or nil if loaded from a binary string.
- #header ⇒ MachO::MachHeader, MachO::MachHeader64 readonly
-
#load_commands ⇒ Array<MachO::LoadCommand>
readonly
An array of the file's load commands.
Class Method Summary collapse
-
.new_from_bin(bin) ⇒ MachO::MachOFile
Creates a new MachOFile instance from a binary string.
Instance Method Summary collapse
-
#bundle? ⇒ Boolean
True if the file is of type
MH_BUNDLE, false otherwise. -
#change_install_name(old_name, new_name) ⇒ void
(also: #change_dylib)
Changes the shared library
old_nametonew_name. -
#change_rpath(old_path, new_path) ⇒ void
private
Changes the runtime path
old_pathtonew_path. -
#command(name) ⇒ Array<MachO::LoadCommand>
(also: #[])
All load commands of a given name.
-
#core? ⇒ Boolean
True if the file is of type
MH_CORE, false otherwise. -
#cpusubtype ⇒ Symbol
A symbol representation of the Mach-O's CPU subtype.
-
#cputype ⇒ Symbol
A symbol representation of the Mach-O's CPU type.
-
#dsym? ⇒ Boolean
True if the file is of type
MH_DSYM, false otherwise. -
#dylib? ⇒ Boolean
True if the file is of type
MH_DYLIB, false otherwise. -
#dylib_id ⇒ String?
The Mach-O's dylib ID, or
nilif not a dylib. -
#dylib_id=(new_id) ⇒ void
Changes the Mach-O's dylib ID to
new_id. -
#dylib_load_commands ⇒ Array<MachO::DylibCommand>
All load commands responsible for loading dylibs.
-
#dylinker? ⇒ Boolean
True if the file is of type
MH_DYLINKER, false otherwise. -
#executable? ⇒ Boolean
True if the file is of type
MH_EXECUTE, false otherwise. -
#filetype ⇒ String
A string representation of the Mach-O's filetype.
-
#flags ⇒ Fixnum
Execution flags set by the linker.
-
#fvmlib? ⇒ Boolean
True if the file is of type
MH_FVMLIB, false otherwise. -
#initialize(filename) ⇒ MachOFile
constructor
Creates a new FatFile from the given filename.
- #initialize_from_bin(bin) ⇒ Object private
-
#kext? ⇒ Boolean
True if the file is of type
MH_KEXT_BUNDLE, false otherwise. -
#linked_dylibs ⇒ Array<String>
All shared libraries linked to the Mach-O.
-
#magic ⇒ Fixnum
The file's magic number.
-
#magic32? ⇒ Boolean
True if the Mach-O has 32-bit magic, false otherwise.
-
#magic64? ⇒ Boolean
True if the Mach-O has 64-bit magic, false otherwise.
-
#magic_string ⇒ String
A string representation of the file's magic number.
-
#ncmds ⇒ Fixnum
The number of load commands in the Mach-O's header.
-
#object? ⇒ Boolean
True if the file is of type
MH_OBJECT, false otherwise. -
#preload? ⇒ Boolean
True if the file is of type
MH_PRELOAD, false otherwise. -
#rpaths ⇒ Array<String>
All runtime paths searched by the dynamic linker for the Mach-O.
-
#sections(segment) ⇒ Array<MachO::Section>, Array<MachO::Section64>
All sections of the segment
segment. -
#segments ⇒ Array<MachO::SegmentCommand>, Array<MachO::SegmentCommand64>
All segment load commands in the Mach-O.
-
#serialize ⇒ String
The file's raw Mach-O data.
-
#sizeofcmds ⇒ Fixnum
The size of all load commands, in bytes.
-
#write(filename) ⇒ void
Write all Mach-O data to the given filename.
-
#write! ⇒ void
Write all Mach-O data to the file used to initialize the instance.
Constructor Details
#initialize(filename) ⇒ MachOFile
Creates a new FatFile from the given filename.
34 35 36 37 38 39 40 41 |
# File 'lib/macho/macho_file.rb', line 34 def initialize(filename) raise ArgumentError.new("#{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 end |
Instance Attribute Details
#endianness ⇒ Symbol (readonly)
Returns the endianness of the file, :big or :little.
12 13 14 |
# File 'lib/macho/macho_file.rb', line 12 def endianness @endianness end |
#filename ⇒ String
Returns the filename loaded from, or nil if loaded from a binary string.
9 10 11 |
# File 'lib/macho/macho_file.rb', line 9 def filename @filename end |
#header ⇒ MachO::MachHeader, MachO::MachHeader64 (readonly)
16 17 18 |
# File 'lib/macho/macho_file.rb', line 16 def header @header end |
#load_commands ⇒ Array<MachO::LoadCommand> (readonly)
Returns an array of the file's load commands.
19 20 21 |
# File 'lib/macho/macho_file.rb', line 19 def load_commands @load_commands end |
Class Method Details
.new_from_bin(bin) ⇒ MachO::MachOFile
Creates a new MachOFile instance from a binary string.
24 25 26 27 28 29 |
# File 'lib/macho/macho_file.rb', line 24 def self.new_from_bin(bin) instance = allocate instance.initialize_from_bin(bin) instance end |
Instance Method Details
#bundle? ⇒ Boolean
Returns true if the file is of type MH_BUNDLE, false otherwise.
103 104 105 |
# File 'lib/macho/macho_file.rb', line 103 def bundle? header.filetype == MH_BUNDLE end |
#change_install_name(old_name, new_name) ⇒ void Also known as: change_dylib
This method returns an undefined value.
Changes the shared library old_name to new_name
238 239 240 241 242 243 |
# File 'lib/macho/macho_file.rb', line 238 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? set_name_in_dylib(dylib_cmd, old_name, new_name) end |
#change_rpath(old_path, new_path) ⇒ void
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.
This method returns an undefined value.
Changes the runtime path old_path to new_path
261 262 263 264 265 266 |
# File 'lib/macho/macho_file.rb', line 261 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? set_path_in_rpath(rpath_cmd, old_path, new_path) end |
#command(name) ⇒ Array<MachO::LoadCommand> Also known as: []
All load commands of a given name.
163 164 165 |
# File 'lib/macho/macho_file.rb', line 163 def command(name) load_commands.select { |lc| lc.type == name.to_sym } end |
#core? ⇒ Boolean
Returns true if the file is of type MH_CORE, false otherwise.
83 84 85 |
# File 'lib/macho/macho_file.rb', line 83 def core? header.filetype == MH_CORE end |
#cpusubtype ⇒ Symbol
Returns a symbol representation of the Mach-O's CPU subtype.
138 139 140 |
# File 'lib/macho/macho_file.rb', line 138 def cpusubtype CPU_SUBTYPES[header.cputype][header.cpusubtype] end |
#cputype ⇒ Symbol
Returns a symbol representation of the Mach-O's CPU type.
133 134 135 |
# File 'lib/macho/macho_file.rb', line 133 def cputype CPU_TYPES[header.cputype] end |
#dsym? ⇒ Boolean
Returns true if the file is of type MH_DSYM, false otherwise.
108 109 110 |
# File 'lib/macho/macho_file.rb', line 108 def dsym? header.filetype == MH_DSYM end |
#dylib? ⇒ Boolean
Returns true if the file is of type MH_DYLIB, false otherwise.
93 94 95 |
# File 'lib/macho/macho_file.rb', line 93 def dylib? header.filetype == MH_DYLIB end |
#dylib_id ⇒ String?
The Mach-O's dylib ID, or nil if not a dylib.
190 191 192 193 194 195 196 197 198 |
# File 'lib/macho/macho_file.rb', line 190 def dylib_id if !dylib? return nil end dylib_id_cmd = command(:LC_ID_DYLIB).first dylib_id_cmd.name.to_s end |
#dylib_id=(new_id) ⇒ void
This method returns an undefined value.
Changes the Mach-O's dylib ID to new_id. Does nothing if not a dylib.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/macho/macho_file.rb', line 206 def dylib_id=(new_id) if !new_id.is_a?(String) raise ArgumentError.new("argument must be a String") end if !dylib? return nil end dylib_cmd = command(:LC_ID_DYLIB).first old_id = dylib_id set_name_in_dylib(dylib_cmd, old_id, new_id) end |
#dylib_load_commands ⇒ Array<MachO::DylibCommand>
All load commands responsible for loading dylibs.
171 172 173 |
# File 'lib/macho/macho_file.rb', line 171 def dylib_load_commands load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) } end |
#dylinker? ⇒ Boolean
Returns true if the file is of type MH_DYLINKER, false otherwise.
98 99 100 |
# File 'lib/macho/macho_file.rb', line 98 def dylinker? header.filetype == MH_DYLINKER end |
#executable? ⇒ Boolean
Returns true if the file is of type MH_EXECUTE, false otherwise.
73 74 75 |
# File 'lib/macho/macho_file.rb', line 73 def executable? header.filetype == MH_EXECUTE end |
#filetype ⇒ String
Returns a string representation of the Mach-O's filetype.
128 129 130 |
# File 'lib/macho/macho_file.rb', line 128 def filetype MH_FILETYPES[header.filetype] end |
#flags ⇒ Fixnum
Returns execution flags set by the linker.
153 154 155 |
# File 'lib/macho/macho_file.rb', line 153 def flags header.flags end |
#fvmlib? ⇒ Boolean
Returns true if the file is of type MH_FVMLIB, false otherwise.
78 79 80 |
# File 'lib/macho/macho_file.rb', line 78 def fvmlib? header.filetype == MH_FVMLIB end |
#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.
44 45 46 47 48 49 |
# File 'lib/macho/macho_file.rb', line 44 def initialize_from_bin(bin) @filename = nil @raw_data = bin @header = get_mach_header @load_commands = get_load_commands end |
#kext? ⇒ Boolean
Returns true if the file is of type MH_KEXT_BUNDLE, false otherwise.
113 114 115 |
# File 'lib/macho/macho_file.rb', line 113 def kext? header.filetype == MH_KEXT_BUNDLE end |
#linked_dylibs ⇒ Array<String>
All shared libraries linked to the Mach-O.
223 224 225 226 227 228 229 |
# File 'lib/macho/macho_file.rb', line 223 def linked_dylibs # Some linkers produce multiple `LC_LOAD_DYLIB` load commands for the same # library, but at this point we're really only interested in a list of # unique libraries this Mach-O file links to, thus: `uniq`. (This is also # for consistency with `FatFile` that merges this list across all archs.) dylib_load_commands.map(&:name).map(&:to_s).uniq end |
#magic ⇒ Fixnum
Returns the file's magic number.
118 119 120 |
# File 'lib/macho/macho_file.rb', line 118 def magic header.magic end |
#magic32? ⇒ Boolean
Returns true if the Mach-O has 32-bit magic, false otherwise.
58 59 60 |
# File 'lib/macho/macho_file.rb', line 58 def magic32? MachO.magic32?(header.magic) end |
#magic64? ⇒ Boolean
Returns true if the Mach-O has 64-bit magic, false otherwise.
63 64 65 |
# File 'lib/macho/macho_file.rb', line 63 def magic64? MachO.magic64?(header.magic) end |
#magic_string ⇒ String
Returns a string representation of the file's magic number.
123 124 125 |
# File 'lib/macho/macho_file.rb', line 123 def magic_string MH_MAGICS[magic] end |
#ncmds ⇒ Fixnum
Returns the number of load commands in the Mach-O's header.
143 144 145 |
# File 'lib/macho/macho_file.rb', line 143 def ncmds header.ncmds end |
#object? ⇒ Boolean
Returns true if the file is of type MH_OBJECT, false otherwise.
68 69 70 |
# File 'lib/macho/macho_file.rb', line 68 def object? header.filetype == MH_OBJECT end |
#preload? ⇒ Boolean
Returns true if the file is of type MH_PRELOAD, false otherwise.
88 89 90 |
# File 'lib/macho/macho_file.rb', line 88 def preload? header.filetype == MH_PRELOAD end |
#rpaths ⇒ Array<String>
All runtime paths searched by the dynamic linker for the Mach-O.
249 250 251 |
# File 'lib/macho/macho_file.rb', line 249 def rpaths command(:LC_RPATH).map(&:path).map(&:to_s) end |
#sections(segment) ⇒ Array<MachO::Section>, Array<MachO::Section64>
All sections of the segment segment.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/macho/macho_file.rb', line 272 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 end |
#segments ⇒ Array<MachO::SegmentCommand>, Array<MachO::SegmentCommand64>
All segment load commands in the Mach-O.
178 179 180 181 182 183 184 |
# File 'lib/macho/macho_file.rb', line 178 def segments if magic32? command(:LC_SEGMENT) else command(:LC_SEGMENT_64) end end |
#serialize ⇒ String
The file's raw Mach-O data.
53 54 55 |
# File 'lib/macho/macho_file.rb', line 53 def serialize @raw_data end |
#sizeofcmds ⇒ Fixnum
Returns the size of all load commands, in bytes.
148 149 150 |
# File 'lib/macho/macho_file.rb', line 148 def sizeofcmds header.sizeofcmds end |
#write(filename) ⇒ void
This method returns an undefined value.
Write all Mach-O data to the given filename.
301 302 303 |
# File 'lib/macho/macho_file.rb', line 301 def write(filename) File.open(filename, "wb") { |f| f.write(@raw_data) } end |
#write! ⇒ void
Overwrites all data in the file!
This method returns an undefined value.
Write all Mach-O data to the file used to initialize the instance.
309 310 311 312 313 314 315 |
# File 'lib/macho/macho_file.rb', line 309 def write! if @filename.nil? raise MachOError.new("cannot write to a default file when initialized from a binary string") else File.open(@filename, "wb") { |f| f.write(@raw_data) } end end |