worf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,128 @@
1
+ ENV["MT_NO_PLUGINS"] = "1"
2
+
3
+ require "helper"
4
+
5
+ module WORF
6
+ class Test < Minitest::Test
7
+ debug_file = "fixtures/out.dSYM/Contents/Resources/DWARF/out"
8
+ DEBUG_FILE = File.expand_path(File.join(__dir__, debug_file))
9
+
10
+ debug_file = "fixtures/a.out.dSYM/Contents/Resources/DWARF/a.out"
11
+ SLOP = File.expand_path(File.join(__dir__, debug_file))
12
+
13
+ def test_uleb128
14
+ assert_equal 624485, WORF.unpackULEB128(StringIO.new("\xE5\x8E\x26".b))
15
+ end
16
+
17
+ def test_sleb128
18
+ assert_equal(-123456, WORF.unpackSLEB128(StringIO.new("\xC0\xBB\x78".b)))
19
+ end
20
+
21
+ def test_debug_abbrev
22
+ File.open DEBUG_FILE do |io|
23
+ mach_o = OdinFlex::MachO.new(io)
24
+
25
+ section = mach_o.find do |thing|
26
+ thing.section? && thing.sectname == "__debug_abbrev"
27
+ end
28
+
29
+ debug_abbrev = WORF::DebugAbbrev.new io, section, mach_o.start_pos
30
+ tags = debug_abbrev.tags
31
+
32
+ assert_equal 5, tags.length
33
+ end
34
+ end
35
+
36
+ def test_debug_info
37
+ File.open DEBUG_FILE do |io|
38
+ mach_o = OdinFlex::MachO.new(io)
39
+
40
+ abbrev = mach_o.find do |thing|
41
+ thing.section? && thing.sectname == "__debug_abbrev"
42
+ end
43
+
44
+ debug_abbrev = WORF::DebugAbbrev.new io, abbrev, mach_o.start_pos
45
+
46
+ info = mach_o.find do |thing|
47
+ thing.section? && thing.sectname == "__debug_info"
48
+ end
49
+
50
+ debug_info = WORF::DebugInfo.new io, info, mach_o.start_pos
51
+ units = debug_info.compile_units(debug_abbrev.tags)
52
+
53
+ assert_equal 1, units.length
54
+ assert_equal Constants::DW_TAG_compile_unit, units.first.die.tag.type
55
+ assert_equal 0xb, units.first.die.offset
56
+ assert_equal 5, units.first.die.children.length
57
+ assert_equal Constants::DW_TAG_subprogram, units.first.die.children.first.tag.type
58
+ assert_equal 0x2a, units.first.die.children.first.offset
59
+ end
60
+ end
61
+
62
+ class StructInfo
63
+ def initialize die, names, ranges
64
+ @die = die
65
+ @ranges = ranges
66
+ end
67
+
68
+ def byte_size; @die.byte_size; end
69
+
70
+ def used_size
71
+ @ranges.map(&:size).inject(:+)
72
+ end
73
+ end
74
+
75
+ def show unit, die, strings, dies
76
+ names = []
77
+ ranges = die.children.map do |child|
78
+ if child.tag.member?
79
+ type_die = dies.find { |d| d.offset == child.type }
80
+ size = if type_die.tag.pointer_type?
81
+ unit.address_size
82
+ else
83
+ type_die.byte_size
84
+ end
85
+ start = child.data_member_location
86
+ name = strings.string_at(child.name_offset)
87
+ names << name
88
+ Range.new(start, start + size, exclude_end: true)
89
+ else
90
+ raise NotImplementedError
91
+ end
92
+ end
93
+
94
+ StructInfo.new die, names, ranges
95
+ end
96
+
97
+ def test_read_struct
98
+ struct_info = nil
99
+
100
+ File.open SLOP do |io|
101
+ mach_o = OdinFlex::MachO.new(io)
102
+
103
+ abbrev = mach_o.find_section "__debug_abbrev"
104
+ debug_abbrev = WORF::DebugAbbrev.new io, abbrev, mach_o.start_pos
105
+
106
+ section_info = mach_o.find_section "__debug_str"
107
+ strings = WORF::DebugStrings.new io, section_info, mach_o.start_pos
108
+
109
+ info = mach_o.find_section "__debug_info"
110
+ debug_info = WORF::DebugInfo.new io, info, mach_o.start_pos
111
+
112
+ units = debug_info.compile_units(debug_abbrev.tags)
113
+
114
+ units.each do |unit|
115
+ unit.die.children.each do |die|
116
+ if die.tag.structure_type?
117
+ struct_info = show unit, die, strings, unit.die.to_a
118
+ break
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ assert_equal 24, struct_info.byte_size
125
+ assert_equal 17, struct_info.used_size
126
+ end
127
+ end
128
+ end
Binary file
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>English</string>
7
+ <key>CFBundleIdentifier</key>
8
+ <string>com.apple.xcode.dsym.a.out</string>
9
+ <key>CFBundleInfoDictionaryVersion</key>
10
+ <string>6.0</string>
11
+ <key>CFBundlePackageType</key>
12
+ <string>dSYM</string>
13
+ <key>CFBundleSignature</key>
14
+ <string>????</string>
15
+ <key>CFBundleShortVersionString</key>
16
+ <string>1.0</string>
17
+ <key>CFBundleVersion</key>
18
+ <string>1</string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>English</string>
7
+ <key>CFBundleIdentifier</key>
8
+ <string>com.apple.xcode.dsym.out</string>
9
+ <key>CFBundleInfoDictionaryVersion</key>
10
+ <string>6.0</string>
11
+ <key>CFBundlePackageType</key>
12
+ <string>dSYM</string>
13
+ <key>CFBundleSignature</key>
14
+ <string>????</string>
15
+ <key>CFBundleShortVersionString</key>
16
+ <string>1.0</string>
17
+ <key>CFBundleVersion</key>
18
+ <string>1</string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,17 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+
4
+ struct hello {
5
+ char *p;
6
+ char c;
7
+ long x;
8
+ };
9
+
10
+ int main(int argc, char **argv) {
11
+ struct hello * m;
12
+ m = malloc(sizeof(struct hello));
13
+ m->c = 123;
14
+ m->x = 123;
15
+ printf("Hello World %lu\n", m->x);
16
+ return 0;
17
+ }
@@ -0,0 +1,6 @@
1
+ #include <stdio.h>
2
+
3
+ int main(int argc, char **argv) {
4
+ printf("Hello World\n");
5
+ return 0;
6
+ }
data/test/helper.rb ADDED
@@ -0,0 +1,59 @@
1
+ ENV["MT_NO_PLUGINS"] = "1"
2
+
3
+ require "minitest/autorun"
4
+ require "worf"
5
+ require "odinflex/mach-o"
6
+ require "odinflex/ar"
7
+ require "rbconfig"
8
+ require "fiddle"
9
+
10
+ module WORF
11
+ class Test < Minitest::Test
12
+ include Fiddle
13
+
14
+ module Hacks
15
+ include Fiddle
16
+
17
+ class Fiddle::Function
18
+ def to_proc
19
+ this = self
20
+ lambda { |*args| this.call(*args) }
21
+ end
22
+ end unless Function.method_defined?(:to_proc)
23
+
24
+ def self.make_function name, args, ret
25
+ ptr = Handle::DEFAULT[name]
26
+ func = Function.new ptr, args, ret, name: name
27
+ define_singleton_method name, &func.to_proc
28
+ end
29
+
30
+ make_function "strerror", [TYPE_INT], TYPE_CONST_STRING
31
+ #make_function "mprotect", [TYPE_VOIDP, TYPE_SIZE_T, TYPE_INT], TYPE_INT
32
+ make_function "_dyld_image_count", [], TYPE_INT32_T
33
+ make_function "_dyld_get_image_name", [TYPE_INT32_T], TYPE_CONST_STRING
34
+ make_function "_dyld_get_image_vmaddr_slide", [TYPE_INT32_T], TYPE_INTPTR_T
35
+ make_function "mach_task_self", [], TYPE_VOIDP
36
+ make_function "vm_protect", [TYPE_VOIDP, -TYPE_INT64_T, TYPE_SIZE_T, TYPE_CHAR, TYPE_INT], TYPE_INT
37
+ make_function "rb_intern", [TYPE_CONST_STRING], TYPE_INT
38
+
39
+ def self.mprotect addr, len, prot
40
+ vm_protect mach_task_self, addr, len, 0, prot | PROT_COPY
41
+ end
42
+
43
+ PROT_READ = 0x01
44
+ PROT_WRITE = 0x02
45
+ PROT_EXEC = 0x04
46
+ PROT_COPY = 0x10
47
+
48
+ def self.slide
49
+ executable = RbConfig.ruby
50
+ Hacks._dyld_image_count.times do |i|
51
+ name = Hacks._dyld_get_image_name(i)
52
+ if executable == name
53
+ return Hacks._dyld_get_image_vmaddr_slide(i)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ require "helper"
2
+
3
+ module WORF
4
+ class MachODWARFIntegrationTest < Test
5
+ def test_find_symbol_and_make_struct
6
+ addr = nil
7
+ archive = nil
8
+
9
+ File.open(RbConfig.ruby) do |f|
10
+ my_macho = OdinFlex::MachO.new f
11
+ my_macho.each do |section|
12
+ if section.symtab?
13
+ addr = section.nlist.find { |symbol|
14
+ symbol.name == "_ruby_api_version" && symbol.value > 0
15
+ }.value + Hacks.slide
16
+
17
+ archive = section.nlist.find_all(&:archive?).map(&:archive).uniq.first
18
+ end
19
+ end
20
+ end
21
+
22
+ assert addr
23
+ assert archive
24
+
25
+ found_object = nil
26
+
27
+ File.open(archive) do |f|
28
+ ar = OdinFlex::AR.new f
29
+ ar.each do |object_file|
30
+ next unless object_file.identifier.end_with?(".o")
31
+ next unless object_file.identifier == "version.o"
32
+
33
+ f.seek object_file.pos, IO::SEEK_SET
34
+ macho = OdinFlex::MachO.new f
35
+ debug_info = macho.find_section("__debug_info")&.as_dwarf || next
36
+ debug_strs = macho.find_section("__debug_str").as_dwarf
37
+ debug_abbrev = macho.find_section("__debug_abbrev").as_dwarf
38
+
39
+ debug_info.compile_units(debug_abbrev.tags).each do |unit|
40
+ unit.die.children.each do |die|
41
+ if die.name(debug_strs) == "ruby_api_version"
42
+ found_object = object_file
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ assert found_object
50
+ end
51
+ end
52
+ end
data/test/ruby_test.rb ADDED
@@ -0,0 +1,238 @@
1
+ require "helper"
2
+
3
+ module WORF
4
+ class RubyTest < WORF::Test
5
+ def ruby_archive
6
+ File.join RbConfig::CONFIG["prefix"], "lib", RbConfig::CONFIG["LIBRUBY"]
7
+ end
8
+
9
+ def test_ruby_archive
10
+ assert File.file?(ruby_archive)
11
+ end
12
+
13
+ def test_read_archive
14
+ files = []
15
+ File.open(ruby_archive) do |f|
16
+ OdinFlex::AR.new(f).each { |file| files << file.identifier }
17
+ end
18
+ assert_includes files, "gc.o"
19
+ end
20
+
21
+ def test_read_archive_twice
22
+ files = []
23
+ File.open(ruby_archive) do |f|
24
+ ar = OdinFlex::AR.new(f)
25
+ ar.each { |file| files << file.identifier }
26
+ assert_includes files, "gc.o"
27
+ files.clear
28
+ ar.each { |file| files << file.identifier }
29
+ assert_includes files, "gc.o"
30
+ end
31
+ end
32
+
33
+ def test_macho_in_archive
34
+ File.open(ruby_archive) do |f|
35
+ ar = OdinFlex::AR.new f
36
+ gc = ar.find { |file| file.identifier == "gc.o" }
37
+
38
+ f.seek gc.pos, IO::SEEK_SET
39
+ macho = OdinFlex::MachO.new f
40
+ section = macho.find_section("__debug_str")
41
+ assert_equal "__debug_str", section.sectname
42
+ end
43
+ end
44
+
45
+ def test_macho_to_dwarf
46
+ File.open(ruby_archive) do |f|
47
+ ar = OdinFlex::AR.new f
48
+ gc = ar.find { |file| file.identifier == "gc.o" }
49
+
50
+ f.seek gc.pos, IO::SEEK_SET
51
+ macho = OdinFlex::MachO.new f
52
+ debug_strs = macho.find_section("__debug_str").as_dwarf
53
+ debug_abbrev = macho.find_section("__debug_abbrev").as_dwarf
54
+ debug_info = macho.find_section("__debug_info").as_dwarf
55
+
56
+ names = []
57
+
58
+ debug_info.compile_units(debug_abbrev.tags).each do |unit|
59
+ unit.die.children.each do |die|
60
+ names << die.name(debug_strs)
61
+ end
62
+ end
63
+
64
+ assert_includes names, "RBasic"
65
+ end
66
+ end
67
+
68
+ def test_rbasic_layout
69
+ File.open(ruby_archive) do |f|
70
+ ar = OdinFlex::AR.new f
71
+ gc = ar.find { |file| file.identifier == "gc.o" }
72
+
73
+ f.seek gc.pos, IO::SEEK_SET
74
+ macho = OdinFlex::MachO.new f
75
+ debug_strs = macho.find_section("__debug_str").as_dwarf
76
+ debug_abbrev = macho.find_section("__debug_abbrev").as_dwarf
77
+ debug_info = macho.find_section("__debug_info").as_dwarf
78
+
79
+ rbasic_layout = []
80
+
81
+ debug_info.compile_units(debug_abbrev.tags).each do |unit|
82
+ unit.die.children.each do |die|
83
+ if die.name(debug_strs) == "RBasic"
84
+ assert_predicate die.tag, :structure_type?
85
+
86
+ die.children.each do |child|
87
+ field_name = child.name(debug_strs)
88
+ field_type = nil
89
+ while child
90
+ field_type = child.name(debug_strs)
91
+ break unless child.type
92
+ child = unit.die.find_type(child)
93
+ end
94
+ rbasic_layout << [field_name, field_type]
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ assert_equal([["flags", "long unsigned int"], ["klass", "long unsigned int"]],
101
+ rbasic_layout)
102
+ end
103
+ end
104
+
105
+ def test_rclass_layout
106
+ File.open(ruby_archive) do |f|
107
+ ar = OdinFlex::AR.new f
108
+ gc = ar.find { |file| file.identifier == "gc.o" }
109
+
110
+ f.seek gc.pos, IO::SEEK_SET
111
+ macho = OdinFlex::MachO.new f
112
+ debug_strs = macho.find_section("__debug_str").as_dwarf
113
+ debug_abbrev = macho.find_section("__debug_abbrev").as_dwarf
114
+ debug_info = macho.find_section("__debug_info").as_dwarf
115
+
116
+ layout = []
117
+
118
+ debug_info.compile_units(debug_abbrev.tags).each do |unit|
119
+ unit.die.children.each do |die|
120
+ if die.name(debug_strs) == "RClass"
121
+ assert_predicate die.tag, :structure_type?
122
+
123
+ die.children.each do |child|
124
+ field_name = child.name(debug_strs)
125
+ type = unit.die.find_type(child)
126
+
127
+ if type.tag.typedef?
128
+ type = unit.die.find_type(type)
129
+ end
130
+
131
+ type_name = if type.tag.pointer_type?
132
+ c = unit.die.find_type(type)
133
+ "#{c.name(debug_strs)} *"
134
+ else
135
+ type.name(debug_strs)
136
+ end
137
+
138
+ type_size = if type.tag.pointer_type?
139
+ unit.address_size
140
+ else
141
+ type.byte_size
142
+ end
143
+
144
+ layout << [field_name, type_name, type_size]
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ assert_equal([["basic", "RBasic", 16],
151
+ ["super", "long unsigned int", 8],
152
+ ["ptr", "rb_classext_struct *", 8],
153
+ ["class_serial", "long long unsigned int", 8]],
154
+ layout)
155
+ end
156
+ end
157
+
158
+ [
159
+ [:section?, OdinFlex::MachO::Section],
160
+ [:symtab?, OdinFlex::MachO::LC_SYMTAB],
161
+ [:segment?, OdinFlex::MachO::LC_SEGMENT_64],
162
+ [:dysymtab?, OdinFlex::MachO::LC_DYSYMTAB],
163
+ [:command?, OdinFlex::MachO::Command],
164
+ ].each do |predicate, klass|
165
+ define_method :"test_find_#{predicate}" do
166
+ File.open(RbConfig.ruby) do |f|
167
+ my_macho = OdinFlex::MachO.new f
168
+ list = my_macho.find_all(&predicate)
169
+ refute_predicate list, :empty?
170
+ assert list.all? { |x| x.is_a?(klass) }
171
+ end
172
+ end
173
+ end
174
+
175
+ def test_rb_vm_get_insns_address_table
176
+ sym = nil
177
+
178
+ File.open(RbConfig.ruby) do |f|
179
+ my_macho = OdinFlex::MachO.new f
180
+
181
+ my_macho.each do |section|
182
+ if section.symtab?
183
+ sym = section.nlist.find do |symbol|
184
+ symbol.name == "_rb_vm_get_insns_address_table" && symbol.value
185
+ end
186
+ break if sym
187
+ end
188
+ end
189
+ end
190
+
191
+ addr = sym.value + Hacks.slide
192
+ ptr = Fiddle::Function.new(addr, [], TYPE_VOIDP).call
193
+ len = RubyVM::INSTRUCTION_NAMES.length
194
+ assert ptr[0, len * Fiddle::SIZEOF_VOIDP].unpack("Q#{len}")
195
+ end
196
+
197
+ def test_guess_slide
198
+ File.open(RbConfig.ruby) do |f|
199
+ my_macho = OdinFlex::MachO.new f
200
+
201
+ my_macho.each do |section|
202
+ if section.symtab?
203
+ section.nlist.each do |symbol|
204
+ if symbol.name == "_rb_st_insert"
205
+ guess_slide = Fiddle::Handle::DEFAULT["rb_st_insert"] - symbol.value
206
+ assert_equal Hacks.slide, guess_slide
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ def test_find_global
215
+ File.open(RbConfig.ruby) do |f|
216
+ my_macho = OdinFlex::MachO.new f
217
+
218
+ my_macho.each do |section|
219
+ if section.symtab?
220
+ section.nlist.each do |symbol|
221
+ if symbol.name == "_ruby_api_version"
222
+ if symbol.value > 0
223
+ addr = symbol.value + Hacks.slide
224
+ pointer = Fiddle::Pointer.new(addr, Fiddle::SIZEOF_INT * 3)
225
+ assert_equal RbConfig::CONFIG["ruby_version"].split(".").map(&:to_i),
226
+ pointer[0, Fiddle::SIZEOF_INT * 3].unpack("LLL")
227
+ else
228
+ assert_predicate symbol, :stab?
229
+ assert_predicate symbol, :gsym?
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end