cfg2asm 0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9fe9c7d9795d226dbb31a45071c764468a29e04d416c060145660acd79c1ccf0
4
+ data.tar.gz: dcac08b911bc46c252629bf150079d81c648d02f0c6be93b36612a5b344907d5
5
+ SHA512:
6
+ metadata.gz: 1b1c86cc939e77ae3ee16fb0ba796dcc2f6a0bae19287f88f2a121cb125bed6a1fcac8e7030a04aa212cfcea2bb6b07b8f45dbfdbbe398af18b877eff5cfdf24
7
+ data.tar.gz: 5b3433186dbf48aab5d00b817f4ba3151ee663394dfac98c22cee9ac57e1e8bcfd456abdd381f05693b8a429751c16856eb3b2dde9a4ae8e725cf938e26d9bc3
data/bin/cfg2asm ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cfg2asm'
4
+
5
+ # This is the 'cfg2asm' command line entry point.
6
+
7
+ begin
8
+ # Run the command line.
9
+ commands = Cfg2asm::Commands.new($stdout)
10
+ commands.cfg2asm(*ARGV)
11
+ rescue StandardError => e
12
+ if $DEBUG
13
+ # Re-raise the exception so the user sees it, if debugging is
14
+ # enabled (ruby -d).
15
+ raise e
16
+ else
17
+ # Otherwise, just print the message.
18
+ warn "seafoam: #{e.message}"
19
+ end
20
+ end
@@ -0,0 +1,93 @@
1
+ require 'stringio'
2
+ require 'zlib'
3
+
4
+ module Cfg2asm
5
+ module CFG
6
+ Code = Struct.new(:arch, :arch_width, :base, :code)
7
+ Comment = Struct.new(:offset, :comment)
8
+ NMethod = Struct.new(:code, :comments)
9
+
10
+ # A parser for CFG files.
11
+ class CFGParser
12
+ def initialize(out, file)
13
+ @out = out
14
+ data = File.read(file, encoding: Encoding::ASCII_8BIT)
15
+ if data[0..1].bytes == [0x1f, 0x8b]
16
+ data = Zlib.gunzip(data)
17
+ end
18
+ @reader = StringIO.new(data)
19
+ @state = :any
20
+ @cfg_name = nil
21
+ end
22
+
23
+ def skip_over_cfg(name)
24
+ loop do
25
+ line = @reader.readline("\n")
26
+ case line
27
+ when "begin_cfg\n"
28
+ @state = :cfg
29
+ @cfg_name = nil
30
+ when / name "(.*)"\n/
31
+ if @state == :cfg
32
+ @cfg_name = Regexp.last_match(1)
33
+ end
34
+ when "end_cfg\n"
35
+ raise unless @state == :cfg
36
+
37
+ @state = :any
38
+ break if @cfg_name == name
39
+ else
40
+ next
41
+ end
42
+ end
43
+ end
44
+
45
+ def read_nmethod
46
+ raise unless @state == :any
47
+
48
+ arch = nil
49
+ arch_width = nil
50
+ code = nil
51
+ comments = []
52
+ raise unless @reader.readline == "begin_nmethod\n"
53
+
54
+ loop do
55
+ line = @reader.readline("\n")
56
+ case line
57
+ when / Platform (.*) (.*) <\|\|@\n/
58
+ arch = Regexp.last_match(1)
59
+ arch_width = Regexp.last_match(2)
60
+ when / HexCode (.*) (.*) <\|\|@\n/
61
+ base = Regexp.last_match(1).to_i(16)
62
+ code = [Regexp.last_match(2)].pack('H*')
63
+ raise if arch.nil? || arch_width.nil?
64
+
65
+ code = Code.new(arch, arch_width, base, code)
66
+ when / Comment (\d*) (.*) <\|\|@\n/
67
+ offset = Regexp.last_match(1).to_i
68
+ comment = Regexp.last_match(2)
69
+ comments.push Comment.new(offset, comment)
70
+ when " <<<HexCodeFile\n"
71
+ next
72
+ when " HexCodeFile>>> <|@\n"
73
+ next
74
+ when "end_nmethod\n"
75
+ break
76
+ when / (.*) <\|\|@\n/
77
+ offset = -1
78
+ comment = Regexp.last_match(1)
79
+ comments.push Comment.new(offset, comment)
80
+ when / (.*)\n/
81
+ offset = -1
82
+ comment = Regexp.last_match(1)
83
+ comments.push Comment.new(offset, comment)
84
+ else
85
+ # In case anything was missed
86
+ raise 'There is currently no case for this line. Please open an issue so it can be addressed.'
87
+ end
88
+ end
89
+ NMethod.new(code, comments)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,70 @@
1
+ require 'stringio'
2
+
3
+ module Cfg2asm
4
+ module CFG
5
+ # Disassemble and print comments from cfg files
6
+ class Disassembler
7
+ def initialize(out)
8
+ @out = out
9
+ end
10
+
11
+ def disassemble(nmethod, print_comments)
12
+ require_crabstone
13
+
14
+ comments = nmethod.comments
15
+ comments_n = 0
16
+
17
+ case [nmethod.code.arch, nmethod.code.arch_width]
18
+ when %w[AMD64 64]
19
+ crabstone_arch = [Crabstone::ARCH_X86, Crabstone::MODE_64]
20
+ else
21
+ raise "Unknown architecture #{nmethod.code.arch} and bit width #{nmethod.code.arch_width}"
22
+ end
23
+
24
+ cs = Crabstone::Disassembler.new(*crabstone_arch)
25
+ begin
26
+ cs.disasm(nmethod.code.code, nmethod.code.base).each do |i|
27
+ if print_comments
28
+ # Print comments associated to the instruction.
29
+ last_comment = i.address + i.bytes.length - nmethod.code.base
30
+ while comments_n < comments.length && comments[comments_n].offset < last_comment
31
+ if comments[comments_n].offset == -1
32
+ @out.printf("\t\t\t\t;%<comment>s\n", comment: comments[comments_n].comment)
33
+ else
34
+ @out.printf(
35
+ "\t\t\t\t;Comment %<loc>i:\t%<comment>s\n",
36
+ loc: comments[comments_n].offset,
37
+ comment: comments[comments_n].comment
38
+ )
39
+ end
40
+ comments_n += 1
41
+ end
42
+ end
43
+
44
+ # Print the instruction.
45
+ @out.printf(
46
+ "\t0x%<address>x:\t%<instruction>s\t%<details>s\n",
47
+ address: i.address,
48
+ instruction: i.mnemonic,
49
+ details: i.op_str
50
+ )
51
+ end
52
+ rescue StandardError => e
53
+ raise "Disassembly error: #{e.message}"
54
+ ensure
55
+ cs.close
56
+ end
57
+ end
58
+
59
+ def require_crabstone
60
+ require 'crabstone'
61
+ rescue LoadError => e
62
+ if $DEBUG
63
+ raise e
64
+ else
65
+ raise 'Could not load Capstone - is it installed?'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require 'set'
3
+
4
+ module Cfg2asm
5
+ # Implementations of the command-line commands that you can run in Seafoam.
6
+ class Commands
7
+ def initialize(out)
8
+ @out = out
9
+ end
10
+
11
+ def cfg2asm(*args)
12
+ case args.first
13
+ when nil, 'help', '-h', '--help', '-help'
14
+ args = args.drop(1)
15
+ raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
16
+
17
+ @out.puts 'cfg2asm file.bgv...'
18
+ @out.puts ' --no-comments'
19
+ @out.puts ' --help'
20
+ @out.puts ' --version'
21
+ when 'version', '-v', '-version', '--version'
22
+ args = args.drop(1)
23
+ version(*args)
24
+ else
25
+ comments = true
26
+ files = []
27
+
28
+ until args.empty?
29
+ arg = args.shift
30
+ if arg.start_with?('-')
31
+ case arg
32
+ when '--no-comments'
33
+ comments = false
34
+ else
35
+ raise ArgumentError, "unknown option #{arg}"
36
+ end
37
+ else
38
+ files.push arg
39
+ end
40
+ end
41
+
42
+ files.each_with_index do |file, n|
43
+ parser = Cfg2asm::CFG::CFGParser.new(@out, file)
44
+ parser.skip_over_cfg 'After code installation'
45
+ nmethod = parser.read_nmethod
46
+
47
+ disassembler = Cfg2asm::CFG::Disassembler.new(@out)
48
+ @out.puts if n.positive?
49
+ @out.puts "[#{file}]"
50
+ disassembler.disassemble(nmethod, comments)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Prints the version.
56
+ def version(*args)
57
+ raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
58
+
59
+ @out.puts "cfg2asm #{VERSION}"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ module Cfg2asm
2
+ MAJOR_VERSION = 0
3
+ MINOR_VERSION = 1
4
+ VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
5
+ end
data/lib/cfg2asm.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'cfg2asm/version'
2
+ require 'cfg2asm/cfg/cfg_parser'
3
+ require 'cfg2asm/cfg/disassembler'
4
+ require 'cfg2asm/commands'
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cfg2asm
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Chris Seaton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: crabstone
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 13.0.6
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 13.0.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: '0.81'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: '0.81'
69
+ description:
70
+ email:
71
+ executables:
72
+ - cfg2asm
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - bin/cfg2asm
77
+ - lib/cfg2asm.rb
78
+ - lib/cfg2asm/cfg/cfg_parser.rb
79
+ - lib/cfg2asm/cfg/disassembler.rb
80
+ - lib/cfg2asm/commands.rb
81
+ - lib/cfg2asm/version.rb
82
+ homepage: https://github.com/Shopify/cfg2asm
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 2.3.7
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubygems_version: 3.3.3
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: A tool for disassembling Graal CFG files
105
+ test_files: []