ruby-macho 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cstruct.rb +0 -28
- data/lib/macho/fat_file.rb +53 -9
- data/lib/macho/macho_file.rb +68 -23
- data/lib/macho.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 119afdca4c1793c3bc3f8285c88a8126ab4e1e76
|
4
|
+
data.tar.gz: 26346fa156b237e2606e8b005c95330bc1b194ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 413afe10c917e3e7365711b821904f058d48a9c92a6fcf36d1fecd132b1bbd5971e2d6e547b1684fb46061a328cfaf71afc35788068a1480d33bbde24a0e6d1d
|
7
|
+
data.tar.gz: cf66bd02bac6b2956bbad64d2f0f82de46e339713ab9ddbfecea88902399f304a58fcb8588294025f647180077f4e054feb40ed85bbeaee941aa00aed26babca
|
data/lib/cstruct.rb
CHANGED
@@ -316,31 +316,3 @@ class CStruct
|
|
316
316
|
# The last expression is returned, so return self instead of junk.
|
317
317
|
self
|
318
318
|
end
|
319
|
-
|
320
|
-
|
321
|
-
# a small test
|
322
|
-
if $0 == __FILE__
|
323
|
-
class MachHeader < CStruct
|
324
|
-
uint :magic
|
325
|
-
int :cputype
|
326
|
-
int :cpusubtype
|
327
|
-
string :segname, 16
|
328
|
-
end
|
329
|
-
puts MachHeader::Members.inspect
|
330
|
-
puts MachHeader::MemberIndex.inspect
|
331
|
-
puts MachHeader::MemberSizes.inspect
|
332
|
-
puts "# of MachHeader members: " + MachHeader.size.to_s + ", size in bytes: " + MachHeader.bytesize.to_s
|
333
|
-
mh = MachHeader.new(0xfeedface, 7, 3, "foobar")
|
334
|
-
%w[magic cputype cpusubtype segname].each do |field|
|
335
|
-
puts "#{field}(#{MachHeader.sizeof(field.to_sym)}): #{mh[field.to_sym].inspect}"
|
336
|
-
end
|
337
|
-
puts mh.pack_pattern.inspect
|
338
|
-
binstr = mh.serialize
|
339
|
-
puts "values: " + mh.values.inspect
|
340
|
-
newmh = MachHeader.new_from_bin(binstr)
|
341
|
-
puts "new values: " + newmh.values.inspect
|
342
|
-
newbinstr = newmh.serialize
|
343
|
-
puts "serialized: " + binstr.inspect
|
344
|
-
puts "unserialized: " + newbinstr.inspect
|
345
|
-
puts "new == old ? " + (newbinstr == binstr).to_s
|
346
|
-
end
|
data/lib/macho/fat_file.rb
CHANGED
@@ -1,7 +1,22 @@
|
|
1
1
|
module MachO
|
2
|
+
# Represents a "Fat" file, which contains a header, a listing of available
|
3
|
+
# architectures, and one or more Mach-O binaries.
|
4
|
+
# @see https://en.wikipedia.org/wiki/Mach-O#Multi-architecture_binaries
|
5
|
+
# @see MachO::MachOFile
|
2
6
|
class FatFile
|
3
|
-
|
7
|
+
# @return [MachO::FatHeader] the file's header
|
8
|
+
attr_reader :header
|
4
9
|
|
10
|
+
# @return [Array<MachO::FatArch>] an array of fat architectures
|
11
|
+
attr_reader :fat_archs
|
12
|
+
|
13
|
+
# @return [Array<MachO::MachOFile>] an array of Mach-O binaries
|
14
|
+
attr_reader :machos
|
15
|
+
|
16
|
+
# Creates a new FatFile from the given filename.
|
17
|
+
# @param filename [String] the fat file to load from
|
18
|
+
# @raise [MachO::MagicError] if the file does not have a Mach-O magic number.
|
19
|
+
# @raise [MachO::MachOBinaryError] if the file is not a fat binary.
|
5
20
|
def initialize(filename)
|
6
21
|
raise ArgumentError.new("filename must be a String") unless filename.is_a? String
|
7
22
|
|
@@ -12,10 +27,16 @@ module MachO
|
|
12
27
|
@machos = get_machos
|
13
28
|
end
|
14
29
|
|
30
|
+
# The file's raw fat data.
|
31
|
+
# @return [String] the raw fat data
|
15
32
|
def serialize
|
16
33
|
@raw_data
|
17
34
|
end
|
18
35
|
|
36
|
+
# The file's dylib ID. If the file is not a dylib, returns `nil`.
|
37
|
+
# @example
|
38
|
+
# file.dylib_id # => 'libBar.dylib'
|
39
|
+
# @return [String] the file's dylib ID
|
19
40
|
def dylib_id
|
20
41
|
if !machos.all?(&:dylib?)
|
21
42
|
return nil
|
@@ -31,6 +52,10 @@ module MachO
|
|
31
52
|
ids.first
|
32
53
|
end
|
33
54
|
|
55
|
+
# Changes the file's dylib ID to `new_id`. If the file is not a dylib, does nothing.
|
56
|
+
# @example
|
57
|
+
# file.dylib_id = 'libFoo.dylib'
|
58
|
+
# @param new_id [String] the new dylib ID
|
34
59
|
def dylib_id=(new_id)
|
35
60
|
if !new_id.is_a?(String)
|
36
61
|
raise ArgumentError.new("argument must be a String")
|
@@ -47,6 +72,8 @@ module MachO
|
|
47
72
|
synchronize_raw_data
|
48
73
|
end
|
49
74
|
|
75
|
+
# All shared libraries linked to the file's Mach-Os.
|
76
|
+
# @return [Array<String>] an array of all shared libraries
|
50
77
|
def linked_dylibs
|
51
78
|
dylibs = machos.map(&:linked_dylibs).flatten
|
52
79
|
|
@@ -54,27 +81,40 @@ module MachO
|
|
54
81
|
dylibs.uniq!
|
55
82
|
end
|
56
83
|
|
57
|
-
#
|
58
|
-
|
84
|
+
# Changes all dependent shared library install names from `old_name` to `new_name`.
|
85
|
+
# In a fat file, this changes install names in all internal Mach-Os.
|
86
|
+
# @example
|
87
|
+
# file.change_install_name('/usr/lib/libFoo.dylib', '/usr/lib/libBar.dylib')
|
88
|
+
# @param old_name [String] the shared library name being changed
|
89
|
+
# @param new_name [String] the new name
|
90
|
+
# @todo incomplete
|
91
|
+
def change_install_name(old_name, new_name)
|
59
92
|
machos.each do |macho|
|
60
|
-
macho.change_install_name(
|
93
|
+
macho.change_install_name(old_name, new_name)
|
61
94
|
end
|
62
95
|
|
63
96
|
synchronize_raw_data
|
64
97
|
end
|
65
98
|
|
66
|
-
alias :
|
99
|
+
alias :change_dylib :change_install_name
|
67
100
|
|
101
|
+
# Write all (fat) data to the given filename.
|
102
|
+
# @param filename [String] the file to write to
|
68
103
|
def write(filename)
|
69
104
|
File.open(filename, "wb") { |f| f.write(@raw_data) }
|
70
105
|
end
|
71
106
|
|
107
|
+
# Write all (fat) data to the file used to initialize the instance.
|
108
|
+
# IMPORTANT: Overwrites all data in the file!
|
72
109
|
def write!
|
73
110
|
File.open(@filename, "wb") { |f| f.write(@raw_data) }
|
74
111
|
end
|
75
112
|
|
76
113
|
private
|
77
114
|
|
115
|
+
# Obtain the fat header from raw file data.
|
116
|
+
# @return [MachO::FatHeader] the fat header
|
117
|
+
# @private
|
78
118
|
def get_fat_header
|
79
119
|
magic, nfat_arch = @raw_data[0..7].unpack("N2")
|
80
120
|
|
@@ -89,6 +129,9 @@ module MachO
|
|
89
129
|
FatHeader.new(magic, nfat_arch)
|
90
130
|
end
|
91
131
|
|
132
|
+
# Obtain an array of fat architectures from raw file data.
|
133
|
+
# @return [Array<MachO::FatArch>] an array of fat architectures
|
134
|
+
# @private
|
92
135
|
def get_fat_archs
|
93
136
|
archs = []
|
94
137
|
|
@@ -100,6 +143,9 @@ module MachO
|
|
100
143
|
archs
|
101
144
|
end
|
102
145
|
|
146
|
+
# Obtain an array of Mach-O blobs from raw file data.
|
147
|
+
# @return [Array<MachO::MachOFile>] an array of Mach-Os
|
148
|
+
# @private
|
103
149
|
def get_machos
|
104
150
|
machos = []
|
105
151
|
|
@@ -110,10 +156,8 @@ module MachO
|
|
110
156
|
machos
|
111
157
|
end
|
112
158
|
|
113
|
-
#
|
114
|
-
#
|
115
|
-
# @raw_data directly, so we need to synchronize it after changing
|
116
|
-
# anything within the machos.
|
159
|
+
# @todo this needs to be redesigned. arch[:offset] and arch[:size] are
|
160
|
+
# already out-of-date, and the header needs to be synchronized as well.
|
117
161
|
def synchronize_raw_data
|
118
162
|
machos.each_with_index do |macho, i|
|
119
163
|
arch = fat_archs[i]
|
data/lib/macho/macho_file.rb
CHANGED
@@ -1,7 +1,19 @@
|
|
1
1
|
module MachO
|
2
|
+
# Represents a Mach-O file, which contains a header and load commands
|
3
|
+
# as well as binary executable instructions. Mach-O binaries are
|
4
|
+
# architecture specific.
|
5
|
+
# @see https://en.wikipedia.org/wiki/Mach-O
|
2
6
|
class MachOFile
|
3
|
-
|
7
|
+
# @return [MachO::MachHeader] if the Mach-O is 32-bit
|
8
|
+
# @return [MachO::MachHeader64] if the Mach-O is 64-bit
|
9
|
+
attr_reader :header
|
4
10
|
|
11
|
+
# @return [Array<MachO::LoadCommand>] an array of the file's load commands
|
12
|
+
attr_reader :load_commands
|
13
|
+
|
14
|
+
# Creates a new MachOFile instance from a binary string.
|
15
|
+
# @param bin [String] a binary string containing raw Mach-O data
|
16
|
+
# @return [MachO::MachOFile] a new MachOFile
|
5
17
|
def self.new_from_bin(bin)
|
6
18
|
instance = allocate
|
7
19
|
instance.initialize_from_bin(bin)
|
@@ -9,6 +21,9 @@ module MachO
|
|
9
21
|
instance
|
10
22
|
end
|
11
23
|
|
24
|
+
# Creates a new FatFile from the given filename.
|
25
|
+
# @param filename [String] the Mach-O file to load from
|
26
|
+
# @todo document all the exceptions propagated here
|
12
27
|
def initialize(filename)
|
13
28
|
raise ArgumentError.new("filename must be a String") unless filename.is_a? String
|
14
29
|
|
@@ -18,6 +33,7 @@ module MachO
|
|
18
33
|
@load_commands = get_load_commands
|
19
34
|
end
|
20
35
|
|
36
|
+
# @private
|
21
37
|
def initialize_from_bin(bin)
|
22
38
|
@filename = nil
|
23
39
|
@raw_data = bin
|
@@ -25,80 +41,91 @@ module MachO
|
|
25
41
|
@load_commands = get_load_commands
|
26
42
|
end
|
27
43
|
|
44
|
+
# The file's raw Mach-O data.
|
45
|
+
# @return [String] the raw Mach-O data
|
28
46
|
def serialize
|
29
47
|
@raw_data
|
30
48
|
end
|
31
49
|
|
50
|
+
# @return [Boolean] true if the Mach-O has 32-bit magic, false otherwise
|
32
51
|
def magic32?
|
33
52
|
MachO.magic32?(header[:magic])
|
34
53
|
end
|
35
54
|
|
55
|
+
# @return [Boolean] true if the Mach-O has 64-bit magic, false otherwise
|
36
56
|
def magic64?
|
37
57
|
MachO.magic64?(header[:magic])
|
38
58
|
end
|
39
59
|
|
40
|
-
#
|
60
|
+
# @return [Boolean] true if the Mach-O is of type `MH_EXECUTE`, false otherwise
|
41
61
|
def executable?
|
42
62
|
header[:filetype] == MH_EXECUTE
|
43
63
|
end
|
44
64
|
|
45
|
-
#
|
65
|
+
# @return [Boolean] true if the Mach-O is of type `MH_DYLIB`, false otherwise
|
46
66
|
def dylib?
|
47
67
|
header[:filetype] == MH_DYLIB
|
48
68
|
end
|
49
69
|
|
50
|
-
#
|
70
|
+
# @return [Boolean] true if the Mach-O is of type `MH_BUNDLE`, false otherwise
|
51
71
|
def bundle?
|
52
72
|
header[:filetype] == MH_BUNDLE
|
53
73
|
end
|
54
74
|
|
75
|
+
# @return [Fixnum] the Mach-O's magic number
|
55
76
|
def magic
|
56
77
|
header[:magic]
|
57
78
|
end
|
58
79
|
|
59
|
-
# string representation of the
|
80
|
+
# @return [String] a string representation of the Mach-O's magic number
|
60
81
|
def magic_string
|
61
82
|
MH_MAGICS[header[:magic]]
|
62
83
|
end
|
63
84
|
|
64
|
-
# string representation of the
|
85
|
+
# @return [String] a string representation of the Mach-O's filetype
|
65
86
|
def filetype
|
66
87
|
MH_FILETYPES[header[:filetype]]
|
67
88
|
end
|
68
89
|
|
69
|
-
# string representation of the
|
90
|
+
# @return [String] a string representation of the Mach-O's CPU type
|
70
91
|
def cputype
|
71
92
|
CPU_TYPES[header[:cputype]]
|
72
93
|
end
|
73
94
|
|
74
|
-
# string representation of the
|
95
|
+
# @return [String] a string representation of the Mach-O's CPU subtype
|
75
96
|
def cpusubtype
|
76
97
|
CPU_SUBTYPES[header[:cpusubtype]]
|
77
98
|
end
|
78
99
|
|
79
|
-
# number of load commands in the header
|
100
|
+
# @return [Fixnum] the number of load commands in the Mach-O's header
|
80
101
|
def ncmds
|
81
102
|
header[:ncmds]
|
82
103
|
end
|
83
104
|
|
84
|
-
# size of all load commands
|
105
|
+
# @return [Fixnum] the size of all load commands, in bytes
|
85
106
|
def sizeofcmds
|
86
107
|
header[:sizeofcmds]
|
87
108
|
end
|
88
109
|
|
89
|
-
#
|
110
|
+
# @return [Fixnum] execution flags set by the linker
|
90
111
|
def flags
|
91
112
|
header[:flags]
|
92
113
|
end
|
93
114
|
|
94
|
-
#
|
115
|
+
# All load commands of a given name.
|
116
|
+
# @example
|
117
|
+
# file.command("LC_LOAD_DYLIB")
|
118
|
+
# file["LC_LOAD_DYLIB"]
|
119
|
+
# @return [Array<MachO::LoadCommand>] an array of LoadCommands corresponding to `name`
|
95
120
|
def command(name)
|
96
121
|
load_commands.select { |lc| lc.to_s == name }
|
97
122
|
end
|
98
123
|
|
99
124
|
alias :[] :command
|
100
125
|
|
101
|
-
#
|
126
|
+
# All segment load commands in the Mach-O.
|
127
|
+
# @return [Array<MachO::SegmentCommand>] if the Mach-O is 32-bit
|
128
|
+
# @return [Array<MachO::SegmentCommand64>] if the Mach-O is 64-bit
|
102
129
|
def segments
|
103
130
|
if magic32?
|
104
131
|
command("LC_SEGMENT")
|
@@ -107,7 +134,10 @@ module MachO
|
|
107
134
|
end
|
108
135
|
end
|
109
136
|
|
110
|
-
#
|
137
|
+
# The Mach-O's dylib ID, or `nil` if not a dylib.
|
138
|
+
# @example
|
139
|
+
# file.dylib_id # => 'libBar.dylib'
|
140
|
+
# @return [String] the Mach-O's dylib ID
|
111
141
|
def dylib_id
|
112
142
|
if !dylib?
|
113
143
|
return nil
|
@@ -124,6 +154,11 @@ module MachO
|
|
124
154
|
dylib_id.delete("\x00")
|
125
155
|
end
|
126
156
|
|
157
|
+
# Changes the Mach-O's dylib ID to `new_id`. Does nothing if not a dylib.
|
158
|
+
# @example
|
159
|
+
# file.dylib_id = "libFoo.dylib"
|
160
|
+
# @return [void]
|
161
|
+
# @todo refactor
|
127
162
|
def dylib_id=(new_id)
|
128
163
|
if !new_id.is_a?(String)
|
129
164
|
raise ArgumentError.new("argument must be a String")
|
@@ -199,7 +234,8 @@ module MachO
|
|
199
234
|
load_commands = get_load_commands
|
200
235
|
end
|
201
236
|
|
202
|
-
#
|
237
|
+
# All shared libraries linked to the Mach-O.
|
238
|
+
# @return [Array<String>] an array of all shared libraries
|
203
239
|
def linked_dylibs
|
204
240
|
dylibs = []
|
205
241
|
dylib_cmds = command('LC_LOAD_DYLIB')
|
@@ -217,10 +253,11 @@ module MachO
|
|
217
253
|
dylibs
|
218
254
|
end
|
219
255
|
|
220
|
-
#
|
221
|
-
|
222
|
-
|
223
|
-
|
256
|
+
# @return [void]
|
257
|
+
# @todo refactor
|
258
|
+
def change_install_name(old_name, new_name)
|
259
|
+
idx = linked_dylibs.index(old_name)
|
260
|
+
raise DylibUnknownError.new(old_name) if idx.nil?
|
224
261
|
|
225
262
|
# this is a bit of a hack - since there is a 1-1 ordered association
|
226
263
|
# between linked_dylibs and command('LC_LOAD_DYLIB'), we can use
|
@@ -234,8 +271,8 @@ module MachO
|
|
234
271
|
end
|
235
272
|
|
236
273
|
new_sizeofcmds = header[:sizeofcmds]
|
237
|
-
old_install_name =
|
238
|
-
new_install_name =
|
274
|
+
old_install_name = old_name.dup
|
275
|
+
new_install_name = new_name.dup
|
239
276
|
|
240
277
|
old_pad = MachO.round(old_install_name.size, cmd_round) - old_install_name.size
|
241
278
|
new_pad = MachO.round(new_install_name.size, cmd_round) - new_install_name.size
|
@@ -283,9 +320,11 @@ module MachO
|
|
283
320
|
load_commands = get_load_commands
|
284
321
|
end
|
285
322
|
|
286
|
-
alias :
|
323
|
+
alias :change_dylib :change_install_name
|
287
324
|
|
288
|
-
#
|
325
|
+
# All sections of the segment `segment`.
|
326
|
+
# @return [Array<MachO::Section>] if the Mach-O is 32-bit
|
327
|
+
# @return [Array<MachO::Section64>] if the Mach-O is 64-bit
|
289
328
|
def sections(segment)
|
290
329
|
sections = []
|
291
330
|
|
@@ -312,10 +351,16 @@ module MachO
|
|
312
351
|
sections
|
313
352
|
end
|
314
353
|
|
354
|
+
# Write all Mach-O data to the given filename.
|
355
|
+
# @param filename [String] the file to write to
|
356
|
+
# @return [void]
|
315
357
|
def write(filename)
|
316
358
|
File.open(filename, "wb") { |f| f.write(@raw_data) }
|
317
359
|
end
|
318
360
|
|
361
|
+
# Write all Mach-O data to the file used to initialize the instance.
|
362
|
+
# @raise [MachOError] if the instance was created from a binary string
|
363
|
+
# @return [void]
|
319
364
|
def write!
|
320
365
|
if @filename.nil?
|
321
366
|
raise MachOError.new("cannot write to a default file when initialized from a binary string")
|
data/lib/macho.rb
CHANGED
@@ -10,6 +10,10 @@ require "#{File.dirname(__FILE__)}/macho/utils"
|
|
10
10
|
require "#{File.dirname(__FILE__)}/macho/tools"
|
11
11
|
|
12
12
|
module MachO
|
13
|
+
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
|
14
|
+
# @param filename [String] the file being opened
|
15
|
+
# @return [MachO::MachOFile] if the file is a Mach-O
|
16
|
+
# @return [MachO::FatFile] if the file is a Fat file
|
13
17
|
def self.open(filename)
|
14
18
|
# open file and test magic instead of using exceptions for control?
|
15
19
|
begin
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-macho
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Woodruff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A library for viewing and manipulating Mach-O files in Ruby.
|
14
14
|
email: william@tuffbizz.com
|