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.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.md +4 -9
- data/lib/macho.rb +2 -2
- data/lib/macho/exceptions.rb +104 -73
- data/lib/macho/fat_file.rb +273 -228
- data/lib/macho/headers.rb +342 -265
- data/lib/macho/load_commands.rb +1009 -999
- data/lib/macho/macho_file.rb +509 -529
- data/lib/macho/open.rb +23 -14
- data/lib/macho/sections.rb +157 -157
- data/lib/macho/structure.rb +33 -17
- data/lib/macho/tools.rb +55 -55
- data/lib/macho/utils.rb +42 -30
- metadata +3 -4
data/lib/macho/open.rb
CHANGED
@@ -1,16 +1,25 @@
|
|
1
1
|
module MachO
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
2
|
+
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
|
3
|
+
# @param filename [String] the file being opened
|
4
|
+
# @return [MachO::MachOFile] if the file is a Mach-O
|
5
|
+
# @return [MachO::FatFile] if the file is a Fat file
|
6
|
+
# @raise [ArgumentError] if the given file does not exist
|
7
|
+
# @raise [MachO::TruncatedFileError] if the file is too small to have a valid header
|
8
|
+
# @raise [MachO::MagicError] if the file's magic is not valid Mach-O magic
|
9
|
+
def self.open(filename)
|
10
|
+
raise ArgumentError.new("#{filename}: no such file") unless File.file?(filename)
|
11
|
+
raise TruncatedFileError.new unless File.stat(filename).size >= 4
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
magic = File.open(filename, "rb") { |f| f.read(4) }.unpack("N").first
|
14
|
+
|
15
|
+
if MachO.fat_magic?(magic)
|
16
|
+
file = FatFile.new(filename)
|
17
|
+
elsif MachO.magic?(magic)
|
18
|
+
file = MachOFile.new(filename)
|
19
|
+
else
|
20
|
+
raise MagicError.new(magic)
|
21
|
+
end
|
22
|
+
|
23
|
+
file
|
24
|
+
end
|
25
|
+
end
|
data/lib/macho/sections.rb
CHANGED
@@ -1,159 +1,159 @@
|
|
1
1
|
module MachO
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
2
|
+
# type mask
|
3
|
+
SECTION_TYPE = 0x000000ff
|
4
|
+
|
5
|
+
# attributes mask
|
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 addrributes 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
|
+
FORMAT = "a16a16L=9"
|
104
|
+
SIZEOF = 68
|
105
|
+
|
106
|
+
# @api private
|
107
|
+
def initialize(sectname, segname, addr, size, offset, align, reloff,
|
108
|
+
nreloc, flags, reserved1, reserved2)
|
109
|
+
@sectname = sectname
|
110
|
+
@segname = segname
|
111
|
+
@addr = addr
|
112
|
+
@size = size
|
113
|
+
@offset = offset
|
114
|
+
@align = align
|
115
|
+
@reloff = reloff
|
116
|
+
@nreloc = nreloc
|
117
|
+
@flags = flags
|
118
|
+
@reserved1 = reserved1
|
119
|
+
@reserved2 = reserved2
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [String] the section's name, with any trailing NULL characters removed
|
123
|
+
def section_name
|
124
|
+
@sectname.delete("\x00")
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [String] the parent segment's name, with any trailing NULL characters removed
|
128
|
+
def segment_name
|
129
|
+
@segname.delete("\x00")
|
130
|
+
end
|
131
|
+
|
132
|
+
# @example
|
133
|
+
# puts "this section is regular" if sect.flag?(:S_REGULAR)
|
134
|
+
# @param flag [Symbol] a section flag symbol
|
135
|
+
# @return [Boolean] true if `flag` is present in the section's flag field
|
136
|
+
def flag?(flag)
|
137
|
+
flag = SECTION_FLAGS[flag]
|
138
|
+
return false if flag.nil?
|
139
|
+
flags & flag == flag
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Represents a section of a segment for 64-bit architectures.
|
144
|
+
class Section64 < Section
|
145
|
+
# @return [void] reserved
|
146
|
+
attr_reader :reserved3
|
147
|
+
|
148
|
+
FORMAT = "a16a16Q=2L=8"
|
149
|
+
SIZEOF = 80
|
150
|
+
|
151
|
+
# @api private
|
152
|
+
def initialize(sectname, segname, addr, size, offset, align, reloff,
|
153
|
+
nreloc, flags, reserved1, reserved2, reserved3)
|
154
|
+
super(sectname, segname, addr, size, offset, align, reloff,
|
155
|
+
nreloc, flags, reserved1, reserved2)
|
156
|
+
@reserved3 = reserved3
|
157
|
+
end
|
158
|
+
end
|
159
159
|
end
|
data/lib/macho/structure.rb
CHANGED
@@ -1,22 +1,38 @@
|
|
1
1
|
module MachO
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
# A general purpose pseudo-structure.
|
3
|
+
# @abstract
|
4
|
+
class MachOStructure
|
5
|
+
# The String#unpack format of the data structure.
|
6
|
+
FORMAT = ""
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
# The size of the data structure, in bytes.
|
9
|
+
SIZEOF = 0
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# @return [Fixnum] the size, in bytes, of the represented structure.
|
12
|
+
def self.bytesize
|
13
|
+
self::SIZEOF
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
# @param endianness [Symbol] either :big or :little
|
17
|
+
# @param bin [String] the string to be unpacked into the new structure
|
18
|
+
# @return [MachO::MachOStructure] a new MachOStructure initialized with `bin`
|
19
|
+
# @api private
|
20
|
+
def self.new_from_bin(endianness, bin)
|
21
|
+
format = specialize_format(self::FORMAT, endianness)
|
22
|
+
|
23
|
+
self.new(*bin.unpack(format))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Convert an abstract (native-endian) String#unpack format to big or little.
|
29
|
+
# @param format [String] the format string being converted
|
30
|
+
# @param endianness [Symbol] either :big or :little
|
31
|
+
# @return [String] the converted string
|
32
|
+
# @api private
|
33
|
+
def self.specialize_format(format, endianness)
|
34
|
+
modifier = (endianness == :big) ? ">" : "<"
|
35
|
+
format.tr("=", modifier)
|
36
|
+
end
|
37
|
+
end
|
22
38
|
end
|
data/lib/macho/tools.rb
CHANGED
@@ -1,65 +1,65 @@
|
|
1
1
|
module MachO
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
# A collection of convenient methods for common operations on Mach-O and Fat binaries.
|
3
|
+
module Tools
|
4
|
+
# @param filename [String] the Mach-O or Fat binary being read
|
5
|
+
# @return [Array<String>] an array of all dylibs linked to the binary
|
6
|
+
def self.dylibs(filename)
|
7
|
+
file = MachO.open(filename)
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
file.linked_dylibs
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
# Changes the dylib ID of a Mach-O or Fat binary, overwriting the source file.
|
13
|
+
# @param filename [String] the Mach-O or Fat binary being modified
|
14
|
+
# @param new_id [String] the new dylib ID for the binary
|
15
|
+
# @return [void]
|
16
|
+
# @todo unstub for fat files
|
17
|
+
def self.change_dylib_id(filename, new_id)
|
18
|
+
file = MachO.open(filename)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
file.dylib_id = new_id
|
21
|
+
file.write!
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
# Changes a shared library install name in a Mach-O or Fat binary, overwriting the source file.
|
25
|
+
# @param filename [String] the Mach-O or Fat binary being modified
|
26
|
+
# @param old_name [String] the old shared library name
|
27
|
+
# @param new_name [String] the new shared library name
|
28
|
+
# @return [void]
|
29
|
+
# @todo unstub for fat files
|
30
|
+
def self.change_install_name(filename, old_name, new_name)
|
31
|
+
file = MachO.open(filename)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
file.change_install_name(old_name, new_name)
|
34
|
+
file.write!
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
37
|
+
# Changes a runtime path in a Mach-O or Fat binary, overwriting the source file.
|
38
|
+
# @param filename [String] the Mach-O or Fat binary being modified
|
39
|
+
# @param old_path [String] the old runtime path
|
40
|
+
# @param new_path [String] the new runtime path
|
41
|
+
# @return [void]
|
42
|
+
# @todo unstub
|
43
|
+
def self.change_rpath(filename, old_path, new_path)
|
44
|
+
raise UnimplementedError.new("changing rpaths in a Mach-O")
|
45
|
+
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
# Add a runtime path to a Mach-O or Fat binary, overwriting the source file.
|
48
|
+
# @param filename [String] the Mach-O or Fat binary being modified
|
49
|
+
# @param new_path [String] the new runtime path
|
50
|
+
# @return [void]
|
51
|
+
# @todo unstub
|
52
|
+
def self.add_rpath(filename, new_path)
|
53
|
+
raise UnimplementedError.new("adding rpaths to a Mach-O")
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
56
|
+
# Delete a runtime path from a Mach-O or Fat binary, overwriting the source file.
|
57
|
+
# @param filename [String] the Mach-O or Fat binary being modified
|
58
|
+
# @param old_path [String] the old runtime path
|
59
|
+
# @return [void]
|
60
|
+
# @todo unstub
|
61
|
+
def self.delete_rpath(filename, old_path)
|
62
|
+
raise UnimplementedError.new("removing rpaths from a Mach-O")
|
63
|
+
end
|
64
|
+
end
|
65
65
|
end
|