ruby-macho 0.2.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|