odinflex 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/odinflex.gemspec ADDED
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "odinflex"
3
+ s.version = "1.0.0"
4
+ s.summary = "Parse AR or Mach-O files"
5
+ s.description = "Do you need to parse an AR file or a Mach-O file? If so, then this is the library for you!"
6
+ s.authors = ["Aaron Patterson"]
7
+ s.email = "tenderlove@ruby-lang.org"
8
+ s.files = `git ls-files -z`.split("\x0")
9
+ s.test_files = s.files.grep(%r{^test/})
10
+ s.homepage = "https://github.com/tenderlove/odinflex"
11
+ s.license = "Apache-2.0"
12
+ 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,58 @@
1
+ ENV["MT_NO_PLUGINS"] = "1"
2
+
3
+ require "minitest/autorun"
4
+ require "odinflex/mach-o"
5
+ require "odinflex/ar"
6
+ require "rbconfig"
7
+ require "fiddle"
8
+
9
+ module OdinFlex
10
+ class Test < Minitest::Test
11
+ include Fiddle
12
+
13
+ module Hacks
14
+ include Fiddle
15
+
16
+ class Fiddle::Function
17
+ def to_proc
18
+ this = self
19
+ lambda { |*args| this.call(*args) }
20
+ end
21
+ end unless Function.method_defined?(:to_proc)
22
+
23
+ def self.make_function name, args, ret
24
+ ptr = Handle::DEFAULT[name]
25
+ func = Function.new ptr, args, ret, name: name
26
+ define_singleton_method name, &func.to_proc
27
+ end
28
+
29
+ make_function "strerror", [TYPE_INT], TYPE_CONST_STRING
30
+ #make_function "mprotect", [TYPE_VOIDP, TYPE_SIZE_T, TYPE_INT], TYPE_INT
31
+ make_function "_dyld_image_count", [], TYPE_INT32_T
32
+ make_function "_dyld_get_image_name", [TYPE_INT32_T], TYPE_CONST_STRING
33
+ make_function "_dyld_get_image_vmaddr_slide", [TYPE_INT32_T], TYPE_INTPTR_T
34
+ make_function "mach_task_self", [], TYPE_VOIDP
35
+ make_function "vm_protect", [TYPE_VOIDP, -TYPE_INT64_T, TYPE_SIZE_T, TYPE_CHAR, TYPE_INT], TYPE_INT
36
+ make_function "rb_intern", [TYPE_CONST_STRING], TYPE_INT
37
+
38
+ def self.mprotect addr, len, prot
39
+ vm_protect mach_task_self, addr, len, 0, prot | PROT_COPY
40
+ end
41
+
42
+ PROT_READ = 0x01
43
+ PROT_WRITE = 0x02
44
+ PROT_EXEC = 0x04
45
+ PROT_COPY = 0x10
46
+
47
+ def self.slide
48
+ executable = RbConfig.ruby
49
+ Hacks._dyld_image_count.times do |i|
50
+ name = Hacks._dyld_get_image_name(i)
51
+ if executable == name
52
+ return Hacks._dyld_get_image_vmaddr_slide(i)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ require "helper"
2
+ require "worf"
3
+
4
+ module OdinFlex
5
+ class MachODWARFIntegrationTest < Test
6
+ def test_find_symbol_and_make_struct
7
+ archive = nil
8
+
9
+ File.open(RbConfig.ruby) do |f|
10
+ my_macho = MachO.new f
11
+ my_macho.each do |section|
12
+ if section.symtab?
13
+ archive = section.nlist.find_all(&:archive?).map(&:archive).uniq.first
14
+ end
15
+ end
16
+ end
17
+
18
+ assert archive
19
+
20
+ found_object = nil
21
+
22
+ File.open(archive) do |f|
23
+ ar = AR.new f
24
+ ar.each do |object_file|
25
+ next unless object_file.identifier.end_with?(".o")
26
+ next unless object_file.identifier == "version.o"
27
+
28
+ f.seek object_file.pos, IO::SEEK_SET
29
+ macho = MachO.new f
30
+ debug_info = macho.find_section("__debug_info")&.as_dwarf || next
31
+ debug_strs = macho.find_section("__debug_str").as_dwarf
32
+ debug_abbrev = macho.find_section("__debug_abbrev").as_dwarf
33
+
34
+ debug_info.compile_units(debug_abbrev.tags).each do |unit|
35
+ unit.die.children.each do |die|
36
+ if die.name(debug_strs) == "ruby_api_version"
37
+ found_object = object_file
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ assert found_object
45
+ end
46
+ end
47
+ end
data/test/ruby_test.rb ADDED
@@ -0,0 +1,238 @@
1
+ require "helper"
2
+
3
+ module OdinFlex
4
+ class RubyTest < OdinFlex::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
+ 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 = 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 = AR.new f
36
+ gc = ar.find { |file| file.identifier == "gc.o" }
37
+
38
+ f.seek gc.pos, IO::SEEK_SET
39
+ macho = 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 = AR.new f
48
+ gc = ar.find { |file| file.identifier == "gc.o" }
49
+
50
+ f.seek gc.pos, IO::SEEK_SET
51
+ macho = 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 = AR.new f
71
+ gc = ar.find { |file| file.identifier == "gc.o" }
72
+
73
+ f.seek gc.pos, IO::SEEK_SET
74
+ macho = 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 = AR.new f
108
+ gc = ar.find { |file| file.identifier == "gc.o" }
109
+
110
+ f.seek gc.pos, IO::SEEK_SET
111
+ macho = 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?, MachO::Section],
160
+ [:symtab?, MachO::LC_SYMTAB],
161
+ [:segment?, MachO::LC_SEGMENT_64],
162
+ [:dysymtab?, MachO::LC_DYSYMTAB],
163
+ [:command?, MachO::Command],
164
+ ].each do |predicate, klass|
165
+ define_method :"test_find_#{predicate}" do
166
+ File.open(RbConfig.ruby) do |f|
167
+ my_macho = 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 = 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 = 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 = 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
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: odinflex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-07-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Do you need to parse an AR file or a Mach-O file? If so, then this is
14
+ the library for you!
15
+ email: tenderlove@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - LICENSE
22
+ - README.md
23
+ - Rakefile
24
+ - lib/odinflex/ar.rb
25
+ - lib/odinflex/mach-o.rb
26
+ - odinflex.gemspec
27
+ - test/fixtures/a.out
28
+ - test/fixtures/a.out.dSYM/Contents/Info.plist
29
+ - test/fixtures/a.out.dSYM/Contents/Resources/DWARF/a.out
30
+ - test/fixtures/out.dSYM/Contents/Info.plist
31
+ - test/fixtures/out.dSYM/Contents/Resources/DWARF/out
32
+ - test/fixtures/slop.c
33
+ - test/fixtures/thing.c
34
+ - test/helper.rb
35
+ - test/mach_o_dwarf_integration_test.rb
36
+ - test/ruby_test.rb
37
+ homepage: https://github.com/tenderlove/odinflex
38
+ licenses:
39
+ - Apache-2.0
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.2.15
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Parse AR or Mach-O files
60
+ test_files:
61
+ - test/fixtures/a.out
62
+ - test/fixtures/a.out.dSYM/Contents/Info.plist
63
+ - test/fixtures/a.out.dSYM/Contents/Resources/DWARF/a.out
64
+ - test/fixtures/out.dSYM/Contents/Info.plist
65
+ - test/fixtures/out.dSYM/Contents/Resources/DWARF/out
66
+ - test/fixtures/slop.c
67
+ - test/fixtures/thing.c
68
+ - test/helper.rb
69
+ - test/mach_o_dwarf_integration_test.rb
70
+ - test/ruby_test.rb