ruby-macho 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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