keep some stuff in storage that just gets in tway in crystal

This commit is contained in:
Torsten Ruger 2014-04-23 11:14:54 +03:00
parent 8de59a85ba
commit 8584c93575
7 changed files with 618 additions and 0 deletions

View File

@ -0,0 +1,48 @@
module Asm
module Arm
# TODO actually find the closest somehow (DROPPED for now)
def self.closest_addrtable(as)
as.values.find do |obj|
obj.is_a?(Asm::Arm::AddrTableObject)
end || (raise Asm::AssemblyError.new('could not find addrtable to use', nil))
end
#this has been DROPPED for now (didn't work) and we can do without it
class AddrTableObject
def initialize
@table = []
@const = []
end
# TODO don't create new entry if there's already an entry for the same label/const
def add_label(label)
d = [label, Asm::LabelObject.new]
@table << d
d[1]
end
def add_const(const)
d = [const, Asm::LabelObject.new]
@const << d
d[1]
end
def assemble(io, as)
@table.each do |pair|
target_label, here_label = *pair
here_label.assemble io, as
as.add_relocation io.tell, target_label, Asm::Arm::R_ARM_ABS32,
Asm::Arm::Instruction::RelocHandler
io.write_uint32 0
end
@const.each do |pair|
const, here_label = *pair
here_label.assemble io, as
io.write_uint32 const
end
end
end
end
end

85
unused/ast_assembler.rb Normal file
View File

