ruby-macho 0.2.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +0 -3
- data/lib/macho.rb +25 -2
- data/lib/macho/fat_file.rb +10 -10
- data/lib/macho/headers.rb +552 -549
- data/lib/macho/load_commands.rb +1064 -1061
- data/lib/macho/macho_file.rb +52 -52
- data/lib/macho/sections.rb +160 -157
- data/lib/macho/utils.rb +6 -6
- metadata +3 -4
- data/lib/macho/open.rb +0 -25
data/lib/macho/macho_file.rb
CHANGED
@@ -11,11 +11,11 @@ module MachO
|
|
11
11
|
# @return [Symbol] the endianness of the file, :big or :little
|
12
12
|
attr_reader :endianness
|
13
13
|
|
14
|
-
# @return [MachO::MachHeader] if the Mach-O is 32-bit
|
15
|
-
# @return [MachO::MachHeader64] if the Mach-O is 64-bit
|
14
|
+
# @return [MachO::Headers::MachHeader] if the Mach-O is 32-bit
|
15
|
+
# @return [MachO::Headers::MachHeader64] if the Mach-O is 64-bit
|
16
16
|
attr_reader :header
|
17
17
|
|
18
|
-
# @return [Array<MachO::LoadCommand>] an array of the file's load commands
|
18
|
+
# @return [Array<MachO::LoadCommands::LoadCommand>] an array of the file's load commands
|
19
19
|
# @note load commands are provided in order of ascending offset.
|
20
20
|
attr_reader :load_commands
|
21
21
|
|
@@ -72,52 +72,52 @@ module MachO
|
|
72
72
|
|
73
73
|
# @return [Boolean] true if the file is of type `MH_OBJECT`, false otherwise
|
74
74
|
def object?
|
75
|
-
header.filetype == MH_OBJECT
|
75
|
+
header.filetype == Headers::MH_OBJECT
|
76
76
|
end
|
77
77
|
|
78
78
|
# @return [Boolean] true if the file is of type `MH_EXECUTE`, false otherwise
|
79
79
|
def executable?
|
80
|
-
header.filetype == MH_EXECUTE
|
80
|
+
header.filetype == Headers::MH_EXECUTE
|
81
81
|
end
|
82
82
|
|
83
83
|
# @return [Boolean] true if the file is of type `MH_FVMLIB`, false otherwise
|
84
84
|
def fvmlib?
|
85
|
-
header.filetype == MH_FVMLIB
|
85
|
+
header.filetype == Headers::MH_FVMLIB
|
86
86
|
end
|
87
87
|
|
88
88
|
# @return [Boolean] true if the file is of type `MH_CORE`, false otherwise
|
89
89
|
def core?
|
90
|
-
header.filetype == MH_CORE
|
90
|
+
header.filetype == Headers::MH_CORE
|
91
91
|
end
|
92
92
|
|
93
93
|
# @return [Boolean] true if the file is of type `MH_PRELOAD`, false otherwise
|
94
94
|
def preload?
|
95
|
-
header.filetype == MH_PRELOAD
|
95
|
+
header.filetype == Headers::MH_PRELOAD
|
96
96
|
end
|
97
97
|
|
98
98
|
# @return [Boolean] true if the file is of type `MH_DYLIB`, false otherwise
|
99
99
|
def dylib?
|
100
|
-
header.filetype == MH_DYLIB
|
100
|
+
header.filetype == Headers::MH_DYLIB
|
101
101
|
end
|
102
102
|
|
103
103
|
# @return [Boolean] true if the file is of type `MH_DYLINKER`, false otherwise
|
104
104
|
def dylinker?
|
105
|
-
header.filetype == MH_DYLINKER
|
105
|
+
header.filetype == Headers::MH_DYLINKER
|
106
106
|
end
|
107
107
|
|
108
108
|
# @return [Boolean] true if the file is of type `MH_BUNDLE`, false otherwise
|
109
109
|
def bundle?
|
110
|
-
header.filetype == MH_BUNDLE
|
110
|
+
header.filetype == Headers::MH_BUNDLE
|
111
111
|
end
|
112
112
|
|
113
113
|
# @return [Boolean] true if the file is of type `MH_DSYM`, false otherwise
|
114
114
|
def dsym?
|
115
|
-
header.filetype == MH_DSYM
|
115
|
+
header.filetype == Headers::MH_DSYM
|
116
116
|
end
|
117
117
|
|
118
118
|
# @return [Boolean] true if the file is of type `MH_KEXT_BUNDLE`, false otherwise
|
119
119
|
def kext?
|
120
|
-
header.filetype == MH_KEXT_BUNDLE
|
120
|
+
header.filetype == Headers::MH_KEXT_BUNDLE
|
121
121
|
end
|
122
122
|
|
123
123
|
# @return [Fixnum] the file's magic number
|
@@ -127,22 +127,22 @@ module MachO
|
|
127
127
|
|
128
128
|
# @return [String] a string representation of the file's magic number
|
129
129
|
def magic_string
|
130
|
-
MH_MAGICS[magic]
|
130
|
+
Headers::MH_MAGICS[magic]
|
131
131
|
end
|
132
132
|
|
133
133
|
# @return [Symbol] a string representation of the Mach-O's filetype
|
134
134
|
def filetype
|
135
|
-
MH_FILETYPES[header.filetype]
|
135
|
+
Headers::MH_FILETYPES[header.filetype]
|
136
136
|
end
|
137
137
|
|
138
138
|
# @return [Symbol] a symbol representation of the Mach-O's CPU type
|
139
139
|
def cputype
|
140
|
-
CPU_TYPES[header.cputype]
|
140
|
+
Headers::CPU_TYPES[header.cputype]
|
141
141
|
end
|
142
142
|
|
143
143
|
# @return [Symbol] a symbol representation of the Mach-O's CPU subtype
|
144
144
|
def cpusubtype
|
145
|
-
CPU_SUBTYPES[header.cputype][header.cpusubtype]
|
145
|
+
Headers::CPU_SUBTYPES[header.cputype][header.cpusubtype]
|
146
146
|
end
|
147
147
|
|
148
148
|
# @return [Fixnum] the number of load commands in the Mach-O's header
|
@@ -165,7 +165,7 @@ module MachO
|
|
165
165
|
# file.command("LC_LOAD_DYLIB")
|
166
166
|
# file[:LC_LOAD_DYLIB]
|
167
167
|
# @param [String, Symbol] name the load command ID
|
168
|
-
# @return [Array<MachO::LoadCommand>] an array of
|
168
|
+
# @return [Array<MachO::LoadCommands::LoadCommand>] an array of load commands corresponding to `name`
|
169
169
|
def command(name)
|
170
170
|
load_commands.select { |lc| lc.type == name.to_sym }
|
171
171
|
end
|
@@ -174,7 +174,7 @@ module MachO
|
|
174
174
|
|
175
175
|
# Inserts a load command at the given offset.
|
176
176
|
# @param offset [Fixnum] the offset to insert at
|
177
|
-
# @param lc [MachO::LoadCommand] the load command to insert
|
177
|
+
# @param lc [MachO::LoadCommands::LoadCommand] the load command to insert
|
178
178
|
# @param options [Hash]
|
179
179
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
180
180
|
# the instance fields
|
@@ -183,7 +183,7 @@ module MachO
|
|
183
183
|
# @note Calling this method with an arbitrary offset in the load command
|
184
184
|
# region **will leave the object in an inconsistent state**.
|
185
185
|
def insert_command(offset, lc, options = {})
|
186
|
-
context = LoadCommand::SerializationContext.context_for(self)
|
186
|
+
context = LoadCommands::LoadCommand::SerializationContext.context_for(self)
|
187
187
|
cmd_raw = lc.serialize(context)
|
188
188
|
|
189
189
|
if offset < header.class.bytesize || offset + cmd_raw.bytesize > low_fileoff
|
@@ -207,14 +207,14 @@ module MachO
|
|
207
207
|
end
|
208
208
|
|
209
209
|
# Replace a load command with another command in the Mach-O, preserving location.
|
210
|
-
# @param old_lc [MachO::LoadCommand] the load command being replaced
|
211
|
-
# @param new_lc [MachO::LoadCommand] the load command being added
|
210
|
+
# @param old_lc [MachO::LoadCommands::LoadCommand] the load command being replaced
|
211
|
+
# @param new_lc [MachO::LoadCommands::LoadCommand] the load command being added
|
212
212
|
# @return [void]
|
213
213
|
# @raise [MachO::HeaderPadError] if the new command exceeds the header pad buffer
|
214
214
|
# @see {#insert_command}
|
215
215
|
# @note This is public, but methods like {#dylib_id=} should be preferred.
|
216
216
|
def replace_command(old_lc, new_lc)
|
217
|
-
context = LoadCommand::SerializationContext.context_for(self)
|
217
|
+
context = LoadCommands::LoadCommand::SerializationContext.context_for(self)
|
218
218
|
cmd_raw = new_lc.serialize(context)
|
219
219
|
new_sizeofcmds = sizeofcmds + cmd_raw.bytesize - old_lc.cmdsize
|
220
220
|
if header.class.bytesize + new_sizeofcmds > low_fileoff
|
@@ -226,7 +226,7 @@ module MachO
|
|
226
226
|
end
|
227
227
|
|
228
228
|
# Appends a new load command to the Mach-O.
|
229
|
-
# @param lc [MachO::LoadCommand] the load command being added
|
229
|
+
# @param lc [MachO::LoadCommands::LoadCommand] the load command being added
|
230
230
|
# @param options [Hash]
|
231
231
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
232
232
|
# the instance fields
|
@@ -241,7 +241,7 @@ module MachO
|
|
241
241
|
end
|
242
242
|
|
243
243
|
# Delete a load command from the Mach-O.
|
244
|
-
# @param lc [MachO::LoadCommand] the load command being deleted
|
244
|
+
# @param lc [MachO::LoadCommands::LoadCommand] the load command being deleted
|
245
245
|
# @param options [Hash]
|
246
246
|
# @option options [Boolean] :repopulate (true) whether or not to repopulate
|
247
247
|
# the instance fields
|
@@ -275,14 +275,14 @@ module MachO
|
|
275
275
|
end
|
276
276
|
|
277
277
|
# All load commands responsible for loading dylibs.
|
278
|
-
# @return [Array<MachO::DylibCommand>] an array of DylibCommands
|
278
|
+
# @return [Array<MachO::LoadCommands::DylibCommand>] an array of DylibCommands
|
279
279
|
def dylib_load_commands
|
280
|
-
load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) }
|
280
|
+
load_commands.select { |lc| LoadCommands::DYLIB_LOAD_COMMANDS.include?(lc.type) }
|
281
281
|
end
|
282
282
|
|
283
283
|
# All segment load commands in the Mach-O.
|
284
|
-
# @return [Array<MachO::SegmentCommand>] if the Mach-O is 32-bit
|
285
|
-
# @return [Array<MachO::SegmentCommand64>] if the Mach-O is 64-bit
|
284
|
+
# @return [Array<MachO::LoadCommands::SegmentCommand>] if the Mach-O is 32-bit
|
285
|
+
# @return [Array<MachO::LoadCommands::SegmentCommand64>] if the Mach-O is 64-bit
|
286
286
|
def segments
|
287
287
|
if magic32?
|
288
288
|
command(:LC_SEGMENT)
|
@@ -319,10 +319,10 @@ module MachO
|
|
319
319
|
old_lc = command(:LC_ID_DYLIB).first
|
320
320
|
raise DylibIdMissingError unless old_lc
|
321
321
|
|
322
|
-
new_lc = LoadCommand.create(:LC_ID_DYLIB, new_id,
|
323
|
-
|
324
|
-
|
325
|
-
|
322
|
+
new_lc = LoadCommands::LoadCommand.create(:LC_ID_DYLIB, new_id,
|
323
|
+
old_lc.timestamp,
|
324
|
+
old_lc.current_version,
|
325
|
+
old_lc.compatibility_version)
|
326
326
|
|
327
327
|
replace_command(old_lc, new_lc)
|
328
328
|
end
|
@@ -353,10 +353,10 @@ module MachO
|
|
353
353
|
old_lc = dylib_load_commands.find { |d| d.name.to_s == old_name }
|
354
354
|
raise DylibUnknownError, old_name if old_lc.nil?
|
355
355
|
|
356
|
-
new_lc = LoadCommand.create(old_lc.type, new_name,
|
357
|
-
|
358
|
-
|
359
|
-
|
356
|
+
new_lc = LoadCommands::LoadCommand.create(old_lc.type, new_name,
|
357
|
+
old_lc.timestamp,
|
358
|
+
old_lc.current_version,
|
359
|
+
old_lc.compatibility_version)
|
360
360
|
|
361
361
|
replace_command(old_lc, new_lc)
|
362
362
|
end
|
@@ -385,7 +385,7 @@ module MachO
|
|
385
385
|
raise RpathUnknownError, old_path if old_lc.nil?
|
386
386
|
raise RpathExistsError, new_path if rpaths.include?(new_path)
|
387
387
|
|
388
|
-
new_lc = LoadCommand.create(:LC_RPATH, new_path)
|
388
|
+
new_lc = LoadCommands::LoadCommand.create(:LC_RPATH, new_path)
|
389
389
|
|
390
390
|
delete_rpath(old_path)
|
391
391
|
insert_command(old_lc.view.offset, new_lc)
|
@@ -405,7 +405,7 @@ module MachO
|
|
405
405
|
def add_rpath(path, _options = {})
|
406
406
|
raise RpathExistsError, path if rpaths.include?(path)
|
407
407
|
|
408
|
-
rpath_cmd = LoadCommand.create(:LC_RPATH, path)
|
408
|
+
rpath_cmd = LoadCommands::LoadCommand.create(:LC_RPATH, path)
|
409
409
|
add_command(rpath_cmd)
|
410
410
|
end
|
411
411
|
|
@@ -432,10 +432,10 @@ module MachO
|
|
432
432
|
end
|
433
433
|
|
434
434
|
# All sections of the segment `segment`.
|
435
|
-
# @param segment [MachO::SegmentCommand, MachO::SegmentCommand64] the segment being inspected
|
436
|
-
# @return [Array<MachO::Section>] if the Mach-O is 32-bit
|
437
|
-
# @return [Array<MachO::Section64>] if the Mach-O is 64-bit
|
438
|
-
# @deprecated use {MachO::SegmentCommand#sections} instead
|
435
|
+
# @param segment [MachO::LoadCommands::SegmentCommand, MachO::LoadCommands::SegmentCommand64] the segment being inspected
|
436
|
+
# @return [Array<MachO::Sections::Section>] if the Mach-O is 32-bit
|
437
|
+
# @return [Array<MachO::Sections::Section64>] if the Mach-O is 64-bit
|
438
|
+
# @deprecated use {MachO::LoadCommands::SegmentCommand#sections} instead
|
439
439
|
def sections(segment)
|
440
440
|
segment.sections
|
441
441
|
end
|
@@ -462,8 +462,8 @@ module MachO
|
|
462
462
|
private
|
463
463
|
|
464
464
|
# The file's Mach-O header structure.
|
465
|
-
# @return [MachO::MachHeader] if the Mach-O is 32-bit
|
466
|
-
# @return [MachO::MachHeader64] if the Mach-O is 64-bit
|
465
|
+
# @return [MachO::Headers::MachHeader] if the Mach-O is 32-bit
|
466
|
+
# @return [MachO::Headers::MachHeader64] if the Mach-O is 64-bit
|
467
467
|
# @raise [MachO::TruncatedFileError] if the file is too small to have a valid header
|
468
468
|
# @api private
|
469
469
|
def populate_mach_header
|
@@ -471,7 +471,7 @@ module MachO
|
|
471
471
|
raise TruncatedFileError if @raw_data.size < 28
|
472
472
|
|
473
473
|
magic = populate_and_check_magic
|
474
|
-
mh_klass = Utils.magic32?(magic) ? MachHeader : MachHeader64
|
474
|
+
mh_klass = Utils.magic32?(magic) ? Headers::MachHeader : Headers::MachHeader64
|
475
475
|
mh = mh_klass.new_from_bin(endianness, @raw_data[0, mh_klass.bytesize])
|
476
476
|
|
477
477
|
check_cputype(mh.cputype)
|
@@ -502,7 +502,7 @@ module MachO
|
|
502
502
|
# @raise [MachO::CPUTypeError] if the CPU type is unknown
|
503
503
|
# @api private
|
504
504
|
def check_cputype(cputype)
|
505
|
-
raise CPUTypeError, cputype unless CPU_TYPES.key?(cputype)
|
505
|
+
raise CPUTypeError, cputype unless Headers::CPU_TYPES.key?(cputype)
|
506
506
|
end
|
507
507
|
|
508
508
|
# Check the file's CPU type/subtype pair.
|
@@ -511,7 +511,7 @@ module MachO
|
|
511
511
|
# @api private
|
512
512
|
def check_cpusubtype(cputype, cpusubtype)
|
513
513
|
# Only check sub-type w/o capability bits (see `populate_mach_header`).
|
514
|
-
raise CPUSubtypeError.new(cputype, cpusubtype) unless CPU_SUBTYPES[cputype].key?(cpusubtype)
|
514
|
+
raise CPUSubtypeError.new(cputype, cpusubtype) unless Headers::CPU_SUBTYPES[cputype].key?(cpusubtype)
|
515
515
|
end
|
516
516
|
|
517
517
|
# Check the file's type.
|
@@ -519,11 +519,11 @@ module MachO
|
|
519
519
|
# @raise [MachO::FiletypeError] if the file type is unknown
|
520
520
|
# @api private
|
521
521
|
def check_filetype(filetype)
|
522
|
-
raise FiletypeError, filetype unless MH_FILETYPES.key?(filetype)
|
522
|
+
raise FiletypeError, filetype unless Headers::MH_FILETYPES.key?(filetype)
|
523
523
|
end
|
524
524
|
|
525
525
|
# All load commands in the file.
|
526
|
-
# @return [Array<MachO::LoadCommand>] an array of load commands
|
526
|
+
# @return [Array<MachO::LoadCommands::LoadCommand>] an array of load commands
|
527
527
|
# @raise [MachO::LoadCommandError] if an unknown load command is encountered
|
528
528
|
# @api private
|
529
529
|
def populate_load_commands
|
@@ -533,13 +533,13 @@ module MachO
|
|
533
533
|
header.ncmds.times do
|
534
534
|
fmt = Utils.specialize_format("L=", endianness)
|
535
535
|
cmd = @raw_data.slice(offset, 4).unpack(fmt).first
|
536
|
-
cmd_sym = LOAD_COMMANDS[cmd]
|
536
|
+
cmd_sym = LoadCommands::LOAD_COMMANDS[cmd]
|
537
537
|
|
538
538
|
raise LoadCommandError, cmd if cmd_sym.nil?
|
539
539
|
|
540
540
|
# why do I do this? i don't like declaring constants below
|
541
541
|
# classes, and i need them to resolve...
|
542
|
-
klass = MachO.const_get LC_STRUCTURES[cmd_sym]
|
542
|
+
klass = MachO::LoadCommands.const_get LoadCommands::LC_STRUCTURES[cmd_sym]
|
543
543
|
view = MachOView.new(@raw_data, endianness, offset)
|
544
544
|
command = klass.new_from_bin(view)
|
545
545
|
|
data/lib/macho/sections.rb
CHANGED
@@ -1,170 +1,173 @@
|
|
1
1
|
module MachO
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
SECTION_ATTRIBUTES = 0xffffff00
|
7
|
-
|
8
|
-
# user settable attributes mask
|
9
|
-
SECTION_ATTRIBUTES_USR = 0xff000000
|
10
|
-
|
11
|
-
# system settable attributes mask
|
12
|
-
SECTION_ATTRIBUTES_SYS = 0x00ffff00
|
13
|
-
|
14
|
-
# association of section flag symbols to values
|
15
|
-
# @api private
|
16
|
-
SECTION_FLAGS = {
|
17
|
-
:S_REGULAR => 0x0,
|
18
|
-
:S_ZEROFILL => 0x1,
|
19
|
-
:S_CSTRING_LITERALS => 0x2,
|
20
|
-
:S_4BYTE_LITERALS => 0x3,
|
21
|
-
:S_8BYTE_LITERALS => 0x4,
|
22
|
-
:S_LITERAL_POINTERS => 0x5,
|
23
|
-
:S_NON_LAZY_SYMBOL_POINTERS => 0x6,
|
24
|
-
:S_LAZY_SYMBOL_POINTERS => 0x7,
|
25
|
-
:S_SYMBOL_STUBS => 0x8,
|
26
|
-
:S_MOD_INIT_FUNC_POINTERS => 0x9,
|
27
|
-
:S_MOD_TERM_FUNC_POINTERS => 0xa,
|
28
|
-
:S_COALESCED => 0xb,
|
29
|
-
:S_GB_ZEROFILE => 0xc,
|
30
|
-
:S_INTERPOSING => 0xd,
|
31
|
-
:S_16BYTE_LITERALS => 0xe,
|
32
|
-
:S_DTRACE_DOF => 0xf,
|
33
|
-
:S_LAZY_DYLIB_SYMBOL_POINTERS => 0x10,
|
34
|
-
:S_THREAD_LOCAL_REGULAR => 0x11,
|
35
|
-
:S_THREAD_LOCAL_ZEROFILL => 0x12,
|
36
|
-
:S_THREAD_LOCAL_VARIABLES => 0x13,
|
37
|
-
:S_THREAD_LOCAL_VARIABLE_POINTERS => 0x14,
|
38
|
-
:S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => 0x15,
|
39
|
-
:S_ATTR_PURE_INSTRUCTIONS => 0x80000000,
|
40
|
-
:S_ATTR_NO_TOC => 0x40000000,
|
41
|
-
:S_ATTR_STRIP_STATIC_SYMS => 0x20000000,
|
42
|
-
:S_ATTR_NO_DEAD_STRIP => 0x10000000,
|
43
|
-
:S_ATTR_LIVE_SUPPORT => 0x08000000,
|
44
|
-
:S_ATTR_SELF_MODIFYING_CODE => 0x04000000,
|
45
|
-
:S_ATTR_DEBUG => 0x02000000,
|
46
|
-
:S_ATTR_SOME_INSTRUCTIONS => 0x00000400,
|
47
|
-
:S_ATTR_EXT_RELOC => 0x00000200,
|
48
|
-
:S_ATTR_LOC_RELOC => 0x00000100,
|
49
|
-
}.freeze
|
50
|
-
|
51
|
-
# association of section name symbols to names
|
52
|
-
# @api private
|
53
|
-
SECTION_NAMES = {
|
54
|
-
:SECT_TEXT => "__text",
|
55
|
-
:SECT_FVMLIB_INIT0 => "__fvmlib_init0",
|
56
|
-
:SECT_FVMLIB_INIT1 => "__fvmlib_init1",
|
57
|
-
:SECT_DATA => "__data",
|
58
|
-
:SECT_BSS => "__bss",
|
59
|
-
:SECT_COMMON => "__common",
|
60
|
-
:SECT_OBJC_SYMBOLS => "__symbol_table",
|
61
|
-
:SECT_OBJC_MODULES => "__module_info",
|
62
|
-
:SECT_OBJC_STRINGS => "__selector_strs",
|
63
|
-
:SECT_OBJC_REFS => "__selector_refs",
|
64
|
-
:SECT_ICON_HEADER => "__header",
|
65
|
-
:SECT_ICON_TIFF => "__tiff",
|
66
|
-
}.freeze
|
67
|
-
|
68
|
-
# Represents a section of a segment for 32-bit architectures.
|
69
|
-
class Section < MachOStructure
|
70
|
-
# @return [String] the name of the section, including null pad bytes
|
71
|
-
attr_reader :sectname
|
72
|
-
|
73
|
-
# @return [String] the name of the segment's section, including null pad bytes
|
74
|
-
attr_reader :segname
|
75
|
-
|
76
|
-
# @return [Fixnum] the memory address of the section
|
77
|
-
attr_reader :addr
|
78
|
-
|
79
|
-
# @return [Fixnum] the size, in bytes, of the section
|
80
|
-
attr_reader :size
|
81
|
-
|
82
|
-
# @return [Fixnum] the file offset of the section
|
83
|
-
attr_reader :offset
|
84
|
-
|
85
|
-
# @return [Fixnum] the section alignment (power of 2) of the section
|
86
|
-
attr_reader :align
|
87
|
-
|
88
|
-
# @return [Fixnum] the file offset of the section's relocation entries
|
89
|
-
attr_reader :reloff
|
90
|
-
|
91
|
-
# @return [Fixnum] the number of relocation entries
|
92
|
-
attr_reader :nreloc
|
93
|
-
|
94
|
-
# @return [Fixnum] flags for type and attributes of the section
|
95
|
-
attr_reader :flags
|
96
|
-
|
97
|
-
# @return [void] reserved (for offset or index)
|
98
|
-
attr_reader :reserved1
|
99
|
-
|
100
|
-
# @return [void] reserved (for count or sizeof)
|
101
|
-
attr_reader :reserved2
|
102
|
-
|
103
|
-
# @see MachOStructure::FORMAT
|
104
|
-
FORMAT = "a16a16L=9".freeze
|
105
|
-
|
106
|
-
# @see MachOStructure::SIZEOF
|
107
|
-
SIZEOF = 68
|
2
|
+
# Classes and constants for parsing sections in Mach-O binaries.
|
3
|
+
module Sections
|
4
|
+
# type mask
|
5
|
+
SECTION_TYPE = 0x000000ff
|
108
6
|
|
109
|
-
#
|
110
|
-
|
111
|
-
nreloc, flags, reserved1, reserved2)
|
112
|
-
@sectname = sectname
|
113
|
-
@segname = segname
|
114
|
-
@addr = addr
|
115
|
-
@size = size
|
116
|
-
@offset = offset
|
117
|
-
@align = align
|
118
|
-
@reloff = reloff
|
119
|
-
@nreloc = nreloc
|
120
|
-
@flags = flags
|
121
|
-
@reserved1 = reserved1
|
122
|
-
@reserved2 = reserved2
|
123
|
-
end
|
7
|
+
# attributes mask
|
8
|
+
SECTION_ATTRIBUTES = 0xffffff00
|
124
9
|
|
125
|
-
#
|
126
|
-
|
127
|
-
sectname.delete("\x00")
|
128
|
-
end
|
129
|
-
|
130
|
-
# @return [String] the parent segment's name, with any trailing NULL characters removed
|
131
|
-
def segment_name
|
132
|
-
segname.delete("\x00")
|
133
|
-
end
|
10
|
+
# user settable attributes mask
|
11
|
+
SECTION_ATTRIBUTES_USR = 0xff000000
|
134
12
|
|
135
|
-
#
|
136
|
-
|
137
|
-
size.zero?
|
138
|
-
end
|
13
|
+
# system settable attributes mask
|
14
|
+
SECTION_ATTRIBUTES_SYS = 0x00ffff00
|
139
15
|
|
140
|
-
#
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
16
|
+
# association of section flag symbols to values
|
17
|
+
# @api private
|
18
|
+
SECTION_FLAGS = {
|
19
|
+
:S_REGULAR => 0x0,
|
20
|
+
:S_ZEROFILL => 0x1,
|
21
|
+
:S_CSTRING_LITERALS => 0x2,
|
22
|
+
:S_4BYTE_LITERALS => 0x3,
|
23
|
+
:S_8BYTE_LITERALS => 0x4,
|
24
|
+
:S_LITERAL_POINTERS => 0x5,
|
25
|
+
:S_NON_LAZY_SYMBOL_POINTERS => 0x6,
|
26
|
+
:S_LAZY_SYMBOL_POINTERS => 0x7,
|
27
|
+
:S_SYMBOL_STUBS => 0x8,
|
28
|
+
:S_MOD_INIT_FUNC_POINTERS => 0x9,
|
29
|
+
:S_MOD_TERM_FUNC_POINTERS => 0xa,
|
30
|
+
:S_COALESCED => 0xb,
|
31
|
+
:S_GB_ZEROFILE => 0xc,
|
32
|
+
:S_INTERPOSING => 0xd,
|
33
|
+
:S_16BYTE_LITERALS => 0xe,
|
34
|
+
:S_DTRACE_DOF => 0xf,
|
35
|
+
:S_LAZY_DYLIB_SYMBOL_POINTERS => 0x10,
|
36
|
+
:S_THREAD_LOCAL_REGULAR => 0x11,
|
37
|
+
:S_THREAD_LOCAL_ZEROFILL => 0x12,
|
38
|
+
:S_THREAD_LOCAL_VARIABLES => 0x13,
|
39
|
+
:S_THREAD_LOCAL_VARIABLE_POINTERS => 0x14,
|
40
|
+
:S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => 0x15,
|
41
|
+
:S_ATTR_PURE_INSTRUCTIONS => 0x80000000,
|
42
|
+
:S_ATTR_NO_TOC => 0x40000000,
|
43
|
+
:S_ATTR_STRIP_STATIC_SYMS => 0x20000000,
|
44
|
+
:S_ATTR_NO_DEAD_STRIP => 0x10000000,
|
45
|
+
:S_ATTR_LIVE_SUPPORT => 0x08000000,
|
46
|
+
:S_ATTR_SELF_MODIFYING_CODE => 0x04000000,
|
47
|
+
:S_ATTR_DEBUG => 0x02000000,
|
48
|
+
:S_ATTR_SOME_INSTRUCTIONS => 0x00000400,
|
49
|
+
:S_ATTR_EXT_RELOC => 0x00000200,
|
50
|
+
:S_ATTR_LOC_RELOC => 0x00000100,
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
# association of section name symbols to names
|
54
|
+
# @api private
|
55
|
+
SECTION_NAMES = {
|
56
|
+
:SECT_TEXT => "__text",
|
57
|
+
:SECT_FVMLIB_INIT0 => "__fvmlib_init0",
|
58
|
+
:SECT_FVMLIB_INIT1 => "__fvmlib_init1",
|
59
|
+
:SECT_DATA => "__data",
|
60
|
+
:SECT_BSS => "__bss",
|
61
|
+
:SECT_COMMON => "__common",
|
62
|
+
:SECT_OBJC_SYMBOLS => "__symbol_table",
|
63
|
+
:SECT_OBJC_MODULES => "__module_info",
|
64
|
+
:SECT_OBJC_STRINGS => "__selector_strs",
|
65
|
+
:SECT_OBJC_REFS => "__selector_refs",
|
66
|
+
:SECT_ICON_HEADER => "__header",
|
67
|
+
:SECT_ICON_TIFF => "__tiff",
|
68
|
+
}.freeze
|
69
|
+
|
70
|
+
# Represents a section of a segment for 32-bit architectures.
|
71
|
+
class Section < MachOStructure
|
72
|
+
# @return [String] the name of the section, including null pad bytes
|
73
|
+
attr_reader :sectname
|
74
|
+
|
75
|
+
# @return [String] the name of the segment's section, including null pad bytes
|
76
|
+
attr_reader :segname
|
77
|
+
|
78
|
+
# @return [Fixnum] the memory address of the section
|
79
|
+
attr_reader :addr
|
80
|
+
|
81
|
+
# @return [Fixnum] the size, in bytes, of the section
|
82
|
+
attr_reader :size
|
83
|
+
|
84
|
+
# @return [Fixnum] the file offset of the section
|
85
|
+
attr_reader :offset
|
86
|
+
|
87
|
+
# @return [Fixnum] the section alignment (power of 2) of the section
|
88
|
+
attr_reader :align
|
89
|
+
|
90
|
+
# @return [Fixnum] the file offset of the section's relocation entries
|
91
|
+
attr_reader :reloff
|
92
|
+
|
93
|
+
# @return [Fixnum] the number of relocation entries
|
94
|
+
attr_reader :nreloc
|
95
|
+
|
96
|
+
# @return [Fixnum] flags for type and attributes of the section
|
97
|
+
attr_reader :flags
|
98
|
+
|
99
|
+
# @return [void] reserved (for offset or index)
|
100
|
+
attr_reader :reserved1
|
101
|
+
|
102
|
+
# @return [void] reserved (for count or sizeof)
|
103
|
+
attr_reader :reserved2
|
104
|
+
|
105
|
+
# @see MachOStructure::FORMAT
|
106
|
+
FORMAT = "a16a16L=9".freeze
|
107
|
+
|
108
|
+
# @see MachOStructure::SIZEOF
|
109
|
+
SIZEOF = 68
|
110
|
+
|
111
|
+
# @api private
|
112
|
+
def initialize(sectname, segname, addr, size, offset, align, reloff,
|
113
|
+
nreloc, flags, reserved1, reserved2)
|
114
|
+
@sectname = sectname
|
115
|
+
@segname = segname
|
116
|
+
@addr = addr
|
117
|
+
@size = size
|
118
|
+
@offset = offset
|
119
|
+
@align = align
|
120
|
+
@reloff = reloff
|
121
|
+
@nreloc = nreloc
|
122
|
+
@flags = flags
|
123
|
+
@reserved1 = reserved1
|
124
|
+
@reserved2 = reserved2
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [String] the section's name, with any trailing NULL characters removed
|
128
|
+
def section_name
|
129
|
+
sectname.delete("\x00")
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [String] the parent segment's name, with any trailing NULL characters removed
|
133
|
+
def segment_name
|
134
|
+
segname.delete("\x00")
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [Boolean] true if the section has no contents (i.e, `size` is 0)
|
138
|
+
def empty?
|
139
|
+
size.zero?
|
140
|
+
end
|
141
|
+
|
142
|
+
# @example
|
143
|
+
# puts "this section is regular" if sect.flag?(:S_REGULAR)
|
144
|
+
# @param flag [Symbol] a section flag symbol
|
145
|
+
# @return [Boolean] true if `flag` is present in the section's flag field
|
146
|
+
def flag?(flag)
|
147
|
+
flag = SECTION_FLAGS[flag]
|
148
|
+
return false if flag.nil?
|
149
|
+
flags & flag == flag
|
150
|
+
end
|
148
151
|
end
|
149
|
-
end
|
150
152
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
153
|
+
# Represents a section of a segment for 64-bit architectures.
|
154
|
+
class Section64 < Section
|
155
|
+
# @return [void] reserved
|
156
|
+
attr_reader :reserved3
|
155
157
|
|
156
|
-
|
157
|
-
|
158
|
+
# @see MachOStructure::FORMAT
|
159
|
+
FORMAT = "a16a16Q=2L=8".freeze
|
158
160
|
|
159
|
-
|
160
|
-
|
161
|
+
# @see MachOStructure::SIZEOF
|
162
|
+
SIZEOF = 80
|
161
163
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
# @api private
|
165
|
+
def initialize(sectname, segname, addr, size, offset, align, reloff,
|
166
|
+
nreloc, flags, reserved1, reserved2, reserved3)
|
167
|
+
super(sectname, segname, addr, size, offset, align, reloff,
|
168
|
+
nreloc, flags, reserved1, reserved2)
|
169
|
+
@reserved3 = reserved3
|
170
|
+
end
|
168
171
|
end
|
169
172
|
end
|
170
173
|
end
|