ruby-macho 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/macho.rb +1 -1
- data/lib/macho/exceptions.rb +0 -10
- data/lib/macho/fat_file.rb +16 -36
- data/lib/macho/headers.rb +6 -78
- data/lib/macho/load_commands.rb +0 -5
- data/lib/macho/macho_file.rb +0 -1
- data/lib/macho/sections.rb +0 -2
- data/lib/macho/tools.rb +2 -3
- data/lib/macho/utils.rb +5 -19
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6341c1a29aeae7169fea7f1e7fc11339331f21c2b07a3a19e7f68b5dd76af03d
|
4
|
+
data.tar.gz: 9b5dd29c4a03c18bf6261e20641c95c584958ee142d4d64a37f9db8352065727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3964af02f0b1abf51de625e1d69a8fd966c30d5bf93cfde410ac127fab57864ae6331441d2afa1acdd100c7187f5d05883ecd8736d88076507823b16627ab10
|
7
|
+
data.tar.gz: c5a8ec50db69dccef0161dd96d6b7f23a47713a0fb8551d1bae10afa2690ed6837b52f9f3f49bd0826610b7c7a1841fadda2d1c53f6ab91f118732e36a3a4bd7
|
data/lib/macho.rb
CHANGED
@@ -12,7 +12,7 @@ require_relative "macho/tools"
|
|
12
12
|
# The primary namespace for ruby-macho.
|
13
13
|
module MachO
|
14
14
|
# release version
|
15
|
-
VERSION = "
|
15
|
+
VERSION = "2.0.0".freeze
|
16
16
|
|
17
17
|
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
|
18
18
|
# @param filename [String] the file being opened
|
data/lib/macho/exceptions.rb
CHANGED
@@ -194,14 +194,4 @@ module MachO
|
|
194
194
|
super "Unimplemented: #{thing}"
|
195
195
|
end
|
196
196
|
end
|
197
|
-
|
198
|
-
# Raised when attempting to create a {FatFile} from one or more {MachOFile}s
|
199
|
-
# whose offsets will not fit within the resulting 32-bit {Headers::FatArch#offset} fields.
|
200
|
-
class FatArchOffsetOverflowError < MachOError
|
201
|
-
# @param offset [Integer] the offending offset
|
202
|
-
def initialize(offset)
|
203
|
-
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset." \
|
204
|
-
" Consider merging with `fat64: true`"
|
205
|
-
end
|
206
|
-
end
|
207
197
|
end
|
data/lib/macho/fat_file.rb
CHANGED
@@ -14,7 +14,7 @@ module MachO
|
|
14
14
|
# @return [Headers::FatHeader] the file's header
|
15
15
|
attr_reader :header
|
16
16
|
|
17
|
-
# @return [Array<Headers::FatArch
|
17
|
+
# @return [Array<Headers::FatArch>] an array of fat architectures
|
18
18
|
attr_reader :fat_archs
|
19
19
|
|
20
20
|
# @return [Array<MachOFile>] an array of Mach-O binaries
|
@@ -22,53 +22,37 @@ module MachO
|
|
22
22
|
|
23
23
|
# Creates a new FatFile from the given (single-arch) Mach-Os
|
24
24
|
# @param machos [Array<MachOFile>] the machos to combine
|
25
|
-
# @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
|
26
25
|
# @return [FatFile] a new FatFile containing the give machos
|
27
26
|
# @raise [ArgumentError] if less than one Mach-O is given
|
28
|
-
|
29
|
-
# in a 32-bit {Headers::FatArch} and `fat64` is `false`.
|
30
|
-
def self.new_from_machos(*machos, fat64: false)
|
27
|
+
def self.new_from_machos(*machos)
|
31
28
|
raise ArgumentError, "expected at least one Mach-O" if machos.empty?
|
32
29
|
|
33
|
-
fa_klass, magic = if fat64
|
34
|
-
[Headers::FatArch64, Headers::FAT_MAGIC_64]
|
35
|
-
else
|
36
|
-
[Headers::FatArch, Headers::FAT_MAGIC]
|
37
|
-
end
|
38
|
-
|
39
30
|
# put the smaller alignments further forwards in fat macho, so that we do less padding
|
40
31
|
machos = machos.sort_by(&:segment_alignment)
|
41
32
|
|
42
|
-
bin =
|
43
|
-
+""
|
44
|
-
else
|
45
|
-
""
|
46
|
-
end
|
33
|
+
bin = +""
|
47
34
|
|
48
|
-
bin << Headers::FatHeader.new(
|
49
|
-
offset = Headers::FatHeader.bytesize + (machos.size *
|
35
|
+
bin << Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size).serialize
|
36
|
+
offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize)
|
50
37
|
|
51
38
|
macho_pads = {}
|
39
|
+
macho_bins = {}
|
52
40
|
|
53
41
|
machos.each do |macho|
|
54
|
-
macho_offset
|
55
|
-
|
56
|
-
if !fat64 && macho_offset > (2**32 - 1)
|
57
|
-
raise FatArchOffsetOverflowError, macho_offset
|
58
|
-
end
|
59
|
-
|
42
|
+
macho_offset = Utils.round(offset, 2**macho.segment_alignment)
|
60
43
|
macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
|
44
|
+
macho_bins[macho] = macho.serialize
|
61
45
|
|
62
|
-
bin <<
|
63
|
-
|
64
|
-
|
46
|
+
bin << Headers::FatArch.new(macho.header.cputype, macho.header.cpusubtype,
|
47
|
+
macho_offset, macho_bins[macho].bytesize,
|
48
|
+
macho.segment_alignment).serialize
|
65
49
|
|
66
|
-
offset += (macho.
|
50
|
+
offset += (macho_bins[macho].bytesize + macho_pads[macho])
|
67
51
|
end
|
68
52
|
|
69
53
|
machos.each do |macho|
|
70
54
|
bin << Utils.nullpad(macho_pads[macho])
|
71
|
-
bin << macho
|
55
|
+
bin << macho_bins[macho]
|
72
56
|
end
|
73
57
|
|
74
58
|
new_from_bin(bin)
|
@@ -294,7 +278,6 @@ module MachO
|
|
294
278
|
# @note Overwrites all data in the file!
|
295
279
|
def write!
|
296
280
|
raise MachOError, "no initial file to write to" if filename.nil?
|
297
|
-
|
298
281
|
File.open(@filename, "wb") { |f| f.write(@raw_data) }
|
299
282
|
end
|
300
283
|
|
@@ -344,12 +327,10 @@ module MachO
|
|
344
327
|
def populate_fat_archs
|
345
328
|
archs = []
|
346
329
|
|
347
|
-
|
348
|
-
|
349
|
-
fa_len = fa_klass.bytesize
|
350
|
-
|
330
|
+
fa_off = Headers::FatHeader.bytesize
|
331
|
+
fa_len = Headers::FatArch.bytesize
|
351
332
|
header.nfat_arch.times do |i|
|
352
|
-
archs <<
|
333
|
+
archs << Headers::FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len])
|
353
334
|
end
|
354
335
|
|
355
336
|
archs
|
@@ -399,7 +380,6 @@ module MachO
|
|
399
380
|
|
400
381
|
# Strict mode: Immediately re-raise. Otherwise: Retain, check later.
|
401
382
|
raise error if strict
|
402
|
-
|
403
383
|
errors << error
|
404
384
|
end
|
405
385
|
end
|
data/lib/macho/headers.rb
CHANGED
@@ -6,19 +6,11 @@ module MachO
|
|
6
6
|
FAT_MAGIC = 0xcafebabe
|
7
7
|
|
8
8
|
# little-endian fat magic
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# this is defined, but should never appear in ruby-macho code because
|
10
|
+
# fat headers are always big-endian and therefore always unpacked as such.
|
11
11
|
# @api private
|
12
12
|
FAT_CIGAM = 0xbebafeca
|
13
13
|
|
14
|
-
# 64-bit big-endian fat magic
|
15
|
-
FAT_MAGIC_64 = 0xcafebabf
|
16
|
-
|
17
|
-
# 64-bit little-endian fat magic
|
18
|
-
# @note This is defined for completeness, but should never appear in ruby-macho code,
|
19
|
-
# since fat headers are always big-endian.
|
20
|
-
FAT_CIGAM_64 = 0xbfbafeca
|
21
|
-
|
22
14
|
# 32-bit big-endian magic
|
23
15
|
# @api private
|
24
16
|
MH_MAGIC = 0xfeedface
|
@@ -39,7 +31,6 @@ module MachO
|
|
39
31
|
# @api private
|
40
32
|
MH_MAGICS = {
|
41
33
|
FAT_MAGIC => "FAT_MAGIC",
|
42
|
-
FAT_MAGIC_64 => "FAT_MAGIC_64",
|
43
34
|
MH_MAGIC => "MH_MAGIC",
|
44
35
|
MH_CIGAM => "MH_CIGAM",
|
45
36
|
MH_MAGIC_64 => "MH_MAGIC_64",
|
@@ -50,11 +41,6 @@ module MachO
|
|
50
41
|
# @api private
|
51
42
|
CPU_ARCH_ABI64 = 0x01000000
|
52
43
|
|
53
|
-
# mask for CPUs with 64-bit architectures (when running a 32-bit ABI?)
|
54
|
-
# @see https://github.com/Homebrew/ruby-macho/issues/113
|
55
|
-
# @api private
|
56
|
-
CPU_ARCH_ABI32 = 0x02000000
|
57
|
-
|
58
44
|
# any CPU (unused?)
|
59
45
|
# @api private
|
60
46
|
CPU_TYPE_ANY = -1
|
@@ -83,10 +69,6 @@ module MachO
|
|
83
69
|
# @api private
|
84
70
|
CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64)
|
85
71
|
|
86
|
-
# 64-bit ARM compatible CPUs (running in 32-bit mode?)
|
87
|
-
# @see https://github.com/Homebrew/ruby-macho/issues/113
|
88
|
-
CPU_TYPE_ARM64_32 = (CPU_TYPE_ARM | CPU_ARCH_ABI32)
|
89
|
-
|
90
72
|
# PowerPC compatible CPUs
|
91
73
|
# @api private
|
92
74
|
CPU_TYPE_POWERPC = 0x12
|
@@ -103,7 +85,6 @@ module MachO
|
|
103
85
|
CPU_TYPE_X86_64 => :x86_64,
|
104
86
|
CPU_TYPE_ARM => :arm,
|
105
87
|
CPU_TYPE_ARM64 => :arm64,
|
106
|
-
CPU_TYPE_ARM64_32 => :arm64_32,
|
107
88
|
CPU_TYPE_POWERPC => :ppc,
|
108
89
|
CPU_TYPE_POWERPC64 => :ppc64,
|
109
90
|
}.freeze
|
@@ -237,14 +218,6 @@ module MachO
|
|
237
218
|
# @api private
|
238
219
|
CPU_SUBTYPE_ARM64_V8 = 1
|
239
220
|
|
240
|
-
# the v8 sub-type for `CPU_TYPE_ARM64_32`
|
241
|
-
# @api private
|
242
|
-
CPU_SUBTYPE_ARM64_32_V8 = 1
|
243
|
-
|
244
|
-
# the e (A12) sub-type for `CPU_TYPE_ARM64`
|
245
|
-
# @api private
|
246
|
-
CPU_SUBTYPE_ARM64E = 2
|
247
|
-
|
248
221
|
# the lowest common sub-type for `CPU_TYPE_MC88000`
|
249
222
|
# @api private
|
250
223
|
CPU_SUBTYPE_MC88000_ALL = 0
|
@@ -354,10 +327,6 @@ module MachO
|
|
354
327
|
CPU_TYPE_ARM64 => {
|
355
328
|
CPU_SUBTYPE_ARM64_ALL => :arm64,
|
356
329
|
CPU_SUBTYPE_ARM64_V8 => :arm64v8,
|
357
|
-
CPU_SUBTYPE_ARM64E => :arm64e,
|
358
|
-
}.freeze,
|
359
|
-
CPU_TYPE_ARM64_32 => {
|
360
|
-
CPU_SUBTYPE_ARM64_32_V8 => :arm64_32v8,
|
361
330
|
}.freeze,
|
362
331
|
CPU_TYPE_POWERPC => {
|
363
332
|
CPU_SUBTYPE_POWERPC_ALL => :ppc,
|
@@ -517,10 +486,8 @@ module MachO
|
|
517
486
|
end
|
518
487
|
end
|
519
488
|
|
520
|
-
#
|
521
|
-
#
|
522
|
-
# @note "32-bit" indicates the fact that this structure stores 32-bit offsets, not that the
|
523
|
-
# Mach-Os that it points to necessarily *are* 32-bit.
|
489
|
+
# Fat binary header architecture structure. A Fat binary has one or more of
|
490
|
+
# these, representing one or more internal Mach-O blobs.
|
524
491
|
# @see MachO::Headers::FatHeader
|
525
492
|
class FatArch < MachOStructure
|
526
493
|
# @return [Integer] the CPU type of the Mach-O
|
@@ -538,10 +505,10 @@ module MachO
|
|
538
505
|
# @return [Integer] the alignment, as a power of 2
|
539
506
|
attr_reader :align
|
540
507
|
|
541
|
-
#
|
508
|
+
# always big-endian
|
542
509
|
# @see MachOStructure::FORMAT
|
543
510
|
# @api private
|
544
|
-
FORMAT = "
|
511
|
+
FORMAT = "N5".freeze
|
545
512
|
|
546
513
|
# @see MachOStructure::SIZEOF
|
547
514
|
# @api private
|
@@ -575,43 +542,6 @@ module MachO
|
|
575
542
|
end
|
576
543
|
end
|
577
544
|
|
578
|
-
# 64-bit fat binary header architecture structure. A 64-bit fat Mach-O has one or more of
|
579
|
-
# these, indicating one or more internal Mach-O blobs.
|
580
|
-
# @note "64-bit" indicates the fact that this structure stores 64-bit offsets, not that the
|
581
|
-
# Mach-Os that it points to necessarily *are* 64-bit.
|
582
|
-
# @see MachO::Headers::FatHeader
|
583
|
-
class FatArch64 < FatArch
|
584
|
-
# @return [void]
|
585
|
-
attr_reader :reserved
|
586
|
-
|
587
|
-
# @note Always big endian.
|
588
|
-
# @see MachOStructure::FORMAT
|
589
|
-
# @api private
|
590
|
-
FORMAT = "L>2Q>2L>2".freeze
|
591
|
-
|
592
|
-
# @see MachOStructure::SIZEOF
|
593
|
-
# @api private
|
594
|
-
SIZEOF = 32
|
595
|
-
|
596
|
-
# @api private
|
597
|
-
def initialize(cputype, cpusubtype, offset, size, align, reserved = 0)
|
598
|
-
super(cputype, cpusubtype, offset, size, align)
|
599
|
-
@reserved = reserved
|
600
|
-
end
|
601
|
-
|
602
|
-
# @return [String] the serialized fields of the fat arch
|
603
|
-
def serialize
|
604
|
-
[cputype, cpusubtype, offset, size, align, reserved].pack(FORMAT)
|
605
|
-
end
|
606
|
-
|
607
|
-
# @return [Hash] a hash representation of this {FatArch64}
|
608
|
-
def to_h
|
609
|
-
{
|
610
|
-
"reserved" => reserved,
|
611
|
-
}.merge super
|
612
|
-
end
|
613
|
-
end
|
614
|
-
|
615
545
|
# 32-bit Mach-O file header structure
|
616
546
|
class MachHeader < MachOStructure
|
617
547
|
# @return [Integer] the magic number
|
@@ -663,9 +593,7 @@ module MachO
|
|
663
593
|
# @return [Boolean] true if `flag` is present in the header's flag section
|
664
594
|
def flag?(flag)
|
665
595
|
flag = MH_FLAGS[flag]
|
666
|
-
|
667
596
|
return false if flag.nil?
|
668
|
-
|
669
597
|
flags & flag == flag
|
670
598
|
end
|
671
599
|
|
data/lib/macho/load_commands.rb
CHANGED
@@ -242,7 +242,6 @@ module MachO
|
|
242
242
|
# @api private
|
243
243
|
def serialize(context)
|
244
244
|
raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable?
|
245
|
-
|
246
245
|
format = Utils.specialize_format(FORMAT, context.endianness)
|
247
246
|
[cmd, SIZEOF].pack(format)
|
248
247
|
end
|
@@ -299,9 +298,7 @@ module MachO
|
|
299
298
|
lc_end = view.offset + lc.cmdsize - 1
|
300
299
|
raw_string = view.raw_data.slice(lc_str_abs..lc_end)
|
301
300
|
@string, null_byte, _padding = raw_string.partition("\x00")
|
302
|
-
|
303
301
|
raise LCStrMalformedError, lc if null_byte.empty?
|
304
|
-
|
305
302
|
@string_offset = lc_str
|
306
303
|
else
|
307
304
|
@string = lc_str
|
@@ -476,9 +473,7 @@ module MachO
|
|
476
473
|
# @return [Boolean] true if `flag` is present in the segment's flag field
|
477
474
|
def flag?(flag)
|
478
475
|
flag = SEGMENT_FLAGS[flag]
|
479
|
-
|
480
476
|
return false if flag.nil?
|
481
|
-
|
482
477
|
flags & flag == flag
|
483
478
|
end
|
484
479
|
|
data/lib/macho/macho_file.rb
CHANGED
data/lib/macho/sections.rb
CHANGED
data/lib/macho/tools.rb
CHANGED
@@ -89,9 +89,8 @@ module MachO
|
|
89
89
|
# Merge multiple Mach-Os into one universal (Fat) binary.
|
90
90
|
# @param filename [String] the fat binary to create
|
91
91
|
# @param files [Array<String>] the files to merge
|
92
|
-
# @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
|
93
92
|
# @return [void]
|
94
|
-
def self.merge_machos(filename, *files
|
93
|
+
def self.merge_machos(filename, *files)
|
95
94
|
machos = files.map do |file|
|
96
95
|
macho = MachO.open(file)
|
97
96
|
case macho
|
@@ -102,7 +101,7 @@ module MachO
|
|
102
101
|
end
|
103
102
|
end.flatten
|
104
103
|
|
105
|
-
fat_macho = MachO::FatFile.new_from_machos(*machos
|
104
|
+
fat_macho = MachO::FatFile.new_from_machos(*machos)
|
106
105
|
fat_macho.write(filename)
|
107
106
|
end
|
108
107
|
end
|
data/lib/macho/utils.rb
CHANGED
@@ -27,7 +27,7 @@ module MachO
|
|
27
27
|
# @return [String] the null string (or empty string, for `size = 0`)
|
28
28
|
# @raise [ArgumentError] if a non-positive nullpad is requested
|
29
29
|
def self.nullpad(size)
|
30
|
-
raise ArgumentError, "size < 0: #{size}" if size
|
30
|
+
raise ArgumentError, "size < 0: #{size}" if size.negative?
|
31
31
|
|
32
32
|
"\x00" * size
|
33
33
|
end
|
@@ -75,49 +75,35 @@ module MachO
|
|
75
75
|
# @param num [Integer] the number being checked
|
76
76
|
# @return [Boolean] whether `num` is a valid Fat magic number
|
77
77
|
def self.fat_magic?(num)
|
78
|
-
[Headers::FAT_MAGIC, Headers::FAT_MAGIC_64].include? num
|
79
|
-
end
|
80
|
-
|
81
|
-
# Compares the given number to valid 32-bit Fat magic numbers.
|
82
|
-
# @param num [Integer] the number being checked
|
83
|
-
# @return [Boolean] whether `num` is a valid 32-bit fat magic number
|
84
|
-
def self.fat_magic32?(num)
|
85
78
|
num == Headers::FAT_MAGIC
|
86
79
|
end
|
87
80
|
|
88
|
-
# Compares the given number to valid 64-bit Fat magic numbers.
|
89
|
-
# @param num [Integer] the number being checked
|
90
|
-
# @return [Boolean] whether `num` is a valid 64-bit fat magic number
|
91
|
-
def self.fat_magic64?(num)
|
92
|
-
num == Headers::FAT_MAGIC_64
|
93
|
-
end
|
94
|
-
|
95
81
|
# Compares the given number to valid 32-bit Mach-O magic numbers.
|
96
82
|
# @param num [Integer] the number being checked
|
97
83
|
# @return [Boolean] whether `num` is a valid 32-bit magic number
|
98
84
|
def self.magic32?(num)
|
99
|
-
|
85
|
+
num == Headers::MH_MAGIC || num == Headers::MH_CIGAM
|
100
86
|
end
|
101
87
|
|
102
88
|
# Compares the given number to valid 64-bit Mach-O magic numbers.
|
103
89
|
# @param num [Integer] the number being checked
|
104
90
|
# @return [Boolean] whether `num` is a valid 64-bit magic number
|
105
91
|
def self.magic64?(num)
|
106
|
-
|
92
|
+
num == Headers::MH_MAGIC_64 || num == Headers::MH_CIGAM_64
|
107
93
|
end
|
108
94
|
|
109
95
|
# Compares the given number to valid little-endian magic numbers.
|
110
96
|
# @param num [Integer] the number being checked
|
111
97
|
# @return [Boolean] whether `num` is a valid little-endian magic number
|
112
98
|
def self.little_magic?(num)
|
113
|
-
|
99
|
+
num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64
|
114
100
|
end
|
115
101
|
|
116
102
|
# Compares the given number to valid big-endian magic numbers.
|
117
103
|
# @param num [Integer] the number being checked
|
118
104
|
# @return [Boolean] whether `num` is a valid big-endian magic number
|
119
105
|
def self.big_magic?(num)
|
120
|
-
|
106
|
+
num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64
|
121
107
|
end
|
122
108
|
end
|
123
109
|
end
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Woodruff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-07-03 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@yossarian.net
|
@@ -42,14 +42,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
43
43
|
- - ">="
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: '2.
|
45
|
+
version: '2.1'
|
46
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
48
|
- - ">="
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '0'
|
51
51
|
requirements: []
|
52
|
-
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.7.6
|
53
54
|
signing_key:
|
54
55
|
specification_version: 4
|
55
56
|
summary: ruby-macho - Mach-O file analyzer.
|