ruby-macho 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/macho.rb +5 -5
- data/lib/macho/exceptions.rb +8 -4
- data/lib/macho/fat_file.rb +96 -96
- data/lib/macho/headers.rb +75 -0
- data/lib/macho/load_commands.rb +86 -53
- data/lib/macho/macho_file.rb +83 -131
- data/lib/macho/sections.rb +8 -5
- data/lib/macho/structure.rb +1 -1
- data/lib/macho/tools.rb +29 -5
- data/lib/macho/utils.rb +12 -9
- metadata +3 -3
data/lib/macho/load_commands.rb
CHANGED
@@ -2,7 +2,7 @@ module MachO
|
|
2
2
|
# Classes and constants for parsing load commands in Mach-O binaries.
|
3
3
|
module LoadCommands
|
4
4
|
# load commands added after OS X 10.1 need to be bitwise ORed with
|
5
|
-
# LC_REQ_DYLD to be recognized by the dynamic
|
5
|
+
# LC_REQ_DYLD to be recognized by the dynamic linker (dyld)
|
6
6
|
# @api private
|
7
7
|
LC_REQ_DYLD = 0x80000000
|
8
8
|
|
@@ -87,14 +87,21 @@ module MachO
|
|
87
87
|
LC_STRUCTURES = {
|
88
88
|
:LC_SEGMENT => "SegmentCommand",
|
89
89
|
:LC_SYMTAB => "SymtabCommand",
|
90
|
-
|
91
|
-
:
|
90
|
+
# "obsolete"
|
91
|
+
:LC_SYMSEG => "SymsegCommand",
|
92
|
+
# seems obsolete, but not documented as such
|
93
|
+
:LC_THREAD => "ThreadCommand",
|
92
94
|
:LC_UNIXTHREAD => "ThreadCommand",
|
93
|
-
|
94
|
-
:
|
95
|
-
|
96
|
-
:
|
97
|
-
|
95
|
+
# "obsolete"
|
96
|
+
:LC_LOADFVMLIB => "FvmlibCommand",
|
97
|
+
# "obsolete"
|
98
|
+
:LC_IDFVMLIB => "FvmlibCommand",
|
99
|
+
# "obsolete"
|
100
|
+
:LC_IDENT => "IdentCommand",
|
101
|
+
# "reserved for internal use only"
|
102
|
+
:LC_FVMFILE => "FvmfileCommand",
|
103
|
+
# "reserved for internal use only", no public struct
|
104
|
+
:LC_PREPAGE => "LoadCommand",
|
98
105
|
:LC_DYSYMTAB => "DysymtabCommand",
|
99
106
|
:LC_LOAD_DYLIB => "DylibCommand",
|
100
107
|
:LC_ID_DYLIB => "DylibCommand",
|
@@ -182,7 +189,7 @@ module MachO
|
|
182
189
|
|
183
190
|
# Instantiates a new LoadCommand given a view into its origin Mach-O
|
184
191
|
# @param view [MachO::MachOView] the load command's raw view
|
185
|
-
# @return [
|
192
|
+
# @return [LoadCommand] the new load command
|
186
193
|
# @api private
|
187
194
|
def self.new_from_bin(view)
|
188
195
|
bin = view.raw_data.slice(view.offset, bytesize)
|
@@ -218,12 +225,12 @@ module MachO
|
|
218
225
|
@cmdsize = cmdsize
|
219
226
|
end
|
220
227
|
|
221
|
-
# @return [Boolean]
|
228
|
+
# @return [Boolean] whether the load command can be serialized
|
222
229
|
def serializable?
|
223
230
|
CREATABLE_LOAD_COMMANDS.include?(LOAD_COMMANDS[cmd])
|
224
231
|
end
|
225
232
|
|
226
|
-
# @param context [
|
233
|
+
# @param context [SerializationContext] the context
|
227
234
|
# to serialize into
|
228
235
|
# @return [String, nil] the serialized fields of the load command, or nil
|
229
236
|
# if the load command can't be serialized
|
@@ -240,14 +247,16 @@ module MachO
|
|
240
247
|
view.offset
|
241
248
|
end
|
242
249
|
|
243
|
-
# @return [Symbol] a symbol representation of the load command's
|
250
|
+
# @return [Symbol] a symbol representation of the load command's
|
251
|
+
# identifying number
|
244
252
|
def type
|
245
253
|
LOAD_COMMANDS[cmd]
|
246
254
|
end
|
247
255
|
|
248
256
|
alias to_sym type
|
249
257
|
|
250
|
-
# @return [String] a string representation of the load command's
|
258
|
+
# @return [String] a string representation of the load command's
|
259
|
+
# identifying number
|
251
260
|
def to_s
|
252
261
|
type.to_s
|
253
262
|
end
|
@@ -257,9 +266,9 @@ module MachO
|
|
257
266
|
# pretend that strings stored in LCs are immediately available without
|
258
267
|
# explicit operations on the raw Mach-O data.
|
259
268
|
class LCStr
|
260
|
-
# @param lc [
|
261
|
-
# @param lc_str [Fixnum, String] the offset to the beginning of the
|
262
|
-
# or the string itself if not being initialized with a view.
|
269
|
+
# @param lc [LoadCommand] the load command
|
270
|
+
# @param lc_str [Fixnum, String] the offset to the beginning of the
|
271
|
+
# string, or the string itself if not being initialized with a view.
|
263
272
|
# @raise [MachO::LCStrMalformedError] if the string is malformed
|
264
273
|
# @todo devise a solution such that the `lc_str` parameter is not
|
265
274
|
# interpreted differently depending on `lc.view`. The current behavior
|
@@ -286,7 +295,8 @@ module MachO
|
|
286
295
|
@string
|
287
296
|
end
|
288
297
|
|
289
|
-
# @return [Fixnum] the offset to the beginning of the string in the
|
298
|
+
# @return [Fixnum] the offset to the beginning of the string in the
|
299
|
+
# load command
|
290
300
|
def to_i
|
291
301
|
@string_offset
|
292
302
|
end
|
@@ -298,11 +308,13 @@ module MachO
|
|
298
308
|
# @return [Symbol] the endianness of the serialized load command
|
299
309
|
attr_reader :endianness
|
300
310
|
|
301
|
-
# @return [Fixnum] the constant alignment value used to pad the
|
311
|
+
# @return [Fixnum] the constant alignment value used to pad the
|
312
|
+
# serialized load command
|
302
313
|
attr_reader :alignment
|
303
314
|
|
304
315
|
# @param macho [MachO::MachOFile] the file to contextualize
|
305
|
-
# @return [
|
316
|
+
# @return [SerializationContext] the
|
317
|
+
# resulting context
|
306
318
|
def self.context_for(macho)
|
307
319
|
new(macho.endianness, macho.alignment)
|
308
320
|
end
|
@@ -317,8 +329,9 @@ module MachO
|
|
317
329
|
end
|
318
330
|
end
|
319
331
|
|
320
|
-
# A load command containing a single 128-bit unique random number
|
321
|
-
# an object produced by static link editor. Corresponds to
|
332
|
+
# A load command containing a single 128-bit unique random number
|
333
|
+
# identifying an object produced by static link editor. Corresponds to
|
334
|
+
# LC_UUID.
|
322
335
|
class UUIDCommand < LoadCommand
|
323
336
|
# @return [Array<Fixnum>] the UUID
|
324
337
|
attr_reader :uuid
|
@@ -413,7 +426,10 @@ module MachO
|
|
413
426
|
MachO::Sections::Section
|
414
427
|
end
|
415
428
|
|
416
|
-
|
429
|
+
offset = view.offset + self.class.bytesize
|
430
|
+
length = nsects * klass.bytesize
|
431
|
+
|
432
|
+
bins = view.raw_data[offset, length]
|
417
433
|
bins.unpack("a#{klass.bytesize}" * nsects).map do |bin|
|
418
434
|
klass.new_from_bin(view.endianness, bin)
|
419
435
|
end
|
@@ -443,10 +459,11 @@ module MachO
|
|
443
459
|
end
|
444
460
|
|
445
461
|
# A load command representing some aspect of shared libraries, depending
|
446
|
-
# on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB,
|
447
|
-
# and LC_REEXPORT_DYLIB.
|
462
|
+
# on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB,
|
463
|
+
# LC_LOAD_WEAK_DYLIB, and LC_REEXPORT_DYLIB.
|
448
464
|
class DylibCommand < LoadCommand
|
449
|
-
# @return [
|
465
|
+
# @return [LCStr] the library's path
|
466
|
+
# name as an LCStr
|
450
467
|
attr_reader :name
|
451
468
|
|
452
469
|
# @return [Fixnum] the library's build time stamp
|
@@ -467,7 +484,8 @@ module MachO
|
|
467
484
|
SIZEOF = 24
|
468
485
|
|
469
486
|
# @api private
|
470
|
-
def initialize(view, cmd, cmdsize, name, timestamp, current_version,
|
487
|
+
def initialize(view, cmd, cmdsize, name, timestamp, current_version,
|
488
|
+
compatibility_version)
|
471
489
|
super(view, cmd, cmdsize)
|
472
490
|
@name = LCStr.new(self, name)
|
473
491
|
@timestamp = timestamp
|
@@ -475,12 +493,15 @@ module MachO
|
|
475
493
|
@compatibility_version = compatibility_version
|
476
494
|
end
|
477
495
|
|
478
|
-
# @param context [
|
496
|
+
# @param context [SerializationContext]
|
497
|
+
# the context
|
479
498
|
# @return [String] the serialized fields of the load command
|
480
499
|
# @api private
|
481
500
|
def serialize(context)
|
482
501
|
format = Utils.specialize_format(FORMAT, context.endianness)
|
483
|
-
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
502
|
+
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
503
|
+
context.alignment,
|
504
|
+
:name => name.to_s)
|
484
505
|
cmdsize = SIZEOF + string_payload.bytesize
|
485
506
|
[cmd, cmdsize, string_offsets[:name], timestamp, current_version,
|
486
507
|
compatibility_version].pack(format) + string_payload
|
@@ -491,7 +512,8 @@ module MachO
|
|
491
512
|
# on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and
|
492
513
|
# LC_DYLD_ENVIRONMENT.
|
493
514
|
class DylinkerCommand < LoadCommand
|
494
|
-
# @return [
|
515
|
+
# @return [LCStr] the dynamic linker's
|
516
|
+
# path name as an LCStr
|
495
517
|
attr_reader :name
|
496
518
|
|
497
519
|
# @see MachOStructure::FORMAT
|
@@ -508,12 +530,15 @@ module MachO
|
|
508
530
|
@name = LCStr.new(self, name)
|
509
531
|
end
|
510
532
|
|
511
|
-
# @param context [
|
533
|
+
# @param context [SerializationContext]
|
534
|
+
# the context
|
512
535
|
# @return [String] the serialized fields of the load command
|
513
536
|
# @api private
|
514
537
|
def serialize(context)
|
515
538
|
format = Utils.specialize_format(FORMAT, context.endianness)
|
516
|
-
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
539
|
+
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
540
|
+
context.alignment,
|
541
|
+
:name => name.to_s)
|
517
542
|
cmdsize = SIZEOF + string_payload.bytesize
|
518
543
|
[cmd, cmdsize, string_offsets[:name]].pack(format) + string_payload
|
519
544
|
end
|
@@ -522,7 +547,8 @@ module MachO
|
|
522
547
|
# A load command used to indicate dynamic libraries used in prebinding.
|
523
548
|
# Corresponds to LC_PREBOUND_DYLIB.
|
524
549
|
class PreboundDylibCommand < LoadCommand
|
525
|
-
# @return [
|
550
|
+
# @return [LCStr] the library's path
|
551
|
+
# name as an LCStr
|
526
552
|
attr_reader :name
|
527
553
|
|
528
554
|
# @return [Fixnum] the number of modules in the library
|
@@ -549,7 +575,8 @@ module MachO
|
|
549
575
|
end
|
550
576
|
|
551
577
|
# A load command used to represent threads.
|
552
|
-
# @note cctools-870
|
578
|
+
# @note cctools-870 and onwards have all fields of thread_command commented
|
579
|
+
# out except the common ones (cmd, cmdsize)
|
553
580
|
class ThreadCommand < LoadCommand
|
554
581
|
# @see MachOStructure::FORMAT
|
555
582
|
# @api private
|
@@ -567,7 +594,8 @@ module MachO
|
|
567
594
|
# @return [Fixnum] the address of the initialization routine
|
568
595
|
attr_reader :init_address
|
569
596
|
|
570
|
-
# @return [Fixnum] the index into the module table that the init routine
|
597
|
+
# @return [Fixnum] the index into the module table that the init routine
|
598
|
+
# is defined in
|
571
599
|
attr_reader :init_module
|
572
600
|
|
573
601
|
# @return [void]
|
@@ -627,7 +655,7 @@ module MachO
|
|
627
655
|
# A load command signifying membership of a subframework containing the name
|
628
656
|
# of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK.
|
629
657
|
class SubFrameworkCommand < LoadCommand
|
630
|
-
# @return [
|
658
|
+
# @return [LCStr] the umbrella framework name as an LCStr
|
631
659
|
attr_reader :umbrella
|
632
660
|
|
633
661
|
# @see MachOStructure::FORMAT
|
@@ -648,7 +676,7 @@ module MachO
|
|
648
676
|
# A load command signifying membership of a subumbrella containing the name
|
649
677
|
# of an umbrella framework. Corresponds to LC_SUB_UMBRELLA.
|
650
678
|
class SubUmbrellaCommand < LoadCommand
|
651
|
-
# @return [
|
679
|
+
# @return [LCStr] the subumbrella framework name as an LCStr
|
652
680
|
attr_reader :sub_umbrella
|
653
681
|
|
654
682
|
# @see MachOStructure::FORMAT
|
@@ -669,7 +697,7 @@ module MachO
|
|
669
697
|
# A load command signifying a sublibrary of a shared library. Corresponds
|
670
698
|
# to LC_SUB_LIBRARY.
|
671
699
|
class SubLibraryCommand < LoadCommand
|
672
|
-
# @return [
|
700
|
+
# @return [LCStr] the sublibrary name as an LCStr
|
673
701
|
attr_reader :sub_library
|
674
702
|
|
675
703
|
# @see MachOStructure::FORMAT
|
@@ -690,7 +718,7 @@ module MachO
|
|
690
718
|
# A load command signifying a shared library that is a subframework of
|
691
719
|
# an umbrella framework. Corresponds to LC_SUB_CLIENT.
|
692
720
|
class SubClientCommand < LoadCommand
|
693
|
-
# @return [
|
721
|
+
# @return [LCStr] the subclient name as an LCStr
|
694
722
|
attr_reader :sub_client
|
695
723
|
|
696
724
|
# @see MachOStructure::FORMAT
|
@@ -843,7 +871,8 @@ module MachO
|
|
843
871
|
# @return [Fixnum] the number of hints in the hint table
|
844
872
|
attr_reader :nhints
|
845
873
|
|
846
|
-
# @return [
|
874
|
+
# @return [TwolevelHintsTable]
|
875
|
+
# the hint table
|
847
876
|
attr_reader :table
|
848
877
|
|
849
878
|
# @see MachOStructure::FORMAT
|
@@ -865,7 +894,7 @@ module MachO
|
|
865
894
|
# A representation of the two-level namespace lookup hints table exposed
|
866
895
|
# by a {TwolevelHintsCommand} (`LC_TWOLEVEL_HINTS`).
|
867
896
|
class TwolevelHintsTable
|
868
|
-
# @return [Array<
|
897
|
+
# @return [Array<TwolevelHint>] all hints in the table
|
869
898
|
attr_reader :hints
|
870
899
|
|
871
900
|
# @param view [MachO::MachOView] the view into the current Mach-O
|
@@ -923,7 +952,7 @@ module MachO
|
|
923
952
|
# be added to the current run path used to find @rpath prefixed dylibs.
|
924
953
|
# Corresponds to LC_RPATH.
|
925
954
|
class RpathCommand < LoadCommand
|
926
|
-
# @return [
|
955
|
+
# @return [LCStr] the path to add to the run path as an LCStr
|
927
956
|
attr_reader :path
|
928
957
|
|
929
958
|
# @see MachOStructure::FORMAT
|
@@ -940,20 +969,23 @@ module MachO
|
|
940
969
|
@path = LCStr.new(self, path)
|
941
970
|
end
|
942
971
|
|
943
|
-
# @param context [
|
972
|
+
# @param context [SerializationContext] the context
|
944
973
|
# @return [String] the serialized fields of the load command
|
945
974
|
# @api private
|
946
975
|
def serialize(context)
|
947
976
|
format = Utils.specialize_format(FORMAT, context.endianness)
|
948
|
-
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
977
|
+
string_payload, string_offsets = Utils.pack_strings(SIZEOF,
|
978
|
+
context.alignment,
|
979
|
+
:path => path.to_s)
|
949
980
|
cmdsize = SIZEOF + string_payload.bytesize
|
950
981
|
[cmd, cmdsize, string_offsets[:path]].pack(format) + string_payload
|
951
982
|
end
|
952
983
|
end
|
953
984
|
|
954
985
|
# A load command representing the offsets and sizes of a blob of data in
|
955
|
-
# the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE,
|
956
|
-
# LC_FUNCTION_STARTS, LC_DATA_IN_CODE,
|
986
|
+
# the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE,
|
987
|
+
# LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE,
|
988
|
+
# LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT.
|
957
989
|
class LinkeditDataCommand < LoadCommand
|
958
990
|
# @return [Fixnum] offset to the data in the __LINKEDIT segment
|
959
991
|
attr_reader :dataoff
|
@@ -1040,7 +1072,8 @@ module MachO
|
|
1040
1072
|
end
|
1041
1073
|
|
1042
1074
|
# A load command containing the minimum OS version on which the binary
|
1043
|
-
# was built to run. Corresponds to LC_VERSION_MIN_MACOSX and
|
1075
|
+
# was built to run. Corresponds to LC_VERSION_MIN_MACOSX and
|
1076
|
+
# LC_VERSION_MIN_IPHONEOS.
|
1044
1077
|
class VersionMinCommand < LoadCommand
|
1045
1078
|
# @return [Fixnum] the version X.Y.Z packed as x16.y8.z8
|
1046
1079
|
attr_reader :version
|
@@ -1249,9 +1282,9 @@ module MachO
|
|
1249
1282
|
end
|
1250
1283
|
end
|
1251
1284
|
|
1252
|
-
# An obsolete load command containing a free format string table. Each
|
1253
|
-
# is null-terminated and the command is zero-padded to a multiple of
|
1254
|
-
# Corresponds to LC_IDENT.
|
1285
|
+
# An obsolete load command containing a free format string table. Each
|
1286
|
+
# string is null-terminated and the command is zero-padded to a multiple of
|
1287
|
+
# 4. Corresponds to LC_IDENT.
|
1255
1288
|
class IdentCommand < LoadCommand
|
1256
1289
|
# @see MachOStructure::FORMAT
|
1257
1290
|
# @api private
|
@@ -1265,7 +1298,7 @@ module MachO
|
|
1265
1298
|
# An obsolete load command containing the path to a file to be loaded into
|
1266
1299
|
# memory. Corresponds to LC_FVMFILE.
|
1267
1300
|
class FvmfileCommand < LoadCommand
|
1268
|
-
# @return [
|
1301
|
+
# @return [LCStr] the pathname of the file being loaded
|
1269
1302
|
attr_reader :name
|
1270
1303
|
|
1271
1304
|
# @return [Fixnum] the virtual address being loaded at
|
@@ -1286,10 +1319,10 @@ module MachO
|
|
1286
1319
|
end
|
1287
1320
|
end
|
1288
1321
|
|
1289
|
-
# An obsolete load command containing the path to a library to be loaded
|
1290
|
-
# memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB.
|
1322
|
+
# An obsolete load command containing the path to a library to be loaded
|
1323
|
+
# into memory. Corresponds to LC_LOADFVMLIB and LC_IDFVMLIB.
|
1291
1324
|
class FvmlibCommand < LoadCommand
|
1292
|
-
# @return [
|
1325
|
+
# @return [LCStr] the library's target pathname
|
1293
1326
|
attr_reader :name
|
1294
1327
|
|
1295
1328
|
# @return [Fixnum] the library's minor version number
|
data/lib/macho/macho_file.rb
CHANGED
@@ -1,27 +1,33 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module MachO
|
2
4
|
# Represents a Mach-O file, which contains a header and load commands
|
3
5
|
# as well as binary executable instructions. Mach-O binaries are
|
4
6
|
# architecture specific.
|
5
7
|
# @see https://en.wikipedia.org/wiki/Mach-O
|
6
|
-
# @see
|
8
|
+
# @see FatFile
|
7
9
|
class MachOFile
|
8
|
-
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
# @return [String] the filename loaded from, or nil if loaded from a binary
|
13
|
+
# string
|
9
14
|
attr_accessor :filename
|
10
15
|
|
11
16
|
# @return [Symbol] the endianness of the file, :big or :little
|
12
17
|
attr_reader :endianness
|
13
18
|
|
14
|
-
# @return [
|
15
|
-
# @return [
|
19
|
+
# @return [Headers::MachHeader] if the Mach-O is 32-bit
|
20
|
+
# @return [Headers::MachHeader64] if the Mach-O is 64-bit
|
16
21
|
attr_reader :header
|
17
22
|
|
18
|
-
# @return [Array<
|
23
|
+
# @return [Array<LoadCommands::LoadCommand>] an array of the file's load
|
24
|
+
# commands
|
19
25
|
# @note load commands are provided in order of ascending offset.
|
20
26
|
attr_reader :load_commands
|
21
27
|
|
22
28
|
# Creates a new MachOFile instance from a binary string.
|
23
29
|
# @param bin [String] a binary string containing raw Mach-O data
|
24
|
-
# @return [
|
30
|
+
# @return [MachOFile] a new MachOFile
|
25
31
|
def self.new_from_bin(bin)
|
26
32
|
instance = allocate
|
27
33
|
instance.initialize_from_bin(bin)
|
@@ -55,75 +61,44 @@ module MachO
|
|
55
61
|
@raw_data
|
56
62
|
end
|
57
63
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
# @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise
|
99
|
-
def dylib?
|
100
|
-
header.filetype == Headers::MH_DYLIB
|
101
|
-
end
|
102
|
-
|
103
|
-
# @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise
|
104
|
-
def dylinker?
|
105
|
-
header.filetype == Headers::MH_DYLINKER
|
106
|
-
end
|
107
|
-
|
108
|
-
# @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise
|
109
|
-
def bundle?
|
110
|
-
header.filetype == Headers::MH_BUNDLE
|
111
|
-
end
|
112
|
-
|
113
|
-
# @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise
|
114
|
-
def dsym?
|
115
|
-
header.filetype == Headers::MH_DSYM
|
116
|
-
end
|
117
|
-
|
118
|
-
# @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise
|
119
|
-
def kext?
|
120
|
-
header.filetype == Headers::MH_KEXT_BUNDLE
|
121
|
-
end
|
122
|
-
|
123
|
-
# @return [Fixnum] the file's magic number
|
124
|
-
def magic
|
125
|
-
header.magic
|
126
|
-
end
|
64
|
+
# @!method magic
|
65
|
+
# @return (see MachO::Headers::MachHeader#magic)
|
66
|
+
# @!method ncmds
|
67
|
+
# @return (see MachO::Headers::MachHeader#ncmds)
|
68
|
+
# @!method sizeofcmds
|
69
|
+
# @return (see MachO::Headers::MachHeader#sizeofcmds)
|
70
|
+
# @!method flags
|
71
|
+
# @return (see MachO::Headers::MachHeader#flags)
|
72
|
+
# @!method object?
|
73
|
+
# @return (see MachO::Headers::MachHeader#object?)
|
74
|
+
# @!method executable?
|
75
|
+
# @return (see MachO::Headers::MachHeader#executable?)
|
76
|
+
# @!method fvmlib?
|
77
|
+
# @return (see MachO::Headers::MachHeader#fvmlib?)
|
78
|
+
# @!method core?
|
79
|
+
# @return (see MachO::Headers::MachHeader#core?)
|
80
|
+
# @!method preload?
|
81
|
+
# @return (see MachO::Headers::MachHeader#preload?)
|
82
|
+
# @!method dylib?
|
83
|
+
# @return (see MachO::Headers::MachHeader#dylib?)
|
84
|
+
# @!method dylinker?
|
85
|
+
# @return (see MachO::Headers::MachHeader#dylinker?)
|
86
|
+
# @!method bundle?
|
87
|
+
# @return (see MachO::Headers::MachHeader#bundle?)
|
88
|
+
# @!method dsym?
|
89
|
+
# @return (see MachO::Headers::MachHeader#dsym?)
|
90
|
+
# @!method kext?
|
91
|
+
# @return (see MachO::Headers::MachHeader#kext?)
|
92
|
+
# @!method magic32?
|
93
|
+
# @return (see MachO::Headers::MachHeader#magic32?)
|
94
|
+
# @!method magic64?
|
95
|
+
# @return (see MachO::Headers::MachHeader#magic64?)
|
96
|
+
# @!method alignment
|
97
|
+
# @return (see MachO::Headers::MachHeader#alignment)
|
98
|
+
def_delegators :header, :magic, :ncmds, :sizeofcmds, :flags, :object?,
|
99
|
+
:executable?, :fvmlib?, :core?, :preload?, :dylib?,
|
100
|
+
:dylinker?, :bundle?, :dsym?, :kext?, :magic32?, :magic64?,
|
101
|
+
:alignment
|
127
102
|
|
128
103
|
# @return [String] a string representation of the file's magic number
|
129
104
|
def magic_string
|
@@ -145,27 +120,13 @@ module MachO
|
|
145
120
|
Headers::CPU_SUBTYPES[header.cputype][header.cpusubtype]
|
146
121
|
end
|
147
122
|
|
148
|
-
# @return [Fixnum] the number of load commands in the Mach-O's header
|
149
|
-
def ncmds
|
150
|
-
header.ncmds
|
151
|
-
end
|
152
|
-
|
153
|
-
# @return [Fixnum] the size of all load commands, in bytes
|
154
|
-
def sizeofcmds
|
155
|
-
header.sizeofcmds
|
156
|
-
end
|
157
|
-
|
158
|
-
# @return [Fixnum] execution flags set by the linker
|
159
|
-
def flags
|
160
|
-
header.flags
|
161
|
-
end
|
162
|
-
|
163
123
|
# All load commands of a given name.
|
164
124
|
# @example
|
165
125
|
# file.command("LC_LOAD_DYLIB")
|
166
126
|
# file[:LC_LOAD_DYLIB]
|
167
127
|
# @param [String, Symbol] name the load command ID
|
168
|
-
# @return [Array<
|
128
|
+
# @return [Array<LoadCommands::LoadCommand>] an array of load commands
|
129
|
+
# corresponding to `name`
|
169
130
|
def command(name)
|
170
131
|
load_commands.select { |lc| lc.type == name.to_sym }
|
171
132
|
end
|
@@ -174,12 +135,12 @@ module MachO
|
|
174
135
|
|
175
136
|
# Inserts a load command at the given offset.
|
176
137
|
# @param offset [Fixnum] the offset to insert at
|
177
|
-
# @param lc [
|
138
|
+
# @param lc [LoadCommands::LoadCommand] the load command to insert
|
178
139
|
# @param options [Hash]
|
179
140
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
180
141
|
# the instance fields
|
181
|
-
# @raise [
|
182
|
-
# @raise [
|
142
|
+
# @raise [OffsetInsertionError] if the offset is not in the load command region
|
143
|
+
# @raise [HeaderPadError] if the new command exceeds the header pad buffer
|
183
144
|
# @note Calling this method with an arbitrary offset in the load command
|
184
145
|
# region **will leave the object in an inconsistent state**.
|
185
146
|
def insert_command(offset, lc, options = {})
|
@@ -207,11 +168,11 @@ module MachO
|
|
207
168
|
end
|
208
169
|
|
209
170
|
# Replace a load command with another command in the Mach-O, preserving location.
|
210
|
-
# @param old_lc [
|
211
|
-
# @param new_lc [
|
171
|
+
# @param old_lc [LoadCommands::LoadCommand] the load command being replaced
|
172
|
+
# @param new_lc [LoadCommands::LoadCommand] the load command being added
|
212
173
|
# @return [void]
|
213
|
-
# @raise [
|
214
|
-
# @see
|
174
|
+
# @raise [HeaderPadError] if the new command exceeds the header pad buffer
|
175
|
+
# @see #insert_command
|
215
176
|
# @note This is public, but methods like {#dylib_id=} should be preferred.
|
216
177
|
def replace_command(old_lc, new_lc)
|
217
178
|
context = LoadCommands::LoadCommand::SerializationContext.context_for(self)
|
@@ -226,12 +187,12 @@ module MachO
|
|
226
187
|
end
|
227
188
|
|
228
189
|
# Appends a new load command to the Mach-O.
|
229
|
-
# @param lc [
|
190
|
+
# @param lc [LoadCommands::LoadCommand] the load command being added
|
230
191
|
# @param options [Hash]
|
231
192
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
232
193
|
# the instance fields
|
233
194
|
# @return [void]
|
234
|
-
# @see
|
195
|
+
# @see #insert_command
|
235
196
|
# @note This is public, but methods like {#add_rpath} should be preferred.
|
236
197
|
# Setting `repopulate` to false **will leave the instance in an
|
237
198
|
# inconsistent state** unless {#populate_fields} is called **immediately**
|
@@ -241,7 +202,7 @@ module MachO
|
|
241
202
|
end
|
242
203
|
|
243
204
|
# Delete a load command from the Mach-O.
|
244
|
-
# @param lc [
|
205
|
+
# @param lc [LoadCommands::LoadCommand] the load command being deleted
|
245
206
|
# @param options [Hash]
|
246
207
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
247
208
|
# the instance fields
|
@@ -275,14 +236,14 @@ module MachO
|
|
275
236
|
end
|
276
237
|
|
277
238
|
# All load commands responsible for loading dylibs.
|
278
|
-
# @return [Array<
|
239
|
+
# @return [Array<LoadCommands::DylibCommand>] an array of DylibCommands
|
279
240
|
def dylib_load_commands
|
280
241
|
load_commands.select { |lc| LoadCommands::DYLIB_LOAD_COMMANDS.include?(lc.type) }
|
281
242
|
end
|
282
243
|
|
283
244
|
# All segment load commands in the Mach-O.
|
284
|
-
# @return [Array<
|
285
|
-
# @return [Array<
|
245
|
+
# @return [Array<LoadCommands::SegmentCommand>] if the Mach-O is 32-bit
|
246
|
+
# @return [Array<LoadCommands::SegmentCommand64>] if the Mach-O is 64-bit
|
286
247
|
def segments
|
287
248
|
if magic32?
|
288
249
|
command(:LC_SEGMENT)
|
@@ -341,12 +302,12 @@ module MachO
|
|
341
302
|
|
342
303
|
# Changes the shared library `old_name` to `new_name`
|
343
304
|
# @example
|
344
|
-
# file.change_install_name("
|
305
|
+
# file.change_install_name("abc.dylib", "def.dylib")
|
345
306
|
# @param old_name [String] the shared library's old name
|
346
307
|
# @param new_name [String] the shared library's new name
|
347
308
|
# @param _options [Hash]
|
348
309
|
# @return [void]
|
349
|
-
# @raise [
|
310
|
+
# @raise [DylibUnknownError] if no shared library has the old name
|
350
311
|
# @note `_options` is currently unused and is provided for signature
|
351
312
|
# compatibility with {MachO::FatFile#change_install_name}
|
352
313
|
def change_install_name(old_name, new_name, _options = {})
|
@@ -376,8 +337,8 @@ module MachO
|
|
376
337
|
# @param new_path [String] the new runtime path
|
377
338
|
# @param _options [Hash]
|
378
339
|
# @return [void]
|
379
|
-
# @raise [
|
380
|
-
# @raise [
|
340
|
+
# @raise [RpathUnknownError] if no such old runtime path exists
|
341
|
+
# @raise [RpathExistsError] if the new runtime path already exists
|
381
342
|
# @note `_options` is currently unused and is provided for signature
|
382
343
|
# compatibility with {MachO::FatFile#change_rpath}
|
383
344
|
def change_rpath(old_path, new_path, _options = {})
|
@@ -399,7 +360,7 @@ module MachO
|
|
399
360
|
# @param path [String] the new runtime path
|
400
361
|
# @param _options [Hash]
|
401
362
|
# @return [void]
|
402
|
-
# @raise [
|
363
|
+
# @raise [RpathExistsError] if the runtime path already exists
|
403
364
|
# @note `_options` is currently unused and is provided for signature
|
404
365
|
# compatibility with {MachO::FatFile#add_rpath}
|
405
366
|
def add_rpath(path, _options = {})
|
@@ -417,7 +378,7 @@ module MachO
|
|
417
378
|
# @param path [String] the runtime path to delete
|
418
379
|
# @param _options [Hash]
|
419
380
|
# @return void
|
420
|
-
# @raise [
|
381
|
+
# @raise [RpathUnknownError] if no such runtime path exists
|
421
382
|
# @note `_options` is currently unused and is provided for signature
|
422
383
|
# compatibility with {MachO::FatFile#delete_rpath}
|
423
384
|
def delete_rpath(path, _options = {})
|
@@ -431,15 +392,6 @@ module MachO
|
|
431
392
|
populate_fields
|
432
393
|
end
|
433
394
|
|
434
|
-
# All sections of the segment `segment`.
|
435
|
-
# @param segment [MachO::LoadCommands::SegmentCommand, MachO::LoadCommands::SegmentCommand64] the segment being inspected
|
436
|
-
# @return [Array<MachO::Sections::Section>] if the Mach-O is 32-bit
|
437
|
-
# @return [Array<MachO::Sections::Section64>] if the Mach-O is 64-bit
|
438
|
-
# @deprecated use {MachO::LoadCommands::SegmentCommand#sections} instead
|
439
|
-
def sections(segment)
|
440
|
-
segment.sections
|
441
|
-
end
|
442
|
-
|
443
395
|
# Write all Mach-O data to the given filename.
|
444
396
|
# @param filename [String] the file to write to
|
445
397
|
# @return [void]
|
@@ -449,7 +401,7 @@ module MachO
|
|
449
401
|
|
450
402
|
# Write all Mach-O data to the file used to initialize the instance.
|
451
403
|
# @return [void]
|
452
|
-
# @raise [
|
404
|
+
# @raise [MachOError] if the instance was initialized without a file
|
453
405
|
# @note Overwrites all data in the file!
|
454
406
|
def write!
|
455
407
|
if @filename.nil?
|
@@ -462,9 +414,9 @@ module MachO
|
|
462
414
|
private
|
463
415
|
|
464
416
|
# The file's Mach-O header structure.
|
465
|
-
# @return [
|
466
|
-
# @return [
|
467
|
-
# @raise [
|
417
|
+
# @return [Headers::MachHeader] if the Mach-O is 32-bit
|
418
|
+
# @return [Headers::MachHeader64] if the Mach-O is 64-bit
|
419
|
+
# @raise [TruncatedFileError] if the file is too small to have a valid header
|
468
420
|
# @api private
|
469
421
|
def populate_mach_header
|
470
422
|
# the smallest Mach-O header is 28 bytes
|
@@ -483,8 +435,8 @@ module MachO
|
|
483
435
|
|
484
436
|
# Read just the file's magic number and check its validity.
|
485
437
|
# @return [Fixnum] the magic
|
486
|
-
# @raise [
|
487
|
-
# @raise [
|
438
|
+
# @raise [MagicError] if the magic is not valid Mach-O magic
|
439
|
+
# @raise [FatBinaryError] if the magic is for a Fat file
|
488
440
|
# @api private
|
489
441
|
def populate_and_check_magic
|
490
442
|
magic = @raw_data[0..3].unpack("N").first
|
@@ -499,7 +451,7 @@ module MachO
|
|
499
451
|
|
500
452
|
# Check the file's CPU type.
|
501
453
|
# @param cputype [Fixnum] the CPU type
|
502
|
-
# @raise [
|
454
|
+
# @raise [CPUTypeError] if the CPU type is unknown
|
503
455
|
# @api private
|
504
456
|
def check_cputype(cputype)
|
505
457
|
raise CPUTypeError, cputype unless Headers::CPU_TYPES.key?(cputype)
|
@@ -507,7 +459,7 @@ module MachO
|
|
507
459
|
|
508
460
|
# Check the file's CPU type/subtype pair.
|
509
461
|
# @param cpusubtype [Fixnum] the CPU subtype
|
510
|
-
# @raise [
|
462
|
+
# @raise [CPUSubtypeError] if the CPU sub-type is unknown
|
511
463
|
# @api private
|
512
464
|
def check_cpusubtype(cputype, cpusubtype)
|
513
465
|
# Only check sub-type w/o capability bits (see `populate_mach_header`).
|
@@ -516,15 +468,15 @@ module MachO
|
|
516
468
|
|
517
469
|
# Check the file's type.
|
518
470
|
# @param filetype [Fixnum] the file type
|
519
|
-
# @raise [
|
471
|
+
# @raise [FiletypeError] if the file type is unknown
|
520
472
|
# @api private
|
521
473
|
def check_filetype(filetype)
|
522
474
|
raise FiletypeError, filetype unless Headers::MH_FILETYPES.key?(filetype)
|
523
475
|
end
|
524
476
|
|
525
477
|
# All load commands in the file.
|
526
|
-
# @return [Array<
|
527
|
-
# @raise [
|
478
|
+
# @return [Array<LoadCommands::LoadCommand>] an array of load commands
|
479
|
+
# @raise [LoadCommandError] if an unknown load command is encountered
|
528
480
|
# @api private
|
529
481
|
def populate_load_commands
|
530
482
|
offset = header.class.bytesize
|
@@ -539,7 +491,7 @@ module MachO
|
|
539
491
|
|
540
492
|
# why do I do this? i don't like declaring constants below
|
541
493
|
# classes, and i need them to resolve...
|
542
|
-
klass =
|
494
|
+
klass = LoadCommands.const_get LoadCommands::LC_STRUCTURES[cmd_sym]
|
543
495
|
view = MachOView.new(@raw_data, endianness, offset)
|
544
496
|
command = klass.new_from_bin(view)
|
545
497
|
|