ruby-macho 0.2.2 → 0.2.3

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.
@@ -1,1001 +1,1011 @@
1
1
  module MachO
2
- # load commands added after OS X 10.1 need to be bitwise ORed with
3
- # LC_REQ_DYLD to be recognized by the dynamic linder (dyld)
4
- LC_REQ_DYLD = 0x80000000
5
-
6
- # association of load commands to symbol representations
7
- # @api private
8
- LOAD_COMMANDS = {
9
- 0x1 => :LC_SEGMENT,
10
- 0x2 => :LC_SYMTAB,
11
- 0x3 => :LC_SYMSEG,
12
- 0x4 => :LC_THREAD,
13
- 0x5 => :LC_UNIXTHREAD,
14
- 0x6 => :LC_LOADFVMLIB,
15
- 0x7 => :LC_IDFVMLIB,
16
- 0x8 => :LC_IDENT,
17
- 0x9 => :LC_FVMFILE,
18
- 0xa => :LC_PREPAGE,
19
- 0xb => :LC_DYSYMTAB,
20
- 0xc => :LC_LOAD_DYLIB,
21
- 0xd => :LC_ID_DYLIB,
22
- 0xe => :LC_LOAD_DYLINKER,
23
- 0xf => :LC_ID_DYLINKER,
24
- 0x10 => :LC_PREBOUND_DYLIB,
25
- 0x11 => :LC_ROUTINES,
26
- 0x12 => :LC_SUB_FRAMEWORK,
27
- 0x13 => :LC_SUB_UMBRELLA,
28
- 0x14 => :LC_SUB_CLIENT,
29
- 0x15 => :LC_SUB_LIBRARY,
30
- 0x16 => :LC_TWOLEVEL_HINTS,
31
- 0x17 => :LC_PREBIND_CKSUM,
32
- (0x18 | LC_REQ_DYLD) => :LC_LOAD_WEAK_DYLIB,
33
- 0x19 => :LC_SEGMENT_64,
34
- 0x1a => :LC_ROUTINES_64,
35
- 0x1b => :LC_UUID,
36
- (0x1c | LC_REQ_DYLD) => :LC_RPATH,
37
- 0x1d => :LC_CODE_SIGNATURE,
38
- 0x1e => :LC_SEGMENT_SPLIT_INFO,
39
- (0x1f | LC_REQ_DYLD) => :LC_REEXPORT_DYLIB,
40
- 0x20 => :LC_LAZY_LOAD_DYLIB,
41
- 0x21 => :LC_ENCRYPTION_INFO,
42
- 0x22 => :LC_DYLD_INFO,
43
- (0x22 | LC_REQ_DYLD) => :LC_DYLD_INFO_ONLY,
44
- (0x23 | LC_REQ_DYLD) => :LC_LOAD_UPWARD_DYLIB,
45
- 0x24 => :LC_VERSION_MIN_MACOSX,
46
- 0x25 => :LC_VERSION_MIN_IPHONEOS,
47
- 0x26 => :LC_FUNCTION_STARTS,
48
- 0x27 => :LC_DYLD_ENVIRONMENT,
49
- (0x28 | LC_REQ_DYLD) => :LC_MAIN,
50
- 0x29 => :LC_DATA_IN_CODE,
51
- 0x2a => :LC_SOURCE_VERSION,
52
- 0x2b => :LC_DYLIB_CODE_SIGN_DRS,
53
- 0x2c => :LC_ENCRYPTION_INFO_64,
54
- 0x2d => :LC_LINKER_OPTION,
55
- 0x2e => :LC_LINKER_OPTIMIZATION_HINT
56
- }
57
-
58
- # load commands responsible for loading dylibs
59
- # @api private
60
- DYLIB_LOAD_COMMANDS = [
61
- :LC_LOAD_DYLIB,
62
- :LC_LOAD_WEAK_DYLIB,
63
- :LC_REEXPORT_DYLIB,
64
- :LC_LAZY_LOAD_DYLIB,
65
- :LC_LOAD_UPWARD_DYLIB,
66
- ].freeze
67
-
68
- # association of load command symbols to string representations of classes
69
- # @api private
70
- LC_STRUCTURES = {
71
- :LC_SEGMENT => "SegmentCommand",
72
- :LC_SYMTAB => "SymtabCommand",
73
- :LC_SYMSEG => "LoadCommand", # obsolete
74
- :LC_THREAD => "ThreadCommand",
75
- :LC_UNIXTHREAD => "ThreadCommand",
76
- :LC_LOADFVMLIB => "LoadCommand", # obsolete
77
- :LC_IDFVMLIB => "LoadCommand", # obsolete
78
- :LC_IDENT => "LoadCommand", # obsolete
79
- :LC_FVMFILE => "LoadCommand", # reserved for internal use only
80
- :LC_PREPAGE => "LoadCommand", # reserved for internal use only
81
- :LC_DYSYMTAB => "DysymtabCommand",
82
- :LC_LOAD_DYLIB => "DylibCommand",
83
- :LC_ID_DYLIB => "DylibCommand",
84
- :LC_LOAD_DYLINKER => "DylinkerCommand",
85
- :LC_ID_DYLINKER => "DylinkerCommand",
86
- :LC_PREBOUND_DYLIB => "PreboundDylibCommand",
87
- :LC_ROUTINES => "RoutinesCommand",
88
- :LC_SUB_FRAMEWORK => "SubFrameworkCommand",
89
- :LC_SUB_UMBRELLA => "SubUmbrellaCommand",
90
- :LC_SUB_CLIENT => "SubClientCommand",
91
- :LC_SUB_LIBRARY => "SubLibraryCommand",
92
- :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand",
93
- :LC_PREBIND_CKSUM => "PrebindCksumCommand",
94
- :LC_LOAD_WEAK_DYLIB => "DylibCommand",
95
- :LC_SEGMENT_64 => "SegmentCommand64",
96
- :LC_ROUTINES_64 => "RoutinesCommand64",
97
- :LC_UUID => "UUIDCommand",
98
- :LC_RPATH => "RpathCommand",
99
- :LC_CODE_SIGNATURE => "LinkeditDataCommand",
100
- :LC_SEGMENT_SPLIT_INFO => "LinkeditDataCommand",
101
- :LC_REEXPORT_DYLIB => "DylibCommand",
102
- :LC_LAZY_LOAD_DYLIB => "DylibCommand",
103
- :LC_ENCRYPTION_INFO => "EncryptionInfoCommand",
104
- :LC_DYLD_INFO => "DyldInfoCommand",
105
- :LC_DYLD_INFO_ONLY => "DyldInfoCommand",
106
- :LC_LOAD_UPWARD_DYLIB => "DylibCommand",
107
- :LC_VERSION_MIN_MACOSX => "VersionMinCommand",
108
- :LC_VERSION_MIN_IPHONEOS => "VersionMinCommand",
109
- :LC_FUNCTION_STARTS => "LinkeditDataCommand",
110
- :LC_DYLD_ENVIRONMENT => "DylinkerCommand",
111
- :LC_MAIN => "EntryPointCommand",
112
- :LC_DATA_IN_CODE => "LinkeditDataCommand",
113
- :LC_SOURCE_VERSION => "SourceVersionCommand",
114
- :LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand",
115
- :LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64",
116
- :LC_LINKER_OPTION => "LinkerOptionCommand",
117
- :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand"
118
- }
119
-
120
- # association of segment name symbols to names
121
- # @api private
122
- SEGMENT_NAMES = {
123
- :SEG_PAGEZERO => "__PAGEZERO",
124
- :SEG_TEXT => "__TEXT",
125
- :SEG_DATA => "__DATA",
126
- :SEG_OBJC => "__OBJC",
127
- :SEG_ICON => "__ICON",
128
- :SEG_LINKEDIT => "__LINKEDIT",
129
- :SEG_UNIXSTACK => "__UNIXSTACK",
130
- :SEG_IMPORT => "__IMPORT"
131
- }
132
-
133
- # association of segment flag symbols to values
134
- # @api private
135
- SEGMENT_FLAGS = {
136
- :SG_HIGHVM => 0x1,
137
- :SG_FVMLIB => 0x2,
138
- :SG_NORELOC => 0x4,
139
- :SG_PROTECTED_VERSION_1 => 0x8
140
- }
141
-
142
- # Mach-O load command structure
143
- # This is the most generic load command - only cmd ID and size are
144
- # represented, and no actual data. Used when a more specific class
145
- # isn't available/implemented.
146
- class LoadCommand < MachOStructure
147
- # @return [Fixnum] the offset in the file the command was created from
148
- attr_reader :offset
149
-
150
- # @return [Fixnum] the load command's identifying number
151
- attr_reader :cmd
152
-
153
- # @return [Fixnum] the size of the load command, in bytes
154
- attr_reader :cmdsize
155
-
156
- FORMAT = "VV"
157
- SIZEOF = 8
158
-
159
- # Creates a new LoadCommand given an offset and binary string
160
- # @param offset [Fixnum] the offset to initialize with
161
- # @param bin [String] the binary string to initialize with
162
- # @return [MachO::LoadCommand] the new load command
163
- # @api private
164
- def self.new_from_bin(raw_data, offset, bin)
165
- self.new(raw_data, offset, *bin.unpack(self::FORMAT))
166
- end
167
-
168
- # @param offset [Fixnum] the offset to initialize with
169
- # @param cmd [Fixnum] the load command's identifying number
170
- # @param cmdsize [Fixnum] the size of the load command in bytes
171
- # @api private
172
- def initialize(raw_data, offset, cmd, cmdsize)
173
- @raw_data = raw_data
174
- @offset = offset
175
- @cmd = cmd
176
- @cmdsize = cmdsize
177
- end
178
-
179
- # @return [Symbol] a symbol representation of the load command's identifying number
180
- def type
181
- LOAD_COMMANDS[cmd]
182
- end
183
-
184
- alias :to_sym :type
185
-
186
- # @return [String] a string representation of the load command's identifying number
187
- def to_s
188
- type.to_s
189
- end
190
-
191
- # Represents a Load Command string. A rough analogue to the lc_str
192
- # struct used internally by OS X. This class allows ruby-macho to
193
- # pretend that strings stored in LCs are immediately available without
194
- # explicit operations on the raw Mach-O data.
195
- class LCStr
196
- # @param raw_data [String] the raw Mach-O data.
197
- # @param lc [MachO::LoadCommand] the load command
198
- # @param lc_str [Fixnum] the offset to the beginning of the string
199
- # @api private
200
- def initialize(raw_data, lc, lc_str)
201
- @raw_data = raw_data
202
- @lc = lc
203
- @lc_str = lc_str
204
- @str = @raw_data.slice(@lc.offset + @lc_str...@lc.offset + @lc.cmdsize).delete("\x00")
205
- end
206
-
207
- # @return [String] a string representation of the LCStr
208
- def to_s
209
- @str
210
- end
211
-
212
- # @return [Fixnum] the offset to the beginning of the string in the load command
213
- def to_i
214
- @lc_str
215
- end
216
- end
217
- end
218
-
219
- # A load command containing a single 128-bit unique random number identifying
220
- # an object produced by static link editor. Corresponds to LC_UUID.
221
- class UUIDCommand < LoadCommand
222
- # @return [Array<Fixnum>] the UUID
223
- attr_reader :uuid
224
-
225
- FORMAT = "VVa16"
226
- SIZEOF = 24
227
-
228
- # @api private
229
- def initialize(raw_data, offset, cmd, cmdsize, uuid)
230
- super(raw_data, offset, cmd, cmdsize)
231
- @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array
232
- end
233
-
234
- # @return [String] a string representation of the UUID
235
- def uuid_string
236
- hexes = uuid.map { |e| "%02x" % e }
237
- segs = [
238
- hexes[0..3].join, hexes[4..5].join, hexes[6..7].join,
239
- hexes[8..9].join, hexes[10..15].join
240
- ]
241
-
242
- segs.join("-")
243
- end
244
- end
245
-
246
- # A load command indicating that part of this file is to be mapped into
247
- # the task's address space. Corresponds to LC_SEGMENT.
248
- class SegmentCommand < LoadCommand
249
- # @return [String] the name of the segment
250
- attr_reader :segname
251
-
252
- # @return [Fixnum] the memory address of the segment
253
- attr_reader :vmaddr
254
-
255
- # @return [Fixnum] the memory size of the segment
256
- attr_reader :vmsize
257
-
258
- # @return [Fixnum] the file offset of the segment
259
- attr_reader :fileoff
260
-
261
- # @return [Fixnum] the amount to map from the file
262
- attr_reader :filesize
263
-
264
- # @return [Fixnum] the maximum VM protection
265
- attr_reader :maxprot
266
-
267
- # @return [Fixnum] the initial VM protection
268
- attr_reader :initprot
269
-
270
- # @return [Fixnum] the number of sections in the segment
271
- attr_reader :nsects
272
-
273
- # @return [Fixnum] any flags associated with the segment
274
- attr_reader :flags
275
-
276
- FORMAT = "VVa16VVVVVVVV"
277
- SIZEOF = 56
278
-
279
- # @api private
280
- def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
281
- filesize, maxprot, initprot, nsects, flags)
282
- super(raw_data, offset, cmd, cmdsize)
283
- @segname = segname.delete("\x00")
284
- @vmaddr = vmaddr
285
- @vmsize = vmsize
286
- @fileoff = fileoff
287
- @filesize = filesize
288
- @maxprot = maxprot
289
- @initprot = initprot
290
- @nsects = nsects
291
- @flags = flags
292
- end
293
-
294
- # @example
295
- # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC)
296
- # @param flag [Symbol] a segment flag symbol
297
- # @return [Boolean] true if `flag` is present in the segment's flag field
298
- def flag?(flag)
299
- flag = SEGMENT_FLAGS[flag]
300
- return false if flag.nil?
301
- flags & flag == flag
302
- end
303
- end
304
-
305
- # A load command indicating that part of this file is to be mapped into
306
- # the task's address space. Corresponds to LC_SEGMENT_64.
307
- class SegmentCommand64 < LoadCommand
308
- # @return [String] the name of the segment
309
- attr_reader :segname
310
-
311
- # @return [Fixnum] the memory address of the segment
312
- attr_reader :vmaddr
313
-
314
- # @return [Fixnum] the memory size of the segment
315
- attr_reader :vmsize
316
-
317
- # @return [Fixnum] the file offset of the segment
318
- attr_reader :fileoff
319
-
320
- # @return [Fixnum] the amount to map from the file
321
- attr_reader :filesize
322
-
323
- # @return [Fixnum] the maximum VM protection
324
- attr_reader :maxprot
325
-
326
- # @return [Fixnum] the initial VM protection
327
- attr_reader :initprot
328
-
329
- # @return [Fixnum] the number of sections in the segment
330
- attr_reader :nsects
331
-
332
- # @return [Fixnum] any flags associated with the segment
333
- attr_reader :flags
334
-
335
- FORMAT = "VVa16QQQQVVVV"
336
- SIZEOF = 72
337
-
338
- # @api private
339
- def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
340
- filesize, maxprot, initprot, nsects, flags)
341
- super(raw_data, offset, cmd, cmdsize)
342
- @segname = segname.delete("\x00")
343
- @vmaddr = vmaddr
344
- @vmsize = vmsize
345
- @fileoff = fileoff
346
- @filesize = filesize
347
- @maxprot = maxprot
348
- @initprot = initprot
349
- @nsects = nsects
350
- @flags = flags
351
- end
352
-
353
- # @example
354
- # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC)
355
- # @param flag [Symbol] a segment flag symbol
356
- # @return [Boolean] true if `flag` is present in the segment's flag field
357
- def flag?(flag)
358
- flag = SEGMENT_FLAGS[flag]
359
- return false if flag.nil?
360
- flags & flag == flag
361
- end
362
- end
363
-
364
- # A load command representing some aspect of shared libraries, depending
365
- # on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB,
366
- # and LC_REEXPORT_DYLIB.
367
- class DylibCommand < LoadCommand
368
- # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr
369
- attr_reader :name
370
-
371
- # @return [Fixnum] the library's build time stamp
372
- attr_reader :timestamp
373
-
374
- # @return [Fixnum] the library's current version number
375
- attr_reader :current_version
376
-
377
- # @return [Fixnum] the library's compatibility version number
378
- attr_reader :compatibility_version
379
-
380
- FORMAT = "VVVVVV"
381
- SIZEOF = 24
382
-
383
- # @api private
384
- def initialize(raw_data, offset, cmd, cmdsize, name, timestamp, current_version,
385
- compatibility_version)
386
- super(raw_data, offset, cmd, cmdsize)
387
- @name = LCStr.new(raw_data, self, name)
388
- @timestamp = timestamp
389
- @current_version = current_version
390
- @compatibility_version = compatibility_version
391
- end
392
- end
393
-
394
- # A load command representing some aspect of the dynamic linker, depending
395
- # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and
396
- # LC_DYLD_ENVIRONMENT.
397
- class DylinkerCommand < LoadCommand
398
- # @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr
399
- attr_reader :name
400
-
401
- FORMAT = "VVV"
402
- SIZEOF = 12
403
-
404
- # @api private
405
- def initialize(raw_data, offset, cmd, cmdsize, name)
406
- super(raw_data, offset, cmd, cmdsize)
407
- @name = LCStr.new(raw_data, self, name)
408
- end
409
- end
410
-
411
- # A load command used to indicate dynamic libraries used in prebinding.
412
- # Corresponds to LC_PREBOUND_DYLIB.
413
- class PreboundDylibCommand < LoadCommand
414
- # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr
415
- attr_reader :name
416
-
417
- # @return [Fixnum] the number of modules in the library
418
- attr_reader :nmodules
419
-
420
- # @return [Fixnum] a bit vector of linked modules
421
- attr_reader :linked_modules
422
-
423
- FORMAT = "VVVVV"
424
- SIZEOF = 20
425
-
426
- # @api private
427
- def initialize(raw_data, offset, cmd, cmdsize, name, nmodules, linked_modules)
428
- super(raw_data, offset, cmd, cmdsize)
429
- @name = LCStr.new(raw_data, self, name)
430
- @nmodules = nmodules
431
- @linked_modules = linked_modules
432
- end
433
- end
434
-
435
- # A load command used to represent threads.
436
- # @note cctools-870 has all fields of thread_command commented out except common ones (cmd, cmdsize)
437
- class ThreadCommand < LoadCommand
438
-
439
- end
440
-
441
- # A load command containing the address of the dynamic shared library
442
- # initialization routine and an index into the module table for the module
443
- # that defines the routine. Corresponds to LC_ROUTINES.
444
- class RoutinesCommand < LoadCommand
445
- # @return [Fixnum] the address of the initialization routine
446
- attr_reader :init_address
447
-
448
- # @return [Fixnum] the index into the module table that the init routine is defined in
449
- attr_reader :init_module
450
-
451
- # @return [void]
452
- attr_reader :reserved1
453
-
454
- # @return [void]
455
- attr_reader :reserved2
456
-
457
- # @return [void]
458
- attr_reader :reserved3
459
-
460
- # @return [void]
461
- attr_reader :reserved4
462
-
463
- # @return [void]
464
- attr_reader :reserved5
465
-
466
- # @return [void]
467
- attr_reader :reserved6
468
-
469
- FORMAT = "VVVVVVVVVV"
470
- SIZEOF = 40
471
-
472
- # @api private
473
- def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
474
- reserved1, reserved2, reserved3, reserved4, reserved5,
475
- reserved6)
476
- super(raw_data, offset, cmd, cmdsize)
477
- @init_address = init_address
478
- @init_module = init_module
479
- @reserved1 = reserved1
480
- @reserved2 = reserved2
481
- @reserved3 = reserved3
482
- @reserved4 = reserved4
483
- @reserved5 = reserved5
484
- @reserved6 = reserved6
485
- end
486
- end
487
-
488
- # A load command containing the address of the dynamic shared library
489
- # initialization routine and an index into the module table for the module
490
- # that defines the routine. Corresponds to LC_ROUTINES_64.
491
- class RoutinesCommand64 < LoadCommand
492
- # @return [Fixnum] the address of the initialization routine
493
- attr_reader :init_address
494
-
495
- # @return [Fixnum] the index into the module table that the init routine is defined in
496
- attr_reader :init_module
497
-
498
- # @return [void]
499
- attr_reader :reserved1
500
-
501
- # @return [void]
502
- attr_reader :reserved2
503
-
504
- # @return [void]
505
- attr_reader :reserved3
506
-
507
- # @return [void]
508
- attr_reader :reserved4
509
-
510
- # @return [void]
511
- attr_reader :reserved5
512
-
513
- # @return [void]
514
- attr_reader :reserved6
515
-
516
- FORMAT = "VVQQQQQQQQ"
517
- SIZEOF = 72
518
-
519
- # @api private
520
- def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
521
- reserved1, reserved2, reserved3, reserved4, reserved5,
522
- reserved6)
523
- super(raw_data, offset, cmd, cmdsize)
524
- @init_address = init_address
525
- @init_module = init_module
526
- @reserved1 = reserved1
527
- @reserved2 = reserved2
528
- @reserved3 = reserved3
529
- @reserved4 = reserved4
530
- @reserved5 = reserved5
531
- @reserved6 = reserved6
532
- end
533
- end
534
-
535
- # A load command signifying membership of a subframework containing the name
536
- # of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK.
537
- class SubFrameworkCommand < LoadCommand
538
- # @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr
539
- attr_reader :umbrella
540
-
541
- FORMAT = "VVV"
542
- SIZEOF = 12
543
-
544
- # @api private
545
- def initialize(raw_data, offset, cmd, cmdsize, umbrella)
546
- super(raw_data, offset, cmd, cmdsize)
547
- @umbrella = LCStr.new(raw_data, self, umbrella)
548
- end
549
- end
550
-
551
- # A load command signifying membership of a subumbrella containing the name
552
- # of an umbrella framework. Corresponds to LC_SUB_UMBRELLA.
553
- class SubUmbrellaCommand < LoadCommand
554
- # @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr
555
- attr_reader :sub_umbrella
556
-
557
- FORMAT = "VVV"
558
- SIZEOF = 12
559
-
560
- # @api private
561
- def initialize(raw_data, offset, cmd, cmdsize, sub_umbrella)
562
- super(raw_data, offset, cmd, cmdsize)
563
- @sub_umbrella = LCStr.new(raw_data, self, sub_umbrella)
564
- end
565
- end
566
-
567
- # A load command signifying a sublibrary of a shared library. Corresponds
568
- # to LC_SUB_LIBRARY.
569
- class SubLibraryCommand < LoadCommand
570
- # @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr
571
- attr_reader :sub_library
572
-
573
- FORMAT = "VVV"
574
- SIZEOF = 12
575
-
576
- # @api private
577
- def initialize(raw_data, offset, cmd, cmdsize, sub_library)
578
- super(raw_data, offset, cmd, cmdsize)
579
- @sub_library = LCStr.new(raw_data, self, sub_library)
580
- end
581
- end
582
-
583
- # A load command signifying a shared library that is a subframework of
584
- # an umbrella framework. Corresponds to LC_SUB_CLIENT.
585
- class SubClientCommand < LoadCommand
586
- # @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr
587
- attr_reader :sub_client
588
-
589
- FORMAT = "VVV"
590
- SIZEOF = 12
591
-
592
- # @api private
593
- def initialize(raw_data, offset, cmd, cmdsize, sub_client)
594
- super(raw_data, offset, cmd, cmdsize)
595
- @sub_client = LCStr.new(raw_data, self, sub_client)
596
- end
597
- end
598
-
599
- # A load command containing the offsets and sizes of the link-edit 4.3BSD
600
- # "stab" style symbol table information. Corresponds to LC_SYMTAB.
601
- class SymtabCommand < LoadCommand
602
- # @return [Fixnum] the symbol table's offset
603
- attr_reader :symoff
604
-
605
- # @return [Fixnum] the number of symbol table entries
606
- attr_reader :nsyms
607
-
608
- # @return the string table's offset
609
- attr_reader :stroff
610
-
611
- # @return the string table size in bytes
612
- attr_reader :strsize
613
-
614
- FORMAT = "VVVVVV"
615
- SIZEOF = 24
616
-
617
- # @api private
618
- def initialize(raw_data, offset, cmd, cmdsize, symoff, nsyms, stroff, strsize)
619
- super(raw_data, offset, cmd, cmdsize)
620
- @symoff = symoff
621
- @nsyms = nsyms
622
- @stroff = stroff
623
- @strsize = strsize
624
- end
625
- end
626
-
627
- # A load command containing symbolic information needed to support data
628
- # structures used by the dynamic link editor. Corresponds to LC_DYSYMTAB.
629
- class DysymtabCommand < LoadCommand
630
- # @return [Fixnum] the index to local symbols
631
- attr_reader :ilocalsym
632
-
633
- # @return [Fixnum] the number of local symbols
634
- attr_reader :nlocalsym
635
-
636
- # @return [Fixnum] the index to externally defined symbols
637
- attr_reader :iextdefsym
638
-
639
- # @return [Fixnum] the number of externally defined symbols
640
- attr_reader :nextdefsym
641
-
642
- # @return [Fixnum] the index to undefined symbols
643
- attr_reader :iundefsym
644
-
645
- # @return [Fixnum] the number of undefined symbols
646
- attr_reader :nundefsym
647
-
648
- # @return [Fixnum] the file offset to the table of contents
649
- attr_reader :tocoff
650
-
651
- # @return [Fixnum] the number of entries in the table of contents
652
- attr_reader :ntoc
653
-
654
- # @return [Fixnum] the file offset to the module table
655
- attr_reader :modtaboff
656
-
657
- # @return [Fixnum] the number of entries in the module table
658
- attr_reader :nmodtab
659
-
660
- # @return [Fixnum] the file offset to the referenced symbol table
661
- attr_reader :extrefsymoff
662
-
663
- # @return [Fixnum] the number of entries in the referenced symbol table
664
- attr_reader :nextrefsyms
665
-
666
- # @return [Fixnum] the file offset to the indirect symbol table
667
- attr_reader :indirectsymoff
668
-
669
- # @return [Fixnum] the number of entries in the indirect symbol table
670
- attr_reader :nindirectsyms
671
-
672
- # @return [Fixnum] the file offset to the external relocation entries
673
- attr_reader :extreloff
674
-
675
- # @return [Fixnum] the number of external relocation entries
676
- attr_reader :nextrel
677
-
678
- # @return [Fixnum] the file offset to the local relocation entries
679
- attr_reader :locreloff
680
-
681
- # @return [Fixnum] the number of local relocation entries
682
- attr_reader :nlocrel
683
-
684
-
685
- FORMAT = "VVVVVVVVVVVVVVVVVVVV"
686
- SIZEOF = 80
687
-
688
- # ugh
689
- # @api private
690
- def initialize(raw_data, offset, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym,
691
- nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff,
692
- nmodtab, extrefsymoff, nextrefsyms, indirectsymoff,
693
- nindirectsyms, extreloff, nextrel, locreloff, nlocrel)
694
- super(raw_data, offset, cmd, cmdsize)
695
- @ilocalsym = ilocalsym
696
- @nlocalsym = nlocalsym
697
- @iextdefsym = iextdefsym
698
- @nextdefsym = nextdefsym
699
- @iundefsym = iundefsym
700
- @nundefsym = nundefsym
701
- @tocoff = tocoff
702
- @ntoc = ntoc
703
- @modtaboff = modtaboff
704
- @nmodtab = nmodtab
705
- @extrefsymoff = extrefsymoff
706
- @nextrefsyms = nextrefsyms
707
- @indirectsymoff = indirectsymoff
708
- @nindirectsyms = nindirectsyms
709
- @extreloff = extreloff
710
- @nextrel = nextrel
711
- @locreloff = locreloff
712
- @nlocrel = nlocrel
713
- end
714
- end
715
-
716
- # A load command containing the offset and number of hints in the two-level
717
- # namespace lookup hints table. Corresponds to LC_TWOLEVEL_HINTS.
718
- class TwolevelHintsCommand < LoadCommand
719
- # @return [Fixnum] the offset to the hint table
720
- attr_reader :htoffset
721
-
722
- # @return [Fixnum] the number of hints in the hint table
723
- attr_reader :nhints
724
-
725
- FORMAT = "VVVV"
726
- SIZEOF = 16
727
-
728
- # @api private
729
- def initialize(raw_data, offset, cmd, cmdsize, htoffset, nhints)
730
- super(raw_data, offset, cmd, cmdsize)
731
- @htoffset = htoffset
732
- @nhints = nhints
733
- end
734
- end
735
-
736
- # A load command containing the value of the original checksum for prebound
737
- # files, or zero. Corresponds to LC_PREBIND_CKSUM.
738
- class PrebindCksumCommand < LoadCommand
739
- # @return [Fixnum] the checksum or 0
740
- attr_reader :cksum
741
-
742
- FORMAT = "VVV"
743
- SIZEOF = 12
744
-
745
- # @api private
746
- def initialize(raw_data, offset, cmd, cmdsize, cksum)
747
- super(raw_data, offset, cmd, cmdsize)
748
- @cksum = cksum
749
- end
750
- end
751
-
752
- # A load command representing an rpath, which specifies a path that should
753
- # be added to the current run path used to find @rpath prefixed dylibs.
754
- # Corresponds to LC_RPATH.
755
- class RpathCommand < LoadCommand
756
- # @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr
757
- attr_reader :path
758
-
759
- FORMAT = "VVV"
760
- SIZEOF = 12
761
-
762
- # @api private
763
- def initialize(raw_data, offset, cmd, cmdsize, path)
764
- super(raw_data, offset, cmd, cmdsize)
765
- @path = LCStr.new(raw_data, self, path)
766
- end
767
- end
768
-
769
- # A load command representing the offsets and sizes of a blob of data in
770
- # the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO,
771
- # LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT.
772
- class LinkeditDataCommand < LoadCommand
773
- # @return [Fixnum] offset to the data in the __LINKEDIT segment
774
- attr_reader :dataoff
775
-
776
- # @return [Fixnum] size of the data in the __LINKEDIT segment
777
- attr_reader :datasize
778
-
779
- FORMAT = "VVVV"
780
- SIZEOF = 16
781
-
782
- # @api private
783
- def initialize(raw_data, offset, cmd, cmdsize, dataoff, datasize)
784
- super(raw_data, offset, cmd, cmdsize)
785
- @dataoff = dataoff
786
- @datasize = datasize
787
- end
788
- end
789
-
790
- # A load command representing the offset to and size of an encrypted
791
- # segment. Corresponds to LC_ENCRYPTION_INFO.
792
- class EncryptionInfoCommand < LoadCommand
793
- # @return [Fixnum] the offset to the encrypted segment
794
- attr_reader :cryptoff
795
-
796
- # @return [Fixnum] the size of the encrypted segment
797
- attr_reader :cryptsize
798
-
799
- # @return [Fixnum] the encryption system, or 0 if not encrypted yet
800
- attr_reader :cryptid
801
-
802
- FORMAT = "VVVVV"
803
- SIZEOF = 20
804
-
805
- # @api private
806
- def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid)
807
- super(raw_data, offset, cmd, cmdsize)
808
- @cryptoff = cryptoff
809
- @cryptsize = cryptsize
810
- @cryptid = cryptid
811
- end
812
- end
813
-
814
- # A load command representing the offset to and size of an encrypted
815
- # segment. Corresponds to LC_ENCRYPTION_INFO_64.
816
- class EncryptionInfoCommand64 < LoadCommand
817
- # @return [Fixnum] the offset to the encrypted segment
818
- attr_reader :cryptoff
819
-
820
- # @return [Fixnum] the size of the encrypted segment
821
- attr_reader :cryptsize
822
-
823
- # @return [Fixnum] the encryption system, or 0 if not encrypted yet
824
- attr_reader :cryptid
825
-
826
- # @return [Fixnum] 64-bit padding value
827
- attr_reader :pad
828
-
829
- FORMAT = "VVVVVV"
830
- SIZEOF = 24
831
-
832
- # @api private
833
- def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid)
834
- super(raw_data, offset, cmd, cmdsize)
835
- @cryptoff = cryptoff
836
- @cryptsize = cryptsize
837
- @cryptid = cryptid
838
- @pad = pad
839
- end
840
- end
841
-
842
- # A load command containing the minimum OS version on which the binary
843
- # was built to run. Corresponds to LC_VERSION_MIN_MACOSX and LC_VERSION_MIN_IPHONEOS.
844
- class VersionMinCommand < LoadCommand
845
- # @return [Fixnum] the version X.Y.Z packed as x16.y8.z8
846
- attr_reader :version
847
-
848
- # @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8
849
- attr_reader :sdk
850
-
851
- FORMAT = "VVVV"
852
- SIZEOF = 16
853
-
854
- # @api private
855
- def initialize(raw_data, offset, cmd, cmdsize, version, sdk)
856
- super(raw_data, offset, cmd, cmdsize)
857
- @version = version
858
- @sdk = sdk
859
- end
860
-
861
- # A string representation of the binary's minimum OS version.
862
- # @return [String] a string representing the minimum OS version.
863
- def version_string
864
- binary = "%032b" % version
865
- segs = [
866
- binary[0..15], binary[16..23], binary[24..31]
867
- ].map { |s| s.to_i(2) }
868
-
869
- segs.join(".")
870
- end
871
-
872
- # A string representation of the binary's SDK version.
873
- # @return [String] a string representing the SDK version.
874
- def sdk_string
875
- binary = "%032b" % sdk
876
- segs = [
877
- binary[0..15], binary[16..23], binary[24..31]
878
- ].map { |s| s.to_i(2) }
879
-
880
- segs.join(".")
881
- end
882
- end
883
-
884
- # A load command containing the file offsets and sizes of the new
885
- # compressed form of the information dyld needs to load the image.
886
- # Corresponds to LC_DYLD_INFO and LC_DYLD_INFO_ONLY.
887
- class DyldInfoCommand < LoadCommand
888
- # @return [Fixnum] the file offset to the rebase information
889
- attr_reader :rebase_off
890
-
891
- # @return [Fixnum] the size of the rebase information
892
- attr_reader :rebase_size
893
-
894
- # @return [Fixnum] the file offset to the binding information
895
- attr_reader :bind_off
896
-
897
- # @return [Fixnum] the size of the binding information
898
- attr_reader :bind_size
899
-
900
- # @return [Fixnum] the file offset to the weak binding information
901
- attr_reader :weak_bind_off
902
-
903
- # @return [Fixnum] the size of the weak binding information
904
- attr_reader :weak_bind_size
905
-
906
- # @return [Fixnum] the file offset to the lazy binding information
907
- attr_reader :lazy_bind_off
908
-
909
- # @return [Fixnum] the size of the lazy binding information
910
- attr_reader :lazy_bind_size
911
-
912
- # @return [Fixnum] the file offset to the export information
913
- attr_reader :export_off
914
-
915
- # @return [Fixnum] the size of the export information
916
- attr_reader :export_size
917
-
918
- FORMAT = "VVVVVVVVVVVV"
919
- SIZEOF = 48
920
-
921
- # @api private
922
- def initialize(raw_data, offset, cmd, cmdsize, rebase_off, rebase_size, bind_off,
923
- bind_size, weak_bind_off, weak_bind_size, lazy_bind_off,
924
- lazy_bind_size, export_off, export_size)
925
- super(raw_data, offset, cmd, cmdsize)
926
- @rebase_off = rebase_off
927
- @rebase_size = rebase_size
928
- @bind_off = bind_off
929
- @bind_size = bind_size
930
- @weak_bind_off = weak_bind_off
931
- @weak_bind_size = weak_bind_size
932
- @lazy_bind_off = lazy_bind_off
933
- @lazy_bind_size = lazy_bind_size
934
- @export_off = export_off
935
- @export_size = export_size
936
- end
937
- end
938
-
939
- # A load command containing linker options embedded in object files.
940
- # Corresponds to LC_LINKER_OPTION.
941
- class LinkerOptionCommand < LoadCommand
942
- # @return [Fixnum] the number of strings
943
- attr_reader :count
944
-
945
- FORMAT = "VVV"
946
- SIZEOF = 12
947
-
948
- # @api private
949
- def initialize(raw_data, offset, cmd, cmdsize, count)
950
- super(raw_data, offset, cmd, cmdsize)
951
- @count = count
952
- end
953
- end
954
-
955
- # A load command specifying the offset of main(). Corresponds to LC_MAIN.
956
- class EntryPointCommand < LoadCommand
957
- # @return [Fixnum] the file (__TEXT) offset of main()
958
- attr_reader :entryoff
959
-
960
- # @return [Fixnum] if not 0, the initial stack size.
961
- attr_reader :stacksize
962
-
963
- FORMAT = "VVQQ"
964
- SIZEOF = 24
965
-
966
- # @api private
967
- def initialize(raw_data, offset, cmd, cmdsize, entryoff, stacksize)
968
- super(raw_data, offset, cmd, cmdsize)
969
- @entryoff = entryoff
970
- @stacksize = stacksize
971
- end
972
- end
973
-
974
- # A load command specifying the version of the sources used to build the
975
- # binary. Corresponds to LC_SOURCE_VERSION.
976
- class SourceVersionCommand < LoadCommand
977
- # @return [Fixnum] the version packed as a24.b10.c10.d10.e10
978
- attr_reader :version
979
-
980
- FORMAT = "VVQ"
981
- SIZEOF = 16
982
-
983
- # @api private
984
- def initialize(raw_data, offset, cmd, cmdsize, version)
985
- super(raw_data, offset, cmd, cmdsize)
986
- @version = version
987
- end
988
-
989
- # A string representation of the sources used to build the binary.
990
- # @return [String] a string representation of the version
991
- def version_string
992
- binary = "%064b" % version
993
- segs = [
994
- binary[0..23], binary[24..33], binary[34..43], binary[44..53],
995
- binary[54..63]
996
- ].map { |s| s.to_i(2) }
997
-
998
- segs.join(".")
999
- end
1000
- end
2
+ # load commands added after OS X 10.1 need to be bitwise ORed with
3
+ # LC_REQ_DYLD to be recognized by the dynamic linder (dyld)
4
+ LC_REQ_DYLD = 0x80000000
5
+
6
+ # association of load commands to symbol representations
7
+ # @api private
8
+ LOAD_COMMANDS = {
9
+ 0x1 => :LC_SEGMENT,
10
+ 0x2 => :LC_SYMTAB,
11
+ 0x3 => :LC_SYMSEG,
12
+ 0x4 => :LC_THREAD,
13
+ 0x5 => :LC_UNIXTHREAD,
14
+ 0x6 => :LC_LOADFVMLIB,
15
+ 0x7 => :LC_IDFVMLIB,
16
+ 0x8 => :LC_IDENT,
17
+ 0x9 => :LC_FVMFILE,
18
+ 0xa => :LC_PREPAGE,
19
+ 0xb => :LC_DYSYMTAB,
20
+ 0xc => :LC_LOAD_DYLIB,
21
+ 0xd => :LC_ID_DYLIB,
22
+ 0xe => :LC_LOAD_DYLINKER,
23
+ 0xf => :LC_ID_DYLINKER,
24
+ 0x10 => :LC_PREBOUND_DYLIB,
25
+ 0x11 => :LC_ROUTINES,
26
+ 0x12 => :LC_SUB_FRAMEWORK,
27
+ 0x13 => :LC_SUB_UMBRELLA,
28
+ 0x14 => :LC_SUB_CLIENT,
29
+ 0x15 => :LC_SUB_LIBRARY,
30
+ 0x16 => :LC_TWOLEVEL_HINTS,
31
+ 0x17 => :LC_PREBIND_CKSUM,
32
+ (0x18 | LC_REQ_DYLD) => :LC_LOAD_WEAK_DYLIB,
33
+ 0x19 => :LC_SEGMENT_64,
34
+ 0x1a => :LC_ROUTINES_64,
35
+ 0x1b => :LC_UUID,
36
+ (0x1c | LC_REQ_DYLD) => :LC_RPATH,
37
+ 0x1d => :LC_CODE_SIGNATURE,
38
+ 0x1e => :LC_SEGMENT_SPLIT_INFO,
39
+ (0x1f | LC_REQ_DYLD) => :LC_REEXPORT_DYLIB,
40
+ 0x20 => :LC_LAZY_LOAD_DYLIB,
41
+ 0x21 => :LC_ENCRYPTION_INFO,
42
+ 0x22 => :LC_DYLD_INFO,
43
+ (0x22 | LC_REQ_DYLD) => :LC_DYLD_INFO_ONLY,
44
+ (0x23 | LC_REQ_DYLD) => :LC_LOAD_UPWARD_DYLIB,
45
+ 0x24 => :LC_VERSION_MIN_MACOSX,
46
+ 0x25 => :LC_VERSION_MIN_IPHONEOS,
47
+ 0x26 => :LC_FUNCTION_STARTS,
48
+ 0x27 => :LC_DYLD_ENVIRONMENT,
49
+ (0x28 | LC_REQ_DYLD) => :LC_MAIN,
50
+ 0x29 => :LC_DATA_IN_CODE,
51
+ 0x2a => :LC_SOURCE_VERSION,
52
+ 0x2b => :LC_DYLIB_CODE_SIGN_DRS,
53
+ 0x2c => :LC_ENCRYPTION_INFO_64,
54
+ 0x2d => :LC_LINKER_OPTION,
55
+ 0x2e => :LC_LINKER_OPTIMIZATION_HINT,
56
+ 0x2f => :LC_VERSION_MIN_TVOS,
57
+ 0x30 => :LC_VERSION_MIN_WATCHOS,
58
+ }.freeze
59
+
60
+ # load commands responsible for loading dylibs
61
+ # @api private
62
+ DYLIB_LOAD_COMMANDS = [
63
+ :LC_LOAD_DYLIB,
64
+ :LC_LOAD_WEAK_DYLIB,
65
+ :LC_REEXPORT_DYLIB,
66
+ :LC_LAZY_LOAD_DYLIB,
67
+ :LC_LOAD_UPWARD_DYLIB,
68
+ ].freeze
69
+
70
+ # association of load command symbols to string representations of classes
71
+ # @api private
72
+ LC_STRUCTURES = {
73
+ :LC_SEGMENT => "SegmentCommand",
74
+ :LC_SYMTAB => "SymtabCommand",
75
+ :LC_SYMSEG => "LoadCommand", # obsolete
76
+ :LC_THREAD => "ThreadCommand", # seems obsolete, but not documented as such
77
+ :LC_UNIXTHREAD => "ThreadCommand",
78
+ :LC_LOADFVMLIB => "LoadCommand", # obsolete
79
+ :LC_IDFVMLIB => "LoadCommand", # obsolete
80
+ :LC_IDENT => "LoadCommand", # obsolete
81
+ :LC_FVMFILE => "LoadCommand", # reserved for internal use only
82
+ :LC_PREPAGE => "LoadCommand", # reserved for internal use only
83
+ :LC_DYSYMTAB => "DysymtabCommand",
84
+ :LC_LOAD_DYLIB => "DylibCommand",
85
+ :LC_ID_DYLIB => "DylibCommand",
86
+ :LC_LOAD_DYLINKER => "DylinkerCommand",
87
+ :LC_ID_DYLINKER => "DylinkerCommand",
88
+ :LC_PREBOUND_DYLIB => "PreboundDylibCommand",
89
+ :LC_ROUTINES => "RoutinesCommand",
90
+ :LC_SUB_FRAMEWORK => "SubFrameworkCommand",
91
+ :LC_SUB_UMBRELLA => "SubUmbrellaCommand",
92
+ :LC_SUB_CLIENT => "SubClientCommand",
93
+ :LC_SUB_LIBRARY => "SubLibraryCommand",
94
+ :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand",
95
+ :LC_PREBIND_CKSUM => "PrebindCksumCommand",
96
+ :LC_LOAD_WEAK_DYLIB => "DylibCommand",
97
+ :LC_SEGMENT_64 => "SegmentCommand64",
98
+ :LC_ROUTINES_64 => "RoutinesCommand64",
99
+ :LC_UUID => "UUIDCommand",
100
+ :LC_RPATH => "RpathCommand",
101
+ :LC_CODE_SIGNATURE => "LinkeditDataCommand",
102
+ :LC_SEGMENT_SPLIT_INFO => "LinkeditDataCommand",
103
+ :LC_REEXPORT_DYLIB => "DylibCommand",
104
+ :LC_LAZY_LOAD_DYLIB => "DylibCommand",
105
+ :LC_ENCRYPTION_INFO => "EncryptionInfoCommand",
106
+ :LC_DYLD_INFO => "DyldInfoCommand",
107
+ :LC_DYLD_INFO_ONLY => "DyldInfoCommand",
108
+ :LC_LOAD_UPWARD_DYLIB => "DylibCommand",
109
+ :LC_VERSION_MIN_MACOSX => "VersionMinCommand",
110
+ :LC_VERSION_MIN_IPHONEOS => "VersionMinCommand",
111
+ :LC_FUNCTION_STARTS => "LinkeditDataCommand",
112
+ :LC_DYLD_ENVIRONMENT => "DylinkerCommand",
113
+ :LC_MAIN => "EntryPointCommand",
114
+ :LC_DATA_IN_CODE => "LinkeditDataCommand",
115
+ :LC_SOURCE_VERSION => "SourceVersionCommand",
116
+ :LC_DYLIB_CODE_SIGN_DRS => "LinkeditDataCommand",
117
+ :LC_ENCRYPTION_INFO_64 => "EncryptionInfoCommand64",
118
+ :LC_LINKER_OPTION => "LinkerOptionCommand",
119
+ :LC_LINKER_OPTIMIZATION_HINT => "LinkeditDataCommand",
120
+ :LC_VERSION_MIN_TVOS => "VersionMinCommand",
121
+ :LC_VERSION_MIN_WATCHOS => "VersionMinCommand",
122
+ }.freeze
123
+
124
+ # association of segment name symbols to names
125
+ # @api private
126
+ SEGMENT_NAMES = {
127
+ :SEG_PAGEZERO => "__PAGEZERO",
128
+ :SEG_TEXT => "__TEXT",
129
+ :SEG_DATA => "__DATA",
130
+ :SEG_OBJC => "__OBJC",
131
+ :SEG_ICON => "__ICON",
132
+ :SEG_LINKEDIT => "__LINKEDIT",
133
+ :SEG_UNIXSTACK => "__UNIXSTACK",
134
+ :SEG_IMPORT => "__IMPORT",
135
+ }.freeze
136
+
137
+ # association of segment flag symbols to values
138
+ # @api private
139
+ SEGMENT_FLAGS = {
140
+ :SG_HIGHVM => 0x1,
141
+ :SG_FVMLIB => 0x2,
142
+ :SG_NORELOC => 0x4,
143
+ :SG_PROTECTED_VERSION_1 => 0x8,
144
+ }.freeze
145
+
146
+ # Mach-O load command structure
147
+ # This is the most generic load command - only cmd ID and size are
148
+ # represented, and no actual data. Used when a more specific class
149
+ # isn't available/implemented.
150
+ class LoadCommand < MachOStructure
151
+ # @return [Fixnum] the offset in the file the command was created from
152
+ attr_reader :offset
153
+
154
+ # @return [Fixnum] the load command's identifying number
155
+ attr_reader :cmd
156
+
157
+ # @return [Fixnum] the size of the load command, in bytes
158
+ attr_reader :cmdsize
159
+
160
+ FORMAT = "L=2"
161
+ SIZEOF = 8
162
+
163
+ # Creates a new LoadCommand given an offset and binary string
164
+ # @param raw_data [String] the raw Mach-O data
165
+ # @param endianness [Symbol] the endianness of the command (:big or :little)
166
+ # @param offset [Fixnum] the offset to initialize with
167
+ # @param bin [String] the binary string to initialize with
168
+ # @return [MachO::LoadCommand] the new load command
169
+ # @api private
170
+ def self.new_from_bin(raw_data, endianness, offset, bin)
171
+ format = specialize_format(self::FORMAT, endianness)
172
+
173
+ self.new(raw_data, offset, *bin.unpack(format))
174
+ end
175
+
176
+ # @param raw_data [String] the raw Mach-O data
177
+ # @param offset [Fixnum] the offset to initialize with
178
+ # @param cmd [Fixnum] the load command's identifying number
179
+ # @param cmdsize [Fixnum] the size of the load command in bytes
180
+ # @api private
181
+ def initialize(raw_data, offset, cmd, cmdsize)
182
+ @raw_data = raw_data
183
+ @offset = offset
184
+ @cmd = cmd
185
+ @cmdsize = cmdsize
186
+ end
187
+
188
+ # @return [Symbol] a symbol representation of the load command's identifying number
189
+ def type
190
+ LOAD_COMMANDS[cmd]
191
+ end
192
+
193
+ alias :to_sym :type
194
+
195
+ # @return [String] a string representation of the load command's identifying number
196
+ def to_s
197
+ type.to_s
198
+ end
199
+
200
+ # Represents a Load Command string. A rough analogue to the lc_str
201
+ # struct used internally by OS X. This class allows ruby-macho to
202
+ # pretend that strings stored in LCs are immediately available without
203
+ # explicit operations on the raw Mach-O data.
204
+ class LCStr
205
+ # @param raw_data [String] the raw Mach-O data.
206
+ # @param lc [MachO::LoadCommand] the load command
207
+ # @param lc_str [Fixnum] the offset to the beginning of the string
208
+ # @api private
209
+ def initialize(raw_data, lc, lc_str)
210
+ @raw_data = raw_data
211
+ @lc = lc
212
+ @lc_str = lc_str
213
+ @str = @raw_data.slice(@lc.offset + @lc_str...@lc.offset + @lc.cmdsize).delete("\x00")
214
+ end
215
+
216
+ # @return [String] a string representation of the LCStr
217
+ def to_s
218
+ @str
219
+ end
220
+
221
+ # @return [Fixnum] the offset to the beginning of the string in the load command
222
+ def to_i
223
+ @lc_str
224
+ end
225
+ end
226
+ end
227
+
228
+ # A load command containing a single 128-bit unique random number identifying
229
+ # an object produced by static link editor. Corresponds to LC_UUID.
230
+ class UUIDCommand < LoadCommand
231
+ # @return [Array<Fixnum>] the UUID
232
+ attr_reader :uuid
233
+
234
+ FORMAT = "L=2a16"
235
+ SIZEOF = 24
236
+
237
+ # @api private
238
+ def initialize(raw_data, offset, cmd, cmdsize, uuid)
239
+ super(raw_data, offset, cmd, cmdsize)
240
+ @uuid = uuid.unpack("C16") # re-unpack for the actual UUID array
241
+ end
242
+
243
+ # @return [String] a string representation of the UUID
244
+ def uuid_string
245
+ hexes = uuid.map { |e| "%02x" % e }
246
+ segs = [
247
+ hexes[0..3].join, hexes[4..5].join, hexes[6..7].join,
248
+ hexes[8..9].join, hexes[10..15].join
249
+ ]
250
+
251
+ segs.join("-")
252
+ end
253
+ end
254
+
255
+ # A load command indicating that part of this file is to be mapped into
256
+ # the task's address space. Corresponds to LC_SEGMENT.
257
+ class SegmentCommand < LoadCommand
258
+ # @return [String] the name of the segment
259
+ attr_reader :segname
260
+
261
+ # @return [Fixnum] the memory address of the segment
262
+ attr_reader :vmaddr
263
+
264
+ # @return [Fixnum] the memory size of the segment
265
+ attr_reader :vmsize
266
+
267
+ # @return [Fixnum] the file offset of the segment
268
+ attr_reader :fileoff
269
+
270
+ # @return [Fixnum] the amount to map from the file
271
+ attr_reader :filesize
272
+
273
+ # @return [Fixnum] the maximum VM protection
274
+ attr_reader :maxprot
275
+
276
+ # @return [Fixnum] the initial VM protection
277
+ attr_reader :initprot
278
+
279
+ # @return [Fixnum] the number of sections in the segment
280
+ attr_reader :nsects
281
+
282
+ # @return [Fixnum] any flags associated with the segment
283
+ attr_reader :flags
284
+
285
+ FORMAT = "L=2a16L=4l=2L=2"
286
+ SIZEOF = 56
287
+
288
+ # @api private
289
+ def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
290
+ filesize, maxprot, initprot, nsects, flags)
291
+ super(raw_data, offset, cmd, cmdsize)
292
+ @segname = segname.delete("\x00")
293
+ @vmaddr = vmaddr
294
+ @vmsize = vmsize
295
+ @fileoff = fileoff
296
+ @filesize = filesize
297
+ @maxprot = maxprot
298
+ @initprot = initprot
299
+ @nsects = nsects
300
+ @flags = flags
301
+ end
302
+
303
+ # @example
304
+ # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC)
305
+ # @param flag [Symbol] a segment flag symbol
306
+ # @return [Boolean] true if `flag` is present in the segment's flag field
307
+ def flag?(flag)
308
+ flag = SEGMENT_FLAGS[flag]
309
+ return false if flag.nil?
310
+ flags & flag == flag
311
+ end
312
+ end
313
+
314
+ # A load command indicating that part of this file is to be mapped into
315
+ # the task's address space. Corresponds to LC_SEGMENT_64.
316
+ class SegmentCommand64 < LoadCommand
317
+ # @return [String] the name of the segment
318
+ attr_reader :segname
319
+
320
+ # @return [Fixnum] the memory address of the segment
321
+ attr_reader :vmaddr
322
+
323
+ # @return [Fixnum] the memory size of the segment
324
+ attr_reader :vmsize
325
+
326
+ # @return [Fixnum] the file offset of the segment
327
+ attr_reader :fileoff
328
+
329
+ # @return [Fixnum] the amount to map from the file
330
+ attr_reader :filesize
331
+
332
+ # @return [Fixnum] the maximum VM protection
333
+ attr_reader :maxprot
334
+
335
+ # @return [Fixnum] the initial VM protection
336
+ attr_reader :initprot
337
+
338
+ # @return [Fixnum] the number of sections in the segment
339
+ attr_reader :nsects
340
+
341
+ # @return [Fixnum] any flags associated with the segment
342
+ attr_reader :flags
343
+
344
+ FORMAT = "L=2a16Q=4l=2L=2"
345
+ SIZEOF = 72
346
+
347
+ # @api private
348
+ def initialize(raw_data, offset, cmd, cmdsize, segname, vmaddr, vmsize, fileoff,
349
+ filesize, maxprot, initprot, nsects, flags)
350
+ super(raw_data, offset, cmd, cmdsize)
351
+ @segname = segname.delete("\x00")
352
+ @vmaddr = vmaddr
353
+ @vmsize = vmsize
354
+ @fileoff = fileoff
355
+ @filesize = filesize
356
+ @maxprot = maxprot
357
+ @initprot = initprot
358
+ @nsects = nsects
359
+ @flags = flags
360
+ end
361
+
362
+ # @example
363
+ # puts "this segment relocated in/to it" if sect.flag?(:SG_NORELOC)
364
+ # @param flag [Symbol] a segment flag symbol
365
+ # @return [Boolean] true if `flag` is present in the segment's flag field
366
+ def flag?(flag)
367
+ flag = SEGMENT_FLAGS[flag]
368
+ return false if flag.nil?
369
+ flags & flag == flag
370
+ end
371
+ end
372
+
373
+ # A load command representing some aspect of shared libraries, depending
374
+ # on filetype. Corresponds to LC_ID_DYLIB, LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB,
375
+ # and LC_REEXPORT_DYLIB.
376
+ class DylibCommand < LoadCommand
377
+ # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr
378
+ attr_reader :name
379
+
380
+ # @return [Fixnum] the library's build time stamp
381
+ attr_reader :timestamp
382
+
383
+ # @return [Fixnum] the library's current version number
384
+ attr_reader :current_version
385
+
386
+ # @return [Fixnum] the library's compatibility version number
387
+ attr_reader :compatibility_version
388
+
389
+ FORMAT = "L=6"
390
+ SIZEOF = 24
391
+
392
+ # @api private
393
+ def initialize(raw_data, offset, cmd, cmdsize, name, timestamp, current_version,
394
+ compatibility_version)
395
+ super(raw_data, offset, cmd, cmdsize)
396
+ @name = LCStr.new(raw_data, self, name)
397
+ @timestamp = timestamp
398
+ @current_version = current_version
399
+ @compatibility_version = compatibility_version
400
+ end
401
+ end
402
+
403
+ # A load command representing some aspect of the dynamic linker, depending
404
+ # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and
405
+ # LC_DYLD_ENVIRONMENT.
406
+ class DylinkerCommand < LoadCommand
407
+ # @return [MachO::LoadCommand::LCStr] the dynamic linker's path name as an LCStr
408
+ attr_reader :name
409
+
410
+ FORMAT = "L=3"
411
+ SIZEOF = 12
412
+
413
+ # @api private
414
+ def initialize(raw_data, offset, cmd, cmdsize, name)
415
+ super(raw_data, offset, cmd, cmdsize)
416
+ @name = LCStr.new(raw_data, self, name)
417
+ end
418
+ end
419
+
420
+ # A load command used to indicate dynamic libraries used in prebinding.
421
+ # Corresponds to LC_PREBOUND_DYLIB.
422
+ class PreboundDylibCommand < LoadCommand
423
+ # @return [MachO::LoadCommand::LCStr] the library's path name as an LCStr
424
+ attr_reader :name
425
+
426
+ # @return [Fixnum] the number of modules in the library
427
+ attr_reader :nmodules
428
+
429
+ # @return [Fixnum] a bit vector of linked modules
430
+ attr_reader :linked_modules
431
+
432
+ FORMAT = "L=5"
433
+ SIZEOF = 20
434
+
435
+ # @api private
436
+ def initialize(raw_data, offset, cmd, cmdsize, name, nmodules, linked_modules)
437
+ super(raw_data, offset, cmd, cmdsize)
438
+ @name = LCStr.new(raw_data, self, name)
439
+ @nmodules = nmodules
440
+ @linked_modules = linked_modules
441
+ end
442
+ end
443
+
444
+ # A load command used to represent threads.
445
+ # @note cctools-870 has all fields of thread_command commented out except common ones (cmd, cmdsize)
446
+ class ThreadCommand < LoadCommand
447
+ FORMAT = "L=2"
448
+ SIZEOF = 8
449
+ end
450
+
451
+ # A load command containing the address of the dynamic shared library
452
+ # initialization routine and an index into the module table for the module
453
+ # that defines the routine. Corresponds to LC_ROUTINES.
454
+ class RoutinesCommand < LoadCommand
455
+ # @return [Fixnum] the address of the initialization routine
456
+ attr_reader :init_address
457
+
458
+ # @return [Fixnum] the index into the module table that the init routine is defined in
459
+ attr_reader :init_module
460
+
461
+ # @return [void]
462
+ attr_reader :reserved1
463
+
464
+ # @return [void]
465
+ attr_reader :reserved2
466
+
467
+ # @return [void]
468
+ attr_reader :reserved3
469
+
470
+ # @return [void]
471
+ attr_reader :reserved4
472
+
473
+ # @return [void]
474
+ attr_reader :reserved5
475
+
476
+ # @return [void]
477
+ attr_reader :reserved6
478
+
479
+ FORMAT = "L=10"
480
+ SIZEOF = 40
481
+
482
+ # @api private
483
+ def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
484
+ reserved1, reserved2, reserved3, reserved4, reserved5,
485
+ reserved6)
486
+ super(raw_data, offset, cmd, cmdsize)
487
+ @init_address = init_address
488
+ @init_module = init_module
489
+ @reserved1 = reserved1
490
+ @reserved2 = reserved2
491
+ @reserved3 = reserved3
492
+ @reserved4 = reserved4
493
+ @reserved5 = reserved5
494
+ @reserved6 = reserved6
495
+ end
496
+ end
497
+
498
+ # A load command containing the address of the dynamic shared library
499
+ # initialization routine and an index into the module table for the module
500
+ # that defines the routine. Corresponds to LC_ROUTINES_64.
501
+ class RoutinesCommand64 < LoadCommand
502
+ # @return [Fixnum] the address of the initialization routine
503
+ attr_reader :init_address
504
+
505
+ # @return [Fixnum] the index into the module table that the init routine is defined in
506
+ attr_reader :init_module
507
+
508
+ # @return [void]
509
+ attr_reader :reserved1
510
+
511
+ # @return [void]
512
+ attr_reader :reserved2
513
+
514
+ # @return [void]
515
+ attr_reader :reserved3
516
+
517
+ # @return [void]
518
+ attr_reader :reserved4
519
+
520
+ # @return [void]
521
+ attr_reader :reserved5
522
+
523
+ # @return [void]
524
+ attr_reader :reserved6
525
+
526
+ FORMAT = "L=2Q=8"
527
+ SIZEOF = 72
528
+
529
+ # @api private
530
+ def initialize(raw_data, offset, cmd, cmdsize, init_address, init_module,
531
+ reserved1, reserved2, reserved3, reserved4, reserved5,
532
+ reserved6)
533
+ super(raw_data, offset, cmd, cmdsize)
534
+ @init_address = init_address
535
+ @init_module = init_module
536
+ @reserved1 = reserved1
537
+ @reserved2 = reserved2
538
+ @reserved3 = reserved3
539
+ @reserved4 = reserved4
540
+ @reserved5 = reserved5
541
+ @reserved6 = reserved6
542
+ end
543
+ end
544
+
545
+ # A load command signifying membership of a subframework containing the name
546
+ # of an umbrella framework. Corresponds to LC_SUB_FRAMEWORK.
547
+ class SubFrameworkCommand < LoadCommand
548
+ # @return [MachO::LoadCommand::LCStr] the umbrella framework name as an LCStr
549
+ attr_reader :umbrella
550
+
551
+ FORMAT = "L=3"
552
+ SIZEOF = 12
553
+
554
+ # @api private
555
+ def initialize(raw_data, offset, cmd, cmdsize, umbrella)
556
+ super(raw_data, offset, cmd, cmdsize)
557
+ @umbrella = LCStr.new(raw_data, self, umbrella)
558
+ end
559
+ end
560
+
561
+ # A load command signifying membership of a subumbrella containing the name
562
+ # of an umbrella framework. Corresponds to LC_SUB_UMBRELLA.
563
+ class SubUmbrellaCommand < LoadCommand
564
+ # @return [MachO::LoadCommand::LCStr] the subumbrella framework name as an LCStr
565
+ attr_reader :sub_umbrella
566
+
567
+ FORMAT = "L=3"
568
+ SIZEOF = 12
569
+
570
+ # @api private
571
+ def initialize(raw_data, offset, cmd, cmdsize, sub_umbrella)
572
+ super(raw_data, offset, cmd, cmdsize)
573
+ @sub_umbrella = LCStr.new(raw_data, self, sub_umbrella)
574
+ end
575
+ end
576
+
577
+ # A load command signifying a sublibrary of a shared library. Corresponds
578
+ # to LC_SUB_LIBRARY.
579
+ class SubLibraryCommand < LoadCommand
580
+ # @return [MachO::LoadCommand::LCStr] the sublibrary name as an LCStr
581
+ attr_reader :sub_library
582
+
583
+ FORMAT = "L=3"
584
+ SIZEOF = 12
585
+
586
+ # @api private
587
+ def initialize(raw_data, offset, cmd, cmdsize, sub_library)
588
+ super(raw_data, offset, cmd, cmdsize)
589
+ @sub_library = LCStr.new(raw_data, self, sub_library)
590
+ end
591
+ end
592
+
593
+ # A load command signifying a shared library that is a subframework of
594
+ # an umbrella framework. Corresponds to LC_SUB_CLIENT.
595
+ class SubClientCommand < LoadCommand
596
+ # @return [MachO::LoadCommand::LCStr] the subclient name as an LCStr
597
+ attr_reader :sub_client
598
+
599
+ FORMAT = "L=3"
600
+ SIZEOF = 12
601
+
602
+ # @api private
603
+ def initialize(raw_data, offset, cmd, cmdsize, sub_client)
604
+ super(raw_data, offset, cmd, cmdsize)
605
+ @sub_client = LCStr.new(raw_data, self, sub_client)
606
+ end
607
+ end
608
+
609
+ # A load command containing the offsets and sizes of the link-edit 4.3BSD
610
+ # "stab" style symbol table information. Corresponds to LC_SYMTAB.
611
+ class SymtabCommand < LoadCommand
612
+ # @return [Fixnum] the symbol table's offset
613
+ attr_reader :symoff
614
+
615
+ # @return [Fixnum] the number of symbol table entries
616
+ attr_reader :nsyms
617
+
618
+ # @return the string table's offset
619
+ attr_reader :stroff
620
+
621
+ # @return the string table size in bytes
622
+ attr_reader :strsize
623
+
624
+ FORMAT = "L=6"
625
+ SIZEOF = 24
626
+
627
+ # @api private
628
+ def initialize(raw_data, offset, cmd, cmdsize, symoff, nsyms, stroff, strsize)
629
+ super(raw_data, offset, cmd, cmdsize)
630
+ @symoff = symoff
631
+ @nsyms = nsyms
632
+ @stroff = stroff
633
+ @strsize = strsize
634
+ end
635
+ end
636
+
637
+ # A load command containing symbolic information needed to support data
638
+ # structures used by the dynamic link editor. Corresponds to LC_DYSYMTAB.
639
+ class DysymtabCommand < LoadCommand
640
+ # @return [Fixnum] the index to local symbols
641
+ attr_reader :ilocalsym
642
+
643
+ # @return [Fixnum] the number of local symbols
644
+ attr_reader :nlocalsym
645
+
646
+ # @return [Fixnum] the index to externally defined symbols
647
+ attr_reader :iextdefsym
648
+
649
+ # @return [Fixnum] the number of externally defined symbols
650
+ attr_reader :nextdefsym
651
+
652
+ # @return [Fixnum] the index to undefined symbols
653
+ attr_reader :iundefsym
654
+
655
+ # @return [Fixnum] the number of undefined symbols
656
+ attr_reader :nundefsym
657
+
658
+ # @return [Fixnum] the file offset to the table of contents
659
+ attr_reader :tocoff
660
+
661
+ # @return [Fixnum] the number of entries in the table of contents
662
+ attr_reader :ntoc
663
+
664
+ # @return [Fixnum] the file offset to the module table
665
+ attr_reader :modtaboff
666
+
667
+ # @return [Fixnum] the number of entries in the module table
668
+ attr_reader :nmodtab
669
+
670
+ # @return [Fixnum] the file offset to the referenced symbol table
671
+ attr_reader :extrefsymoff
672
+
673
+ # @return [Fixnum] the number of entries in the referenced symbol table
674
+ attr_reader :nextrefsyms
675
+
676
+ # @return [Fixnum] the file offset to the indirect symbol table
677
+ attr_reader :indirectsymoff
678
+
679
+ # @return [Fixnum] the number of entries in the indirect symbol table
680
+ attr_reader :nindirectsyms
681
+
682
+ # @return [Fixnum] the file offset to the external relocation entries
683
+ attr_reader :extreloff
684
+
685
+ # @return [Fixnum] the number of external relocation entries
686
+ attr_reader :nextrel
687
+
688
+ # @return [Fixnum] the file offset to the local relocation entries
689
+ attr_reader :locreloff
690
+
691
+ # @return [Fixnum] the number of local relocation entries
692
+ attr_reader :nlocrel
693
+
694
+
695
+ FORMAT = "L=20"
696
+ SIZEOF = 80
697
+
698
+ # ugh
699
+ # @api private
700
+ def initialize(raw_data, offset, cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym,
701
+ nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff,
702
+ nmodtab, extrefsymoff, nextrefsyms, indirectsymoff,
703
+ nindirectsyms, extreloff, nextrel, locreloff, nlocrel)
704
+ super(raw_data, offset, cmd, cmdsize)
705
+ @ilocalsym = ilocalsym
706
+ @nlocalsym = nlocalsym
707
+ @iextdefsym = iextdefsym
708
+ @nextdefsym = nextdefsym
709
+ @iundefsym = iundefsym
710
+ @nundefsym = nundefsym
711
+ @tocoff = tocoff
712
+ @ntoc = ntoc
713
+ @modtaboff = modtaboff
714
+ @nmodtab = nmodtab
715
+ @extrefsymoff = extrefsymoff
716
+ @nextrefsyms = nextrefsyms
717
+ @indirectsymoff = indirectsymoff
718
+ @nindirectsyms = nindirectsyms
719
+ @extreloff = extreloff
720
+ @nextrel = nextrel
721
+ @locreloff = locreloff
722
+ @nlocrel = nlocrel
723
+ end
724
+ end
725
+
726
+ # A load command containing the offset and number of hints in the two-level
727
+ # namespace lookup hints table. Corresponds to LC_TWOLEVEL_HINTS.
728
+ class TwolevelHintsCommand < LoadCommand
729
+ # @return [Fixnum] the offset to the hint table
730
+ attr_reader :htoffset
731
+
732
+ # @return [Fixnum] the number of hints in the hint table
733
+ attr_reader :nhints
734
+
735
+ FORMAT = "L=4"
736
+ SIZEOF = 16
737
+
738
+ # @api private
739
+ def initialize(raw_data, offset, cmd, cmdsize, htoffset, nhints)
740
+ super(raw_data, offset, cmd, cmdsize)
741
+ @htoffset = htoffset
742
+ @nhints = nhints
743
+ end
744
+ end
745
+
746
+ # A load command containing the value of the original checksum for prebound
747
+ # files, or zero. Corresponds to LC_PREBIND_CKSUM.
748
+ class PrebindCksumCommand < LoadCommand
749
+ # @return [Fixnum] the checksum or 0
750
+ attr_reader :cksum
751
+
752
+ FORMAT = "L=3"
753
+ SIZEOF = 12
754
+
755
+ # @api private
756
+ def initialize(raw_data, offset, cmd, cmdsize, cksum)
757
+ super(raw_data, offset, cmd, cmdsize)
758
+ @cksum = cksum
759
+ end
760
+ end
761
+
762
+ # A load command representing an rpath, which specifies a path that should
763
+ # be added to the current run path used to find @rpath prefixed dylibs.
764
+ # Corresponds to LC_RPATH.
765
+ class RpathCommand < LoadCommand
766
+ # @return [MachO::LoadCommand::LCStr] the path to add to the run path as an LCStr
767
+ attr_reader :path
768
+
769
+ FORMAT = "L=3"
770
+ SIZEOF = 12
771
+
772
+ # @api private
773
+ def initialize(raw_data, offset, cmd, cmdsize, path)
774
+ super(raw_data, offset, cmd, cmdsize)
775
+ @path = LCStr.new(raw_data, self, path)
776
+ end
777
+ end
778
+
779
+ # A load command representing the offsets and sizes of a blob of data in
780
+ # the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO,
781
+ # LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, and LC_LINKER_OPTIMIZATION_HINT.
782
+ class LinkeditDataCommand < LoadCommand
783
+ # @return [Fixnum] offset to the data in the __LINKEDIT segment
784
+ attr_reader :dataoff
785
+
786
+ # @return [Fixnum] size of the data in the __LINKEDIT segment
787
+ attr_reader :datasize
788
+
789
+ FORMAT = "L=4"
790
+ SIZEOF = 16
791
+
792
+ # @api private
793
+ def initialize(raw_data, offset, cmd, cmdsize, dataoff, datasize)
794
+ super(raw_data, offset, cmd, cmdsize)
795
+ @dataoff = dataoff
796
+ @datasize = datasize
797
+ end
798
+ end
799
+
800
+ # A load command representing the offset to and size of an encrypted
801
+ # segment. Corresponds to LC_ENCRYPTION_INFO.
802
+ class EncryptionInfoCommand < LoadCommand
803
+ # @return [Fixnum] the offset to the encrypted segment
804
+ attr_reader :cryptoff
805
+
806
+ # @return [Fixnum] the size of the encrypted segment
807
+ attr_reader :cryptsize
808
+
809
+ # @return [Fixnum] the encryption system, or 0 if not encrypted yet
810
+ attr_reader :cryptid
811
+
812
+ FORMAT = "L=5"
813
+ SIZEOF = 20
814
+
815
+ # @api private
816
+ def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid)
817
+ super(raw_data, offset, cmd, cmdsize)
818
+ @cryptoff = cryptoff
819
+ @cryptsize = cryptsize
820
+ @cryptid = cryptid
821
+ end
822
+ end
823
+
824
+ # A load command representing the offset to and size of an encrypted
825
+ # segment. Corresponds to LC_ENCRYPTION_INFO_64.
826
+ class EncryptionInfoCommand64 < LoadCommand
827
+ # @return [Fixnum] the offset to the encrypted segment
828
+ attr_reader :cryptoff
829
+
830
+ # @return [Fixnum] the size of the encrypted segment
831
+ attr_reader :cryptsize
832
+
833
+ # @return [Fixnum] the encryption system, or 0 if not encrypted yet
834
+ attr_reader :cryptid
835
+
836
+ # @return [Fixnum] 64-bit padding value
837
+ attr_reader :pad
838
+
839
+ FORMAT = "L=6"
840
+ SIZEOF = 24
841
+
842
+ # @api private
843
+ def initialize(raw_data, offset, cmd, cmdsize, cryptoff, cryptsize, cryptid, pad)
844
+ super(raw_data, offset, cmd, cmdsize)
845
+ @cryptoff = cryptoff
846
+ @cryptsize = cryptsize
847
+ @cryptid = cryptid
848
+ @pad = pad
849
+ end
850
+ end
851
+
852
+ # A load command containing the minimum OS version on which the binary
853
+ # was built to run. Corresponds to LC_VERSION_MIN_MACOSX and LC_VERSION_MIN_IPHONEOS.
854
+ class VersionMinCommand < LoadCommand
855
+ # @return [Fixnum] the version X.Y.Z packed as x16.y8.z8
856
+ attr_reader :version
857
+
858
+ # @return [Fixnum] the SDK version X.Y.Z packed as x16.y8.z8
859
+ attr_reader :sdk
860
+
861
+ FORMAT = "L=4"
862
+ SIZEOF = 16
863
+
864
+ # @api private
865
+ def initialize(raw_data, offset, cmd, cmdsize, version, sdk)
866
+ super(raw_data, offset, cmd, cmdsize)
867
+ @version = version
868
+ @sdk = sdk
869
+ end
870
+
871
+ # A string representation of the binary's minimum OS version.
872
+ # @return [String] a string representing the minimum OS version.
873
+ def version_string
874
+ binary = "%032b" % version
875
+ segs = [
876
+ binary[0..15], binary[16..23], binary[24..31]
877
+ ].map { |s| s.to_i(2) }
878
+
879
+ segs.join(".")
880
+ end
881
+
882
+ # A string representation of the binary's SDK version.
883
+ # @return [String] a string representing the SDK version.
884
+ def sdk_string
885
+ binary = "%032b" % sdk
886
+ segs = [
887
+ binary[0..15], binary[16..23], binary[24..31]
888
+ ].map { |s| s.to_i(2) }
889
+
890
+ segs.join(".")
891
+ end
892
+ end
893
+
894
+ # A load command containing the file offsets and sizes of the new
895
+ # compressed form of the information dyld needs to load the image.
896
+ # Corresponds to LC_DYLD_INFO and LC_DYLD_INFO_ONLY.
897
+ class DyldInfoCommand < LoadCommand
898
+ # @return [Fixnum] the file offset to the rebase information
899
+ attr_reader :rebase_off
900
+
901
+ # @return [Fixnum] the size of the rebase information
902
+ attr_reader :rebase_size
903
+
904
+ # @return [Fixnum] the file offset to the binding information
905
+ attr_reader :bind_off
906
+
907
+ # @return [Fixnum] the size of the binding information
908
+ attr_reader :bind_size
909
+
910
+ # @return [Fixnum] the file offset to the weak binding information
911
+ attr_reader :weak_bind_off
912
+
913
+ # @return [Fixnum] the size of the weak binding information
914
+ attr_reader :weak_bind_size
915
+
916
+ # @return [Fixnum] the file offset to the lazy binding information
917
+ attr_reader :lazy_bind_off
918
+
919
+ # @return [Fixnum] the size of the lazy binding information
920
+ attr_reader :lazy_bind_size
921
+
922
+ # @return [Fixnum] the file offset to the export information
923
+ attr_reader :export_off
924
+
925
+ # @return [Fixnum] the size of the export information
926
+ attr_reader :export_size
927
+
928
+ FORMAT = "L=12"
929
+ SIZEOF = 48
930
+
931
+ # @api private
932
+ def initialize(raw_data, offset, cmd, cmdsize, rebase_off, rebase_size, bind_off,
933
+ bind_size, weak_bind_off, weak_bind_size, lazy_bind_off,
934
+ lazy_bind_size, export_off, export_size)
935
+ super(raw_data, offset, cmd, cmdsize)
936
+ @rebase_off = rebase_off
937
+ @rebase_size = rebase_size
938
+ @bind_off = bind_off
939
+ @bind_size = bind_size
940
+ @weak_bind_off = weak_bind_off
941
+ @weak_bind_size = weak_bind_size
942
+ @lazy_bind_off = lazy_bind_off
943
+ @lazy_bind_size = lazy_bind_size
944
+ @export_off = export_off
945
+ @export_size = export_size
946
+ end
947
+ end
948
+
949
+ # A load command containing linker options embedded in object files.
950
+ # Corresponds to LC_LINKER_OPTION.
951
+ class LinkerOptionCommand < LoadCommand
952
+ # @return [Fixnum] the number of strings
953
+ attr_reader :count
954
+
955
+ FORMAT = "L=3"
956
+ SIZEOF = 12
957
+
958
+ # @api private
959
+ def initialize(raw_data, offset, cmd, cmdsize, count)
960
+ super(raw_data, offset, cmd, cmdsize)
961
+ @count = count
962
+ end
963
+ end
964
+
965
+ # A load command specifying the offset of main(). Corresponds to LC_MAIN.
966
+ class EntryPointCommand < LoadCommand
967
+ # @return [Fixnum] the file (__TEXT) offset of main()
968
+ attr_reader :entryoff
969
+
970
+ # @return [Fixnum] if not 0, the initial stack size.
971
+ attr_reader :stacksize
972
+
973
+ FORMAT = "L=2Q=2"
974
+ SIZEOF = 24
975
+
976
+ # @api private
977
+ def initialize(raw_data, offset, cmd, cmdsize, entryoff, stacksize)
978
+ super(raw_data, offset, cmd, cmdsize)
979
+ @entryoff = entryoff
980
+ @stacksize = stacksize
981
+ end
982
+ end
983
+
984
+ # A load command specifying the version of the sources used to build the
985
+ # binary. Corresponds to LC_SOURCE_VERSION.
986
+ class SourceVersionCommand < LoadCommand
987
+ # @return [Fixnum] the version packed as a24.b10.c10.d10.e10
988
+ attr_reader :version
989
+
990
+ FORMAT = "L=2Q=1"
991
+ SIZEOF = 16
992
+
993
+ # @api private
994
+ def initialize(raw_data, offset, cmd, cmdsize, version)
995
+ super(raw_data, offset, cmd, cmdsize)
996
+ @version = version
997
+ end
998
+
999
+ # A string representation of the sources used to build the binary.
1000
+ # @return [String] a string representation of the version
1001
+ def version_string
1002
+ binary = "%064b" % version
1003
+ segs = [
1004
+ binary[0..23], binary[24..33], binary[34..43], binary[44..53],
1005
+ binary[54..63]
1006
+ ].map { |s| s.to_i(2) }
1007
+
1008
+ segs.join(".")
1009
+ end
1010
+ end
1001
1011
  end