haxor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +414 -0
- data/Rakefile +1 -0
- data/bin/hcc +21 -0
- data/bin/hld +38 -0
- data/bin/hvm +17 -0
- data/examples/build.sh +10 -0
- data/examples/guess-the-number.hax +100 -0
- data/haxor.gemspec +24 -0
- data/lib/haxor.rb +44 -0
- data/lib/haxor/compiler/component/arithmetic.rb +55 -0
- data/lib/haxor/compiler/component/base.rb +37 -0
- data/lib/haxor/compiler/component/data.rb +32 -0
- data/lib/haxor/compiler/component/jumps.rb +95 -0
- data/lib/haxor/compiler/component/logical.rb +43 -0
- data/lib/haxor/compiler/component/other.rb +21 -0
- data/lib/haxor/compiler/component/transfer.rb +29 -0
- data/lib/haxor/compiler/component/various.rb +33 -0
- data/lib/haxor/compiler/core.rb +134 -0
- data/lib/haxor/compiler/section.rb +6 -0
- data/lib/haxor/compiler/unit.rb +39 -0
- data/lib/haxor/consts.rb +28 -0
- data/lib/haxor/header.rb +40 -0
- data/lib/haxor/linker.rb +89 -0
- data/lib/haxor/token/base.rb +7 -0
- data/lib/haxor/token/cmd.rb +9 -0
- data/lib/haxor/token/data.rb +17 -0
- data/lib/haxor/token/int64.rb +17 -0
- data/lib/haxor/token/label.rb +23 -0
- data/lib/haxor/token/pointer.rb +13 -0
- data/lib/haxor/vm/core.rb +53 -0
- data/lib/haxor/vm/cpu/core.rb +129 -0
- data/lib/haxor/vm/cpu/unit/arithmetic.rb +92 -0
- data/lib/haxor/vm/cpu/unit/base.rb +46 -0
- data/lib/haxor/vm/cpu/unit/jumps.rb +123 -0
- data/lib/haxor/vm/cpu/unit/logical.rb +59 -0
- data/lib/haxor/vm/cpu/unit/transfer.rb +37 -0
- data/lib/haxor/vm/cpu/unit/various.rb +47 -0
- data/lib/haxor/vm/mem.rb +101 -0
- data/lib/haxor/vm/os.rb +115 -0
- data/lib/haxor/vm/stack.rb +31 -0
- data/lib/haxor/vm/subsystem.rb +16 -0
- data/media/memory.png +0 -0
- data/media/vm.png +0 -0
- metadata +122 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Logical < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'and', :cmd_and
|
7
|
+
bind_cmd 'neg', :cmd_neg
|
8
|
+
bind_cmd 'not', :cmd_not
|
9
|
+
bind_cmd 'or', :cmd_or
|
10
|
+
bind_cmd 'xor', :cmd_xor
|
11
|
+
end
|
12
|
+
|
13
|
+
def cmd_and(a, b)
|
14
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_AND | offset_flags(a, b))
|
15
|
+
parse_value a
|
16
|
+
parse_value b
|
17
|
+
end
|
18
|
+
|
19
|
+
def cmd_neg(a)
|
20
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_NEG | offset_flags(a))
|
21
|
+
parse_value a
|
22
|
+
end
|
23
|
+
|
24
|
+
def cmd_not(a)
|
25
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_NOT | offset_flags(a))
|
26
|
+
parse_value a
|
27
|
+
end
|
28
|
+
|
29
|
+
def cmd_or(a, b)
|
30
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_OR | offset_flags(a, b))
|
31
|
+
parse_value a
|
32
|
+
parse_value b
|
33
|
+
end
|
34
|
+
|
35
|
+
def cmd_xor(a, b)
|
36
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_XOR | offset_flags(a, b))
|
37
|
+
parse_value a
|
38
|
+
parse_value b
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Other < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'label', :cmd_label
|
7
|
+
bind_cmd 'rem', :cmd_rem
|
8
|
+
bind_cmd '#', :cmd_rem
|
9
|
+
end
|
10
|
+
|
11
|
+
def cmd_rem(*_args)
|
12
|
+
# nothing
|
13
|
+
end
|
14
|
+
|
15
|
+
def cmd_label(*args)
|
16
|
+
add Token::Label.new(args[0])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Transfer < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'mov', :cmd_mov
|
7
|
+
bind_cmd 'push', :cmd_push
|
8
|
+
bind_cmd 'pop', :cmd_pop
|
9
|
+
end
|
10
|
+
|
11
|
+
def cmd_push(a)
|
12
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_PUSH | offset_flags(a))
|
13
|
+
parse_value a
|
14
|
+
end
|
15
|
+
|
16
|
+
def cmd_pop(a)
|
17
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_POP | offset_flags(a))
|
18
|
+
parse_value a
|
19
|
+
end
|
20
|
+
|
21
|
+
def cmd_mov(a, b)
|
22
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_MOV | offset_flags(a, b))
|
23
|
+
parse_value a
|
24
|
+
parse_value b
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Various < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'nop', :cmd_nop
|
7
|
+
bind_cmd 'lea', :cmd_lea
|
8
|
+
bind_cmd 'int', :cmd_int
|
9
|
+
bind_cmd 'syscall', :cmd_syscall
|
10
|
+
end
|
11
|
+
|
12
|
+
def cmd_nop(*_args)
|
13
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_NOP)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cmd_lea(a, b)
|
17
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_LEA | offset_flags(a, b))
|
18
|
+
parse_value a
|
19
|
+
parse_value b
|
20
|
+
end
|
21
|
+
|
22
|
+
def cmd_int(a)
|
23
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_INT)
|
24
|
+
parse_value a
|
25
|
+
end
|
26
|
+
|
27
|
+
def cmd_syscall
|
28
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_SYSCALL)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
class Core
|
4
|
+
def initialize
|
5
|
+
@units = []
|
6
|
+
@cmds = {}
|
7
|
+
@autolabel = 0
|
8
|
+
@prev_sections = []
|
9
|
+
|
10
|
+
bind_cmd 'section', self, :cmd_section
|
11
|
+
end
|
12
|
+
|
13
|
+
def register_unit(unit)
|
14
|
+
@units << unit
|
15
|
+
unit.compiler = self
|
16
|
+
unit.register
|
17
|
+
end
|
18
|
+
|
19
|
+
def bind_cmd(cmd, object, method)
|
20
|
+
@cmds[cmd] = {
|
21
|
+
object: object,
|
22
|
+
method: method
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def cmd_section(name)
|
27
|
+
@unit.section = name[1..-1].to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
# this is ugly and must be reworked
|
31
|
+
def split_arguments(string)
|
32
|
+
inside_quotes = false
|
33
|
+
splits = []
|
34
|
+
string.each_char.with_index(0) do |x, i|
|
35
|
+
if x == '"' && string[i - 1] != '\\'
|
36
|
+
inside_quotes ^= true
|
37
|
+
end
|
38
|
+
|
39
|
+
if x == ',' && !inside_quotes
|
40
|
+
splits << i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
splits << string.size
|
45
|
+
|
46
|
+
last_x = 0
|
47
|
+
args = []
|
48
|
+
splits.each do |x|
|
49
|
+
args << string[last_x...x]
|
50
|
+
last_x = x + 1
|
51
|
+
end
|
52
|
+
args.map!(&:strip).delete_if { |x| x.length == 0 }
|
53
|
+
end
|
54
|
+
|
55
|
+
def compile(filename)
|
56
|
+
input = File.read(filename, encoding: 'ASCII-8BIT')
|
57
|
+
|
58
|
+
@unit = Unit.new
|
59
|
+
|
60
|
+
input.lines.map(&:chomp).each_with_index do |line, index|
|
61
|
+
next if line.empty?
|
62
|
+
|
63
|
+
tmp = line.split ' ', 2
|
64
|
+
cmd = tmp[0]
|
65
|
+
args = split_arguments(tmp[1] || '')
|
66
|
+
|
67
|
+
fail "Unknown command #{cmd} in line #{index}." unless @cmds.key? cmd
|
68
|
+
|
69
|
+
@cmds[cmd][:object].send(@cmds[cmd][:method], *args)
|
70
|
+
end
|
71
|
+
|
72
|
+
@unit.save(filename + '.u')
|
73
|
+
end
|
74
|
+
|
75
|
+
def add(token)
|
76
|
+
@unit.add token
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_number(value)
|
80
|
+
md = value.match(/\A([-+]?[0-9]+[0-9A-F]*)([bhd]?)\z/)
|
81
|
+
fail if md.nil?
|
82
|
+
|
83
|
+
case md[2]
|
84
|
+
when 'b'
|
85
|
+
base = 2
|
86
|
+
when 'h'
|
87
|
+
base = 16
|
88
|
+
when 'd'
|
89
|
+
else
|
90
|
+
base = 10
|
91
|
+
end
|
92
|
+
|
93
|
+
md[1].to_i(base)
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_value(value)
|
97
|
+
value = strip_offset value
|
98
|
+
|
99
|
+
begin
|
100
|
+
number = parse_number(value)
|
101
|
+
@autolabel += 1
|
102
|
+
label = "__autolabel_#{@autolabel}"
|
103
|
+
|
104
|
+
old_section = @unit.section
|
105
|
+
@unit.section = :data
|
106
|
+
add Token::Label.new(label)
|
107
|
+
add Token::Int64.new(number)
|
108
|
+
@unit.section = old_section
|
109
|
+
|
110
|
+
add Token::Pointer.new(label)
|
111
|
+
rescue
|
112
|
+
add Token::Pointer.new(value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def offset_flags(a, b = nil)
|
117
|
+
result = 0
|
118
|
+
result |= Consts::OPCODE_FLG_DA if offset?(a)
|
119
|
+
result |= Consts::OPCODE_FLG_DB if offset?(b)
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
def offset?(value)
|
124
|
+
return false unless value.is_a? String
|
125
|
+
value.start_with?('[') && value.end_with?(']')
|
126
|
+
end
|
127
|
+
|
128
|
+
def strip_offset(value)
|
129
|
+
return value[1...-1] if offset?(value)
|
130
|
+
value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
class Unit
|
4
|
+
attr_reader :section
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@section = :text
|
8
|
+
@sections = {
|
9
|
+
text: [],
|
10
|
+
data: [],
|
11
|
+
bss: []
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(item)
|
16
|
+
@sections[@section] << item
|
17
|
+
end
|
18
|
+
|
19
|
+
def section=(section)
|
20
|
+
fail unless @sections.key? section
|
21
|
+
@section = section
|
22
|
+
end
|
23
|
+
|
24
|
+
def tokens
|
25
|
+
@sections[@section]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.load(filename)
|
29
|
+
Marshal.load File.read(filename)
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(filename)
|
33
|
+
fd = File.open(filename, 'wb')
|
34
|
+
fd.write Marshal.dump(self)
|
35
|
+
fd.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/haxor/consts.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Consts
|
3
|
+
MAJOR = 0
|
4
|
+
MINOR = 1
|
5
|
+
PATCH = 0
|
6
|
+
GEM_VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
7
|
+
VERSION = (MAJOR << 16) | (MINOR << 8) | PATCH
|
8
|
+
|
9
|
+
ROOT_PATH = __dir__ + '/../..'
|
10
|
+
|
11
|
+
WORD_SIZE = 8 # 64bit
|
12
|
+
WORD_UNPACK = 'q<'
|
13
|
+
|
14
|
+
RESERVED_MEM = 2048 # 1024 for CPU, 1024 for IVT
|
15
|
+
IVT_ADDR = 1024
|
16
|
+
|
17
|
+
# OpCode
|
18
|
+
OPCODE_CMD_MASK = 0x0000_0000_0000_00ff
|
19
|
+
OPCODE_FLG_MASK = 0xffff_ffff_ffff_ff00
|
20
|
+
OPCODE_FLG_OFFSET = 8
|
21
|
+
OPCODE_FLG_DA = 1 << OPCODE_FLG_OFFSET # dereference A operand
|
22
|
+
OPCODE_FLG_DB = 2 << OPCODE_FLG_OFFSET # dereference B operand
|
23
|
+
|
24
|
+
# Flags Registry Flags
|
25
|
+
FR_ZERO = 1 << 0 # a-b == 0
|
26
|
+
FR_SIGN = 1 << 1 # a-b < 0
|
27
|
+
end
|
28
|
+
end
|
data/lib/haxor/header.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Header
|
3
|
+
MAGIC = 0x72_65_88_79_82 # HAXOR (ASCII)
|
4
|
+
SIZE = 64 # max 8 values (64bit per entry)
|
5
|
+
|
6
|
+
attr_accessor :version
|
7
|
+
attr_accessor :entry_point
|
8
|
+
attr_accessor :bss_size
|
9
|
+
attr_accessor :stack_size
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@magic = MAGIC
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump
|
16
|
+
result = ''
|
17
|
+
result << [
|
18
|
+
@magic,
|
19
|
+
@version,
|
20
|
+
@entry_point,
|
21
|
+
@bss_size,
|
22
|
+
@stack_size
|
23
|
+
].pack('q<*')
|
24
|
+
result.ljust(SIZE)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse!(data)
|
28
|
+
data = data.unpack('q<*')
|
29
|
+
@magic = data.shift
|
30
|
+
@version = data.shift
|
31
|
+
@entry_point = data.shift
|
32
|
+
@bss_size = data.shift
|
33
|
+
@stack_size = data.shift
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
SIZE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/haxor/linker.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Linker
|
3
|
+
attr_accessor :stack
|
4
|
+
|
5
|
+
SECTIONS = [:text, :data, :bss]
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@cpu = Vm::Cpu::Core.new
|
9
|
+
@units = []
|
10
|
+
@tokens = []
|
11
|
+
@labels = @cpu.labels.clone
|
12
|
+
@stack = 4096
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_unit(filename)
|
16
|
+
@units << Haxor::Compiler::Unit.load(filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
def link(filename)
|
20
|
+
calc_absolute_addr
|
21
|
+
collect_labels
|
22
|
+
unwind_pointers
|
23
|
+
|
24
|
+
output = File.open(filename, 'wb')
|
25
|
+
output.write build
|
26
|
+
output.close
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def walk_tokens(type = nil)
|
32
|
+
SECTIONS.each do |section|
|
33
|
+
@units.each do |unit|
|
34
|
+
unit.section = section
|
35
|
+
unit.tokens.each do |token|
|
36
|
+
next unless type.nil? || token.is_a?(type)
|
37
|
+
yield token, section
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def calc_absolute_addr
|
44
|
+
addr = Consts::RESERVED_MEM
|
45
|
+
walk_tokens do |token|
|
46
|
+
token.absolute_addr = addr
|
47
|
+
addr += token.size
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def collect_labels
|
52
|
+
walk_tokens(Token::Label) do |token|
|
53
|
+
fail "Label already exists: #{token.label}." if @labels.key? token.label
|
54
|
+
@labels[token.label] = token
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def unwind_pointers
|
59
|
+
walk_tokens(Token::Pointer) do |token|
|
60
|
+
fail "Label not found: #{token.label}." unless @labels.key? token.label
|
61
|
+
token.data = @labels[token.label].absolute_addr
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_header
|
66
|
+
bss_size = 0
|
67
|
+
walk_tokens do |token, section|
|
68
|
+
bss_size += token.size if section == :bss
|
69
|
+
end
|
70
|
+
|
71
|
+
hdr = Header.new
|
72
|
+
hdr.version = Consts::VERSION
|
73
|
+
hdr.entry_point = @labels['main'].absolute_addr
|
74
|
+
hdr.stack_size = @stack
|
75
|
+
hdr.bss_size = bss_size
|
76
|
+
hdr
|
77
|
+
end
|
78
|
+
|
79
|
+
def build
|
80
|
+
result = build_header.dump
|
81
|
+
walk_tokens do |token, section|
|
82
|
+
next unless [:text, :data].include? section
|
83
|
+
puts "[#{section}] [#{token.absolute_addr}] #{token}"
|
84
|
+
result << token.to_bytecode
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|