summaryrefslogtreecommitdiff
path: root/tool
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2020-09-24 03:07:53 -0400
committerAlan Wu <XrXr@users.noreply.github.com>2021-10-20 18:19:24 -0400
commit453218b272e694b10ca3ad781001600df0a1c3d9 (patch)
treefaa7f434f84e7977eb95c6e46aa2ae9542f1c43e /tool
parent3d87eadf16a086d2f3bec0b556760c0ebfe1e7cd (diff)
Preliminary GNU/Linux support for code scraper
Let's see if this works on CI
Diffstat (limited to 'tool')
-rw-r--r--tool/ruby_vm/models/micro_jit.rb82
1 files changed, 77 insertions, 5 deletions
diff --git a/tool/ruby_vm/models/micro_jit.rb b/tool/ruby_vm/models/micro_jit.rb
index eee829a2e6..61c28c84b2 100644
--- a/tool/ruby_vm/models/micro_jit.rb
+++ b/tool/ruby_vm/models/micro_jit.rb
@@ -12,6 +12,17 @@
module RubyVM::MicroJIT
class << self
+ def target_platform
+ # Note, checking RUBY_PLATRFORM doesn't work when cross compiling
+ @platform ||= if RUBY_PLATFORM.include?('darwin')
+ :darwin
+ elsif RUBY_PLATFORM.include?('linux')
+ :linux
+ else
+ :unknown
+ end
+ end
+
def get_fileoff
# use the load command to figure out the offset to the start of the content of vm.o
`otool -l vm.o`.each_line do |line|
@@ -38,16 +49,58 @@ module RubyVM::MicroJIT
bytes.unpack('q').first # this is native endian but we want little endian. it's fine if the host moachine is x86
end
+ def get_symbol_section_and_offset(name)
+ `objdump -w -t vm.o`.each_line do |line|
+ split_line = line.split
+ next unless split_line.size >= 6
+ # the table should go into a data section
+ if split_line[5].include?('insns_address_table') && split_line[3].include?('data')
+ p line if $DEBUG
+ return [split_line[3], Integer(split_line[0], 16)]
+ end
+ end
+ raise 'Failed to find section and offset for the the instruction address table'
+ end
+
+ def get_handler_offset(table_section, table_offset, insn_id)
+ target_offset = insn_id * 8 + table_offset
+ reloc_start_message = "RELOCATION RECORDS FOR [#{table_section}]:"
+ `objdump -w -r vm.o`.each_line do |line|
+ line.strip!
+ if (line == reloc_start_message)...(line.empty?)
+ split_line = line.split
+ next if split_line.first == 'RELOCATION'
+ next if split_line == ['OFFSET', 'TYPE', 'VALUE']
+ if Integer(split_line.first, 16) == target_offset
+ section, offset = split_line[2].split('+')
+ p line if $DEBUG
+ return section, Integer(offset, 16)
+ end
+ end
+ end
+ raise 'Failed to find relocation info for the target instruction'
+ end
+
+ def objdump_disassemble_command(offset)
+ case target_platform
+ when :darwin
+ "objdump --x86-asm-syntax=intel --start-address=#{offset} --stop-address=#{offset+50} -d vm.o"
+ when :linux
+ "objdump -M intel --start-address=#{offset} --stop-address=#{offset+50} -d vm.o"
+ else
+ raise "unkown platform"
+ end
+ end
+
def disassemble(offset)
- command = "objdump --x86-asm-syntax=intel --start-address=#{offset} --stop-address=#{offset+50} -d vm.o"
+ command = objdump_disassemble_command(offset)
puts "Running: #{command}"
- puts "feel free to verify with --reloc"
disassembly = `#{command}`
instructions = []
puts disassembly if $DEBUG
disassembly.each_line do |line|
line = line.strip
- match_data = /\h+: ((?:\h\h\s?)+)\s+(\w+)/.match(line)
+ match_data = /\s*\h+:\s*((?:\h\h\s)+)\s+(\w+)/.match(line)
if match_data
bytes = match_data[1]
mnemonic = match_data[2]
@@ -91,8 +144,7 @@ module RubyVM::MicroJIT
end
end
- def scrape
- instruction_id = RubyVM::Instructions.find_index { |insn| insn.name == 'ujit_call_example' }
+ def darwin_scrape(instruction_id)
fileoff = get_fileoff
tc_table_offset = get_symbol_offset('vm_exec_core.insns_address_table')
vm_exec_core_offset = get_symbol_offset('vm_exec_core')
@@ -106,6 +158,26 @@ module RubyVM::MicroJIT
disassemble(vm_exec_core_offset + offset_to_handler_code_from_vm_exec_core)
end
+ def linux_scrape(instruction_id)
+ table_section, table_offset = get_symbol_section_and_offset('vm_exec_core.insns_address_table')
+ p [table_section, table_offset] if $DEBUG
+ handler_section, handler_offset = get_handler_offset(table_section, table_offset, instruction_id)
+ p [handler_section, handler_offset] if $DEBUG
+ disassemble(handler_offset)
+ end
+
+ def scrape
+ instruction_id = RubyVM::Instructions.find_index { |insn| insn.name == 'ujit_call_example' }
+ case target_platform
+ when :darwin
+ darwin_scrape(instruction_id)
+ when :linux
+ linux_scrape(instruction_id)
+ else
+ raise 'Unkonwn platform. Only Mach-O on macOS and ELF on Linux are supported'
+ end
+ end
+
def comma_separated_hex_string(nums)
nums.map{ |byte| '0x'+byte}.join(', ')
end