ruby-macho 0.2.2 → 0.2.3
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/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
|