odinflex 1.0.0

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.
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