@ -0,0 +1,85 @@
module Asm
class AstAssembler
def initialize(asm_arch)
@asm_arch = asm_arch
@symbols = {}
@inst_label_context = {}
@asm = Asm::Assembler.new
end
def assembler
@asm
end
def load_ast(ast)
label_breadcrumb = []
ast.children.each do |cmd|
if (cmd.is_a?(Asm::LabelNode))
m = /^\/+/.match(cmd.name)
count = m ? m[0].length : 0
label_breadcrumb = label_breadcrumb[0,count]
label_breadcrumb << cmd.name[count..-1]
@asm.add_value object_for_label(label_breadcrumb.join('/'))
elsif (cmd.is_a?(Asm::InstructionNode))
inst = @asm_arch::Instruction.new(cmd, self)
@asm.add_value inst
@inst_label_context[inst] = label_breadcrumb
elsif (cmd.is_a?(Asm::DirectiveNode))
if (cmd.name == 'global')
symbol_for_label(cmd.value)[:linkage] = Elf::Constants::STB_GLOBAL
elsif (cmd.name == 'extern')
object_for_label(cmd.value).extern!
elsif (cmd.name == 'hexdata')
bytes = cmd.value.strip.split(/\s+/).map do |hex|
hex.to_i(16)
end.pack('C*')
@asm.add_value Asm::StringNode.new(bytes)
elsif (cmd.name == "asciz")
str = eval(cmd.value) + "\x00"
@asm.add_value Asm::StringNode.new(str)
elsif (defined?(Asm::Arm) and cmd.name == 'addrtable')
@asm.add_value Asm::Arm::AddrTableObject.new
else
raise Asm::AssemblyError.new('unknown directive', cmd)
end
end
end
end
# instruction is user for label context
def symbol_for_label(name, instruction=nil)
if (instruction)
context = @inst_label_context[instruction]
m = /^(\/*)(.+)/.match(name)
breadcrumb = context[0,m[1].length]
breadcrumb << m[2]
qual_name = breadcrumb.join('/')
else
qual_name = name
end
if (not @symbols[qual_name])
@symbols[name] = {:label => Asm::LabelObject.new, :linkage => Elf::Constants::STB_LOCAL, :name => qual_name}
end
@symbols[qual_name]
end
def object_for_label(name, instruction=nil)
symbol_for_label(name, instruction)[:label]
end
def assemble(io)
@asm.assemble io
end
def symbols
@symbols.values
end
def relocations
@asm.relocations
end
end
end

29
unused/code_generator.rb Normal file
View File

@ -0,0 +1,29 @@
require "asm/arm/code_generator"
if (__FILE__ == $0)
gen = Asm::Arm::ArmAssembler.new
gen.instance_eval {
mov r0, 5
loop_start = label
loop_start.set!
subs r0, r0, 1
bne loop_start
mov r7, 1
swi 0
}
gen.add_string("printf"+ "\x00")
require 'asm/object_writer'
writer = Asm::ObjectWriter.new(Elf::Constants::TARGET_ARM)
writer.set_text gen.assemble_to_string
begin
writer.save('arm_as_generated.o')
rescue => err
puts 'as: cannot save output file: ' + err.message
exit
end
end

149
unused/command_line.rb Normal file
View File

@ -0,0 +1,149 @@
require_relative 'parser'
require_relative 'assembler'
require_relative 'objectwriter'
require 'optparse'
require 'ostruct'
module Asm
class CommandLine
def initialize
options = OpenStruct.new
options.output_file = "a.out"
options.target = :arm
opts = OptionParser.new do |opts|
opts.banner = "Usage: as [options] <input file>"
opts.separator ""
opts.separator "Options:"
opts.on("-t", "--target TARGET",
"Specify target architecture (arm [default], ttk91)") { |o|
options.target = o.to_sym
if (not [:arm, :ttk91].include?(options.target))
puts opts
exit
end
}
opts.on("-o", "--output FILENAME",
"Specify output filename for object file") { |o|
options.output_file = o
}
opts.on("-s", "--show-ast",
"Show parse tree") { |o|
options.show_ast = true
}
opts.on_tail("-h", "--help", "Show this message") {
puts opts
exit
}
end
opts.parse!(ARGV)
options.input_file = ARGV.shift
if (not options.input_file)
puts opts
exit
end
@options = options
end
attr_reader :options
def run
begin
if (options.input_file == '-')
code = $stdin.read
else
code = File.read(options.input_file)
end
rescue => err
puts 'as: could not read input file: ' + err.message
exit 2
end
begin
ast = Asm::Parser.parse(code)
rescue Asm::ParseError => err
puts 'as: parse error on line %d, column %d' % [err.line+1, err.column+1]
line = code.split("\n")[err.line]
puts line.gsub(/\s/, ' ')
puts ' ' * (err.column-1) + '^'
puts ' ' + err.message
exit 3
end
if (options.show_ast)
require 'pp'
pp ast
exit 0
end
case options.target
when :arm
require_relative 'arm_assembler.rb'
as_module = Asm::Arm
as_target = Elf::Constants::TARGET_ARM
when :ttk91
require_relative 'ttk91_assembler.rb'
as_module = Asm::TTK91
as_target = Elf::Constants::TARGET_TTK91
end
asm = Asm::AstAssembler.new(as_module)
begin
asm.load_ast ast
data = StringIO.new
asm.assemble(data)
symbols = asm.symbols
rescue Asm::AssemblyError => err
if (err.node)
puts 'as: assembly error on line %d, column %d' % [
err.node.line+1, err.node.column+1]
line = code.split("\n")[err.node.line]
puts line.gsub(/\s/, ' ')
puts ' ' * (err.node.column-1) + '^'
puts ' ' + err.message
else
puts 'as: ' + err.message
end
exit 4
end
writer = Asm::ObjectWriter.new(as_target)
writer.set_text data.string
reloc_name_ref = {}
symbols.each { |symbol|
label = symbol[:label]
if (label.extern?)
reloc_name_ref[label] = symbol[:name]
writer.add_reloc_symbol symbol[:name]
else
writer.add_symbol symbol[:name], symbol[:label].address, symbol[:linkage]
end
}
asm.relocations.each { |reloc|
writer.add_reloc reloc.position, reloc_name_ref[reloc.label], reloc.type
}
begin
writer.save(options.output_file)
rescue => err
puts 'as: cannot save output file: ' + err.message
end
end
end
end
if (__FILE__ == $0)
Asm::CommandLine.new.run
end

19
unused/elf_object.rb Normal file
View File

@ -0,0 +1,19 @@
if (__FILE__ == $0)
obj = Elf::ObjectFile.new Elf::TARGET_ARM
sym_strtab = Elf::StringTableSection.new(".strtab")
obj.add_section sym_strtab
symtab = Elf::SymbolTableSection.new(".symtab", sym_strtab)
obj.add_section symtab
text_section = Elf::TextSection.new(".text")
obj.add_section text_section
symtab.add_func_symbol "_start", 0, text_section, Elf::STB_GLOBAL
fp = File.open("test.o", "wb")
obj.write fp
fp.close
end

223
unused/parser.rb Normal file
View File

@ -0,0 +1,223 @@
require_relative 'str_scanner'
require_relative 'nodes'
class NumEquivAddrNode < NumLiteralNode
end
class LabelEquivAddrNode < LabelRefNode
end
class ToplevelNode < Node
attr_accessor :children
end
class DirectiveNode < Node
attr_accessor :name, :value
end
class LabelNode < Node
attr_accessor :name
end
class MathNode < Node
attr_accessor :left, :right, :op
alias_method :argument, :left
alias_method :argument=, :left=
end
module Asm
class Parser
def initialize(str)
scanner = Asm::Scanner.new(str)
@ast = parse_toplevel scanner
end
attr_reader :ast
def self.parse(str)
new(str).ast
end
def parse_toplevel(s)
node = ToplevelNode.new(s)
node.children = []
while (not s.eos?)
node.children << parse(s)
end
node
end
def parse(s)
s.scan /\s*/
node = nil
%w(comment directive label instruction).each { |em|
if (node = send('parse_'+em, s))
break
end
}
raise Asm::ParseError.new('could not parse element', s) unless node
s.scan /\s*/
node
end
def parse_comment(s)
if (s.scan(/;.*?$/))
CommentNode.new(s)
end
end
def parse_directive(s)
if (m = s.scan(/\.(\w+)(?:(?!$)\s+(.+)\s*?$)?/))
DirectiveNode.new(s) { |n|
n.name = m[0]
n.value = m[1]
}
end
end
def parse_label(s)
if (m = s.scan(/(\/*\w+):/))
LabelNode.new(s) { |n|
n.name = m[0]
}
end
end
def parse_instruction(s)
if (m = s.scan(/(\w+)/))
node = InstructionNode.new(s) { |n|
n.opcode = m[0]
n.args = []
}
if (not s.scan(/\s*($|;)/))
loop {
arg = parse_arg(s)
node.args << arg
break if not s.scan(/\s*,/)
}
end
node
end
end
def parse_arg(s)
s.scan /\s*/
node = nil
%w(reference register register_list num_literal label_ref).each { |em|
if (node = send('parse_'+em, s))
break
end
}
raise Asm::ParseError.new('expected argument but none found', s) unless node
if (node2 = parse_arg_op(s))
node2.argument = node
node = node2
end
s.scan /\s*/
node
end
def parse_arg_op(s)
s.scan /\s*/
node = nil
%w(shift math).each do |em|
if (node = send('parse_'+em, s))
break
end
end
s.scan /\s*/
node
end
def parse_shift(s)
if (m = s.scan(/(lsl|lsr|asr|ror|rrx)\s+/i))
op = m[0].downcase
if (op == 'rrx' or arg = parse_arg(s))
ShiftNode.new(s) { |n|
n.type = m[0].downcase
n.value = arg
}
else
nil
end
end
end
def parse_math(s)
if (m = s.scan_str(/[\+\-]/))
if (arg1 = parse_arg(s))
MathNode.new(s) do |n|
n.right = arg1
n.op = m
end
else
raise Asm::ParseError.new('expected right side for arithmetic op', s)
end
end
end
REGISTER_REGEXP = Regexp.union(*%w(r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12
r13 r14 r15 a1 a2 a3 a4 v1 v2 v3 v4 v5 v6
rfp sl fp ip sp lr pc
))
def parse_register(s)
if (m = s.scan_str(REGISTER_REGEXP))
RegisterNode.new(s) { |n|
n.name = m
}
end
end
def parse_register_list(s)
if (m = s.scan(/\{/))
node = RegisterListNode.new(s) do |n|
n.registers = []
end
loop do
s.scan /\s*/
reg = parse_register(s)
if (not reg)
return nil
end
s.scan /\s*,?/
node.registers << reg
if (s.scan(/\}/))
break
end
end
node
end
end
def parse_num_literal(s)
if (m = s.scan(/(=?)#(-?(?:0x)?[0-9A-Fa-f]+)/))
(m[0] == '=' ? NumEquivAddrNode : NumLiteralNode).new(s) { |n|
n.value = Integer(m[1])
}
end
end
def parse_label_ref(s)
if (m = s.scan(/(=?)(\/*\w+)/))
(m[0] == '=' ? LabelEquivAddrNode : LabelRefNode).new(s) { |n|
n.label = m[1]
}
end
end
def parse_reference(s)
if (m = s.scan(/\[/))
arg = parse_arg(s)
if (arg and s.scan(/\]/))
ReferenceNode.new(s) do |n|
n.argument = arg
end
end
end
end
end
end
if (__FILE__ == $0)
p Asm::Parser.parse ARGV[0]
end

65
unused/str_scanner.rb Normal file
View File

@ -0,0 +1,65 @@
module AS; end
if (not defined? RUBY_ENGINE or not RUBY_ENGINE == 'rbx')
class Regexp
def match_start(str, idx)
Regexp.compile('\A(?:'+source+')').match(str[idx..-1])
end
end
end
class Asm::Scanner
def initialize(str)
@string = str
@pos = 0
@line = 0
@column = 0
end
attr_accessor :string, :pos, :line, :column, :prev_line, :prev_column
def rest
string[pos..-1]
end
def advance_str(str)
self.prev_line = line
self.prev_column = column
self.pos += str.length
self.line += str.count("\n")
if (str.include?("\n"))
self.column = str.length - str.rindex("\n")
else
self.column += str.length
end
end
def scan(regexp)
if (match = regexp.match_start(rest, 0))
advance_str match.to_s
match.captures
else
nil
end
end
def scan_str(regexp)
if (match = regexp.match_start(rest, 0))
advance_str match.to_s
match.to_s
else
nil
end
end
def lookahead(regexp)
if (match = regexp.match_start(rest, 0))
true
else
false
end
end
def eos?
pos == string.length
end
end