From 6cda7d0c57ef3b4632b982284467062a82788e44 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 20 May 2023 15:36:08 +0900 Subject: [PATCH 01/58] change compile method to to_elf method --- lib/vaporware/compiler.rb | 2 +- lib/vaporware/compiler/generator.rb | 16 ++++++--- sig/vaporware.rbs | 53 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 96f6493..bb270a6 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -40,7 +40,7 @@ def compile(compiler: "gcc", compiler_options: ["-O0"]) end output.close compiler_options += @generator.compile_shared_option if @generator.shared - @generator.call_compiler(compiler:, compiler_options:) + @generator.to_elf(input: @generator.precompile, compiler:, compiler_options:, debug: @generator.debug) end end end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index b0eb4eb..19f17d0 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -31,16 +31,22 @@ def register_var_and_method(node) def already_build_methods? = defined_methods.sort == @doned.to_a.sort - def call_compiler(output: precompile, compiler: "gcc", compiler_options: ["-O0"]) - base_name = File.basename(output, ".*") + def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: false) + base_name = File.basename(input, ".*") name = shared ? "lib#{base_name}.so" : base_name - compile_commands = [compiler, *compiler_options, "-o", name, output].compact - IO.popen(compile_commands).close + if compiler.nil? + Vaporware::Compiler::Assemble.compile!(name, input) + else + compile_commands = [compiler, *compiler_options, "-o", name, input].compact + call_compiler(compile_commands) + end - File.delete(output) unless debug + File.delete(input) unless debug nil end + def call_compiler(compile_commands) = IO.popen(compile_commands).close + def epilogue(output) output.puts " mov rsp, rbp" output.puts " pop rbp" diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index fb62e27..3e9535e 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -3,4 +3,57 @@ module Vaporware # See the writing guide of rbs: https://github.com/ruby/rbs#guides class Error end + class Compiler + # class methods + def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> nil + def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> untyped + @generator: Vaporware::Compiler::Generator + # instance methods + def compile: (?compiler: String, ?compiler_options: Array[String]) -> nil + class Generator + REGISTER: Array[String] + # instance variables + @precompile: String + @debug: bool + @doned: Set[Symbol] + @defined_methods: Set[Symbol] + @defined_variables: Set[Symbol] + @seq: Integer + @main: bool + @shared: bool + # temporarily using untyped types since parser.gem's rbs information is unchecked. + @ast: untyped + # class methods + def initialize: (String, precompile: String, debug: bool, shared: bool) -> untyped + + # attr reader for instance variables + def precompile: -> String + def ast: -> untyped # Parser::AST::Node + def defined_methods: -> Set[Symbol] + def defined_variables: -> Set[Symbol] + def debug: -> bool + def doned: -> Set[Symbol] + def seq: -> Integer + def shared: -> bool + # instance private methods + def already_build_methods?: -> bool + def args: (untyped, File) -> nil + def build: (untyped, File, ?bool) -> nil + def call_compiler: (Array[String]) -> nil + def call_method: (untyped, File, bool) -> nil + def comp: (String, File) -> nil + def compile_shared_option: () -> Array[String] + def define_method_prologue: (untyped, File) -> nil + def epilogue: (File) -> nil + def lvar: (untyped, File) -> nil + def method: (Symbol, untyped, File) -> nil + def prologue: (untyped, File) -> nil + def prologue_methods: (File) -> nil + def ret: (File) -> nil + def lvar_offset: (Symbol | nil) -> Integer + def register_var_and_method: (untyped) -> nil + def to_elf: (?input: String, ?compiler: String, ?compiler_options: Array[String], ?debug: bool) -> nil + def variable_or_method?: (Symbol) -> bool + end + end end From 35cb1a72e5ce00beeaa8fd0f2aecf5278ed96ee0 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 20 May 2023 15:40:36 +0900 Subject: [PATCH 02/58] fix missreading comment --- sig/vaporware.rbs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 3e9535e..bece5aa 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -35,7 +35,8 @@ module Vaporware def doned: -> Set[Symbol] def seq: -> Integer def shared: -> bool - # instance private methods + + # instance methods def already_build_methods?: -> bool def args: (untyped, File) -> nil def build: (untyped, File, ?bool) -> nil From e0c1cb48fdf6a4566a58de2678ac039210a1d3b6 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 22 May 2023 09:09:30 +0900 Subject: [PATCH 03/58] add interface for to_elf --- lib/vaporware/compiler/assemble.rb | 17 +++++++++++++++++ sig/vaporware.rbs | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 lib/vaporware/compiler/assemble.rb diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assemble.rb new file mode 100644 index 0000000..ea28ca1 --- /dev/null +++ b/lib/vaporware/compiler/assemble.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Vaporware + class Compiler::Assemble + attr_reader :input, :output + def self.compile!(input, output = File.basename(input, ".*")) = new(input, output).compile + + def initialize(input, output = File.basename(input, ".*")) + @input, @output = input, output + @target_file = File.open(output, "wb") + end + + def compile(compile_options = []) + self + end + end +end diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index bece5aa..7b3cdfa 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -10,6 +10,19 @@ module Vaporware @generator: Vaporware::Compiler::Generator # instance methods def compile: (?compiler: String, ?compiler_options: Array[String]) -> nil + + class Assemble + @input: String + @output: String + @target_file: File + # class methods + def self.compile!: (String, ?String) -> Vaporware::Compiler::Assemble + def initialize: (String, ?String) -> untyped + + # instance methods + def compile: (?Array[String]) -> self + end + class Generator REGISTER: Array[String] # instance variables From 75bea270f549621fdd15521defc89913f2863ad3 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 7 Aug 2023 09:37:19 +0900 Subject: [PATCH 04/58] assemble class for assembling --- lib/vaporware/compiler.rb | 18 ++++ lib/vaporware/compiler/assemble.rb | 144 +++++++++++++++++++++++++-- lib/vaporware/compiler/generator.rb | 2 +- sig/vaporware/compiler.rbs | 2 + sig/vaporware/compiler/assemble.rbs | 9 ++ sig/vaporware/compiler/generator.rbs | 1 - 6 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 sig/vaporware/compiler/assemble.rbs diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index bb270a6..417d772 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -9,12 +9,30 @@ def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_op _precompile = "#{dest}.s" s = new(source, _precompile: _precompile, debug:, shared:) s.compile(compiler:, compiler_options:) + obj = s.assemble(input: _precompile, compmiler:, compiler_options:, debug:) + s.link(input: obj,link_options:) end def initialize(source, _precompile: "tmp.s", debug: false, shared: false) @generator = Vaporware::Compiler::Generator.new(source, precompile: _precompile, debug:, shared:) end + def assemble(input: precompile, output: File.basename(precompile, ".*") + ".o", compiler: "gcc", compiler_options: ["-O0"], debug: false) + base_name = File.basename(input, ".*") + name = shared ? "lib#{base_name}.so" : base_name + if compiler == "gcc" + compile_commands = [compiler, *compiler_options, "-o", name, input].compact + call_compiler(compile_commands) + else + Vaporware::Compiler::Assemble.assemble!(input, name) + end + + File.delete(input) unless debug + nil + end + + def call_compiler(compile_commands) = IO.popen(compile_commands).close + def compile(compiler: "gcc", compiler_options: ["-O0"]) @generator.register_var_and_method(@generator.ast) diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assemble.rb index ea28ca1..37efc42 100644 --- a/lib/vaporware/compiler/assemble.rb +++ b/lib/vaporware/compiler/assemble.rb @@ -1,17 +1,143 @@ # frozen_string_literal: true module Vaporware - class Compiler::Assemble - attr_reader :input, :output - def self.compile!(input, output = File.basename(input, ".*")) = new(input, output).compile + class Compiler + class Assemble + EXEC_ELF_HEADER = %w( + 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + 01 00 3e 00 01 00 00 00 b0 00 40 00 00 00 00 00 + 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 40 00 38 00 00 00 40 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") - def initialize(input, output = File.basename(input, ".*")) - @input, @output = input, output - @target_file = File.open(output, "wb") - end + PREFIX = { + REX_W: 0x48, + } + + REGISTER_CODE = { + RAX: 0, + RDI: 7, + } + + OPECODE = { + ADD: 0x01, + CQO: 0x99, + IDIV: 0xf7, + IMUL: 0x0f, + MOV: 0x89, + SUB: 0x83, + }.freeze + + attr_reader :input, :output + def self.assemble!(input, output = File.basename(input, ".*")) = new(input, output).assemble + + def initialize(input, output = File.basename(input, ".*") + ".o") + @input, @output = input, output + @target_file = File.open(output, "wb") + end + + def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) + read = { main: false } + opecodes = [] + l = 0 + File.open(input, "r") do |r| + r.each_line do |line| + read[:main] = /main:/.match(line) unless read[:main] + next unless read[:main] && !/main:/.match(line) + op, *args = line.split(/\s+/).reject{ _1 == "" }.map { _1.gsub(/,/, "") } + puts "% 10x: %s\t%s\t%s" % [l, opecode(op, args).map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] + opecodes << opecode(op, args).pack("C*") + l+=opecodes.last.bytesize + end + end + f.write(EXEC_ELF_HEADER) + # f.write(ELF_PROGRAM_HEADER) + opecodes.each { f.write(_1) } + f.close + f.path + end + + private + + def opecode(op, args) + case op + when "push" + push(args) + when "mov" + [PREFIX[:REX_W], *mov(op, *args)] + when "sub", "add", "imul", "cqo", "idiv" + [PREFIX[:REX_W], *calc(op, *args)] + when "pop" + pop(args) + when "ret" + [0xc3] + end + end + + def mov(op, *arguments) + reg = case arguments + in ["rbp", "rsp"] + [0xe5] + in ["rsp", "rbp"] + [0xec] + else + arguments&.map { reg(_1) } + end + [OPECODE[op.upcase.to_sym], *reg] + end + + def calc(op, *arguments) + ope_code = OPECODE[op.upcase.to_sym] + case [op, *arguments] + in ["sub", "rax", "rdi"] + [0x29, 0xf8] + in ["add", "rax", "rdi"] + [ope_code, 0xf8] + in ["imul", "rax", "rdi"] + [ope_code, 0xaf, 0xc7] + in ["idiv", "rdi"] + [ope_code, 0xff] + in ["sub", "rsp", *num] + [ope_code, 0xec, *num.map { |n| n.to_i(16) }] + in ["cqo"] + [0x99] + end + end + + def push(args) + case args + in ["rbp"] | ["rdi"] + [0x55] + in ["rax"] + [0x50] + else + [0x6a, *args.map { reg(_1) }] + end + end + + def pop(args) + case args + in ["rax"] | ["rdi"] + [0x58 + REGISTER_CODE[args.first.upcase.to_sym]] + in ["rbp"] + [0x5d] + end + end - def compile(compile_options = []) - self + def reg(r) + case r + in "rsp" + 0xec + in "rbp" + 0x5e + in "rax" + 0x29 + in "rdi" + 0xf8 + in /\d+/ + ("%02x" % r).to_i(16) + end + end end end end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index 19f17d0..d620414 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -35,7 +35,7 @@ def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: base_name = File.basename(input, ".*") name = shared ? "lib#{base_name}.so" : base_name if compiler.nil? - Vaporware::Compiler::Assemble.compile!(name, input) + Vaporware::Compiler::Assemble.assemble!(name, input) else compile_commands = [compiler, *compiler_options, "-o", name, input].compact call_compiler(compile_commands) diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index 59e7d9c..e02a603 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -5,6 +5,8 @@ class Vaporware::Compiler @generator: Vaporware::Compiler::Generator # instance methods + def assemble: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> void + def link: (?output: String, ?linker: String, ?linker_options: Array[String]) -> void def compile: (?compiler: String, ?compiler_options: Array[String]) -> void end diff --git a/sig/vaporware/compiler/assemble.rbs b/sig/vaporware/compiler/assemble.rbs new file mode 100644 index 0000000..16e7d9d --- /dev/null +++ b/sig/vaporware/compiler/assemble.rbs @@ -0,0 +1,9 @@ +class Vaporware::Compiler::Assemble + attr_reader input: String + attr_reader output: String + + @target_file: File + + def initialize: (String, String) -> void + def compile: (?compiler_options: Array[String]) -> void +end diff --git a/sig/vaporware/compiler/generator.rbs b/sig/vaporware/compiler/generator.rbs index 3af1e8c..954a00d 100644 --- a/sig/vaporware/compiler/generator.rbs +++ b/sig/vaporware/compiler/generator.rbs @@ -22,7 +22,6 @@ class Vaporware::Compiler::Generator # instance private methods def already_build_methods?: -> bool - def call_compiler: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> void def call_method: (RubyVM::AbstractSyntaxTree::Node, File, bool) -> void def comp: (String, File) -> void def compile_shared_option: () -> Array[String] From 4780a255e00dc38ebeeea40735c2514934bc2adb Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Fri, 18 Aug 2023 16:37:42 +0900 Subject: [PATCH 05/58] pass only sample/plus.rb --- lib/vaporware/compiler/assemble.rb | 125 ++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assemble.rb index 37efc42..7c412e9 100644 --- a/lib/vaporware/compiler/assemble.rb +++ b/lib/vaporware/compiler/assemble.rb @@ -5,9 +5,28 @@ class Compiler class Assemble EXEC_ELF_HEADER = %w( 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 - 01 00 3e 00 01 00 00 00 b0 00 40 00 00 00 00 00 - 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 40 00 38 00 00 00 40 00 00 00 00 00 + 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 20 01 00 00 00 00 00 00 + 00 00 00 00 40 00 00 00 00 00 40 00 08 00 07 00 + ).map { _1.to_i(16) }.pack("C*") + + NOTE_GNU_PROPERTY_SECTION = %w( + 04 00 00 00 20 00 00 00 05 00 00 00 47 4e 55 00 + 02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 + 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") + + SYMTAB_SECTION = %w( + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 01 00 00 00 10 00 01 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") + + SHSYMTAB_SECTION = %w( + 00 2e 73 79 6d 74 61 62 00 2e 73 74 72 74 61 62 + 00 2e 73 68 73 74 72 74 61 62 00 2e 74 65 78 74 + 00 2e 64 61 74 61 00 2e 62 73 73 00 2e 6e 6f 74 + 65 2e 67 6e 75 2e 70 72 6f 70 65 72 74 79 00 ).map { _1.to_i(16) }.pack("C*") PREFIX = { @@ -29,7 +48,7 @@ class Assemble }.freeze attr_reader :input, :output - def self.assemble!(input, output = File.basename(input, ".*")) = new(input, output).assemble + def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input, output).assemble def initialize(input, output = File.basename(input, ".*") + ".o") @input, @output = input, output @@ -39,26 +58,114 @@ def initialize(input, output = File.basename(input, ".*") + ".o") def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) read = { main: false } opecodes = [] - l = 0 + program_size = 0 File.open(input, "r") do |r| r.each_line do |line| read[:main] = /main:/.match(line) unless read[:main] next unless read[:main] && !/main:/.match(line) op, *args = line.split(/\s+/).reject{ _1 == "" }.map { _1.gsub(/,/, "") } - puts "% 10x: %s\t%s\t%s" % [l, opecode(op, args).map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] + puts "% 10x: %s\t%s\t%s" % [program_size, opecode(op, args).map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] opecodes << opecode(op, args).pack("C*") - l+=opecodes.last.bytesize + program_size += opecodes.last.bytesize end end f.write(EXEC_ELF_HEADER) - # f.write(ELF_PROGRAM_HEADER) - opecodes.each { f.write(_1) } + opecodes << [0].pack("C*") until opecodes.map(&:bytesize).sum % 8 == 0 + opecodes.each { |op| f.write(op) } + section_headers = section_header(str_section_names: "main") + section_headers << [0].pack("C*") until section_headers.map(&:bytesize).sum % 8 == 0 + section_headers << null_section_header + ps = ("%016x" % program_size).scan(/.{1,2}/).reverse.map { |p| p.to_i(16) } + section_headers << text_section_header(name: [0x1b, *[0]*3], size: ps) + section_headers << data_section_header + section_headers << bss_section_header + section_headers << note_section_header + section_headers << symtab_section_header + section_headers << strtab_section_header(size: [0x06, *[0] * 7]) + section_headers << shstrtab_section_header + section_headers.map { |section| f.write(section) } f.close f.path end private + def section_header(str_section_names:) + [ + NOTE_GNU_PROPERTY_SECTION, + SYMTAB_SECTION, + start_table_section(str_section_names), + SHSYMTAB_SECTION, + ] + end + + def start_table_section(main = "main") + b = main.bytes << 0 + b.unshift 0 until b.size % 2 ==0 + b.pack("C*") + end + + def null_section_header = section_headers + + def text_section_header(name: [0x1b, *[0] * 3], offset: [0x40, *[0]* 7], size:) + type = [1, *[0] * 3] + flags = [6, *[0] * 7] + addralign = [1, *[0] * 7] + section_headers(name:, type:, flags:, size:, offset:, addralign:) + end + + def data_section_header(offset: [0x72, *[0]*7]) + name = [0x21, *[0] * 3] + type = [1, *[0] * 3] + flags = [3, *[0] * 7] + addralign = [1, *[0] * 7] + section_headers(name:, type:, flags:, offset:, addralign:) + end + + def bss_section_header(offset: [0x72, *[0]*7]) + name = [0x27, *[0] * 3] + type = [8, *[0] * 3] + flags = [3, *[0] * 7] + addralign = [1, *[0]* 7] + section_headers(name:, type:, flags:, offset:, addralign:) + end + + def note_section_header(offset: [0x78, *[0]*7]) + name = [0x2c, *[0] * 3] + type = [0x07, *[0] * 3] + flags = [0x02, *[0] * 7] + size = [0x30, *[0] * 7] + addralign = [0x08, *[0] * 7] + section_headers(name:, type:, flags:, offset:, size:, addralign:) + end + + def symtab_section_header(name: [0x01, *[0] * 3], offset: [0xa8, *[0] * 7], size: [0x30, *[0] * 7]) + type = [0x02, *[0]* 3] + link = [0x06, *[0] * 3] + info = [0x01, *[0] * 3] + addralign = [0x08, *[0] * 7] + entsize = [0x18, *[0] * 7] + section_headers(name:, type:, offset:, size:, link:, info:, addralign:, entsize:) + end + + def strtab_section_header(name: [0x09, *[0] * 3], offset: [0xd8, *[0] * 7], size:) + type = [0x03, *[0] * 3] + addralign = [0x01, *[0]*7] + section_headers(name:, type:, offset:, size:, addralign:) + end + + def shstrtab_section_header(name: [0x11, *[0] * 3], offset: [0xde, *[0] * 7], size: [0x3f, *[0] * 7]) + type = [0x03, *[0] * 3] + addralign = [0x01, *[0] * 7] + section_headers(name:, type:, offset:, size:, addralign:) + end + + def section_headers( + name: [0]*4, type: [0]*4, flags: [0]*8, addr: [0]*8, + offset: [0]*8, size: [0]*8, link: [0]*4, info: [0]*4, + addralign: [0]*8, entsize: [0]*8) = + [name, type, flags, addr, offset, size, link, info, addralign, entsize].flatten.pack("C*") + def opecode(op, args) case op when "push" From eccc7a95bb62a93803ee459a6c80ecba009b838a Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 24 Aug 2023 09:17:33 +0900 Subject: [PATCH 06/58] refactoring assemble.rb --- exe/vaporware | 2 +- lib/vaporware/compiler/assemble.rb | 49 +++----- lib/vaporware/compiler/assembler/elf.rb | 16 +++ .../compiler/assembler/elf/header.rb | 71 +++++++++++ .../compiler/assembler/elf/section.rb | 29 +++++ .../compiler/assembler/elf/section/strtab.rb | 8 ++ .../compiler/assembler/elf/section/symtab.rb | 17 +++ .../compiler/assembler/elf/section/text.rb | 119 ++++++++++++++++++ .../compiler/assembler/elf/section_header.rb | 66 ++++++++++ lib/vaporware/compiler/linker.rb | 20 +++ 10 files changed, 364 insertions(+), 33 deletions(-) create mode 100644 lib/vaporware/compiler/assembler/elf.rb create mode 100644 lib/vaporware/compiler/assembler/elf/header.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/strtab.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/symtab.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/text.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section_header.rb create mode 100644 lib/vaporware/compiler/linker.rb diff --git a/exe/vaporware b/exe/vaporware index a87b053..8baab68 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -4,7 +4,7 @@ require "vaporware" require "optparse" opt = OptionParser.new options = {} -opt.on("-c", "--compiler [VAL]", "this option is selecting compiler precompiled file, default: gcc") { |v| options[:compiler] = v } +opt.on("-c", "--compiler [VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v } opt.on("-D", "--debug") { |v| options[:debug] = v } opt.on("-o", "--objects [VAL]") { |v| options[:dest] = v } opt.on("--compiler-options[=VAL]", "compiler options") { |v| options[:compiler_options] = v.split(",") } diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assemble.rb index 7c412e9..ce4e408 100644 --- a/lib/vaporware/compiler/assemble.rb +++ b/lib/vaporware/compiler/assemble.rb @@ -3,13 +3,6 @@ module Vaporware class Compiler class Assemble - EXEC_ELF_HEADER = %w( - 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 - 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 20 01 00 00 00 00 00 00 - 00 00 00 00 40 00 00 00 00 00 40 00 08 00 07 00 - ).map { _1.to_i(16) }.pack("C*") - NOTE_GNU_PROPERTY_SECTION = %w( 04 00 00 00 20 00 00 00 05 00 00 00 47 4e 55 00 02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 @@ -33,48 +26,40 @@ class Assemble REX_W: 0x48, } - REGISTER_CODE = { - RAX: 0, - RDI: 7, - } - - OPECODE = { - ADD: 0x01, - CQO: 0x99, - IDIV: 0xf7, - IMUL: 0x0f, - MOV: 0x89, - SUB: 0x83, - }.freeze - attr_reader :input, :output + def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input, output).assemble - def initialize(input, output = File.basename(input, ".*") + ".o") + def initialize(input, output = File.basename(input, ".*") + ".o", type: :relocator, debug: false) @input, @output = input, output @target_file = File.open(output, "wb") + @elf_header = ELF::Header.new(type:) + @text = ELF::Section::Text.new + @symtabs = [ELF::Section::Symtab.new, ELF::Section::Symtab.new] + @strtab = ELF::Section::Strtab.new + @shsymtab = ELF::Section::Shsymtab.new + @section_headers = [ELF::SectionHeader.new.set_null!] + @debug = debug end def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) read = { main: false } - opecodes = [] program_size = 0 + text = @section[:text] File.open(input, "r") do |r| r.each_line do |line| read[:main] = /main:/.match(line) unless read[:main] next unless read[:main] && !/main:/.match(line) - op, *args = line.split(/\s+/).reject{ _1 == "" }.map { _1.gsub(/,/, "") } - puts "% 10x: %s\t%s\t%s" % [program_size, opecode(op, args).map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] - opecodes << opecode(op, args).pack("C*") - program_size += opecodes.last.bytesize + text.assemble(line) + puts "% 10x: %s\t%s\t%s" % [text.size, text.bytes.last.map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] if @debug end end - f.write(EXEC_ELF_HEADER) - opecodes << [0].pack("C*") until opecodes.map(&:bytesize).sum % 8 == 0 - opecodes.each { |op| f.write(op) } + f.write(@elf_header.build!) + text << [0].pack("C*") until text.bytes.map(&:bytesize).sum % 8 == 0 + @sections.values.map { |section| section. section_headers = section_header(str_section_names: "main") section_headers << [0].pack("C*") until section_headers.map(&:bytesize).sum % 8 == 0 - section_headers << null_section_header + ps = ("%016x" % program_size).scan(/.{1,2}/).reverse.map { |p| p.to_i(16) } section_headers << text_section_header(name: [0x1b, *[0]*3], size: ps) section_headers << data_section_header @@ -83,7 +68,7 @@ def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @ta section_headers << symtab_section_header section_headers << strtab_section_header(size: [0x06, *[0] * 7]) section_headers << shstrtab_section_header - section_headers.map { |section| f.write(section) } + section_headers.map { |section| f.write(section.build!) } f.close f.path end diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb new file mode 100644 index 0000000..a948cf3 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Vaporware + class Assembler::ELF + class ERROR < StarndardError; end + def initialize(endian: :littel) = @endian = endian + class ProgramHeader + end + + class SectionHeader + end + + class SectionHeaders + end + end +end diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb new file mode 100644 index 0000000..5450f01 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -0,0 +1,71 @@ +class Vaporware::Compiler::Assembler::ELF + class Header + IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze + ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze + + def initialize(endian: :littel, type: :reloacatable, machine: :amd64) + @e_ident = IDENT + @type = ELF_FILE_TYPE[elf(type)] + @machine = arch(machine) + @version = [0x01, 0x00, 0x00, 0x00] + @entry = nil + @phoffset = nil + @shoffset = nil + @flags = [0x00,] * 4 + @ehsize = [0x40, 0x00] + @phsize = [0x00, 0x00] + @phnum = [0x00, 0x00] + @shentsize = [0x40, 0x00] + @shnum = nil + @shstrndex = nil + end + + def build! + unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndex].all? + raise Vaporware::Assembler::ELF::ERROR + end + [ + @e_ident, @type, @machine, @version, @entry, @phoffset, + @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, + @shnum, @shstrndex + ].flatten.pack("C*") + end + + def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndex: nil) + @entry = entry if check(entry, 8) + @phoffset = phoffset if check(phoffset, 8) + @shoffset = shoffset if check(shoffset, 8) + @shnum = shnum if check(shnum, 4) + @shstrndex = shstrndex if check(shstrndex, 4) + end + + private + + def arch(machine) + case machine.to_s + in "amd64" | "x86_64" | "x64" + [0x3e, 0x00] + end + end + + def check(val, bytes) = + val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes + + def num2bytes(val, bytes: 4) + ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.then { |ret| @endian == :littel ? ret.reverse : ret } + end + + def elf(type) + case type.to_s + in "relocatable" | "rel" + :REL + in "exe" | "ex" | "exec" + :EXEC + in "shared" | "share" | "dynamic" | "dyn" + :DYN + else + :NONE + end + end + end +end diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb new file mode 100644 index 0000000..7f52c7d --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -0,0 +1,29 @@ +class Vaporware::Compiler::Assemble + class ELF::Section + def build + build = @bytes.map { |b| b.pack("C*") } + build << [0].pack("C*") until build.map(&:bytesize).sum % 8 == 0 + build + end + + def check(val, bytes: 8) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes + + class Symtab + def initialize = @bytes = nil + end + + class Strtab + def initialize(name:) + @name = name + @bytes = nil + end + end + + class Note + def initialize(type) + @type = type + @bytes = nil + end + end + end +end diff --git a/lib/vaporware/compiler/assembler/elf/section/strtab.rb b/lib/vaporware/compiler/assembler/elf/section/strtab.rb new file mode 100644 index 0000000..d574ff8 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/strtab.rb @@ -0,0 +1,8 @@ +class Vaporware::Compiler::Assemble + class ELF::Section::Strtab + attr_reader :bytes + def initialize(name: "main") = @bytes = "\0main\0" + + def build! = @bytes.bytes.pack("C*") + end +end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb new file mode 100644 index 0000000..3b96445 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -0,0 +1,17 @@ +class Vaporware::Compiler::Assemble + class ELF::Section::Symtab + attr_reader :bytes + def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) + @name = num2bytes(name, bytes: 4) + @info = num2bytes(info, bytes: 1) + @other = num2bytes(other, bytes: 1) + @shndx = num2bytes(shndx, bytes: 4) + @value = num2bytes(value) + @size = num2bytes(size) + @bytes = [@name, @info, @other, @shndx, @value, @size] + end + + def build! = @bytes.flatten.pack("C*") + def num2bytes(val, bytes: 8) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + end +end diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb new file mode 100644 index 0000000..f73cdb9 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -0,0 +1,119 @@ +class Vaporware::Compiler::Assemble::ELF::Section + class Text + PREFIX = { + REX_W: 0x48, + }.freeze + + REGISTER_CODE = { + RAX: 0, + RDI: 7, + }.freeze + + OPECODE = { + ADD: 0x01, + CQO: 0x99, + IDIV: 0xf7, + IMUL: 0x0f, + MOV: 0x89, + SUB: 0x83, + }.freeze + + attr_reader :bytes, :size, :offset + + def initialize + @bytes = [] + @size = 0 + @offset = 0 + end + + def assemble!(line) + op, *operands = line.split(/\s+/).reject { |o| o.empty? }.map { |op| op.gsub(/,/, "") } + @bytes << opecode(op, *operands) + @size += @bytes.last.bytesize + end + + def align!(bytes) = @bytes << [0x00] until @bytes.map(:bytesize).sum % bytes == 0 + + private + + def opecode(op, *operands) + case op + when "push" + push(operands) + when "mov" + [PREFIX[:REX_W], *mov(op, operands)] + when "sub", "add", "imul", "cqo", "idiv" + [PREFIX[:REX_W], *calc(op, operands)] + when "pop" + pop(operands) + when "ret" + [0xc3] + end + end + + def mov(op, operands) + reg = case operands + in ["rbp", "rsp"] + [0xe5] + in ["rsp", "rbp"] + [0xec] + else + operands&.map { reg(_1) } + end + [OPECODE[op.upcase.to_sym], *reg] + end + + def calc(op, operands) + ope_code = OPECODE[op.upcase.to_sym] + case [op, *operands] + in ["sub", "rax", "rdi"] + [0x29, 0xf8] + in ["add", "rax", "rdi"] + [ope_code, 0xf8] + in ["imul", "rax", "rdi"] + [ope_code, 0xaf, 0xc7] + in ["idiv", "rdi"] + [ope_code, 0xff] + in ["sub", "rsp", *num] + [ope_code, 0xec, *num.map { |n| n.to_i(16) }] + in ["cqo"] + [0x99] + end + end + + def push(operands) + case operands + in ["rbp"] | ["rdi"] + [0x55] + in ["rax"] + [0x50] + else + [0x6a, *operands.map { reg(_1) }] + end + end + + def pop(operands) + case operands + in ["rax"] | ["rdi"] + [0x58 + REGISTER_CODE[operands.first.upcase.to_sym]] + in ["rbp"] + [0x5d] + end + end + + def reg(r) + case r + in "rsp" + 0xec + in "rbp" + 0x5e + in "rax" + 0x29 + in "rdi" + 0xf8 + in /\d+/ + ("%02x" % r).to_i(16) + end + end + end +end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb new file mode 100644 index 0000000..323cf92 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -0,0 +1,66 @@ +class Vaporware::Compiler::Assembler::ELF + class SectionHeader + def initialize(endian: :littel, addr: [0]*8) + @name = nil + @type = nil + @flags = nil + @addr = addr + @offset = nil + @size = nil + @link = nil + @info = nil + @addralign = nil + @entsize = nil + end + + def build! + [ + @name, + @type, + @flags, + @addr, + @offset, + @size, + @link, + @info, + @addralign, + @entsize, + ].flatten.pack("C*") + end + + def set!(name: nil, type: nil, flags: nil, addr: nil, + offset: nil, size: nil, link: nil, info: nil, + addralign: nil, entsize: nil + ) + @name = name if check(name, 4) + @type = type if check(type, 4) + @flags = flags if check(flags, 8) + @addr = addr if check(addr, 8) + @offset = offset if check(offset, 8) + @size = size if check(size, 8) + @link = link if check(link, 4) + @info = info if check(info, 4) + @addralign = addralign if check(addralign, 8) + @entsize = entsize if check(entsize, 8) + end + + def set_null! + @name = num2bytes(@name, bytes: 4) + @type = num2bytes(@type, bytes: 4) + @flags = num2bytes(@flags) + @addr = num2bytes(@addr) + @offset = num2bytes(@offset) + @size = num2bytes(@size) + @link = num2bytes(@link, bytes: 4) + @info = num2bytes(@info, bytes: 4) + @addralign = num2bytes(@addralign) + @entsize = num2bytes(@entsie) + end + + private + + def check(val, bytes) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes + + def num2bytes(val, bytes: 8, endian: :littel) = ("%0#{byte}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.then { |a| endian == :littel ? a.reverse : a } + end +end diff --git a/lib/vaporware/compiler/linker.rb b/lib/vaporware/compiler/linker.rb new file mode 100644 index 0000000..6d0de19 --- /dev/null +++ b/lib/vaporware/compiler/linker.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +module Vaporware + class Compiler + class Linker + DEFAULT_LIBRARY_PATH = %w(/lib64/libc.so.6 /usr/lib64/crt1.o /usr/lib64/crtn.o) + def self.link!(source, dest = "a.out", linker: "mold", lib_path: [], options: []) = new(source, dest, linker:, lib_path:, options:) + + def initialize(input, output = "a.out", linker: "mold", lib_path: [], options: []) + @input, @output, @linker = input, output, linker + @lib_path = DEFAULT_LIBRARY_PATH + lib_path + @options = options + end + + def link = IO.popen(link_command).close + + private + def link_command = %Q|#{@linker} -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o #{@output} #{@lib_path.join(' ')} #{@input}|.split(/\s+/) + end + end +end From 980de729fbf633d7b78f893f3861f0e672b93dbb Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 31 Aug 2023 21:33:01 +0900 Subject: [PATCH 07/58] rename assemble to assembler --- lib/vaporware/compiler.rb | 2 +- .../compiler/{assemble.rb => assembler.rb} | 59 +++--- lib/vaporware/compiler/assembler/elf.rb | 17 +- .../compiler/assembler/elf/header.rb | 112 +++++----- .../compiler/assembler/elf/section.rb | 34 +-- .../compiler/assembler/elf/section/note.rb | 31 +++ .../compiler/assembler/elf/section/strtab.rb | 10 +- .../compiler/assembler/elf/section/symtab.rb | 39 ++-- .../compiler/assembler/elf/section/text.rb | 198 +++++++++--------- .../compiler/assembler/elf/section_header.rb | 94 +++------ lib/vaporware/compiler/generator.rb | 2 +- 11 files changed, 287 insertions(+), 311 deletions(-) rename lib/vaporware/compiler/{assemble.rb => assembler.rb} (78%) create mode 100644 lib/vaporware/compiler/assembler/elf/section/note.rb diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 417d772..ee8153b 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -24,7 +24,7 @@ def assemble(input: precompile, output: File.basename(precompile, ".*") + ".o", compile_commands = [compiler, *compiler_options, "-o", name, input].compact call_compiler(compile_commands) else - Vaporware::Compiler::Assemble.assemble!(input, name) + Vaporware::Compiler::Assembler.assemble!(input, name) end File.delete(input) unless debug diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assembler.rb similarity index 78% rename from lib/vaporware/compiler/assemble.rb rename to lib/vaporware/compiler/assembler.rb index ce4e408..129720e 100644 --- a/lib/vaporware/compiler/assemble.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -1,14 +1,16 @@ # frozen_string_literal: true +require_relative "assembler/elf" +require_relative "assembler/elf/header" +require_relative "assembler/elf/section" +require_relative "assembler/elf/section/note" +require_relative "assembler/elf/section/text" +require_relative "assembler/elf/section/symtab" +require_relative "assembler/elf/section/shsymtab" +require_relative "assembler/elf/section_header" module Vaporware class Compiler - class Assemble - NOTE_GNU_PROPERTY_SECTION = %w( - 04 00 00 00 20 00 00 00 05 00 00 00 47 4e 55 00 - 02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 - 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00 - ).map { _1.to_i(16) }.pack("C*") - + class Assembler SYMTAB_SECTION = %w( 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 10 00 01 00 @@ -22,10 +24,6 @@ class Assemble 65 2e 67 6e 75 2e 70 72 6f 70 65 72 74 79 00 ).map { _1.to_i(16) }.pack("C*") - PREFIX = { - REX_W: 0x48, - } - attr_reader :input, :output def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input, output).assemble @@ -34,41 +32,36 @@ def initialize(input, output = File.basename(input, ".*") + ".o", type: :relocat @input, @output = input, output @target_file = File.open(output, "wb") @elf_header = ELF::Header.new(type:) - @text = ELF::Section::Text.new - @symtabs = [ELF::Section::Symtab.new, ELF::Section::Symtab.new] - @strtab = ELF::Section::Strtab.new - @shsymtab = ELF::Section::Shsymtab.new - @section_headers = [ELF::SectionHeader.new.set_null!] + @sections = { + null: { body: nil, header: ELF::SectionHeader.new.null! }, + text: { body: ELF::Section::Text.new, header: ELF::SectionHeader.new.text! }, + note: { body: ELF::Section::Note.new.gnu_property!, header: ELF::SectionHeader.new.note! }, + symtab: { body: ELF::Section::Symtab.new, header: ELF::SectionHeader.new.symtab! }, + strtab: { body: ELF::Section::Strtab.new, header: ELF::SectionHeader.new.strtab! }, + shsymtab: { body: ELF::Section::Shsymtab.new, header: ELF::SectionHeader.new.shsymtab! }, + } @debug = debug end def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) read = { main: false } program_size = 0 - text = @section[:text] + text = @section[:text][:body] File.open(input, "r") do |r| r.each_line do |line| read[:main] = /main:/.match(line) unless read[:main] next unless read[:main] && !/main:/.match(line) - text.assemble(line) - puts "% 10x: %s\t%s\t%s" % [text.size, text.bytes.last.map{ "%02x" % _1 }.join(" ").ljust(25), op, args.join(", ")] if @debug + text.assemble!(line) end end f.write(@elf_header.build!) - text << [0].pack("C*") until text.bytes.map(&:bytesize).sum % 8 == 0 - @sections.values.map { |section| section. - section_headers = section_header(str_section_names: "main") - section_headers << [0].pack("C*") until section_headers.map(&:bytesize).sum % 8 == 0 - - ps = ("%016x" % program_size).scan(/.{1,2}/).reverse.map { |p| p.to_i(16) } - section_headers << text_section_header(name: [0x1b, *[0]*3], size: ps) - section_headers << data_section_header - section_headers << bss_section_header - section_headers << note_section_header - section_headers << symtab_section_header - section_headers << strtab_section_header(size: [0x06, *[0] * 7]) - section_headers << shstrtab_section_header - section_headers.map { |section| f.write(section.build!) } + bins = [] + section_headers = [] + @sections.values.map do |section| + bins << section[:body].build + section_headers << section[:header].build + end + f.close f.path end diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb index a948cf3..c2c77ca 100644 --- a/lib/vaporware/compiler/assembler/elf.rb +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -1,16 +1,9 @@ -# frozen_string_literal: true - module Vaporware - class Assembler::ELF - class ERROR < StarndardError; end - def initialize(endian: :littel) = @endian = endian - class ProgramHeader - end - - class SectionHeader - end - - class SectionHeaders + class Compiler + class Assembler + class ELF + class ERROR < StandardError; end + end end end end diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb index 5450f01..84e0c96 100644 --- a/lib/vaporware/compiler/assembler/elf/header.rb +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -1,71 +1,67 @@ -class Vaporware::Compiler::Assembler::ELF - class Header - IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze - ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze +class Vaporware::Compiler::Assembler::ELF::Header + IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze + ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze - def initialize(endian: :littel, type: :reloacatable, machine: :amd64) - @e_ident = IDENT - @type = ELF_FILE_TYPE[elf(type)] - @machine = arch(machine) - @version = [0x01, 0x00, 0x00, 0x00] - @entry = nil - @phoffset = nil - @shoffset = nil - @flags = [0x00,] * 4 - @ehsize = [0x40, 0x00] - @phsize = [0x00, 0x00] - @phnum = [0x00, 0x00] - @shentsize = [0x40, 0x00] - @shnum = nil - @shstrndex = nil - end + def initialize(endian: :littel, type: :reloacatable, machine: :amd64) + @e_ident = IDENT + @type = ELF_FILE_TYPE[elf(type)] + @machine = arch(machine) + @version = num2bytes(1, 4) + @entry = nil + @phoffset = nil + @shoffset = nil + @flags = num2bytes(0, 4) + @ehsize = num2bytes(0x40, 2) + @phsize = num2bytes(0x00, 2) + @phnum = num2bytes(0x00, 2) + @shentsize = num2bytes(0x40, 2) + @shnum = nil + @shstrndex = nil + end - def build! - unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndex].all? - raise Vaporware::Assembler::ELF::ERROR - end - [ - @e_ident, @type, @machine, @version, @entry, @phoffset, - @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, - @shnum, @shstrndex - ].flatten.pack("C*") + def build! + unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndex].all? + raise Vaporware::Assembler::ELF::ERROR end + [ + @e_ident, @type, @machine, @version, @entry, @phoffset, + @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, + @shnum, @shstrndex + ].flatten.pack("C*") + end - def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndex: nil) - @entry = entry if check(entry, 8) - @phoffset = phoffset if check(phoffset, 8) - @shoffset = shoffset if check(shoffset, 8) - @shnum = shnum if check(shnum, 4) - @shstrndex = shstrndex if check(shstrndex, 4) - end + def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndex: nil) + @entry = num2bytes(entry, 8) if check(entry, 8) + @phoffset = num2bytes(phoffset, 8) if check(phoffset, 8) + @shoffset = num2bytes(shoffset, 8) if check(shoffset, 8) + @shnum = num2bytes(shnum, 4) if check(shnum, 4) + @shstrndex = num2bytes(shstrndex, 4) if check(shstrndex, 4) + end - private + private - def arch(machine) - case machine.to_s - in "amd64" | "x86_64" | "x64" - [0x3e, 0x00] - end - end + def check(val, bytes) = + (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) - def check(val, bytes) = - val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse - def num2bytes(val, bytes: 4) - ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.then { |ret| @endian == :littel ? ret.reverse : ret } + def arch(machine) + case machine.to_s + in "amd64" | "x86_64" | "x64" + [0x3e, 0x00] end + end - def elf(type) - case type.to_s - in "relocatable" | "rel" - :REL - in "exe" | "ex" | "exec" - :EXEC - in "shared" | "share" | "dynamic" | "dyn" - :DYN - else - :NONE - end + def elf(type) + case type.to_s + in "relocatable" | "rel" + :REL + in "exe" | "ex" | "exec" + :EXEC + in "shared" | "share" | "dynamic" | "dyn" + :DYN + else + :NONE end end end diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index 7f52c7d..a750f9d 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -1,29 +1,9 @@ -class Vaporware::Compiler::Assemble - class ELF::Section - def build - build = @bytes.map { |b| b.pack("C*") } - build << [0].pack("C*") until build.map(&:bytesize).sum % 8 == 0 - build - end - - def check(val, bytes: 8) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes - - class Symtab - def initialize = @bytes = nil - end - - class Strtab - def initialize(name:) - @name = name - @bytes = nil - end - end - - class Note - def initialize(type) - @type = type - @bytes = nil - end - end +class Vaporware::Compiler::Assembler::ELF::Section + def build! + build = @bytes.map { |b| b.pack("C*") } + build << [0].pack("C*") until build.map(&:bytesize).sum % 8 == 0 + build end + + def check(val, bytes: 8) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb new file mode 100644 index 0000000..177658e --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -0,0 +1,31 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Note + def self.gnu_property = new.gnu_property! + def initialize + @nsize = nil + @dsize = nil + @type = nil + @name = nil + @desc = nil + end + + def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) + @nsize = num2bytes(nsize, 4) if check(nsize, 4) + @dsize = num2bytes(dsize, 4) if check(disze, 4) + @type = num2bytes(type, 4) if check(type, 4) + @name = name!(name) if name + @desc = desc!(desc) if desc + end + + def gnu_property! = set!(nsize: 0x04, dsize: 0x20, type: 0x05, name: "GNU", desc: %w(02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00).map { |val| val.to_i(16) }) + + def build = bytes.flatten.pack("C*") + + private + def name!(name) = align!(@name = name.bytes, 4) + def desc!(desc) = align!(@desc = desc.bytes, 4) + + def bytes = [@nsize, @dsize, @type, @name, @desc] + def align!(val, bytes) = (val << 0 until val.size % bytes == 0) + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) +end diff --git a/lib/vaporware/compiler/assembler/elf/section/strtab.rb b/lib/vaporware/compiler/assembler/elf/section/strtab.rb index d574ff8..9b9dcc6 100644 --- a/lib/vaporware/compiler/assembler/elf/section/strtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/strtab.rb @@ -1,8 +1,6 @@ -class Vaporware::Compiler::Assemble - class ELF::Section::Strtab - attr_reader :bytes - def initialize(name: "main") = @bytes = "\0main\0" +class Vaporware::Compiler::Assembler::ELF::Section::Strtab + attr_reader :bytes + def initialize(name = "\0main\0") = @bytes = name - def build! = @bytes.bytes.pack("C*") - end + def build = @bytes.bytes.pack("C*") end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index 3b96445..f4b8e0e 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,17 +1,28 @@ -class Vaporware::Compiler::Assemble - class ELF::Section::Symtab - attr_reader :bytes - def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) - @name = num2bytes(name, bytes: 4) - @info = num2bytes(info, bytes: 1) - @other = num2bytes(other, bytes: 1) - @shndx = num2bytes(shndx, bytes: 4) - @value = num2bytes(value) - @size = num2bytes(size) - @bytes = [@name, @info, @other, @shndx, @value, @size] - end +class Vaporware::Compiler::Assembler::ELF::Section::Symtab + attr_reader :bytes + def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) + @name = num2bytes(name, 4) + @info = num2bytes(info, 1) + @other = num2bytes(other, 1) + @shndx = num2bytes(shndx, 4) + @value = num2bytes(value, 8) + @size = num2bytes(size, 8) + @bytes = [@name, @info, @other, @shndx, @value, @size] + end + + def build = @bytes.flatten.pack("C*") - def build! = @bytes.flatten.pack("C*") - def num2bytes(val, bytes: 8) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) + @name = num2bytes(name, 4) if check(name, 4) + @info = num2bytes(info, 1) if check(info, 4) + @other = num2bytes(other, 1) if check(other, 1) + @shndx = num2bytes(shndx, 4) if check(shndx, 4) + @value = num2bytes(value, 8) if check(value, 8) + @size = num2bytes(size, 8) if check(size, 8) end + + private + + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index f73cdb9..9c8c645 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -1,119 +1,119 @@ -class Vaporware::Compiler::Assemble::ELF::Section - class Text - PREFIX = { - REX_W: 0x48, - }.freeze +class Vaporware::Compiler::Assembler::ELF::Section::Text + PREFIX = { + REX_W: 0x48, + }.freeze - REGISTER_CODE = { - RAX: 0, - RDI: 7, - }.freeze + REGISTER_CODE = { + RAX: 0, + RDI: 7, + }.freeze - OPECODE = { - ADD: 0x01, - CQO: 0x99, - IDIV: 0xf7, - IMUL: 0x0f, - MOV: 0x89, - SUB: 0x83, - }.freeze + OPECODE = { + ADD: 0x01, + CQO: 0x99, + IDIV: 0xf7, + IMUL: 0x0f, + MOV: 0x89, + SUB: 0x83, + }.freeze - attr_reader :bytes, :size, :offset + attr_reader :bytes, :size, :offset - def initialize - @bytes = [] - @size = 0 - @offset = 0 - end + def initialize + @bytes = [] + @size = 0 + @offset = 0 + end - def assemble!(line) - op, *operands = line.split(/\s+/).reject { |o| o.empty? }.map { |op| op.gsub(/,/, "") } - @bytes << opecode(op, *operands) - @size += @bytes.last.bytesize - end + def assemble!(line) + op, *operands = line.split(/\s+/).reject { |o| o.empty? }.map { |op| op.gsub(/,/, "") } + @bytes << opecode(op, *operands) + @size += @bytes.last.bytesize + end - def align!(bytes) = @bytes << [0x00] until @bytes.map(:bytesize).sum % bytes == 0 + def align!(bytes) + @bytes << [0x00] until @bytes.map(:bytesize).sum % bytes == 0 + end - private + private - def opecode(op, *operands) - case op - when "push" - push(operands) - when "mov" - [PREFIX[:REX_W], *mov(op, operands)] - when "sub", "add", "imul", "cqo", "idiv" - [PREFIX[:REX_W], *calc(op, operands)] - when "pop" - pop(operands) - when "ret" - [0xc3] - end + def opecode(op, *operands) + case op + when "push" + push(operands) + when "mov" + [PREFIX[:REX_W], *mov(op, operands)] + when "sub", "add", "imul", "cqo", "idiv" + [PREFIX[:REX_W], *calc(op, operands)] + when "pop" + pop(operands) + when "ret" + [0xc3] end + end - def mov(op, operands) - reg = case operands - in ["rbp", "rsp"] - [0xe5] - in ["rsp", "rbp"] - [0xec] - else - operands&.map { reg(_1) } - end - [OPECODE[op.upcase.to_sym], *reg] - end + def mov(op, operands) + reg = case operands + in ["rbp", "rsp"] + [0xe5] + in ["rsp", "rbp"] + [0xec] + else + operands&.map { reg(_1) } + end + [OPECODE[op.upcase.to_sym], *reg] + end - def calc(op, operands) - ope_code = OPECODE[op.upcase.to_sym] - case [op, *operands] - in ["sub", "rax", "rdi"] - [0x29, 0xf8] - in ["add", "rax", "rdi"] - [ope_code, 0xf8] - in ["imul", "rax", "rdi"] - [ope_code, 0xaf, 0xc7] - in ["idiv", "rdi"] - [ope_code, 0xff] - in ["sub", "rsp", *num] - [ope_code, 0xec, *num.map { |n| n.to_i(16) }] - in ["cqo"] - [0x99] - end + def calc(op, operands) + ope_code = OPECODE[op.upcase.to_sym] + case [op, *operands] + in ["sub", "rax", "rdi"] + [0x29, 0xf8] + in ["add", "rax", "rdi"] + [ope_code, 0xf8] + in ["imul", "rax", "rdi"] + [ope_code, 0xaf, 0xc7] + in ["idiv", "rdi"] + [ope_code, 0xff] + in ["sub", "rsp", *num] + [ope_code, 0xec, *num.map { |n| n.to_i(16) }] + in ["cqo"] + [0x99] end + end - def push(operands) - case operands - in ["rbp"] | ["rdi"] - [0x55] - in ["rax"] - [0x50] - else - [0x6a, *operands.map { reg(_1) }] - end + def push(operands) + case operands + in ["rbp"] | ["rdi"] + [0x55] + in ["rax"] + [0x50] + else + [0x6a, *operands.map { reg(_1) }] end + end - def pop(operands) - case operands - in ["rax"] | ["rdi"] - [0x58 + REGISTER_CODE[operands.first.upcase.to_sym]] - in ["rbp"] - [0x5d] - end + def pop(operands) + case operands + in ["rax"] | ["rdi"] + [0x58 + REGISTER_CODE[operands.first.upcase.to_sym]] + in ["rbp"] + [0x5d] end + end - def reg(r) - case r - in "rsp" - 0xec - in "rbp" - 0x5e - in "rax" - 0x29 - in "rdi" - 0xf8 - in /\d+/ - ("%02x" % r).to_i(16) - end + def reg(r) + case r + in "rsp" + 0xec + in "rbp" + 0x5e + in "rax" + 0x29 + in "rdi" + 0xf8 + in /\d+/ + ("%02x" % r).to_i(16) end end end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index 323cf92..10aaf80 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -1,66 +1,40 @@ -class Vaporware::Compiler::Assembler::ELF - class SectionHeader - def initialize(endian: :littel, addr: [0]*8) - @name = nil - @type = nil - @flags = nil - @addr = addr - @offset = nil - @size = nil - @link = nil - @info = nil - @addralign = nil - @entsize = nil - end - - def build! - [ - @name, - @type, - @flags, - @addr, - @offset, - @size, - @link, - @info, - @addralign, - @entsize, - ].flatten.pack("C*") - end +class Vaporware::Compiler::Assembler::ELF::SectionHeader + def initialize + @name = nil + @type = nil + @flags = nil + @addr = nil + @offset = nil + @size = nil + @link = nil + @info = nil + @addralign = nil + @entsize = nil + end - def set!(name: nil, type: nil, flags: nil, addr: nil, - offset: nil, size: nil, link: nil, info: nil, - addralign: nil, entsize: nil - ) - @name = name if check(name, 4) - @type = type if check(type, 4) - @flags = flags if check(flags, 8) - @addr = addr if check(addr, 8) - @offset = offset if check(offset, 8) - @size = size if check(size, 8) - @link = link if check(link, 4) - @info = info if check(info, 4) - @addralign = addralign if check(addralign, 8) - @entsize = entsize if check(entsize, 8) - end + def build = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,].flatten.pack("C*") - def set_null! - @name = num2bytes(@name, bytes: 4) - @type = num2bytes(@type, bytes: 4) - @flags = num2bytes(@flags) - @addr = num2bytes(@addr) - @offset = num2bytes(@offset) - @size = num2bytes(@size) - @link = num2bytes(@link, bytes: 4) - @info = num2bytes(@info, bytes: 4) - @addralign = num2bytes(@addralign) - @entsize = num2bytes(@entsie) - end + def set!(name: nil, type: nil, flags: nil, addr: nil, + offset: nil, size: nil, link: nil, info: nil, + addralign: nil, entsize: nil) + @name = num2bytes(name, 4) if check(name, 4) + @type = num2bytes(type, 4) if check(type, 4) + @flags = num2bytes(flags, 8) if check(flags, 8) + @addr = num2bytes(addr, 8) if check(addr, 8) + @offset = num2bytes(offset, 8) if check(offset, 8) + @size = num2bytes(size, 8) if check(size, 8) + @link = num2bytes(link, 4) if check(link, 4) + @info = num2bytes(info, 4) if check(info, 4) + @addralign = num2bytes(addralign, 8) if check(addralign, 8) + @entsize = num2bytes(entsize, 8) if check(entsize, 8) + end - private + def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) + def text! = set!(flags: 0x06, addralign: 0x01) + def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) - def check(val, bytes) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes + private - def num2bytes(val, bytes: 8, endian: :littel) = ("%0#{byte}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.then { |a| endian == :littel ? a.reverse : a } - end + def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index d620414..ef33303 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -35,7 +35,7 @@ def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: base_name = File.basename(input, ".*") name = shared ? "lib#{base_name}.so" : base_name if compiler.nil? - Vaporware::Compiler::Assemble.assemble!(name, input) + Vaporware::Compiler::Assembler.assemble!(name, input) else compile_commands = [compiler, *compiler_options, "-o", name, input].compact call_compiler(compile_commands) From f37dfcf121380f102c4b13ec39039dc007ef5172 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Fri, 1 Sep 2023 11:10:23 +0900 Subject: [PATCH 08/58] add rbs for refactoringed compiler.rbs --- lib/vaporware/compiler/assembler.rb | 2 - .../compiler/assembler/elf/header.rb | 25 +++---- .../compiler/assembler/elf/section.rb | 7 -- .../compiler/assembler/elf/section/note.rb | 6 +- .../compiler/assembler/elf/section/strtab.rb | 6 +- .../compiler/assembler/elf/section/symtab.rb | 6 +- .../compiler/assembler/elf/section/text.rb | 2 + .../compiler/assembler/elf/section_header.rb | 4 +- sig/vaporware.rbs | 67 ------------------- sig/vaporware/compiler.rbs | 1 - sig/vaporware/compiler/assemble.rbs | 9 --- sig/vaporware/compiler/assembler.rbs | 8 +++ sig/vaporware/compiler/assembler/elf.rbs | 2 + .../compiler/assembler/elf/header.rbs | 30 +++++++++ .../compiler/assembler/elf/section.rbs | 2 + .../compiler/assembler/elf/section/note.rbs | 21 ++++++ .../compiler/assembler/elf/section/strtab.rbs | 5 ++ .../compiler/assembler/elf/section/symtab.rbs | 19 ++++++ .../compiler/assembler/elf/section/text.rbs | 21 ++++++ .../compiler/assembler/elf/section_header.rbs | 18 +++++ 20 files changed, 152 insertions(+), 109 deletions(-) delete mode 100644 sig/vaporware/compiler/assemble.rbs create mode 100644 sig/vaporware/compiler/assembler.rbs create mode 100644 sig/vaporware/compiler/assembler/elf.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/header.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/note.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/strtab.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/symtab.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/text.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section_header.rbs diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 129720e..c93d2a7 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -24,8 +24,6 @@ class Assembler 65 2e 67 6e 75 2e 70 72 6f 70 65 72 74 79 00 ).map { _1.to_i(16) }.pack("C*") - attr_reader :input, :output - def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input, output).assemble def initialize(input, output = File.basename(input, ".*") + ".o", type: :relocator, debug: false) diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb index 84e0c96..0b92e06 100644 --- a/lib/vaporware/compiler/assembler/elf/header.rb +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -2,10 +2,10 @@ class Vaporware::Compiler::Assembler::ELF::Header IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze - def initialize(endian: :littel, type: :reloacatable, machine: :amd64) + def initialize(endian: :littel, type: :reloacatable, arch: :amd64) @e_ident = IDENT @type = ELF_FILE_TYPE[elf(type)] - @machine = arch(machine) + @arch = arch(arch) @version = num2bytes(1, 4) @entry = nil @phoffset = nil @@ -16,26 +16,22 @@ def initialize(endian: :littel, type: :reloacatable, machine: :amd64) @phnum = num2bytes(0x00, 2) @shentsize = num2bytes(0x40, 2) @shnum = nil - @shstrndex = nil + @shstrndx = nil end - def build! - unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndex].all? + def build + unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndx].all? raise Vaporware::Assembler::ELF::ERROR end - [ - @e_ident, @type, @machine, @version, @entry, @phoffset, - @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, - @shnum, @shstrndex - ].flatten.pack("C*") + bytes.flatten.pack("C*") end - def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndex: nil) + def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil) @entry = num2bytes(entry, 8) if check(entry, 8) @phoffset = num2bytes(phoffset, 8) if check(phoffset, 8) @shoffset = num2bytes(shoffset, 8) if check(shoffset, 8) @shnum = num2bytes(shnum, 4) if check(shnum, 4) - @shstrndex = num2bytes(shstrndex, 4) if check(shstrndex, 4) + @shstrndx = num2bytes(shstrndex, 4) if check(shstrndx, 4) end private @@ -44,6 +40,11 @@ def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse + def bytes = [ + @e_ident, @type, @machine, @version, @entry, @phoffset, + @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, + @shnum, @shstrndx + ] def arch(machine) case machine.to_s diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index a750f9d..8f33433 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -1,9 +1,2 @@ class Vaporware::Compiler::Assembler::ELF::Section - def build! - build = @bytes.map { |b| b.pack("C*") } - build << [0].pack("C*") until build.map(&:bytesize).sum % 8 == 0 - build - end - - def check(val, bytes: 8) = val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index 177658e..49f83c0 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -1,5 +1,9 @@ class Vaporware::Compiler::Assembler::ELF::Section::Note - def self.gnu_property = new.gnu_property! + def self.gnu_property + note = new + note.gnu_property! + note.build + end def initialize @nsize = nil @dsize = nil diff --git a/lib/vaporware/compiler/assembler/elf/section/strtab.rb b/lib/vaporware/compiler/assembler/elf/section/strtab.rb index 9b9dcc6..61c0d0a 100644 --- a/lib/vaporware/compiler/assembler/elf/section/strtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/strtab.rb @@ -1,6 +1,4 @@ class Vaporware::Compiler::Assembler::ELF::Section::Strtab - attr_reader :bytes - def initialize(name = "\0main\0") = @bytes = name - - def build = @bytes.bytes.pack("C*") + def initialize(names = "\0main\0") = @names = names + def build = @names.bytes.pack("C*") end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index f4b8e0e..0e67f02 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,5 +1,4 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab - attr_reader :bytes def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) @name = num2bytes(name, 4) @info = num2bytes(info, 1) @@ -7,10 +6,9 @@ def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) @shndx = num2bytes(shndx, 4) @value = num2bytes(value, 8) @size = num2bytes(size, 8) - @bytes = [@name, @info, @other, @shndx, @value, @size] end - def build = @bytes.flatten.pack("C*") + def build = bytes.flatten.pack("C*") def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) @name = num2bytes(name, 4) if check(name, 4) @@ -22,7 +20,7 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) end private - + def bytes = [@name, @info, @other, @shndx, @value, @size] def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 9c8c645..7fb713d 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -49,6 +49,8 @@ def opecode(op, *operands) pop(operands) when "ret" [0xc3] + else + raise Compiler::Assembler::ERROR end end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index 10aaf80..e05b2e7 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -12,7 +12,7 @@ def initialize @entsize = nil end - def build = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,].flatten.pack("C*") + def build = bytes.flatten.pack("C*") def set!(name: nil, type: nil, flags: nil, addr: nil, offset: nil, size: nil, link: nil, info: nil, @@ -34,7 +34,7 @@ def text! = set!(flags: 0x06, addralign: 0x01) def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) private - + def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,] def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse end diff --git a/sig/vaporware.rbs b/sig/vaporware.rbs index 7b3cdfa..91b07d5 100644 --- a/sig/vaporware.rbs +++ b/sig/vaporware.rbs @@ -1,73 +1,6 @@ module Vaporware VERSION: String # See the writing guide of rbs: https://github.com/ruby/rbs#guides - class Error - end class Compiler - # class methods - def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> nil - def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> untyped - @generator: Vaporware::Compiler::Generator - # instance methods - def compile: (?compiler: String, ?compiler_options: Array[String]) -> nil - - class Assemble - @input: String - @output: String - @target_file: File - # class methods - def self.compile!: (String, ?String) -> Vaporware::Compiler::Assemble - def initialize: (String, ?String) -> untyped - - # instance methods - def compile: (?Array[String]) -> self - end - - class Generator - REGISTER: Array[String] - # instance variables - @precompile: String - @debug: bool - @doned: Set[Symbol] - @defined_methods: Set[Symbol] - @defined_variables: Set[Symbol] - @seq: Integer - @main: bool - @shared: bool - # temporarily using untyped types since parser.gem's rbs information is unchecked. - @ast: untyped - # class methods - def initialize: (String, precompile: String, debug: bool, shared: bool) -> untyped - - # attr reader for instance variables - def precompile: -> String - def ast: -> untyped # Parser::AST::Node - def defined_methods: -> Set[Symbol] - def defined_variables: -> Set[Symbol] - def debug: -> bool - def doned: -> Set[Symbol] - def seq: -> Integer - def shared: -> bool - - # instance methods - def already_build_methods?: -> bool - def args: (untyped, File) -> nil - def build: (untyped, File, ?bool) -> nil - def call_compiler: (Array[String]) -> nil - def call_method: (untyped, File, bool) -> nil - def comp: (String, File) -> nil - def compile_shared_option: () -> Array[String] - def define_method_prologue: (untyped, File) -> nil - def epilogue: (File) -> nil - def lvar: (untyped, File) -> nil - def method: (Symbol, untyped, File) -> nil - def prologue: (untyped, File) -> nil - def prologue_methods: (File) -> nil - def ret: (File) -> nil - def lvar_offset: (Symbol | nil) -> Integer - def register_var_and_method: (untyped) -> nil - def to_elf: (?input: String, ?compiler: String, ?compiler_options: Array[String], ?debug: bool) -> nil - def variable_or_method?: (Symbol) -> bool - end end end diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index e02a603..1ab0dbc 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -9,4 +9,3 @@ class Vaporware::Compiler def link: (?output: String, ?linker: String, ?linker_options: Array[String]) -> void def compile: (?compiler: String, ?compiler_options: Array[String]) -> void end - diff --git a/sig/vaporware/compiler/assemble.rbs b/sig/vaporware/compiler/assemble.rbs deleted file mode 100644 index 16e7d9d..0000000 --- a/sig/vaporware/compiler/assemble.rbs +++ /dev/null @@ -1,9 +0,0 @@ -class Vaporware::Compiler::Assemble - attr_reader input: String - attr_reader output: String - - @target_file: File - - def initialize: (String, String) -> void - def compile: (?compiler_options: Array[String]) -> void -end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs new file mode 100644 index 0000000..d28d2ac --- /dev/null +++ b/sig/vaporware/compiler/assembler.rbs @@ -0,0 +1,8 @@ +class Vaporware::Compiler::Assembler + @input: String + @output: String + @target_file: File + @elf_header: Vaporware::Compiler::Assembler::ELF::Header + def initialize: (String, ?String, type: Symbol, debug: bool) -> void + def assemble: (?asemble_command: String, ?assemble_options: Array[String], input: String, f: File) -> void +end diff --git a/sig/vaporware/compiler/assembler/elf.rbs b/sig/vaporware/compiler/assembler/elf.rbs new file mode 100644 index 0000000..3c7d856 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf.rbs @@ -0,0 +1,2 @@ +class Vaporware::Compiler::Assembler::ELF +end diff --git a/sig/vaporware/compiler/assembler/elf/header.rbs b/sig/vaporware/compiler/assembler/elf/header.rbs new file mode 100644 index 0000000..fdea5a2 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/header.rbs @@ -0,0 +1,30 @@ +class Vaporware::Compiler::Assembler::ELF::Header + IDENT: Array[Integer] + ELF_FILE_TYPE: Hash[Symbol, Integer] + + @e_ident: Array[Integer] + @type: Integer + @arch: Array[Integer] + @version: Array[Integer] + @entry: Array[Integer] | nil + @phoffset: Array[Integer] | nil + @shoffset: Array[Integer] | nil + @flags: Array[Integer] + @ehsize: Array[Integer] + @phsize: Array[Integer] + @ehnum: Array[Integer] + @shentsize: Array[Integer] + @shnum: Array[Integer] | nil + @shstrndx: Array[Integer] | nil + + def initialize: (?endian: Symbol, ?type: Symbol, ?arche: Symbol) -> void + def build: () -> String + def set!: (?entry: (nil | Integer), ?phoffset: (nil | Integer), ?shoffset: (nil | Integer), ?shnum: (nil | Integer), ?shstrndx: (nil | Integer)) -> void + + private + def check: (Array[Integer] | Integer, Integer) -> bool + def num2bytes: (Integer, Integer) -> Array[Integer] + def bytes: () -> Array[Array[Integer]] + def arch: (String | Symbol) -> Array[Integer] + def elf: (String | Symbol) -> Symbol +end diff --git a/sig/vaporware/compiler/assembler/elf/section.rbs b/sig/vaporware/compiler/assembler/elf/section.rbs new file mode 100644 index 0000000..8f33433 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section.rbs @@ -0,0 +1,2 @@ +class Vaporware::Compiler::Assembler::ELF::Section +end diff --git a/sig/vaporware/compiler/assembler/elf/section/note.rbs b/sig/vaporware/compiler/assembler/elf/section/note.rbs new file mode 100644 index 0000000..15d6fca --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/note.rbs @@ -0,0 +1,21 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Note + def self.gnu_property: () -> String + + @nsize: Array[Integer]? + @dsize: Array[Integer]? + @type: Array[Integer]? + @name: Array[Integer]? + @desc: Array[Integer]? + + def set!: (?nsize: Integer?, ?dsize: Integer?, ?type: Integer?, ?name: Integer?, ?desc: Integer?) -> void + def gnu_property!: () -> void + def build: () -> String + + private + def name!: (String) -> void + def desc!: (String) -> void + def bytes: () -> Array[Array[Integer] | nil] + def align!: (Array[Integer], Integer) -> void + def num2bytes: (Integer, Integer) -> Array[Integer] + def check: (Integer | Array[Integer], Integer) -> bool +end diff --git a/sig/vaporware/compiler/assembler/elf/section/strtab.rbs b/sig/vaporware/compiler/assembler/elf/section/strtab.rbs new file mode 100644 index 0000000..3f40b9c --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/strtab.rbs @@ -0,0 +1,5 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Strtab + @name: String + def initialize: (String) -> void + def build: () -> String +end diff --git a/sig/vaporware/compiler/assembler/elf/section/symtab.rbs b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs new file mode 100644 index 0000000..9d6159f --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs @@ -0,0 +1,19 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Symtab + @name: Array[Integer] + @info: Array[Integer] + @other: Array[Integer] + @shndx: Array[Integer] + @value: Array[Integer] + @size: Array[Integer] + + def set!: (?name: Integer?, ?info: Integer?, ?other: Integer?, ?shndx: Integer?, ?value: Integer?, ?size: Integer?) -> void + def build: () -> String + + private + def name!: (String) -> void + def desc!: (String) -> void + def bytes: () -> Array[Array[Integer]?] + def align!: (Array[Integer], Integer) -> void + def num2bytes: (Integer, Integer) -> Array[Integer] + def check: (Integer | Array[Integer], Integer) -> bool +end diff --git a/sig/vaporware/compiler/assembler/elf/section/text.rbs b/sig/vaporware/compiler/assembler/elf/section/text.rbs new file mode 100644 index 0000000..bfbee83 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/text.rbs @@ -0,0 +1,21 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Text + PREFIX: Hash[Symbol, Integer] + REGISTER_CODE: Hash[Symbol, Integer] + OPECODE: Hash[Symbol, Integer] + + attr_reader bytes: Array[Integer] + attr_reader size: Integer + attr_reader offset: Integer + + def assemble!: (String) -> void + def align!: (Integer) -> void + + private + + def opecode: ((String | Symbol), Array[String]) -> (Array[Integer] | nil) + def mov: ((String | Symbol), Array[String]) -> Array[Integer] + def calc: ((String | Symbol), Array[String]) -> Array[Integer] + def push: (Array[String]) -> Array[Integer] + def pop: (Array[String]) -> Array[Integer] + def reg: ((String | Symbol)) -> Integer +end diff --git a/sig/vaporware/compiler/assembler/elf/section_header.rbs b/sig/vaporware/compiler/assembler/elf/section_header.rbs new file mode 100644 index 0000000..df5da89 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section_header.rbs @@ -0,0 +1,18 @@ +class Vaporware::Compiler::Assembler::ELF::SectionHeader + @name: Array[Integer] + @type: Array[Integer] + @flags: Array[Integer] + @addr: Array[Integer] + @offset: Array[Integer] + @size: Array[Integer] + @link: Array[Integer] + @info: Array[Integer] + @addralign: Array[Integer] + @entsize: Array[Integer] + + def build: () -> String + def set!: (?name: Integer, ?type: Integer, ?flags: Integer, ?addr: Integer, ?offset: Integer, ?size: Integer, ?link: Integer, ?info: Integer, ?addralign: Integer, ?entsize: Integer) -> void + def null!: () -> void + def text!: () -> void + def note!: () -> void +end From 654e55c0da0fc9b229e75c1feda935174d1edc62 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 5 Sep 2023 20:59:21 +0900 Subject: [PATCH 09/58] reduction nest --- lib/vaporware/compiler.rb | 105 ++++++++++++++-------------- lib/vaporware/compiler/generator.rb | 16 ----- 2 files changed, 52 insertions(+), 69 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index ee8153b..81c1e75 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -1,64 +1,63 @@ # frozen_string_literal: true require_relative "compiler/generator" +require_relative "compiler/assembler" + +class Vaporware::Compiler + def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) + _precompile = "#{dest}.s" + s = new(source, _precompile: _precompile, debug:, shared:) + s.compile(compiler:, compiler_options:) + obj_file = s.assemble(input: _precompile, assembler: "as", debug:) + s.link(obj_file) + end -module Vaporware - # Your code goes here... - class Compiler - def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) - _precompile = "#{dest}.s" - s = new(source, _precompile: _precompile, debug:, shared:) - s.compile(compiler:, compiler_options:) - obj = s.assemble(input: _precompile, compmiler:, compiler_options:, debug:) - s.link(input: obj,link_options:) - end - - def initialize(source, _precompile: "tmp.s", debug: false, shared: false) - @generator = Vaporware::Compiler::Generator.new(source, precompile: _precompile, debug:, shared:) - end - - def assemble(input: precompile, output: File.basename(precompile, ".*") + ".o", compiler: "gcc", compiler_options: ["-O0"], debug: false) - base_name = File.basename(input, ".*") - name = shared ? "lib#{base_name}.so" : base_name - if compiler == "gcc" - compile_commands = [compiler, *compiler_options, "-o", name, input].compact - call_compiler(compile_commands) - else - Vaporware::Compiler::Assembler.assemble!(input, name) - end + def initialize(source, _precompile: "tmp.s", debug: false, shared: false) + @generator = Vaporware::Compiler::Generator.new(source, precompile: _precompile, debug:, shared:) + @assembler = Vaporware::Compiler::Assembler.new(@generator.precompile, debug:) + end - File.delete(input) unless debug - nil + def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "gcc", assembler_options: [], debug: false) + if ["gcc", "as"].include?(assembler) + assemble_commands = [assembler, *assembler_options, "-o", output, input].compact + call_commands(assemble_commands) + else + @assembler.assemble(input, output) end + output + end - def call_compiler(compile_commands) = IO.popen(compile_commands).close - - def compile(compiler: "gcc", compiler_options: ["-O0"]) - @generator.register_var_and_method(@generator.ast) + def link(input, output = File.basename(input, ".*"), linker: "mold", linker_options: ["-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "/lib64/libc.so.6", "/usr/lib64/crt1.o", input]) + linker_commands = [linker, *linker_options, "-o", output].compact + call_commands(linker_commands) + end - output = File.open(@generator.precompile, "w") - # prologue - output.puts ".intel_syntax noprefix" - if @generator.defined_methods.empty? - @generator.main = true - output.puts ".globl main" - output.puts "main:" - output.puts " push rbp" - output.puts " mov rbp, rsp" - output.puts " sub rsp, #{@generator.defined_variables.size * 8}" - @generator.to_asm(@generator.ast, output) - # epilogue - @generator.epilogue(output) - else - @generator.prologue_methods(output) - output.puts ".globl main" unless @generator.shared - @generator.to_asm(@generator.ast, output) - # epilogue - @generator.epilogue(output) - end - output.close - compiler_options += @generator.compile_shared_option if @generator.shared - @generator.to_elf(input: @generator.precompile, compiler:, compiler_options:, debug: @generator.debug) + def call_commands(commands) = IO.popen(commands).close + + def compile(compiler: "gcc", compiler_options: ["-O0"]) + @generator.register_var_and_method(@generator.ast) + + output = File.open(@generator.precompile, "w") + # prologue + output.puts ".intel_syntax noprefix" + if @generator.defined_methods.empty? + @generator.main = true + output.puts ".globl main" + output.puts "main:" + output.puts " push rbp" + output.puts " mov rbp, rsp" + output.puts " sub rsp, #{@generator.defined_variables.size * 8}" + @generator.to_asm(@generator.ast, output) + # epilogue + @generator.epilogue(output) + else + @generator.prologue_methods(output) + output.puts ".globl main" unless @generator.shared + @generator.to_asm(@generator.ast, output) + # epilogue + @generator.epilogue(output) end + output.close + compiler_options += @generator.compile_shared_option if @generator.shared end end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index ef33303..e432d86 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -31,22 +31,6 @@ def register_var_and_method(node) def already_build_methods? = defined_methods.sort == @doned.to_a.sort - def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: false) - base_name = File.basename(input, ".*") - name = shared ? "lib#{base_name}.so" : base_name - if compiler.nil? - Vaporware::Compiler::Assembler.assemble!(name, input) - else - compile_commands = [compiler, *compiler_options, "-o", name, input].compact - call_compiler(compile_commands) - end - - File.delete(input) unless debug - nil - end - - def call_compiler(compile_commands) = IO.popen(compile_commands).close - def epilogue(output) output.puts " mov rsp, rbp" output.puts " pop rbp" From 2f7fbf408f68004b0a32cdc2f3529edc7ea6d577 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 5 Sep 2023 20:59:57 +0900 Subject: [PATCH 10/58] refactoring testing interface --- lib/vaporware/compiler.rb | 1 + test/test_vaporware.rb | 18 ++++++------------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 81c1e75..497147b 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -10,6 +10,7 @@ def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_op s.compile(compiler:, compiler_options:) obj_file = s.assemble(input: _precompile, assembler: "as", debug:) s.link(obj_file) + File.delete(_precompile) if debug end def initialize(source, _precompile: "tmp.s", debug: false, shared: false) diff --git a/test/test_vaporware.rb b/test/test_vaporware.rb index 0d71327..44abf2e 100644 --- a/test/test_vaporware.rb +++ b/test/test_vaporware.rb @@ -5,8 +5,7 @@ class VaporwareTest < Test::Unit::TestCase def tear_down = File.delete("tmp") rescue File.delete(@generated) def test_sample_plus @file = "sample/plus.rb" - @vaporware = Vaporware::Compiler.new(@file) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(9, exit_code) @@ -15,8 +14,7 @@ def test_sample_plus def test_sample_variable @file = "sample/variable.rb" - @vaporware = Vaporware::Compiler.new(@file) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(1, exit_code) @@ -25,8 +23,7 @@ def test_sample_variable def test_sample_if @file = "sample/if.rb" - @vaporware = Vaporware::Compiler.new(@file) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(1, exit_code) @@ -35,8 +32,7 @@ def test_sample_if def test_sample_else @file = "sample/else.rb" - @vaporware = Vaporware::Compiler.new(@file) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(2, exit_code) @@ -45,8 +41,7 @@ def test_sample_else def test_sample_while @file = "sample/while.rb" - @vaporware = Vaporware::Compiler.new(@file) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(55, exit_code) @@ -56,8 +51,7 @@ def test_sample_while def test_sample_call_method @generated = "libtmp.so" @file = "sample/method.rb" - @vaporware = Vaporware::Compiler.new(@file, shared: true) - @vaporware.compile + @vaporware = Vaporware::Compiler.compile(@file, shared: true) require './sample/fiddle.rb' assert_equal(10, X.aibo) end From 51cfad0442542f95a7a63ee7da3708d68975c12a Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 10 Sep 2023 09:37:39 +0900 Subject: [PATCH 11/58] use inner classes for compile --- lib/vaporware/compiler.rb | 65 +++---- lib/vaporware/compiler/assembler.rb | 255 +++++---------------------- lib/vaporware/compiler/generator.rb | 63 ++++--- sig/vaporware/compiler.rbs | 10 +- sig/vaporware/compiler/assembler.rbs | 7 +- sig/vaporware/compiler/generator.rbs | 29 ++- 6 files changed, 133 insertions(+), 296 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 497147b..22db0a4 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -6,59 +6,42 @@ class Vaporware::Compiler def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) _precompile = "#{dest}.s" - s = new(source, _precompile: _precompile, debug:, shared:) - s.compile(compiler:, compiler_options:) + s = new(input: source, output: _precompile, debug:, shared:) + s.compile(compiler_options:) obj_file = s.assemble(input: _precompile, assembler: "as", debug:) - s.link(obj_file) - File.delete(_precompile) if debug + output = File.basename(obj_file, ".o") + output = "lib#{output}.so" if shared + s.link(input: obj_file, output:, shared:) + File.delete(obj_file) unless debug + File.delete(_precompile) unless debug end - def initialize(source, _precompile: "tmp.s", debug: false, shared: false) - @generator = Vaporware::Compiler::Generator.new(source, precompile: _precompile, debug:, shared:) - @assembler = Vaporware::Compiler::Assembler.new(@generator.precompile, debug:) + def initialize(input:, output: File.basename(input, ".*") + ".s", debug: false, shared: false) + @generator = Vaporware::Compiler::Generator.new(input:, output:, debug:, shared:) + @assembler = Vaporware::Compiler::Assembler.new(input: @generator.precompile, debug:,) end - def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "gcc", assembler_options: [], debug: false) + def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "as", assembler_options: [], debug: false) if ["gcc", "as"].include?(assembler) - assemble_commands = [assembler, *assembler_options, "-o", output, input].compact - call_commands(assemble_commands) + assemble = [assembler, *assembler_options, "-o", output, input].compact + call_command(assemble) else - @assembler.assemble(input, output) + @assembler.assemble(input:, output:) end output end - def link(input, output = File.basename(input, ".*"), linker: "mold", linker_options: ["-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "/lib64/libc.so.6", "/usr/lib64/crt1.o", input]) - linker_commands = [linker, *linker_options, "-o", output].compact - call_commands(linker_commands) + def link(input:, output: File.basename(input, ".*"), linker: "mold", linker_options: [], dyn_ld_path: ["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"], ld_path: ["/lib64/libc.so.6", "/usr/lib64/crt1.o"], shared: false) + if shared + dyn_ld_path = [] + ld_path = ["/usr/lib64/crti.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtbeginS.o"] + linker_options = ["-shared"] + end + linker_commands = [linker, *linker_options, *dyn_ld_path, "-o", output, *ld_path, input].compact + call_command(linker_commands) end - def call_commands(commands) = IO.popen(commands).close - - def compile(compiler: "gcc", compiler_options: ["-O0"]) - @generator.register_var_and_method(@generator.ast) + def call_command(commands) = IO.popen(commands.join(" ")).close - output = File.open(@generator.precompile, "w") - # prologue - output.puts ".intel_syntax noprefix" - if @generator.defined_methods.empty? - @generator.main = true - output.puts ".globl main" - output.puts "main:" - output.puts " push rbp" - output.puts " mov rbp, rsp" - output.puts " sub rsp, #{@generator.defined_variables.size * 8}" - @generator.to_asm(@generator.ast, output) - # epilogue - @generator.epilogue(output) - else - @generator.prologue_methods(output) - output.puts ".globl main" unless @generator.shared - @generator.to_asm(@generator.ast, output) - # epilogue - @generator.epilogue(output) - end - output.close - compiler_options += @generator.compile_shared_option if @generator.shared - end + def compile(compiler_options: ["-O0"]) = @generator.compile end diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index c93d2a7..dacc3d6 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -2,225 +2,56 @@ require_relative "assembler/elf" require_relative "assembler/elf/header" require_relative "assembler/elf/section" -require_relative "assembler/elf/section/note" require_relative "assembler/elf/section/text" +require_relative "assembler/elf/section/bss" +require_relative "assembler/elf/section/data" +require_relative "assembler/elf/section/note" require_relative "assembler/elf/section/symtab" +require_relative "assembler/elf/section/strtab" require_relative "assembler/elf/section/shsymtab" +require_relative "assembler/elf/section/shstrtab" require_relative "assembler/elf/section_header" -module Vaporware - class Compiler - class Assembler - SYMTAB_SECTION = %w( - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 01 00 00 00 10 00 01 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ).map { _1.to_i(16) }.pack("C*") - - SHSYMTAB_SECTION = %w( - 00 2e 73 79 6d 74 61 62 00 2e 73 74 72 74 61 62 - 00 2e 73 68 73 74 72 74 61 62 00 2e 74 65 78 74 - 00 2e 64 61 74 61 00 2e 62 73 73 00 2e 6e 6f 74 - 65 2e 67 6e 75 2e 70 72 6f 70 65 72 74 79 00 - ).map { _1.to_i(16) }.pack("C*") - - def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input, output).assemble - - def initialize(input, output = File.basename(input, ".*") + ".o", type: :relocator, debug: false) - @input, @output = input, output - @target_file = File.open(output, "wb") - @elf_header = ELF::Header.new(type:) - @sections = { - null: { body: nil, header: ELF::SectionHeader.new.null! }, - text: { body: ELF::Section::Text.new, header: ELF::SectionHeader.new.text! }, - note: { body: ELF::Section::Note.new.gnu_property!, header: ELF::SectionHeader.new.note! }, - symtab: { body: ELF::Section::Symtab.new, header: ELF::SectionHeader.new.symtab! }, - strtab: { body: ELF::Section::Strtab.new, header: ELF::SectionHeader.new.strtab! }, - shsymtab: { body: ELF::Section::Shsymtab.new, header: ELF::SectionHeader.new.shsymtab! }, - } - @debug = debug - end - - def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) - read = { main: false } - program_size = 0 - text = @section[:text][:body] - File.open(input, "r") do |r| - r.each_line do |line| - read[:main] = /main:/.match(line) unless read[:main] - next unless read[:main] && !/main:/.match(line) - text.assemble!(line) - end - end - f.write(@elf_header.build!) - bins = [] - section_headers = [] - @sections.values.map do |section| - bins << section[:body].build - section_headers << section[:header].build - end - - f.close - f.path - end - - private - - def section_header(str_section_names:) - [ - NOTE_GNU_PROPERTY_SECTION, - SYMTAB_SECTION, - start_table_section(str_section_names), - SHSYMTAB_SECTION, - ] - end - - def start_table_section(main = "main") - b = main.bytes << 0 - b.unshift 0 until b.size % 2 ==0 - b.pack("C*") - end - - def null_section_header = section_headers - - def text_section_header(name: [0x1b, *[0] * 3], offset: [0x40, *[0]* 7], size:) - type = [1, *[0] * 3] - flags = [6, *[0] * 7] - addralign = [1, *[0] * 7] - section_headers(name:, type:, flags:, size:, offset:, addralign:) - end - - def data_section_header(offset: [0x72, *[0]*7]) - name = [0x21, *[0] * 3] - type = [1, *[0] * 3] - flags = [3, *[0] * 7] - addralign = [1, *[0] * 7] - section_headers(name:, type:, flags:, offset:, addralign:) - end - - def bss_section_header(offset: [0x72, *[0]*7]) - name = [0x27, *[0] * 3] - type = [8, *[0] * 3] - flags = [3, *[0] * 7] - addralign = [1, *[0]* 7] - section_headers(name:, type:, flags:, offset:, addralign:) - end - - def note_section_header(offset: [0x78, *[0]*7]) - name = [0x2c, *[0] * 3] - type = [0x07, *[0] * 3] - flags = [0x02, *[0] * 7] - size = [0x30, *[0] * 7] - addralign = [0x08, *[0] * 7] - section_headers(name:, type:, flags:, offset:, size:, addralign:) - end - - def symtab_section_header(name: [0x01, *[0] * 3], offset: [0xa8, *[0] * 7], size: [0x30, *[0] * 7]) - type = [0x02, *[0]* 3] - link = [0x06, *[0] * 3] - info = [0x01, *[0] * 3] - addralign = [0x08, *[0] * 7] - entsize = [0x18, *[0] * 7] - section_headers(name:, type:, offset:, size:, link:, info:, addralign:, entsize:) - end - - def strtab_section_header(name: [0x09, *[0] * 3], offset: [0xd8, *[0] * 7], size:) - type = [0x03, *[0] * 3] - addralign = [0x01, *[0]*7] - section_headers(name:, type:, offset:, size:, addralign:) - end - - def shstrtab_section_header(name: [0x11, *[0] * 3], offset: [0xde, *[0] * 7], size: [0x3f, *[0] * 7]) - type = [0x03, *[0] * 3] - addralign = [0x01, *[0] * 7] - section_headers(name:, type:, offset:, size:, addralign:) - end - - def section_headers( - name: [0]*4, type: [0]*4, flags: [0]*8, addr: [0]*8, - offset: [0]*8, size: [0]*8, link: [0]*4, info: [0]*4, - addralign: [0]*8, entsize: [0]*8) = - [name, type, flags, addr, offset, size, link, info, addralign, entsize].flatten.pack("C*") - - def opecode(op, args) - case op - when "push" - push(args) - when "mov" - [PREFIX[:REX_W], *mov(op, *args)] - when "sub", "add", "imul", "cqo", "idiv" - [PREFIX[:REX_W], *calc(op, *args)] - when "pop" - pop(args) - when "ret" - [0xc3] - end - end - - def mov(op, *arguments) - reg = case arguments - in ["rbp", "rsp"] - [0xe5] - in ["rsp", "rbp"] - [0xec] - else - arguments&.map { reg(_1) } - end - [OPECODE[op.upcase.to_sym], *reg] - end - - def calc(op, *arguments) - ope_code = OPECODE[op.upcase.to_sym] - case [op, *arguments] - in ["sub", "rax", "rdi"] - [0x29, 0xf8] - in ["add", "rax", "rdi"] - [ope_code, 0xf8] - in ["imul", "rax", "rdi"] - [ope_code, 0xaf, 0xc7] - in ["idiv", "rdi"] - [ope_code, 0xff] - in ["sub", "rsp", *num] - [ope_code, 0xec, *num.map { |n| n.to_i(16) }] - in ["cqo"] - [0x99] - end - end - - def push(args) - case args - in ["rbp"] | ["rdi"] - [0x55] - in ["rax"] - [0x50] - else - [0x6a, *args.map { reg(_1) }] - end - end - - def pop(args) - case args - in ["rax"] | ["rdi"] - [0x58 + REGISTER_CODE[args.first.upcase.to_sym]] - in ["rbp"] - [0x5d] - end - end +class Vaporware::Compiler::Assembler + def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input:, output:).assemble + + def initialize(input:, output: File.basename(input, ".*") + ".o", type: :relocator, debug: false) + @input, @output = input, output + @elf_header = ELF::Header.new(type:) + @sections = { + null: { body: nil, header: ELF::SectionHeader.new.null! }, + text: { body: ELF::Section::Text.new, header: ELF::SectionHeader.new.text! }, + data: { body: ELF::Section::Data.new, header: ELF::SectionHeader.new.data! }, + bss: { body: ELF::Section::BSS.new, header: ELF::SectionHeader.new.bss! }, + note: { body: ELF::Section::Note.new.gnu_property!, header: ELF::SectionHeader.new.note! }, + symtab: { body: ELF::Section::Symtab.new, header: ELF::SectionHeader.new.symtab! }, + strtab: { body: ELF::Section::Strtab.new, header: ELF::SectionHeader.new.strtab! }, + shsymtab: { body: ELF::Section::Shsymtab.new, header: ELF::SectionHeader.new.shsymtab! }, + } + @debug = debug + end - def reg(r) - case r - in "rsp" - 0xec - in "rbp" - 0x5e - in "rax" - 0x29 - in "rdi" - 0xf8 - in /\d+/ - ("%02x" % r).to_i(16) - end + def assemble(assemble_command: "as", assemble_options: [], input: @input, output: @output) + f = File.open(output, "wb") + read = { main: false } + program_size = 0 + text = @sections[:text][:body] + File.open(input, "r") do |r| + r.each_line do |line| + read[:main] = /main:/.match(line) unless read[:main] + next unless read[:main] && !/main:/.match(line) + text.assemble!(line) end end + f.write(@elf_header.build) + bins = [] + section_headers = [] + @sections.values.map do |section| + bins << section[:body].build + section_headers << section[:header].build + end + + f.close + f.path end end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index e432d86..3526dfd 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -5,15 +5,38 @@ class Compiler class Generator REGISTER = %w(rdi rsi rdx rcx r8 r9) attr_accessor :main - attr_reader :ast, :precompile, :debug, :seq, :defined_variables, :doned, :shared, :defined_methods - def initialize(source, precompile:, debug:, shared:) - @precompile, @debug, @shared = precompile, debug, shared + attr_reader :precompile, :shared + def initialize(input:, output: File.basename(input, "*") + ".s", debug: false, shared: false) + @source, @precompile, @debug, @shared = input, output, debug, shared @doned, @defined_methods, @defined_variables = Set.new, Set.new, Set.new @seq, @main = 0, false - @ast = RubyVM::AbstractSyntaxTree.parse_file(source) + @ast = RubyVM::AbstractSyntaxTree.parse_file(@source) end def compile_shared_option = %w(-shared -fPIC) + def compile + register_var_and_method(@ast) + + output = File.open(@precompile, "w") + # prologue + output.puts " .intel_syntax noprefix" + if @defined_methods.empty? + @main = true + output.puts " .globl main" + output.puts "main:" + output.puts " push rbp" + output.puts " mov rbp, rsp" + output.puts " sub rsp, #{@defined_variables.size * 8}" + to_asm(@ast, output) + epilogue(output) + else + prologue_methods(output) + output.puts " .globl main" unless @shared + to_asm(@ast, output) + epilogue(output) + end + output.close + end def register_var_and_method(node) return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node) @@ -29,7 +52,7 @@ def register_var_and_method(node) nil end - def already_build_methods? = defined_methods.sort == @doned.to_a.sort + def already_build_methods? = @defined_methods.sort == @doned.to_a.sort def epilogue(output) output.puts " mov rsp, rbp" @@ -38,7 +61,7 @@ def epilogue(output) end def prologue_methods(output) - defined_methods.each do |name| + @defined_methods.each do |name| output.puts ".globl #{name}" output.puts ".type #{name}, @function" if shared end @@ -75,10 +98,10 @@ def call_method(node, output, method_tree) output.puts " idiv rdi" output.puts " mov rax, 0" output.puts " cmp rdi, 0" - output.puts " jne .Lprecall#{seq}" + output.puts " jne .Lprecall#{@seq}" output.puts " push 0" output.puts " mov rax, 1" - output.puts ".Lprecall#{seq}:" + output.puts ".Lprecall#{@seq}:" output.puts " push rax" _, name, *args = node.children args.each_with_index do |arg, i| @@ -89,9 +112,9 @@ def call_method(node, output, method_tree) output.puts " call #{name}" output.puts " pop rdi" output.puts " cmp rdi, 0" - output.puts " je .Lpostcall#{seq}" + output.puts " je .Lpostcall#{@seq}" output.puts " pop rdi" - output.puts ".Lpostcall#{seq}:" + output.puts ".Lpostcall#{@seq}:" output.puts " push rax" @seq += 1 nil @@ -184,33 +207,33 @@ def to_asm(node, output, method_tree = false) output.puts " push rax" output.puts " cmp rax, 0" if fblock - output.puts " je .Lelse#{seq}" + output.puts " je .Lelse#{@seq}" to_asm(tblock, output, method_tree) ret(output) - output.puts " jmp .Lend#{seq}" - output.puts ".Lelse#{seq}:" + output.puts " jmp .Lend#{@seq}" + output.puts ".Lelse#{@seq}:" to_asm(fblock, output, method_tree) ret(output) - output.puts ".Lend#{seq}:" + output.puts ".Lend#{@seq}:" else - output.puts " je .Lend#{seq}" + output.puts " je .Lend#{@seq}" to_asm(tblock, output, method_tree) ret(output) - output.puts ".Lend#{seq}:" + output.puts ".Lend#{@seq}:" end @seq += 1 return when :WHILE cond, tblock = node.children - output.puts ".Lbegin#{seq}:" + output.puts ".Lbegin#{@seq}:" to_asm(cond, output, method_tree) output.puts " pop rax" output.puts " push rax" output.puts " cmp rax, 0" - output.puts " je .Lend#{seq}" + output.puts " je .Lend#{@seq}" to_asm(tblock, output, method_tree) - output.puts " jmp .Lbegin#{seq}" - output.puts ".Lend#{seq}:" + output.puts " jmp .Lbegin#{@seq}" + output.puts ".Lend#{@seq}:" @seq += 1 return when :OPCALL diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index 1ab0dbc..39ca913 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -1,11 +1,13 @@ class Vaporware::Compiler # class methods def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> void - def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> void + def initialize: (input: String, ?output: String, ?debug: bool, ?shared: bool) -> void @generator: Vaporware::Compiler::Generator + @assembler: Vaporware::Compiler::Assembler # instance methods - def assemble: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> void - def link: (?output: String, ?linker: String, ?linker_options: Array[String]) -> void - def compile: (?compiler: String, ?compiler_options: Array[String]) -> void + def assemble: (input: String, ?output: String, ?assembler: String, ?assembler_options: Array[String]?, ?debug: bool) -> String + def link: (input: String, ?output: String, ?linker: String, ?linker_options: Array[String], ?shared: bool) -> void + def compile: (?compiler_options: Array[String]) -> void + def call_command: (Array[String]) -> void end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index d28d2ac..82a784b 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -1,8 +1,9 @@ class Vaporware::Compiler::Assembler @input: String @output: String - @target_file: File @elf_header: Vaporware::Compiler::Assembler::ELF::Header - def initialize: (String, ?String, type: Symbol, debug: bool) -> void - def assemble: (?asemble_command: String, ?assemble_options: Array[String], input: String, f: File) -> void + @debug: bool + + def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void + def assemble: (?assemble_command: String, ?assemble_options: Array[String], ?input: String, ?output: String) -> void end diff --git a/sig/vaporware/compiler/generator.rbs b/sig/vaporware/compiler/generator.rbs index 954a00d..82bad73 100644 --- a/sig/vaporware/compiler/generator.rbs +++ b/sig/vaporware/compiler/generator.rbs @@ -1,29 +1,26 @@ class Vaporware::Compiler::Generator REGISTER: Array[String] - # attr_accessor and define instance variables - @main: bool - attr_accessor main (): bool - - # attr_reader for instance variables - attr_reader debug: bool - attr_reader doned: Set[Symbol] - attr_reader defined_methods: Set[Symbol] - attr_reader defined_variables: Set[Symbol] + # attr_reader attr_reader precompile: String - attr_reader seq: Integer - attr_reader shared: bool - # temporarily using untyped types since parser.gem's rbs information is unchecked. - attr_reader ast: RubyVM::AbstractSyntaxTree::Node + @main: bool + @debug: bool + @doned: Set[Symbol] + @defined_methods: Set[Symbol] + @defined_variables: Set[Symbol] + @seq: Integer + @shared: bool + @ast: RubyVM::AbstractSyntaxTree::Node + @source: String # class methods - def initialize: (String, precompile: String, debug: bool, shared: bool) -> void + def initialize: (input: String, ?output: String, ?debug: bool, ?shared: bool) -> void # instance private methods def already_build_methods?: -> bool def call_method: (RubyVM::AbstractSyntaxTree::Node, File, bool) -> void - def comp: (String, File) -> void + def compile: () -> void def compile_shared_option: () -> Array[String] def define_method_prologue: (RubyVM::AbstractSyntaxTree::Node, File) -> void def epilogue: (File) -> void @@ -32,7 +29,7 @@ class Vaporware::Compiler::Generator def method: (Symbol, RubyVM::AbstractSyntaxTree::Node, File) -> void def prologue: (RubyVM::AbstractSyntaxTree::Node, File) -> void def prologue_methods: (File) -> void - def register_var_and_method: (RubyVM::AbstractSyntaxTree::Node) -> void + def register_var_and_method: (RubyVM::AbstractSyntaxTree::Node?) -> void def ret: (File) -> void def to_asm: (RubyVM::AbstractSyntaxTree::Node, File, ?bool) -> void def variable_or_method?: (Symbol) -> bool From 4b83552cd15af592bb2b8b92bd370be742c5004f Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 10 Sep 2023 12:52:00 +0900 Subject: [PATCH 12/58] link shared library linking object files --- lib/vaporware/compiler.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 22db0a4..2796d31 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -10,7 +10,7 @@ def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_op s.compile(compiler_options:) obj_file = s.assemble(input: _precompile, assembler: "as", debug:) output = File.basename(obj_file, ".o") - output = "lib#{output}.so" if shared + output = "lib#{output}.so" if shared && output !~ /^lib.+\.so$/ s.link(input: obj_file, output:, shared:) File.delete(obj_file) unless debug File.delete(_precompile) unless debug @@ -34,7 +34,8 @@ def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "as", def link(input:, output: File.basename(input, ".*"), linker: "mold", linker_options: [], dyn_ld_path: ["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"], ld_path: ["/lib64/libc.so.6", "/usr/lib64/crt1.o"], shared: false) if shared dyn_ld_path = [] - ld_path = ["/usr/lib64/crti.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtbeginS.o"] + ld_path = ["/usr/lib64/crti.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtbeginS.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtendS.o", "/usr/lib64/crtn.o",] + linker_options = ["-shared"] end linker_commands = [linker, *linker_options, *dyn_ld_path, "-o", output, *ld_path, input].compact From a68eb23eb9f24434ad747c588be6a8aa87ae69e8 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Wed, 13 Sep 2023 20:28:21 +0900 Subject: [PATCH 13/58] pass ci --- lib/vaporware/compiler.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/bss.rb | 6 ++++++ lib/vaporware/compiler/assembler/elf/section/data.rb | 6 ++++++ lib/vaporware/compiler/assembler/elf/section/note.rb | 6 +++--- .../compiler/assembler/elf/section/shstrtab.rb | 6 ++++++ .../compiler/assembler/elf/section/shsymtab.rb | 2 ++ lib/vaporware/compiler/assembler/elf/section/symtab.rb | 2 +- lib/vaporware/compiler/assembler/elf/section_header.rb | 5 +++++ sig/vaporware/compiler/assembler/elf/header.rbs | 10 +++++----- 9 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 lib/vaporware/compiler/assembler/elf/section/bss.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/data.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/shstrtab.rb create mode 100644 lib/vaporware/compiler/assembler/elf/section/shsymtab.rb diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 2796d31..1ff8604 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -31,7 +31,7 @@ def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "as", output end - def link(input:, output: File.basename(input, ".*"), linker: "mold", linker_options: [], dyn_ld_path: ["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"], ld_path: ["/lib64/libc.so.6", "/usr/lib64/crt1.o"], shared: false) + def link(input:, output: File.basename(input, ".*"), linker: "ld", linker_options: [], dyn_ld_path: ["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"], ld_path: ["/lib64/libc.so.6", "/usr/lib64/crt1.o"], shared: false) if shared dyn_ld_path = [] ld_path = ["/usr/lib64/crti.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtbeginS.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtendS.o", "/usr/lib64/crtn.o",] diff --git a/lib/vaporware/compiler/assembler/elf/section/bss.rb b/lib/vaporware/compiler/assembler/elf/section/bss.rb new file mode 100644 index 0000000..6ced12c --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/bss.rb @@ -0,0 +1,6 @@ +class Vaporware::Compiler::Assembler::ELF::Section::BSS + + def build = bytes.flatten.pack("C*") + private + def bytes = [] +end diff --git a/lib/vaporware/compiler/assembler/elf/section/data.rb b/lib/vaporware/compiler/assembler/elf/section/data.rb new file mode 100644 index 0000000..7b99987 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/data.rb @@ -0,0 +1,6 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Data + + def build = bytes.flatten.pack("C*") + private + def bytes = [] +end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index 49f83c0..ea83ceb 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -14,7 +14,7 @@ def initialize def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) @nsize = num2bytes(nsize, 4) if check(nsize, 4) - @dsize = num2bytes(dsize, 4) if check(disze, 4) + @dsize = num2bytes(dsize, 4) if check(dsize, 4) @type = num2bytes(type, 4) if check(type, 4) @name = name!(name) if name @desc = desc!(desc) if desc @@ -26,10 +26,10 @@ def build = bytes.flatten.pack("C*") private def name!(name) = align!(@name = name.bytes, 4) - def desc!(desc) = align!(@desc = desc.bytes, 4) + def desc!(desc) = align!(@desc = desc.is_a?(Array) ? desc : desc.bytes, 4) def bytes = [@nsize, @dsize, @type, @name, @desc] def align!(val, bytes) = (val << 0 until val.size % bytes == 0) - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb new file mode 100644 index 0000000..c7f75e4 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb @@ -0,0 +1,6 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Strtab + + def build = bytes.flatten.pack("C*") + private + def bytes = [] +end diff --git a/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb b/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb new file mode 100644 index 0000000..2e0401f --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb @@ -0,0 +1,2 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Shsymtab +end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index 0e67f02..6414755 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -21,6 +21,6 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) private def bytes = [@name, @info, @other, @shndx, @value, @size] - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.revert + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index e05b2e7..de9ef98 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -32,6 +32,11 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) def text! = set!(flags: 0x06, addralign: 0x01) def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) + def data! = set! + def symtab! = set! + def strtab! = set! + def bss! = set! + def shsymtab! = set! private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,] diff --git a/sig/vaporware/compiler/assembler/elf/header.rbs b/sig/vaporware/compiler/assembler/elf/header.rbs index fdea5a2..af8ceca 100644 --- a/sig/vaporware/compiler/assembler/elf/header.rbs +++ b/sig/vaporware/compiler/assembler/elf/header.rbs @@ -6,16 +6,16 @@ class Vaporware::Compiler::Assembler::ELF::Header @type: Integer @arch: Array[Integer] @version: Array[Integer] - @entry: Array[Integer] | nil - @phoffset: Array[Integer] | nil - @shoffset: Array[Integer] | nil + @entry: Array[Integer]? + @phoffset: Array[Integer]? + @shoffset: Array[Integer]? @flags: Array[Integer] @ehsize: Array[Integer] @phsize: Array[Integer] @ehnum: Array[Integer] @shentsize: Array[Integer] - @shnum: Array[Integer] | nil - @shstrndx: Array[Integer] | nil + @shnum: Array[Integer]? + @shstrndx: Array[Integer]? def initialize: (?endian: Symbol, ?type: Symbol, ?arche: Symbol) -> void def build: () -> String From a253c01d588000cc1c8611b975a37ad70cc30dfd Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Fri, 15 Sep 2023 20:51:28 +0900 Subject: [PATCH 14/58] pass ci --- Rakefile | 2 +- lib/vaporware/compiler.rb | 45 +++++-------------- lib/vaporware/compiler/assembler.rb | 15 ++++++- lib/vaporware/compiler/linker.rb | 44 ++++++++++++------ sig/vaporware/compiler.rbs | 4 ++ sig/vaporware/compiler/assembler.rbs | 5 ++- sig/vaporware/compiler/linker.rbs | 15 +++++++ test/vaporware/compiler/test_linker.rb | 17 +++++++ .../test_compiler.rb} | 2 +- 9 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 sig/vaporware/compiler/linker.rbs create mode 100644 test/vaporware/compiler/test_linker.rb rename test/{test_vaporware.rb => vaporware/test_compiler.rb} (97%) diff --git a/Rakefile b/Rakefile index 2d97dc4..4b0fee6 100644 --- a/Rakefile +++ b/Rakefile @@ -8,7 +8,7 @@ require "steep/cli" task default: %i[test] Rake::TestTask.new do |t| - t.test_files = FileList['test/test*.rb'] + t.test_files = FileList['test/test*.rb', 'test/**/test*.rb'] end namespace :steep do diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index 1ff8604..a3b1f7f 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -2,47 +2,24 @@ require_relative "compiler/generator" require_relative "compiler/assembler" +require_relative "compiler/linker" class Vaporware::Compiler def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) - _precompile = "#{dest}.s" - s = new(input: source, output: _precompile, debug:, shared:) + s = new(input: source, output: dest, debug:, shared:) s.compile(compiler_options:) - obj_file = s.assemble(input: _precompile, assembler: "as", debug:) - output = File.basename(obj_file, ".o") - output = "lib#{output}.so" if shared && output !~ /^lib.+\.so$/ - s.link(input: obj_file, output:, shared:) - File.delete(obj_file) unless debug - File.delete(_precompile) unless debug - end - - def initialize(input:, output: File.basename(input, ".*") + ".s", debug: false, shared: false) - @generator = Vaporware::Compiler::Generator.new(input:, output:, debug:, shared:) - @assembler = Vaporware::Compiler::Assembler.new(input: @generator.precompile, debug:,) - end - - def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "as", assembler_options: [], debug: false) - if ["gcc", "as"].include?(assembler) - assemble = [assembler, *assembler_options, "-o", output, input].compact - call_command(assemble) - else - @assembler.assemble(input:, output:) - end - output + s.assemble(input: dest.to_s + ".s", assembler: "as", debug:) + s.link end - def link(input:, output: File.basename(input, ".*"), linker: "ld", linker_options: [], dyn_ld_path: ["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"], ld_path: ["/lib64/libc.so.6", "/usr/lib64/crt1.o"], shared: false) - if shared - dyn_ld_path = [] - ld_path = ["/usr/lib64/crti.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtbeginS.o", "/usr/lib/gcc/x86_64-pc-linux-gnu/13/crtendS.o", "/usr/lib64/crtn.o",] - - linker_options = ["-shared"] - end - linker_commands = [linker, *linker_options, *dyn_ld_path, "-o", output, *ld_path, input].compact - call_command(linker_commands) + def initialize(input:, output: File.basename(input, ".*"), linker: "ld", assembler: "as", debug: false, shared: false) + @generator = Vaporware::Compiler::Generator.new(input:, output: output + ".s", debug:, shared:) + @assembler = Vaporware::Compiler::Assembler.new(input: @generator.precompile, output: output + ".o", assembler:, debug:) + output = "lib#{output}.so" if shared && output !~ /^lib.+\.so$/ + @linker = Vaporware::Compiler::Linker.new(input: @assembler.obj_file, output:, linker:, debug:, shared:) end - def call_command(commands) = IO.popen(commands.join(" ")).close - + def assemble(input:, output: File.basename(input, ".*") + ".o", assembler: "as", assembler_options: [], debug: false) = @assembler.assemble(input:, output:, assembler:, assembler_options:, debug:) + def link = @linker.link def compile(compiler_options: ["-O0"]) = @generator.compile end diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index dacc3d6..447d349 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -15,7 +15,7 @@ class Vaporware::Compiler::Assembler def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input:, output:).assemble - def initialize(input:, output: File.basename(input, ".*") + ".o", type: :relocator, debug: false) + def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocator, debug: false) @input, @output = input, output @elf_header = ELF::Header.new(type:) @sections = { @@ -31,7 +31,18 @@ def initialize(input:, output: File.basename(input, ".*") + ".o", type: :relocat @debug = debug end - def assemble(assemble_command: "as", assemble_options: [], input: @input, output: @output) + def assemble(assembler: "as", assembler_options: [], input: @input, output: @output, debug: false) + if ["gcc", "as"].include?(assembler) + IO.popen([assembler, *assembler_options, "-o", output, input].join(" ")).close + else + to_elf(input:, output:) + end + output + end + def obj_file = @output + + private + def to_elf(input: @input, output: @output, debug: false) f = File.open(output, "wb") read = { main: false } program_size = 0 diff --git a/lib/vaporware/compiler/linker.rb b/lib/vaporware/compiler/linker.rb index 6d0de19..2614300 100644 --- a/lib/vaporware/compiler/linker.rb +++ b/lib/vaporware/compiler/linker.rb @@ -1,20 +1,38 @@ # frozen_string_literal: true -module Vaporware - class Compiler - class Linker - DEFAULT_LIBRARY_PATH = %w(/lib64/libc.so.6 /usr/lib64/crt1.o /usr/lib64/crtn.o) - def self.link!(source, dest = "a.out", linker: "mold", lib_path: [], options: []) = new(source, dest, linker:, lib_path:, options:) +class Vaporware::Compiler::Linker + def self.link!(source, dest = "a.out", linker: "mold", options: []) = new(input: source, output: dest, linker:, options:).link - def initialize(input, output = "a.out", linker: "mold", lib_path: [], options: []) - @input, @output, @linker = input, output, linker - @lib_path = DEFAULT_LIBRARY_PATH + lib_path - @options = options - end + def initialize(input:, output: "a.out", linker: "mold", linker_options: [], shared: false, debug: false) + @input, @output, @linker = input, output, linker + @options = linker_options + @debug, @shared = debug, shared + end - def link = IO.popen(link_command).close + def link(input: @input, output: @output, debug: @debug, shared: @shared) = IO.popen(link_command).close - private - def link_command = %Q|#{@linker} -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o #{@output} #{@lib_path.join(' ')} #{@input}|.split(/\s+/) + def link_command + ld_path = [] + if @shared + ld_path << "--shared" + ld_path << "#{libpath}/crti.o" + ld_path << "#{gcc_libpath}/crtbeginS.o" + ld_path << "#{gcc_libpath}/crtendS.o" + else + ld_path << "-dynamic-linker" + ld_path << "/lib64/ld-linux-x86-64.so.2" + ld_path << "#{libpath}/crt1.o" + ld_path << "#{libpath}/crti.o" + ld_path << "#{gcc_libpath}/crtbegin.o" + # for not static compile + ld_path << "#{gcc_libpath}/crtend.o" end + ld_path << "#{libpath}/libc.so" + ld_path << "#{libpath}/crtn.o" + cmd = [@linker, "-o", @output, "-m", "elf_x86_64", *@options, *ld_path, @input].join(' ') + puts cmd if @debug + cmd end + + def libpath = @libpath ||= File.dirname(Dir.glob("/usr/lib*/**/crti.o").last) + def gcc_libpath = @gcc_libpath ||= File.dirname(Dir.glob("/usr/lib/gcc/x86_64-*/*/crtbegin.o").last) end diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index 39ca913..7402c62 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -1,4 +1,8 @@ class Vaporware::Compiler + GCC_LD_PATH: String + LD_PATH: String + DYN_LD_PATH: String + SHARED_LD_PATH: String # class methods def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> void def initialize: (input: String, ?output: String, ?debug: bool, ?shared: bool) -> void diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index 82a784b..de96fdf 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -5,5 +5,8 @@ class Vaporware::Compiler::Assembler @debug: bool def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void - def assemble: (?assemble_command: String, ?assemble_options: Array[String], ?input: String, ?output: String) -> void + def assemble: (?assembler: String, ?assembler_options: Array[String] | [], ?input: String, ?output: String, ?debug: bool) -> String + def obj_file: () -> String + + private def to_elf: (?input: String, ?output: String, ?debug: bool) -> void end diff --git a/sig/vaporware/compiler/linker.rbs b/sig/vaporware/compiler/linker.rbs new file mode 100644 index 0000000..2c90bee --- /dev/null +++ b/sig/vaporware/compiler/linker.rbs @@ -0,0 +1,15 @@ +class Vaporware::Compiler::Linker + @input: String + @output: String + @linker: String + @options: Array[String] + @shared: bool + @debug: bool + + def initialize: (input: String, ?output: String, ?linker: String, ?linker_options: Array[String], ?shared: bool, ?debug: bool) -> void + def link: (input: String, ?output: String, ?shared: bool, ?debug: bool) -> void + + def link_command: () -> String + def libpath: () -> String + def gcc_libpath: () -> String +end diff --git a/test/vaporware/compiler/test_linker.rb b/test/vaporware/compiler/test_linker.rb new file mode 100644 index 0000000..2db39fb --- /dev/null +++ b/test/vaporware/compiler/test_linker.rb @@ -0,0 +1,17 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::LinkerTest < Test::Unit::TestCase + def test_librarie + linker = Vaporware::Compiler::Linker.new(input: "tmp.o") + assert_false(linker.libpath.empty?, "should not be empty") + assert_false(linker.gcc_libpath.empty?, "should not be empty") + end + + def test_link_command + linker = Vaporware::Compiler::Linker.new(input: "tmp.o", output: "tmp") + assert_match(%r|mold -o tmp -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /.+/crt1.o /.+/crti.o /.+/crtbegin.o /.+/crtend.o /.+/libc.so /.+/crtn.o tmp.o|, linker.link_command) + linker = Vaporware::Compiler::Linker.new(input: "tmp.o", output: "tmp", shared: true) + assert_match(%r|mold -o tmp -m elf_x86_64 --shared /.+/crti.o /.+/crtbeginS.o /.+/crtendS.o /.+/libc.so /.+/crtn.o tmp.o|, linker.link_command) + end +end diff --git a/test/test_vaporware.rb b/test/vaporware/test_compiler.rb similarity index 97% rename from test/test_vaporware.rb rename to test/vaporware/test_compiler.rb index 44abf2e..4002f7c 100644 --- a/test/test_vaporware.rb +++ b/test/vaporware/test_compiler.rb @@ -1,7 +1,7 @@ require "vaporware" require "test/unit" -class VaporwareTest < Test::Unit::TestCase +class Vaporware::CompilerTest < Test::Unit::TestCase def tear_down = File.delete("tmp") rescue File.delete(@generated) def test_sample_plus @file = "sample/plus.rb" From 71abafa1c9ef83659e35965cd3e231510cb233bf Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 18 Sep 2023 12:37:38 +0900 Subject: [PATCH 15/58] replaced class files for elf sections --- lib/vaporware/compiler/assembler.rb | 25 +++------------ .../compiler/assembler/elf/section.rb | 18 +++++++++++ .../compiler/assembler/elf/section/bss.rb | 7 +++- .../compiler/assembler/elf/section_header.rb | 3 +- .../compiler/assembler/elf/sections.rb | 8 +++++ sig/vaporware/compiler/assembler.rbs | 2 ++ .../compiler/assembler/elf/section.rbs | 4 +++ .../compiler/assembler/elf/section/bss.rbs | 11 +++++++ .../compiler/assembler/elf/section/data.rbs | 11 +++++++ .../assembler/elf/section/shsymtab.rbs | 8 +++++ .../compiler/assembler/elf/section_header.rbs | 32 +++++++++++-------- .../compiler/assembler/elf/sections.rbs | 10 ++++++ 12 files changed, 102 insertions(+), 37 deletions(-) create mode 100644 lib/vaporware/compiler/assembler/elf/sections.rb create mode 100644 sig/vaporware/compiler/assembler/elf/section/bss.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/data.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/sections.rbs diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 447d349..7078af7 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -1,15 +1,7 @@ # frozen_string_literal: true require_relative "assembler/elf" require_relative "assembler/elf/header" -require_relative "assembler/elf/section" -require_relative "assembler/elf/section/text" -require_relative "assembler/elf/section/bss" -require_relative "assembler/elf/section/data" -require_relative "assembler/elf/section/note" -require_relative "assembler/elf/section/symtab" -require_relative "assembler/elf/section/strtab" -require_relative "assembler/elf/section/shsymtab" -require_relative "assembler/elf/section/shstrtab" +require_relative "assembler/elf/sections" require_relative "assembler/elf/section_header" class Vaporware::Compiler::Assembler @@ -18,20 +10,12 @@ def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(inpu def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocator, debug: false) @input, @output = input, output @elf_header = ELF::Header.new(type:) - @sections = { - null: { body: nil, header: ELF::SectionHeader.new.null! }, - text: { body: ELF::Section::Text.new, header: ELF::SectionHeader.new.text! }, - data: { body: ELF::Section::Data.new, header: ELF::SectionHeader.new.data! }, - bss: { body: ELF::Section::BSS.new, header: ELF::SectionHeader.new.bss! }, - note: { body: ELF::Section::Note.new.gnu_property!, header: ELF::SectionHeader.new.note! }, - symtab: { body: ELF::Section::Symtab.new, header: ELF::SectionHeader.new.symtab! }, - strtab: { body: ELF::Section::Strtab.new, header: ELF::SectionHeader.new.strtab! }, - shsymtab: { body: ELF::Section::Shsymtab.new, header: ELF::SectionHeader.new.shsymtab! }, - } + @assembler = assembler + @sections = Vaporware::Compiler::Assembler::ELF::Sections.new @debug = debug end - def assemble(assembler: "as", assembler_options: [], input: @input, output: @output, debug: false) + def assemble(assembler: @assembler, assembler_options: [], input: @input, output: @output, debug: false) if ["gcc", "as"].include?(assembler) IO.popen([assembler, *assembler_options, "-o", output, input].join(" ")).close else @@ -41,7 +25,6 @@ def assemble(assembler: "as", assembler_options: [], input: @input, output: @out end def obj_file = @output - private def to_elf(input: @input, output: @output, debug: false) f = File.open(output, "wb") read = { main: false } diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index 8f33433..e75f12a 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -1,2 +1,20 @@ +require_relative "section/text" +require_relative "section/bss" +require_relative "section/data" +require_relative "section/note" +require_relative "section/symtab" +require_relative "section/strtab" +require_relative "section/shsymtab" +require_relative "section/shstrtab" +require_relative "section_headers" + class Vaporware::Compiler::Assembler::ELF::Section + def initialize(type:) + type_string = type.to_s.capitalize + type_string = type_string.upcase if type_string == "Bss" + @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{type_string.downcase}!") + base = "Vaporware::Compiler::Assembler::ELF::Section" + eval_string = type_string == "Null" ? "#{base}.new" : "#{base}::#{type_string}.new" + @body = eval(eval_string) + end end diff --git a/lib/vaporware/compiler/assembler/elf/section/bss.rb b/lib/vaporware/compiler/assembler/elf/section/bss.rb index 6ced12c..5550a4d 100644 --- a/lib/vaporware/compiler/assembler/elf/section/bss.rb +++ b/lib/vaporware/compiler/assembler/elf/section/bss.rb @@ -1,6 +1,11 @@ class Vaporware::Compiler::Assembler::ELF::Section::BSS - + def initialize + end def build = bytes.flatten.pack("C*") + def set!() + self + end private def bytes = [] + def check(val, bytes) = false end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index de9ef98..e0a4c78 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -27,6 +27,7 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, @info = num2bytes(info, 4) if check(info, 4) @addralign = num2bytes(addralign, 8) if check(addralign, 8) @entsize = num2bytes(entsize, 8) if check(entsize, 8) + self end def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) @@ -39,7 +40,7 @@ def bss! = set! def shsymtab! = set! private - def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,] + def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,].then { |v| v.compact.size == v.size ? v : raise Vaporware::Error, "must be to fill all attributes" } def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse end diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb new file mode 100644 index 0000000..00686a8 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -0,0 +1,8 @@ +require_relative "section" + +class Vaporware::Compiler::Assembler::ELF::Sections + attr_reader %i|null text data bss note symtab strtab shsymtab| + def initialize + @null, @text, @data, @bss, @note, @symtab, @strtab, @shsymtab = %i|null text data bss note symtab strtab shsymtab|.map { |cn| Section.new(type: cn) } + end +end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index de96fdf..d334898 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -1,7 +1,9 @@ class Vaporware::Compiler::Assembler @input: String @output: String + @assembler: String @elf_header: Vaporware::Compiler::Assembler::ELF::Header + @sections: Hash[Symbol, Hash[Symbol, Object]] @debug: bool def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void diff --git a/sig/vaporware/compiler/assembler/elf/section.rbs b/sig/vaporware/compiler/assembler/elf/section.rbs index 8f33433..6225cc0 100644 --- a/sig/vaporware/compiler/assembler/elf/section.rbs +++ b/sig/vaporware/compiler/assembler/elf/section.rbs @@ -1,2 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section + @header: Vaporware::Compiler::Assembler::ELF::SectionHeader + @body: Vaporware::Compiler::Assembler::ELF::Section::Text | Vaporware::Compiler::Assembler::ELF::Section | Vaporware::Compiler::Assembler::ELF::Section::Data | Vaporware::Compiler::Assembler::ELF::Section::BSS | Vaporware::Compiler::Assembler::ELF::Section::Symtab | Vaporware::Compiler::Assembler::ELF::Section::Shsymtab | Vaporware::Compiler::Assembler::ELF::Section::Strtab | Vaporware::Compiler::Assembler::ELF::Section::Note + + def initialize: (type: (String | Symbol)) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section/bss.rbs b/sig/vaporware/compiler/assembler/elf/section/bss.rbs new file mode 100644 index 0000000..05b60d6 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/bss.rbs @@ -0,0 +1,11 @@ +class Vaporware::Compiler::Assembler::ELF::Section::BSS + attr_reader size: Integer + attr_reader offset: Integer + + def build: () -> String + def set!: () -> self + + private + def bytes: () -> Array[Array[Integer?]] + def check: (Integer, Integer) -> bool +end diff --git a/sig/vaporware/compiler/assembler/elf/section/data.rbs b/sig/vaporware/compiler/assembler/elf/section/data.rbs new file mode 100644 index 0000000..3a91458 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/data.rbs @@ -0,0 +1,11 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Data + PREFIX: Hash[Symbol, Integer] + REGISTER_CODE: Hash[Symbol, Integer] + OPECODE: Hash[Symbol, Integer] + + attr_reader bytes: Array[Integer] + attr_reader size: Integer + attr_reader offset: Integer + + def build: () -> String +end diff --git a/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs b/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs new file mode 100644 index 0000000..d2c5528 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs @@ -0,0 +1,8 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Shsymtab + attr_reader size: Integer + attr_reader offset: Integer + + def build: () -> String + def set!: () -> self + private def bytes: () -> Array[Array[Integer]?] +end diff --git a/sig/vaporware/compiler/assembler/elf/section_header.rbs b/sig/vaporware/compiler/assembler/elf/section_header.rbs index df5da89..a0da0ad 100644 --- a/sig/vaporware/compiler/assembler/elf/section_header.rbs +++ b/sig/vaporware/compiler/assembler/elf/section_header.rbs @@ -1,18 +1,22 @@ class Vaporware::Compiler::Assembler::ELF::SectionHeader - @name: Array[Integer] - @type: Array[Integer] - @flags: Array[Integer] - @addr: Array[Integer] - @offset: Array[Integer] - @size: Array[Integer] - @link: Array[Integer] - @info: Array[Integer] - @addralign: Array[Integer] - @entsize: Array[Integer] + @name: Array[Integer]? + @type: Array[Integer]? + @flags: Array[Integer]? + @addr: Array[Integer]? + @offset: Array[Integer]? + @size: Array[Integer]? + @link: Array[Integer]? + @info: Array[Integer]? + @addralign: Array[Integer]? + @entsize: Array[Integer]? def build: () -> String - def set!: (?name: Integer, ?type: Integer, ?flags: Integer, ?addr: Integer, ?offset: Integer, ?size: Integer, ?link: Integer, ?info: Integer, ?addralign: Integer, ?entsize: Integer) -> void - def null!: () -> void - def text!: () -> void - def note!: () -> void + def set!: (?name: Integer?, ?type: Integer?, ?flags: Integer?, ?addr: Integer?, ?offset: Integer?, ?size: Integer?, ?link: Integer?, ?info: Integer?, ?addralign: Integer?, ?entsize: Integer?) -> Vaporware::Compiler::Assembler::ELF::SectionHeader + def null!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader + def text!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader + def note!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader + + private def bytes: () -> Array[Array[Integer]] + private def check: ((Array[Integer] | Integer)?, Integer) -> bool + private def num2bytes: (Integer?, Integer) -> Array[Integer] end diff --git a/sig/vaporware/compiler/assembler/elf/sections.rbs b/sig/vaporware/compiler/assembler/elf/sections.rbs new file mode 100644 index 0000000..be48277 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/sections.rbs @@ -0,0 +1,10 @@ +class Vaporware::Compiler::Assembler::ELF::Sections + attr_reader null: Vaporware::Compiler::Assembler::ELF::Section + attr_reader text: Vaporware::Compiler::Assembler::ELF::Section + attr_reader data: Vaporware::Compiler::Assembler::ELF::Section + attr_reader bss: Vaporware::Compiler::Assembler::ELF::Section + attr_reader note: Vaporware::Compiler::Assembler::ELF::Section + attr_reader symtab: Vaporware::Compiler::Assembler::ELF::Section + attr_reader strtab: Vaporware::Compiler::Assembler::ELF::Section + attr_reader shsymtab: Vaporware::Compiler::Assembler::ELF::Section +end From 3294c2480abb9d3d2ea5b256e1febcfc43c351e8 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 23 Sep 2023 11:15:15 +0900 Subject: [PATCH 16/58] replace utils for common methods --- lib/vaporware/compiler/assembler.rb | 41 ++++++++++++------- lib/vaporware/compiler/assembler/elf.rb | 3 ++ .../compiler/assembler/elf/section.rb | 13 +++--- .../compiler/assembler/elf/section/bss.rb | 9 ++-- .../compiler/assembler/elf/section/data.rb | 4 +- .../compiler/assembler/elf/section/note.rb | 11 ++--- .../compiler/assembler/elf/section/null.rb | 7 ++++ .../assembler/elf/section/shstrtab.rb | 8 ++-- .../compiler/assembler/elf/section/strtab.rb | 1 + .../compiler/assembler/elf/section/symtab.rb | 6 +-- .../compiler/assembler/elf/section/text.rb | 15 ++----- .../compiler/assembler/elf/section_header.rb | 8 +--- .../compiler/assembler/elf/sections.rb | 18 +++++++- lib/vaporware/compiler/assembler/elf/utils.rb | 11 +++++ .../compiler/assembler/elf/section/null.rbs | 2 + .../compiler/assembler/elf/utils.rbs | 7 ++++ 16 files changed, 104 insertions(+), 60 deletions(-) create mode 100644 lib/vaporware/compiler/assembler/elf/section/null.rb create mode 100644 lib/vaporware/compiler/assembler/elf/utils.rb create mode 100644 sig/vaporware/compiler/assembler/elf/section/null.rbs create mode 100644 sig/vaporware/compiler/assembler/elf/utils.rbs diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 7078af7..253683f 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -11,7 +11,7 @@ def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as @input, @output = input, output @elf_header = ELF::Header.new(type:) @assembler = assembler - @sections = Vaporware::Compiler::Assembler::ELF::Sections.new + @sections = ELF::Sections.new @debug = debug end @@ -26,26 +26,39 @@ def assemble(assembler: @assembler, assembler_options: [], input: @input, output def obj_file = @output def to_elf(input: @input, output: @output, debug: false) + program_size = 0 + read(input:) + header = @elf_header.build + + offset = 0 + section_headers = [] + name = [] + bins = [] + @sections.each do |section| + name << section.name + bin = section.body.build + size = bin.bytesize + offset += size + section.body.align(bin, 8) + bins << bin + header = section.header + header.set!(offset:) + section_headers << header.build + end f = File.open(output, "wb") + ensure + f.close + f.path + end + + def read(input: @input) read = { main: false } - program_size = 0 - text = @sections[:text][:body] File.open(input, "r") do |r| r.each_line do |line| read[:main] = /main:/.match(line) unless read[:main] next unless read[:main] && !/main:/.match(line) - text.assemble!(line) + @sections.text.assemble!(line) end end - f.write(@elf_header.build) - bins = [] - section_headers = [] - @sections.values.map do |section| - bins << section[:body].build - section_headers << section[:header].build - end - - f.close - f.path end end diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb index c2c77ca..4dfe014 100644 --- a/lib/vaporware/compiler/assembler/elf.rb +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -3,6 +3,9 @@ class Compiler class Assembler class ELF class ERROR < StandardError; end + class Section; end + class SectionHeader; end + module Utils; end end end end diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index e75f12a..10e3fb1 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -2,19 +2,20 @@ require_relative "section/bss" require_relative "section/data" require_relative "section/note" +require_relative "section/null" require_relative "section/symtab" require_relative "section/strtab" -require_relative "section/shsymtab" require_relative "section/shstrtab" -require_relative "section_headers" +require_relative "section_header" class Vaporware::Compiler::Assembler::ELF::Section + attr_reader :header, :body, :name def initialize(type:) type_string = type.to_s.capitalize type_string = type_string.upcase if type_string == "Bss" - @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{type_string.downcase}!") - base = "Vaporware::Compiler::Assembler::ELF::Section" - eval_string = type_string == "Null" ? "#{base}.new" : "#{base}::#{type_string}.new" - @body = eval(eval_string) + section_name = type_string.downcase + @name = "\0.#{section_name}\0" + @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{section_name}!") + @body = Module.const_get("Vaporware::Compiler::Assembler::ELF::Section::#{type_string}").new end end diff --git a/lib/vaporware/compiler/assembler/elf/section/bss.rb b/lib/vaporware/compiler/assembler/elf/section/bss.rb index 5550a4d..942ce1b 100644 --- a/lib/vaporware/compiler/assembler/elf/section/bss.rb +++ b/lib/vaporware/compiler/assembler/elf/section/bss.rb @@ -1,10 +1,9 @@ class Vaporware::Compiler::Assembler::ELF::Section::BSS - def initialize - end + include Vaporware::Compiler::Assembler::ELF::Utils + def initialize = nil def build = bytes.flatten.pack("C*") - def set!() - self - end + def set! = self + private def bytes = [] def check(val, bytes) = false diff --git a/lib/vaporware/compiler/assembler/elf/section/data.rb b/lib/vaporware/compiler/assembler/elf/section/data.rb index 7b99987..fe6ad04 100644 --- a/lib/vaporware/compiler/assembler/elf/section/data.rb +++ b/lib/vaporware/compiler/assembler/elf/section/data.rb @@ -1,6 +1,8 @@ class Vaporware::Compiler::Assembler::ELF::Section::Data - + include Vaporware::Compiler::Assembler::ELF::Utils + def initialize = nil def build = bytes.flatten.pack("C*") + def set! = self private def bytes = [] end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index ea83ceb..e7d54fc 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -1,9 +1,11 @@ class Vaporware::Compiler::Assembler::ELF::Section::Note + include Vaporware::Compiler::Assembler::ELF::Utils def self.gnu_property note = new note.gnu_property! note.build end + def initialize @nsize = nil @dsize = nil @@ -22,14 +24,9 @@ def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) def gnu_property! = set!(nsize: 0x04, dsize: 0x20, type: 0x05, name: "GNU", desc: %w(02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00).map { |val| val.to_i(16) }) - def build = bytes.flatten.pack("C*") - private - def name!(name) = align!(@name = name.bytes, 4) - def desc!(desc) = align!(@desc = desc.is_a?(Array) ? desc : desc.bytes, 4) + def name!(name) = align(@name = name.bytes, 4) + def desc!(desc) = align(@desc = desc.is_a?(Array) ? desc : desc.bytes, 4) def bytes = [@nsize, @dsize, @type, @name, @desc] - def align!(val, bytes) = (val << 0 until val.size % bytes == 0) - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse - def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section/null.rb b/lib/vaporware/compiler/assembler/elf/section/null.rb new file mode 100644 index 0000000..ea49ce3 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/section/null.rb @@ -0,0 +1,7 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Null + include Vaporware::Compiler::Assembler::ELF::Utils + def initialize = nil + def build = bytes.flatten.pack("C*") + def set! = self + private def bytes = [] +end diff --git a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb index c7f75e4..a4d5700 100644 --- a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb @@ -1,6 +1,8 @@ -class Vaporware::Compiler::Assembler::ELF::Section::Strtab - +class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab + include Vaporware::Compiler::Assembler::ELF::Utils + def initialize = @strtab = [] def build = bytes.flatten.pack("C*") + def set!(name:) = @strtab << name private - def bytes = [] + def bytes = [@strtab] end diff --git a/lib/vaporware/compiler/assembler/elf/section/strtab.rb b/lib/vaporware/compiler/assembler/elf/section/strtab.rb index 61c0d0a..8253008 100644 --- a/lib/vaporware/compiler/assembler/elf/section/strtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/strtab.rb @@ -1,4 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::Section::Strtab + include Vaporware::Compiler::Assembler::ELF::Utils def initialize(names = "\0main\0") = @names = names def build = @names.bytes.pack("C*") end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index 6414755..d06f41f 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,4 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab + include Vaporware::Compiler::Assembler::ELF::Utils def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) @name = num2bytes(name, 4) @info = num2bytes(info, 1) @@ -18,9 +19,4 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) @value = num2bytes(value, 8) if check(value, 8) @size = num2bytes(size, 8) if check(size, 8) end - - private - def bytes = [@name, @info, @other, @shndx, @value, @size] - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse - def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) end diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 7fb713d..63a6d22 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -17,23 +17,16 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text SUB: 0x83, }.freeze - attr_reader :bytes, :size, :offset - - def initialize - @bytes = [] - @size = 0 - @offset = 0 - end + def initialize = @bytes = [] def assemble!(line) op, *operands = line.split(/\s+/).reject { |o| o.empty? }.map { |op| op.gsub(/,/, "") } @bytes << opecode(op, *operands) - @size += @bytes.last.bytesize end - def align!(bytes) - @bytes << [0x00] until @bytes.map(:bytesize).sum % bytes == 0 - end + def build = @bytes.flatten.pack("C*") + def size = build.bytesize + def align(val, bytes) = (val << [0] until @bytes.map(:bytesize).sum % bytes == 0) private diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index e0a4c78..727dce8 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -1,4 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::SectionHeader + include Vaporware::Compiler::Assembler::ELF::Utils def initialize @name = nil @type = nil @@ -33,14 +34,9 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) def text! = set!(flags: 0x06, addralign: 0x01) def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) - def data! = set! + def data! = set!() def symtab! = set! def strtab! = set! def bss! = set! def shsymtab! = set! - - private - def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize,].then { |v| v.compact.size == v.size ? v : raise Vaporware::Error, "must be to fill all attributes" } - def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse end diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 00686a8..293f93e 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -1,8 +1,22 @@ require_relative "section" class Vaporware::Compiler::Assembler::ELF::Sections - attr_reader %i|null text data bss note symtab strtab shsymtab| + ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| + attr_reader *ATTRIBUTES def initialize - @null, @text, @data, @bss, @note, @symtab, @strtab, @shsymtab = %i|null text data bss note symtab strtab shsymtab|.map { |cn| Section.new(type: cn) } + @null = Section.new(type: :null) + @text = Section.new(type: :text) + @data = Section.new(type: :data) + @bss = Section.new(type: :bss) + @note = Section.new(type: :note) + @symtab = Section.new(type: :symtab) + @strtab = Section.new(type: :strtab) + @shstrtab = Section.new(type: :shstrtab) + end + + def each(&block) + ATTRIBUTES.each do |t| + yield t + end end end diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb new file mode 100644 index 0000000..d02afd3 --- /dev/null +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -0,0 +1,11 @@ +module Vaporware::Compiler::Assembler::ELF::Utils + def build = bytes.flatten.pack("C*") + def size = build.bytesize + + private + + def align(val, bytes) = (val << 0 until val.size % bytes == 0) + def bytes = (raise Vaporware::Error, "should be implement this class") + def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse + def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) +end diff --git a/sig/vaporware/compiler/assembler/elf/section/null.rbs b/sig/vaporware/compiler/assembler/elf/section/null.rbs new file mode 100644 index 0000000..0bf63b3 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/null.rbs @@ -0,0 +1,2 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Null +end diff --git a/sig/vaporware/compiler/assembler/elf/utils.rbs b/sig/vaporware/compiler/assembler/elf/utils.rbs new file mode 100644 index 0000000..bb49b94 --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/utils.rbs @@ -0,0 +1,7 @@ +module Vaporware::Utils + def build: () -> String + private def align: (Array[Integer], Integer) -> void + private def check: (Array[Integer]?, Integer) -> bool + private def num2bytes: (Integer?, Integer) -> Array[Integer] + private def bytes: () -> Array[Array[Integer]?] +end From 32b9f23135ace01e646b68c23ed33d0c80cd6751 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 3 Oct 2023 21:06:51 +0900 Subject: [PATCH 17/58] fix name to Shstrtab --- sig/vaporware/compiler/assembler/elf/section.rbs | 2 +- sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs | 8 ++++++++ sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs | 8 -------- sig/vaporware/compiler/assembler/elf/sections.rbs | 4 +++- 4 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs delete mode 100644 sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs diff --git a/sig/vaporware/compiler/assembler/elf/section.rbs b/sig/vaporware/compiler/assembler/elf/section.rbs index 6225cc0..9ef883d 100644 --- a/sig/vaporware/compiler/assembler/elf/section.rbs +++ b/sig/vaporware/compiler/assembler/elf/section.rbs @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section @header: Vaporware::Compiler::Assembler::ELF::SectionHeader - @body: Vaporware::Compiler::Assembler::ELF::Section::Text | Vaporware::Compiler::Assembler::ELF::Section | Vaporware::Compiler::Assembler::ELF::Section::Data | Vaporware::Compiler::Assembler::ELF::Section::BSS | Vaporware::Compiler::Assembler::ELF::Section::Symtab | Vaporware::Compiler::Assembler::ELF::Section::Shsymtab | Vaporware::Compiler::Assembler::ELF::Section::Strtab | Vaporware::Compiler::Assembler::ELF::Section::Note + @body: Vaporware::Compiler::Assembler::ELF::Section::Text | Vaporware::Compiler::Assembler::ELF::Section::Null | Vaporware::Compiler::Assembler::ELF::Section::Data | Vaporware::Compiler::Assembler::ELF::Section::BSS | Vaporware::Compiler::Assembler::ELF::Section::Symtab | Vaporware::Compiler::Assembler::ELF::Section::Shstrtab | Vaporware::Compiler::Assembler::ELF::Section::Strtab | Vaporware::Compiler::Assembler::ELF::Section::Note def initialize: (type: (String | Symbol)) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs new file mode 100644 index 0000000..def9aee --- /dev/null +++ b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs @@ -0,0 +1,8 @@ +class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab + @strtab: Array[Integer] + + def build: () -> String + def set!: (name: String | Array[Integer]) -> self + private def bytes: () -> Array[Array[Integer]?] + private def set_name: (String | Array[Integer]) -> Array[Integer]? +end diff --git a/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs b/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs deleted file mode 100644 index d2c5528..0000000 --- a/sig/vaporware/compiler/assembler/elf/section/shsymtab.rbs +++ /dev/null @@ -1,8 +0,0 @@ -class Vaporware::Compiler::Assembler::ELF::Section::Shsymtab - attr_reader size: Integer - attr_reader offset: Integer - - def build: () -> String - def set!: () -> self - private def bytes: () -> Array[Array[Integer]?] -end diff --git a/sig/vaporware/compiler/assembler/elf/sections.rbs b/sig/vaporware/compiler/assembler/elf/sections.rbs index be48277..ea72751 100644 --- a/sig/vaporware/compiler/assembler/elf/sections.rbs +++ b/sig/vaporware/compiler/assembler/elf/sections.rbs @@ -6,5 +6,7 @@ class Vaporware::Compiler::Assembler::ELF::Sections attr_reader note: Vaporware::Compiler::Assembler::ELF::Section attr_reader symtab: Vaporware::Compiler::Assembler::ELF::Section attr_reader strtab: Vaporware::Compiler::Assembler::ELF::Section - attr_reader shsymtab: Vaporware::Compiler::Assembler::ELF::Section + attr_reader shstrtab: Vaporware::Compiler::Assembler::ELF::Section + + def each: (){ (Vaporware::Compiler::Assembler::ELF::Section) -> void } -> void end From 86cbe154066325d54e9e91e1342a7bb2fc6440aa Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 3 Oct 2023 21:08:01 +0900 Subject: [PATCH 18/58] fix replace variable name and use text body --- lib/vaporware/compiler/assembler.rb | 6 +++--- sig/vaporware/compiler/assembler.rbs | 6 +++--- sig/vaporware/compiler/assembler/elf/header.rbs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 253683f..fdd7c6e 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -28,7 +28,7 @@ def obj_file = @output def to_elf(input: @input, output: @output, debug: false) program_size = 0 read(input:) - header = @elf_header.build + elf_header = @elf_header.build offset = 0 section_headers = [] @@ -51,13 +51,13 @@ def to_elf(input: @input, output: @output, debug: false) f.path end - def read(input: @input) + def read(input: @input, text: @sections.text.body) read = { main: false } File.open(input, "r") do |r| r.each_line do |line| read[:main] = /main:/.match(line) unless read[:main] next unless read[:main] && !/main:/.match(line) - @sections.text.assemble!(line) + text.assemble!(line) end end end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index d334898..fc2bc7a 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -3,12 +3,12 @@ class Vaporware::Compiler::Assembler @output: String @assembler: String @elf_header: Vaporware::Compiler::Assembler::ELF::Header - @sections: Hash[Symbol, Hash[Symbol, Object]] + @sections: Vaporware::Compiler::Assembler::ELF::Sections @debug: bool def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void def assemble: (?assembler: String, ?assembler_options: Array[String] | [], ?input: String, ?output: String, ?debug: bool) -> String def obj_file: () -> String - - private def to_elf: (?input: String, ?output: String, ?debug: bool) -> void + def to_elf: (?input: String, ?output: String, ?debug: bool) -> void + def read: (?input: String, ?text: Vaporware::Compiler::Assembler::ELF::Section::Text) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/header.rbs b/sig/vaporware/compiler/assembler/elf/header.rbs index af8ceca..6580aef 100644 --- a/sig/vaporware/compiler/assembler/elf/header.rbs +++ b/sig/vaporware/compiler/assembler/elf/header.rbs @@ -19,7 +19,7 @@ class Vaporware::Compiler::Assembler::ELF::Header def initialize: (?endian: Symbol, ?type: Symbol, ?arche: Symbol) -> void def build: () -> String - def set!: (?entry: (nil | Integer), ?phoffset: (nil | Integer), ?shoffset: (nil | Integer), ?shnum: (nil | Integer), ?shstrndx: (nil | Integer)) -> void + def set!: (?entry: Integer?, ?phoffset: Integer?, ?shoffset: Integer?, ?shnum: Integer?, ?shstrndx: Integer?) -> void private def check: (Array[Integer] | Integer, Integer) -> bool From 3399f558dc081f08af835cece22d67b1e691ea9d Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 3 Oct 2023 21:13:24 +0900 Subject: [PATCH 19/58] add setter name for Shstrtab --- lib/vaporware/compiler/assembler.rb | 1 + lib/vaporware/compiler/assembler/elf.rb | 2 +- .../assembler/elf/section/shstrtab.rb | 25 ++++++++++++++++--- .../compiler/assembler/elf/section_header.rb | 2 +- .../compiler/assembler/elf/sections.rb | 16 ++++++------ sig/vaporware/compiler/assembler/elf.rbs | 2 ++ .../assembler/elf/section/shstrtab.rbs | 3 ++- .../compiler/assembler/elf/utils.rbs | 2 +- .../assembler/elf/section/test_shstrtab.rb | 14 +++++++++++ 9 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index fdd7c6e..3675cf2 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require_relative "assembler/elf" +require_relative "assembler/elf/utils" require_relative "assembler/elf/header" require_relative "assembler/elf/sections" require_relative "assembler/elf/section_header" diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb index 4dfe014..3040799 100644 --- a/lib/vaporware/compiler/assembler/elf.rb +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -2,7 +2,7 @@ module Vaporware class Compiler class Assembler class ELF - class ERROR < StandardError; end + class Error < StandardError; end class Section; end class SectionHeader; end module Utils; end diff --git a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb index a4d5700..f281a9d 100644 --- a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb @@ -1,8 +1,27 @@ class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab include Vaporware::Compiler::Assembler::ELF::Utils - def initialize = @strtab = [] + def initialize = @name = [] def build = bytes.flatten.pack("C*") - def set!(name:) = @strtab << name + def set!(name:) + @name << set(name) + self + end + + def set(name:) = name!(name) + private - def bytes = [@strtab] + def bytes = [@name] + def name!(name) + case name + when String + (name.match(/\A\0.+\0\z/) ? name : "\0#{name}\0").bytes + when Array + raise Vaporware::Compiler::Assembler::ELF::Error, "unaccepted type in Array" unless name.all? { |elem| elem.is_a?(Integer) } + n = name + n.unshift(0) && n.push(0) unless n.first == 0 && n.last == 0 + n + else + raise Vaporware::Compiler::Assembler::ELF::Error, "unsupported type" + end + end end diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index 727dce8..f5b366f 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -38,5 +38,5 @@ def data! = set!() def symtab! = set! def strtab! = set! def bss! = set! - def shsymtab! = set! + def shstrtab! = set! end diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 293f93e..8ee288d 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -4,14 +4,14 @@ class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| attr_reader *ATTRIBUTES def initialize - @null = Section.new(type: :null) - @text = Section.new(type: :text) - @data = Section.new(type: :data) - @bss = Section.new(type: :bss) - @note = Section.new(type: :note) - @symtab = Section.new(type: :symtab) - @strtab = Section.new(type: :strtab) - @shstrtab = Section.new(type: :shstrtab) + @null = Vaporware::Compiler::Assembler::ELF::Section.new(type: :null) + @text = Vaporware::Compiler::Assembler::ELF::Section.new(type: :text) + @data = Vaporware::Compiler::Assembler::ELF::Section.new(type: :data) + @bss = Vaporware::Compiler::Assembler::ELF::Section.new(type: :bss) + @note = Vaporware::Compiler::Assembler::ELF::Section.new(type: :note) + @symtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :symtab) + @strtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :strtab) + @shstrtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :shstrtab) end def each(&block) diff --git a/sig/vaporware/compiler/assembler/elf.rbs b/sig/vaporware/compiler/assembler/elf.rbs index 3c7d856..ba3e3cb 100644 --- a/sig/vaporware/compiler/assembler/elf.rbs +++ b/sig/vaporware/compiler/assembler/elf.rbs @@ -1,2 +1,4 @@ class Vaporware::Compiler::Assembler::ELF + class Error + end end diff --git a/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs index def9aee..f37a41e 100644 --- a/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs @@ -3,6 +3,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab def build: () -> String def set!: (name: String | Array[Integer]) -> self + def set: (name: String | Array[Integer]) -> Array[Integer]? private def bytes: () -> Array[Array[Integer]?] - private def set_name: (String | Array[Integer]) -> Array[Integer]? + private def name!: (String | Array[Integer]) -> Array[Integer]? end diff --git a/sig/vaporware/compiler/assembler/elf/utils.rbs b/sig/vaporware/compiler/assembler/elf/utils.rbs index bb49b94..8f123e9 100644 --- a/sig/vaporware/compiler/assembler/elf/utils.rbs +++ b/sig/vaporware/compiler/assembler/elf/utils.rbs @@ -1,4 +1,4 @@ -module Vaporware::Utils +module Vaporware::Compiler::Assembler::ELF::Utils def build: () -> String private def align: (Array[Integer], Integer) -> void private def check: (Array[Integer]?, Integer) -> bool diff --git a/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb new file mode 100644 index 0000000..ce55bd3 --- /dev/null +++ b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb @@ -0,0 +1,14 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::Assembler::ELF::Section::TestShstrtab < Test::Unit::TestCase + def setup = @shstrtab = Vaporware::Compiler::Assembler::ELF::Section::Shstrtab.new + def test_set_values + assert_equal(@shstrtab.set(name: "main"), [0, 109, 97, 105, 110, 0]) + assert_equal(@shstrtab.set(name: [0, 109, 97, 105, 110, 0]), [0, 109, 97, 105, 110, 0]) + end + def test_alert_values + assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set(name: :main) } + assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set(name: 123) } + end +end From 87608ea75dbad42bc98b82ee94300d3d8c8eeff1 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 8 Oct 2023 11:19:52 +0900 Subject: [PATCH 20/58] fill section headers binary --- lib/vaporware/compiler/assembler.rb | 2 +- lib/vaporware/compiler/assembler/elf/section_header.rb | 2 ++ lib/vaporware/compiler/assembler/elf/sections.rb | 4 ++-- lib/vaporware/compiler/assembler/elf/utils.rb | 9 +++++---- .../compiler/assembler/elf/section_header.rbs | 2 +- sig/vaporware/compiler/assembler/elf/sections.rbs | 1 + .../compiler/assembler/elf/test_section_header.rb | 10 ++++++++++ 7 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 test/vaporware/compiler/assembler/elf/test_section_header.rb diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 3675cf2..911d735 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -40,7 +40,7 @@ def to_elf(input: @input, output: @output, debug: false) bin = section.body.build size = bin.bytesize offset += size - section.body.align(bin, 8) + bin = section.body.align(bin, 8) bins << bin header = section.header header.set!(offset:) diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index f5b366f..d0f9465 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -39,4 +39,6 @@ def symtab! = set! def strtab! = set! def bss! = set! def shstrtab! = set! + + private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize] end diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 8ee288d..180fb53 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -15,8 +15,8 @@ def initialize end def each(&block) - ATTRIBUTES.each do |t| - yield t + (ATTRIBUTES - [:shstrtab]).each do |t| + yield send(t) end end end diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index d02afd3..3a100a3 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -1,11 +1,12 @@ module Vaporware::Compiler::Assembler::ELF::Utils def build = bytes.flatten.pack("C*") def size = build.bytesize + def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement this class") private - def align(val, bytes) = (val << 0 until val.size % bytes == 0) - def bytes = (raise Vaporware::Error, "should be implement this class") - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.reverse - def check(val, bytes) = (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) + def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement this class") + def num2bytes(val, bytes) = bites(val, bytes*2).reverse + def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (bites(val, byte*2).size == bytes)) + def hexes(val, hex) = ("%0#{hex}x" % val).split(/.{1,#{hex}/).map { |v| v.to_i(16) } end diff --git a/sig/vaporware/compiler/assembler/elf/section_header.rbs b/sig/vaporware/compiler/assembler/elf/section_header.rbs index a0da0ad..98829a2 100644 --- a/sig/vaporware/compiler/assembler/elf/section_header.rbs +++ b/sig/vaporware/compiler/assembler/elf/section_header.rbs @@ -16,7 +16,7 @@ class Vaporware::Compiler::Assembler::ELF::SectionHeader def text!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader def note!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader - private def bytes: () -> Array[Array[Integer]] + private def bytes: () -> Array[Array[Integer]?] private def check: ((Array[Integer] | Integer)?, Integer) -> bool private def num2bytes: (Integer?, Integer) -> Array[Integer] end diff --git a/sig/vaporware/compiler/assembler/elf/sections.rbs b/sig/vaporware/compiler/assembler/elf/sections.rbs index ea72751..edc8e2a 100644 --- a/sig/vaporware/compiler/assembler/elf/sections.rbs +++ b/sig/vaporware/compiler/assembler/elf/sections.rbs @@ -1,4 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::Sections + ATTRIBUTES: Array[Symbol] attr_reader null: Vaporware::Compiler::Assembler::ELF::Section attr_reader text: Vaporware::Compiler::Assembler::ELF::Section attr_reader data: Vaporware::Compiler::Assembler::ELF::Section diff --git a/test/vaporware/compiler/assembler/elf/test_section_header.rb b/test/vaporware/compiler/assembler/elf/test_section_header.rb new file mode 100644 index 0000000..e2822ce --- /dev/null +++ b/test/vaporware/compiler/assembler/elf/test_section_header.rb @@ -0,0 +1,10 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::Assembler::ELF::SectionHeaderTest < Test::Unit::TestCase + def setup = @section_header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new + def test_null! + assert(@section_header.null!) + assert_equal(@section_header.build, [[0] * 4, [0] * 4, [0] * 8, [0] * 8, [0] * 8, [0] * 8, [0] * 4, [0] * 4, [0] * 8, [0] * 8].flatten.pack("C*")) + end +end From e26363a37451ea9ffebf49393c2b6ea31447a1fc Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 17 Oct 2023 21:15:57 +0900 Subject: [PATCH 21/58] use directive for Assembler::ELF --- sig/vaporware/compiler/assembler.rbs | 10 +++++++--- .../compiler/assembler/elf/section.rbs | 7 +++++-- .../compiler/assembler/elf/section_header.rbs | 8 +++++--- .../compiler/assembler/elf/sections.rbs | 18 ++++++++++-------- sig/vaporware/compiler/assembler/elf/utils.rbs | 5 +++-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index fc2bc7a..05d3174 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -1,14 +1,18 @@ +use Vaporware::Compiler::Assembler::ELF::Header +use Vaporware::Compiler::Assembler::ELF::Sections +use Vaporware::Compiler::Assembler::ELF::Section::Text + class Vaporware::Compiler::Assembler @input: String @output: String @assembler: String - @elf_header: Vaporware::Compiler::Assembler::ELF::Header - @sections: Vaporware::Compiler::Assembler::ELF::Sections + @elf_header: Header + @sections: Sections @debug: bool def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void def assemble: (?assembler: String, ?assembler_options: Array[String] | [], ?input: String, ?output: String, ?debug: bool) -> String def obj_file: () -> String def to_elf: (?input: String, ?output: String, ?debug: bool) -> void - def read: (?input: String, ?text: Vaporware::Compiler::Assembler::ELF::Section::Text) -> void + def read: (?input: String, ?text: Text) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section.rbs b/sig/vaporware/compiler/assembler/elf/section.rbs index 9ef883d..bcbf638 100644 --- a/sig/vaporware/compiler/assembler/elf/section.rbs +++ b/sig/vaporware/compiler/assembler/elf/section.rbs @@ -1,6 +1,9 @@ +use Vaporware::Compiler::Assembler::ELF::SectionHeader +use Vaporware::Compiler::Assembler::ELF::Section::* + class Vaporware::Compiler::Assembler::ELF::Section - @header: Vaporware::Compiler::Assembler::ELF::SectionHeader - @body: Vaporware::Compiler::Assembler::ELF::Section::Text | Vaporware::Compiler::Assembler::ELF::Section::Null | Vaporware::Compiler::Assembler::ELF::Section::Data | Vaporware::Compiler::Assembler::ELF::Section::BSS | Vaporware::Compiler::Assembler::ELF::Section::Symtab | Vaporware::Compiler::Assembler::ELF::Section::Shstrtab | Vaporware::Compiler::Assembler::ELF::Section::Strtab | Vaporware::Compiler::Assembler::ELF::Section::Note + @header: SectionHeader + @body: Text | Null | Data | BSS | Symtab | Shstrtab | Strtab | Note def initialize: (type: (String | Symbol)) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section_header.rbs b/sig/vaporware/compiler/assembler/elf/section_header.rbs index 98829a2..b057212 100644 --- a/sig/vaporware/compiler/assembler/elf/section_header.rbs +++ b/sig/vaporware/compiler/assembler/elf/section_header.rbs @@ -1,3 +1,5 @@ +use Vaporware::Compiler::Assembler::ELF + class Vaporware::Compiler::Assembler::ELF::SectionHeader @name: Array[Integer]? @type: Array[Integer]? @@ -12,9 +14,9 @@ class Vaporware::Compiler::Assembler::ELF::SectionHeader def build: () -> String def set!: (?name: Integer?, ?type: Integer?, ?flags: Integer?, ?addr: Integer?, ?offset: Integer?, ?size: Integer?, ?link: Integer?, ?info: Integer?, ?addralign: Integer?, ?entsize: Integer?) -> Vaporware::Compiler::Assembler::ELF::SectionHeader - def null!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader - def text!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader - def note!: () -> Vaporware::Compiler::Assembler::ELF::SectionHeader + def null!: () -> ELF::SectionHeader + def text!: () -> ELF::SectionHeader + def note!: () -> ELF::SectionHeader private def bytes: () -> Array[Array[Integer]?] private def check: ((Array[Integer] | Integer)?, Integer) -> bool diff --git a/sig/vaporware/compiler/assembler/elf/sections.rbs b/sig/vaporware/compiler/assembler/elf/sections.rbs index edc8e2a..85264b9 100644 --- a/sig/vaporware/compiler/assembler/elf/sections.rbs +++ b/sig/vaporware/compiler/assembler/elf/sections.rbs @@ -1,13 +1,15 @@ +use Vaporware::Compiler::Assembler::ELF::Section + class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES: Array[Symbol] - attr_reader null: Vaporware::Compiler::Assembler::ELF::Section - attr_reader text: Vaporware::Compiler::Assembler::ELF::Section - attr_reader data: Vaporware::Compiler::Assembler::ELF::Section - attr_reader bss: Vaporware::Compiler::Assembler::ELF::Section - attr_reader note: Vaporware::Compiler::Assembler::ELF::Section - attr_reader symtab: Vaporware::Compiler::Assembler::ELF::Section - attr_reader strtab: Vaporware::Compiler::Assembler::ELF::Section - attr_reader shstrtab: Vaporware::Compiler::Assembler::ELF::Section + attr_reader null: Section + attr_reader text: Section + attr_reader data: Section + attr_reader bss: Section + attr_reader note: Section + attr_reader symtab: Section + attr_reader strtab: Section + attr_reader shstrtab: Section def each: (){ (Vaporware::Compiler::Assembler::ELF::Section) -> void } -> void end diff --git a/sig/vaporware/compiler/assembler/elf/utils.rbs b/sig/vaporware/compiler/assembler/elf/utils.rbs index 8f123e9..e412f0e 100644 --- a/sig/vaporware/compiler/assembler/elf/utils.rbs +++ b/sig/vaporware/compiler/assembler/elf/utils.rbs @@ -1,7 +1,8 @@ module Vaporware::Compiler::Assembler::ELF::Utils def build: () -> String private def align: (Array[Integer], Integer) -> void - private def check: (Array[Integer]?, Integer) -> bool - private def num2bytes: (Integer?, Integer) -> Array[Integer] + private def check: ((Array[Integer] | Integer)?, Integer) -> bool + private def num2bytes: (Integer, Integer) -> Array[Integer] private def bytes: () -> Array[Array[Integer]?] + private def hexes: (Integer, Integer) -> Array[Array[Integer]?] end From 0b488d40f8919bb5c6220d9399456b98440640f8 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 17 Oct 2023 21:16:31 +0900 Subject: [PATCH 22/58] reset hex align 2 times for bytes --- lib/vaporware/compiler/assembler/elf/utils.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 3a100a3..1f742ac 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -6,7 +6,7 @@ def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be impleme private def align(val, bytes) = (val << 0 until val.size % bytes == 0) def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement this class") - def num2bytes(val, bytes) = bites(val, bytes*2).reverse - def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (bites(val, byte*2).size == bytes)) - def hexes(val, hex) = ("%0#{hex}x" % val).split(/.{1,#{hex}/).map { |v| v.to_i(16) } + def num2bytes(val, bytes) = hexas(val, bytes*2).reverse + def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, byte*2).size == bytes)) + def hexas(val, hex) = ("%0#{hex}x" % val).split(/.{1,2/).map { |v| v.to_i(16) } end From f6344f8eba13f4bb0a46a50730d2235d007530e6 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 12 Nov 2023 09:46:36 +0900 Subject: [PATCH 23/58] set class name for exceptions --- lib/vaporware/compiler/assembler/elf/utils.rb | 6 +++--- sig/vaporware/compiler/assembler/elf/utils.rbs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 1f742ac..4878934 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -1,12 +1,12 @@ module Vaporware::Compiler::Assembler::ELF::Utils def build = bytes.flatten.pack("C*") def size = build.bytesize - def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement this class") + def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") private def align(val, bytes) = (val << 0 until val.size % bytes == 0) - def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement this class") + def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") def num2bytes(val, bytes) = hexas(val, bytes*2).reverse - def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, byte*2).size == bytes)) + def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes*2).size == bytes))) def hexas(val, hex) = ("%0#{hex}x" % val).split(/.{1,2/).map { |v| v.to_i(16) } end diff --git a/sig/vaporware/compiler/assembler/elf/utils.rbs b/sig/vaporware/compiler/assembler/elf/utils.rbs index e412f0e..b0f5ba5 100644 --- a/sig/vaporware/compiler/assembler/elf/utils.rbs +++ b/sig/vaporware/compiler/assembler/elf/utils.rbs @@ -1,8 +1,10 @@ +use Vaporware::Compiler::Assembler::ELF::Error + module Vaporware::Compiler::Assembler::ELF::Utils def build: () -> String private def align: (Array[Integer], Integer) -> void private def check: ((Array[Integer] | Integer)?, Integer) -> bool private def num2bytes: (Integer, Integer) -> Array[Integer] private def bytes: () -> Array[Array[Integer]?] - private def hexes: (Integer, Integer) -> Array[Array[Integer]?] + private def hexas: (Integer, Integer) -> Array[Integer] end From 1f30ecd9c5815b07218e579f7eec0e64ea8986f1 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 14 Nov 2023 21:03:19 +0900 Subject: [PATCH 24/58] fix split to scan in hexas method --- lib/vaporware/compiler/assembler/elf/utils.rb | 6 +++--- .../vaporware/compiler/assembler/elf/test_section_header.rb | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 4878934..1d06a04 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -6,7 +6,7 @@ def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be impleme private def align(val, bytes) = (val << 0 until val.size % bytes == 0) def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") - def num2bytes(val, bytes) = hexas(val, bytes*2).reverse - def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes*2).size == bytes))) - def hexas(val, hex) = ("%0#{hex}x" % val).split(/.{1,2/).map { |v| v.to_i(16) } + def num2bytes(val, bytes) = hexas(val, bytes).reverse + def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes).size == bytes))) + def hexas(val, hex) = ("%0#{hex*2}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.then { |list| list.unshift(0) until list.size >= hex; list } end diff --git a/test/vaporware/compiler/assembler/elf/test_section_header.rb b/test/vaporware/compiler/assembler/elf/test_section_header.rb index e2822ce..6d282ea 100644 --- a/test/vaporware/compiler/assembler/elf/test_section_header.rb +++ b/test/vaporware/compiler/assembler/elf/test_section_header.rb @@ -5,6 +5,7 @@ class Vaporware::Compiler::Assembler::ELF::SectionHeaderTest < Test::Unit::TestC def setup = @section_header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new def test_null! assert(@section_header.null!) + assert_equal(@section_header.build.size, 64) assert_equal(@section_header.build, [[0] * 4, [0] * 4, [0] * 8, [0] * 8, [0] * 8, [0] * 8, [0] * 4, [0] * 4, [0] * 8, [0] * 8].flatten.pack("C*")) end end From 2be5249b0782717f7f0d67915aa1eca13b5a1a8c Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 30 Dec 2023 00:14:21 +0900 Subject: [PATCH 25/58] set null for unused section's section header --- lib/vaporware/compiler/assembler/elf/section_header.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index d0f9465..ac7b559 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -34,10 +34,10 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) def text! = set!(flags: 0x06, addralign: 0x01) def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) - def data! = set!() + def data! = null! def symtab! = set! def strtab! = set! - def bss! = set! + def bss! = null! def shstrtab! = set! private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize] From 02a4f1873c71f3aaf564c84f4b9a70eae2a8a731 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 2 Jan 2024 02:31:22 +0900 Subject: [PATCH 26/58] add :BEGIN node for ruby 3.3 --- lib/vaporware/compiler/generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index 3526dfd..9c59e5f 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -157,7 +157,7 @@ def to_asm(node, output, method_tree = false) when :LIT output.puts " push #{node.children.last}" return - when :LIST, :BLOCK + when :LIST, :BLOCK, :BEGIN node.children.each { |n| to_asm(n, output, method_tree) } return when :SCOPE From 603d84880321c873d506b288ac8d7e5c58154643 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Wed, 3 Jan 2024 23:30:58 +0900 Subject: [PATCH 27/58] check debug log --- test/vaporware/test_compiler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 4002f7c..f4f241e 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -5,7 +5,7 @@ class Vaporware::CompilerTest < Test::Unit::TestCase def tear_down = File.delete("tmp") rescue File.delete(@generated) def test_sample_plus @file = "sample/plus.rb" - @vaporware = Vaporware::Compiler.compile(@file) + @vaporware = Vaporware::Compiler.compile(@file, debug: true) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(9, exit_code) From 95c3ee7d28ccde4fe178e1698b19a1c3ae83fb75 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 7 Jan 2024 23:13:34 +0900 Subject: [PATCH 28/58] change default assembler gcc to (g)as --- lib/vaporware/compiler.rb | 13 +++++++------ lib/vaporware/compiler/linker.rb | 3 ++- test/vaporware/test_compiler.rb | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index a3b1f7f..5ee940a 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -5,17 +5,18 @@ require_relative "compiler/linker" class Vaporware::Compiler - def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) - s = new(input: source, output: dest, debug:, shared:) - s.compile(compiler_options:) - s.assemble(input: dest.to_s + ".s", assembler: "as", debug:) - s.link + attr_reader *%i(generator assembler linker) + + def self.compile(source, assembler: "as", linker: "ld", dest: "tmp", debug: false, compiler_options: ["-O0"], shared: false) + compiler = new(input: source, output: dest, debug:, shared:, linker:, assembler:) + compiler.compile(compiler_options:) + compiler.assemble(input: dest.to_s + ".s", assembler:, debug:) + compiler.link end def initialize(input:, output: File.basename(input, ".*"), linker: "ld", assembler: "as", debug: false, shared: false) @generator = Vaporware::Compiler::Generator.new(input:, output: output + ".s", debug:, shared:) @assembler = Vaporware::Compiler::Assembler.new(input: @generator.precompile, output: output + ".o", assembler:, debug:) - output = "lib#{output}.so" if shared && output !~ /^lib.+\.so$/ @linker = Vaporware::Compiler::Linker.new(input: @assembler.obj_file, output:, linker:, debug:, shared:) end diff --git a/lib/vaporware/compiler/linker.rb b/lib/vaporware/compiler/linker.rb index 2614300..2c436e5 100644 --- a/lib/vaporware/compiler/linker.rb +++ b/lib/vaporware/compiler/linker.rb @@ -10,7 +10,7 @@ def initialize(input:, output: "a.out", linker: "mold", linker_options: [], shar def link(input: @input, output: @output, debug: @debug, shared: @shared) = IO.popen(link_command).close - def link_command + def link_command(input: @input, output: @output, debug: @debug, shared: @shared) ld_path = [] if @shared ld_path << "--shared" @@ -26,6 +26,7 @@ def link_command # for not static compile ld_path << "#{gcc_libpath}/crtend.o" end + ld_path << "#{libpath}/libc.so" ld_path << "#{libpath}/crtn.o" cmd = [@linker, "-o", @output, "-m", "elf_x86_64", *@options, *ld_path, @input].join(' ') diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index f4f241e..7e6e278 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -51,7 +51,7 @@ def test_sample_while def test_sample_call_method @generated = "libtmp.so" @file = "sample/method.rb" - @vaporware = Vaporware::Compiler.compile(@file, shared: true) + @vaporware = Vaporware::Compiler.compile(@file, dest: "./libtmp.so", shared: true) require './sample/fiddle.rb' assert_equal(10, X.aibo) end From b8291c2773ae6d40d4d9c7f3a1c6372b05010983 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 8 Jan 2024 23:00:00 +0900 Subject: [PATCH 29/58] fix option description using = for args value --- exe/vaporware | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exe/vaporware b/exe/vaporware index 8baab68..e2d639b 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -4,11 +4,12 @@ require "vaporware" require "optparse" opt = OptionParser.new options = {} -opt.on("-c", "--compiler [VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v } +opt.on("-c", "--compiler[=VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v } opt.on("-D", "--debug") { |v| options[:debug] = v } -opt.on("-o", "--objects [VAL]") { |v| options[:dest] = v } +opt.on("-o", "--objects[=VAL]") { |v| options[:dest] = v } opt.on("--compiler-options[=VAL]", "compiler options") { |v| options[:compiler_options] = v.split(",") } -opt.on("-s", "--shared") { |v| options[:shared] = v } +opt.on("-s", "--shared-library") { |v| options[:shared] = v } +opt.on("-l", "--linker[=VAL]", "selecting linker: gold, lld, and mold.") { |v| options[:linker] = v } begin opt.parse!(ARGV) From 9c225d2ef5b0adf44998b2543218538baa42fea8 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 11 Feb 2024 13:22:14 +0900 Subject: [PATCH 30/58] notify elf header fields error and add elf header test --- lib/vaporware/compiler/assembler/elf/header.rb | 13 +++++-------- lib/vaporware/compiler/assembler/elf/utils.rb | 2 ++ sig/vaporware/compiler/assembler/elf/utils.rbs | 3 +++ .../compiler/assembler/elf/test_header.rb | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 test/vaporware/compiler/assembler/elf/test_header.rb diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb index 0b92e06..94bf366 100644 --- a/lib/vaporware/compiler/assembler/elf/header.rb +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -1,4 +1,7 @@ +require_relative "../elf" + class Vaporware::Compiler::Assembler::ELF::Header + include Vaporware::Compiler::Assembler::ELF::Utils IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze @@ -20,9 +23,7 @@ def initialize(endian: :littel, type: :reloacatable, arch: :amd64) end def build - unless [@entry, @phoffset, @shoffset, @shentsize, @shnum, @shstrndx].all? - raise Vaporware::Assembler::ELF::ERROR - end + raise Vaporware::Compiler::Assembler::ELF::Error, "input for the following variables: #{empties}" unless empties.empty? bytes.flatten.pack("C*") end @@ -31,15 +32,11 @@ def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil) @phoffset = num2bytes(phoffset, 8) if check(phoffset, 8) @shoffset = num2bytes(shoffset, 8) if check(shoffset, 8) @shnum = num2bytes(shnum, 4) if check(shnum, 4) - @shstrndx = num2bytes(shstrndex, 4) if check(shstrndx, 4) + @shstrndx = num2bytes(shstrndx, 4) if check(shstrndx, 4) end private - def check(val, bytes) = - (val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || val.is_a?(Integer) - - def num2bytes(val, bytes) = ("%0#{bytes}x" % val).scan(/.{1,2}/).map { |x| x.to_i(16) }.reverse def bytes = [ @e_ident, @type, @machine, @version, @entry, @phoffset, @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 1d06a04..86e5469 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -2,10 +2,12 @@ module Vaporware::Compiler::Assembler::ELF::Utils def build = bytes.flatten.pack("C*") def size = build.bytesize def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") + def empties = must_be_filled_section_fields private def align(val, bytes) = (val << 0 until val.size % bytes == 0) def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") + def must_be_filled_section_fields = instance_variables.reject { |i| instance_variable_get(i) } def num2bytes(val, bytes) = hexas(val, bytes).reverse def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes).size == bytes))) def hexas(val, hex) = ("%0#{hex*2}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.then { |list| list.unshift(0) until list.size >= hex; list } diff --git a/sig/vaporware/compiler/assembler/elf/utils.rbs b/sig/vaporware/compiler/assembler/elf/utils.rbs index b0f5ba5..eb4bb36 100644 --- a/sig/vaporware/compiler/assembler/elf/utils.rbs +++ b/sig/vaporware/compiler/assembler/elf/utils.rbs @@ -2,9 +2,12 @@ use Vaporware::Compiler::Assembler::ELF::Error module Vaporware::Compiler::Assembler::ELF::Utils def build: () -> String + def size: () -> Integer + def empties: () -> Array[Symbol] private def align: (Array[Integer], Integer) -> void private def check: ((Array[Integer] | Integer)?, Integer) -> bool private def num2bytes: (Integer, Integer) -> Array[Integer] private def bytes: () -> Array[Array[Integer]?] private def hexas: (Integer, Integer) -> Array[Integer] + private def must_be_filled_section_fields: () -> Array[Symbol] end diff --git a/test/vaporware/compiler/assembler/elf/test_header.rb b/test/vaporware/compiler/assembler/elf/test_header.rb new file mode 100644 index 0000000..05fb486 --- /dev/null +++ b/test/vaporware/compiler/assembler/elf/test_header.rb @@ -0,0 +1,15 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::Assembler::ELF::HeaderTest < Test::Unit::TestCase + def setup = @elf_header = Vaporware::Compiler::Assembler::ELF::Header.new + def test_filled_fields + assert_equal(@elf_header.empties, [:@entry, :@phoffset, :@shoffset, :@shnum, :@shstrndx]) + @elf_header.set!(shoffset: 1) + assert_equal(@elf_header.empties, [:@entry, :@phoffset, :@shnum, :@shstrndx]) + end + + def test_build_elf_header + # TODO: ELF Header Section binary + end +end From 4fbd0f886fe47b92d79088153238314eea2c9648 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 2 Mar 2024 22:28:12 +0900 Subject: [PATCH 31/58] add test for note section --- .../assembler/elf/section/test_note.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/vaporware/compiler/assembler/elf/section/test_note.rb diff --git a/test/vaporware/compiler/assembler/elf/section/test_note.rb b/test/vaporware/compiler/assembler/elf/section/test_note.rb new file mode 100644 index 0000000..49c249e --- /dev/null +++ b/test/vaporware/compiler/assembler/elf/section/test_note.rb @@ -0,0 +1,18 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::Assembler::ELF::Section::NoteTest < Test::Unit::TestCase + def reference_binary = "\x04\x00\x00\x00 \x00\x00\x00\x05\x00\x00\x00GNU\x00\x02\x00\x01\xC0\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\xC0\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00".force_encoding("ASCII-8BIT") + def setup = @note = Vaporware::Compiler::Assembler::ELF::Section::Note.new + + def test_gnu_property! + assert_equal( + Vaporware::Compiler::Assembler::ELF::Section::Note.gnu_property, + reference_binary + ) + assert_equal( + @note.gnu_property!.build, + reference_binary + ) + end +end From de8705c3e620b13af274006419a64f325c40b3f9 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 2 Mar 2024 22:28:34 +0900 Subject: [PATCH 32/58] fix note implementation --- .../compiler/assembler/elf/section/note.rb | 1 + lib/vaporware/compiler/assembler/elf/utils.rb | 9 ++++++--- .../compiler/assembler/elf/section/note.rbs | 18 ++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index e7d54fc..c63349c 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -20,6 +20,7 @@ def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) @type = num2bytes(type, 4) if check(type, 4) @name = name!(name) if name @desc = desc!(desc) if desc + self end def gnu_property! = set!(nsize: 0x04, dsize: 0x20, type: 0x05, name: "GNU", desc: %w(02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00).map { |val| val.to_i(16) }) diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 86e5469..344e9e0 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -1,12 +1,15 @@ module Vaporware::Compiler::Assembler::ELF::Utils def build = bytes.flatten.pack("C*") def size = build.bytesize - def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") + def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implementing #{self.class}") def empties = must_be_filled_section_fields private - def align(val, bytes) = (val << 0 until val.size % bytes == 0) - def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implement #{self.class}") + def align(val, bytes) + val << 0 until val.size % bytes == 0 + val + end + def bytes = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implementing #{self.class}") def must_be_filled_section_fields = instance_variables.reject { |i| instance_variable_get(i) } def num2bytes(val, bytes) = hexas(val, bytes).reverse def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes).size == bytes))) diff --git a/sig/vaporware/compiler/assembler/elf/section/note.rbs b/sig/vaporware/compiler/assembler/elf/section/note.rbs index 15d6fca..1bb7af6 100644 --- a/sig/vaporware/compiler/assembler/elf/section/note.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/note.rbs @@ -7,15 +7,21 @@ class Vaporware::Compiler::Assembler::ELF::Section::Note @name: Array[Integer]? @desc: Array[Integer]? - def set!: (?nsize: Integer?, ?dsize: Integer?, ?type: Integer?, ?name: Integer?, ?desc: Integer?) -> void + def set!: ( + ?nsize: Integer?, + ?dsize: Integer?, + ?type: Integer?, + ?name: (Integer | Array[Integer] | String)?, + ?desc: (Integer | Array[Integer] | String)? + ) -> self def gnu_property!: () -> void def build: () -> String private - def name!: (String) -> void - def desc!: (String) -> void + def name!: ((Integer | Array[Integer] | String)?) -> Array[Integer] + def desc!: ((String | Array[Integer])?) -> Array[Integer] def bytes: () -> Array[Array[Integer] | nil] - def align!: (Array[Integer], Integer) -> void - def num2bytes: (Integer, Integer) -> Array[Integer] - def check: (Integer | Array[Integer], Integer) -> bool + def align!: (Array[Integer], Integer) -> Array[Integer] + def num2bytes: (Integer?, Integer) -> Array[Integer] + def check: ((Integer | Array[Integer])?, Integer) -> bool end From dce57ef497380f0d415181157f29ed568b18034b Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 2 Mar 2024 22:47:27 +0900 Subject: [PATCH 33/58] add test for shstrtab and fix implementation --- .../compiler/assembler/elf/section/shstrtab.rb | 11 +++-------- .../compiler/assembler/elf/section/shstrtab.rbs | 2 +- .../compiler/assembler/elf/section/test_shstrtab.rb | 10 ++++++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb index f281a9d..f214def 100644 --- a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb @@ -2,19 +2,14 @@ class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab include Vaporware::Compiler::Assembler::ELF::Utils def initialize = @name = [] def build = bytes.flatten.pack("C*") - def set!(name:) - @name << set(name) - self - end - - def set(name:) = name!(name) + def set!(name:) = (@name << name!(name); self) private - def bytes = [@name] + def bytes = [@name, [0]] def name!(name) case name when String - (name.match(/\A\0.+\0\z/) ? name : "\0#{name}\0").bytes + (name.match(/\A\0\..+\z/) ? name : "\0.#{name}").bytes when Array raise Vaporware::Compiler::Assembler::ELF::Error, "unaccepted type in Array" unless name.all? { |elem| elem.is_a?(Integer) } n = name diff --git a/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs index f37a41e..cebdb11 100644 --- a/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/shstrtab.rbs @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab @strtab: Array[Integer] - + def initialize: () -> void def build: () -> String def set!: (name: String | Array[Integer]) -> self def set: (name: String | Array[Integer]) -> Array[Integer]? diff --git a/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb index ce55bd3..3fae396 100644 --- a/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb +++ b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb @@ -4,11 +4,13 @@ class Vaporware::Compiler::Assembler::ELF::Section::TestShstrtab < Test::Unit::TestCase def setup = @shstrtab = Vaporware::Compiler::Assembler::ELF::Section::Shstrtab.new def test_set_values - assert_equal(@shstrtab.set(name: "main"), [0, 109, 97, 105, 110, 0]) - assert_equal(@shstrtab.set(name: [0, 109, 97, 105, 110, 0]), [0, 109, 97, 105, 110, 0]) + binary = "\x00.main\x00".force_encoding("ASCII-8BIT") + @shstrtab.set!(name: "main") + + assert_equal(@shstrtab.build, binary) end def test_alert_values - assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set(name: :main) } - assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set(name: 123) } + assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set!(name: :main) } + assert_raise(Vaporware::Compiler::Assembler::ELF::Error) { @shstrtab.set!(name: 123) } end end From b0fe9f919777a917f5d3ddf3f22c65201cc12cac Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 2 Mar 2024 22:48:55 +0900 Subject: [PATCH 34/58] fix types for implementations --- sig/vaporware/compiler.rbs | 4 ++-- .../compiler/assembler/elf/section/symtab.rbs | 14 +++++++------- .../compiler/assembler/elf/section/text.rbs | 4 +++- sig/vaporware/compiler/assembler/elf/sections.rbs | 1 + 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index 7402c62..4363803 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -4,8 +4,8 @@ class Vaporware::Compiler DYN_LD_PATH: String SHARED_LD_PATH: String # class methods - def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> void - def initialize: (input: String, ?output: String, ?debug: bool, ?shared: bool) -> void + def self.compile: (String, ?assembler: String, ?linker: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?linker_options: Array[String], ?shared: bool) -> void + def initialize: (input: String, ?output: String, ?assembler: String, ?linker: String, ?debug: bool, ?shared: bool) -> void @generator: Vaporware::Compiler::Generator @assembler: Vaporware::Compiler::Assembler diff --git a/sig/vaporware/compiler/assembler/elf/section/symtab.rbs b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs index 9d6159f..1d4c584 100644 --- a/sig/vaporware/compiler/assembler/elf/section/symtab.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs @@ -6,14 +6,14 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab @value: Array[Integer] @size: Array[Integer] + def initialize: () -> void def set!: (?name: Integer?, ?info: Integer?, ?other: Integer?, ?shndx: Integer?, ?value: Integer?, ?size: Integer?) -> void def build: () -> String - private - def name!: (String) -> void - def desc!: (String) -> void - def bytes: () -> Array[Array[Integer]?] - def align!: (Array[Integer], Integer) -> void - def num2bytes: (Integer, Integer) -> Array[Integer] - def check: (Integer | Array[Integer], Integer) -> bool + private def name!: (String) -> void + private def desc!: (String) -> void + private def bytes: () -> Array[Array[Integer]?] + private def align!: (Array[Integer], Integer) -> void + private def num2bytes: (Integer, Integer) -> Array[Integer] + private def check: (Integer | Array[Integer], Integer) -> bool end diff --git a/sig/vaporware/compiler/assembler/elf/section/text.rbs b/sig/vaporware/compiler/assembler/elf/section/text.rbs index bfbee83..26926ac 100644 --- a/sig/vaporware/compiler/assembler/elf/section/text.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/text.rbs @@ -3,10 +3,12 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text REGISTER_CODE: Hash[Symbol, Integer] OPECODE: Hash[Symbol, Integer] - attr_reader bytes: Array[Integer] + @bytes: Array[untyped]? + attr_reader size: Integer attr_reader offset: Integer + def initialize: () -> void def assemble!: (String) -> void def align!: (Integer) -> void diff --git a/sig/vaporware/compiler/assembler/elf/sections.rbs b/sig/vaporware/compiler/assembler/elf/sections.rbs index 85264b9..a31365f 100644 --- a/sig/vaporware/compiler/assembler/elf/sections.rbs +++ b/sig/vaporware/compiler/assembler/elf/sections.rbs @@ -11,5 +11,6 @@ class Vaporware::Compiler::Assembler::ELF::Sections attr_reader strtab: Section attr_reader shstrtab: Section + def initialize: () -> void def each: (){ (Vaporware::Compiler::Assembler::ELF::Section) -> void } -> void end From d17933e7fea0f74ccbe4e464f3b7e6f09a551d34 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 2 Mar 2024 22:51:56 +0900 Subject: [PATCH 35/58] add shstrtab for twice --- .../compiler/assembler/elf/section/test_shstrtab.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb index 3fae396..55c2df9 100644 --- a/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb +++ b/test/vaporware/compiler/assembler/elf/section/test_shstrtab.rb @@ -4,9 +4,11 @@ class Vaporware::Compiler::Assembler::ELF::Section::TestShstrtab < Test::Unit::TestCase def setup = @shstrtab = Vaporware::Compiler::Assembler::ELF::Section::Shstrtab.new def test_set_values - binary = "\x00.main\x00".force_encoding("ASCII-8BIT") - @shstrtab.set!(name: "main") - + binary = "\x00.text\x00".force_encoding("ASCII-8BIT") + @shstrtab.set!(name: "text") + assert_equal(@shstrtab.build, binary) + binary = "\x00.text\x00.strtab\x00".force_encoding("ASCII-8BIT") + @shstrtab.set!(name: "strtab") assert_equal(@shstrtab.build, binary) end def test_alert_values From ce320e5bc55951adf0f96115b758dd6e2a27a343 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 9 Mar 2024 16:53:04 +0900 Subject: [PATCH 36/58] don't understanding of elf --- lib/vaporware/compiler/assembler/elf/section/shsymtab.rb | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 lib/vaporware/compiler/assembler/elf/section/shsymtab.rb diff --git a/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb b/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb deleted file mode 100644 index 2e0401f..0000000 --- a/lib/vaporware/compiler/assembler/elf/section/shsymtab.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Vaporware::Compiler::Assembler::ELF::Section::Shsymtab -end From ea9d844bc36e25b80a9953389ed6c7140a87aa8e Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 11 Mar 2024 09:14:12 +0900 Subject: [PATCH 37/58] add test for null section symtab --- .../compiler/assembler/elf/section/symtab.rb | 19 ++++++++++--------- .../assembler/elf/section/test_symtab.rb | 10 ++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 test/vaporware/compiler/assembler/elf/section/test_symtab.rb diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index d06f41f..b316e5d 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,16 +1,14 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab include Vaporware::Compiler::Assembler::ELF::Utils - def initialize(name: 0, info: 0, other: 0, shndx: 0, value: 0, size: 0) - @name = num2bytes(name, 4) - @info = num2bytes(info, 1) - @other = num2bytes(other, 1) - @shndx = num2bytes(shndx, 4) - @value = num2bytes(value, 8) - @size = num2bytes(size, 8) + def initialize + @name = num2bytes(0, 4) + @info = num2bytes(0, 1) + @other = num2bytes(0, 1) + @shndx = num2bytes(0, 2) + @value = num2bytes(0, 8) + @size = num2bytes(0, 8) end - def build = bytes.flatten.pack("C*") - def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) @name = num2bytes(name, 4) if check(name, 4) @info = num2bytes(info, 1) if check(info, 4) @@ -19,4 +17,7 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) @value = num2bytes(value, 8) if check(value, 8) @size = num2bytes(size, 8) if check(size, 8) end + + private + def bytes = [@name, @info, @other, @shndx, @value, @size] end diff --git a/test/vaporware/compiler/assembler/elf/section/test_symtab.rb b/test/vaporware/compiler/assembler/elf/section/test_symtab.rb new file mode 100644 index 0000000..01bb13b --- /dev/null +++ b/test/vaporware/compiler/assembler/elf/section/test_symtab.rb @@ -0,0 +1,10 @@ +require "vaporware" +require "test/unit" + +class Vaporware::Compiler::Assembler::ELF::Section::TestSymtab < Test::Unit::TestCase + def setup = @symtab = Vaporware::Compiler::Assembler::ELF::Section::Symtab.new + def test_set_values + binary = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding("ASCII-8BIT") + assert_equal(@symtab.build.size, binary.size) + end +end From b286897bc1c06f3dfdf16397e5832dfd69f99147 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 4 May 2024 13:08:28 +0900 Subject: [PATCH 38/58] update rbs collection --- rbs_collection.lock.yaml | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/rbs_collection.lock.yaml b/rbs_collection.lock.yaml index 621039e..7335a6e 100644 --- a/rbs_collection.lock.yaml +++ b/rbs_collection.lock.yaml @@ -1,10 +1,4 @@ --- -sources: -- type: git - name: ruby/gem_rbs_collection - revision: 28208148c7e64a25e9b86b9723b4c3a2cef14e81 - remote: https://github.com/ruby/gem_rbs_collection.git - repo_dir: gems path: ".gem_rbs_collection" gems: - name: ast @@ -12,7 +6,27 @@ gems: source: type: git name: ruby/gem_rbs_collection - revision: 12631190e75c631eca0908ecc8c03bcfd78c0c99 + revision: 1663d48383d01e0088cf23206ef8662e48212e00 + remote: https://github.com/ruby/gem_rbs_collection.git + repo_dir: gems +- name: fileutils + version: '0' + source: + type: stdlib +- name: parser + version: '3.2' + source: + type: git + name: ruby/gem_rbs_collection + revision: 1663d48383d01e0088cf23206ef8662e48212e00 + remote: https://github.com/ruby/gem_rbs_collection.git + repo_dir: gems +- name: rake + version: '13.0' + source: + type: git + name: ruby/gem_rbs_collection + revision: 1663d48383d01e0088cf23206ef8662e48212e00 remote: https://github.com/ruby/gem_rbs_collection.git repo_dir: gems gemfile_lock_path: Gemfile.lock From c33fe2f3e93b2698e674aa0519c915edb1c9b11b Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 5 May 2024 08:48:42 +0900 Subject: [PATCH 39/58] set name bytes --- .../compiler/assembler/elf/section/symtab.rb | 14 +++++++++++++- .../compiler/assembler/elf/section/symtab.rbs | 7 ++++--- .../assembler/elf/section/test_symtab.rb | 17 ++++++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index b316e5d..ef53e11 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -10,7 +10,7 @@ def initialize end def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) - @name = num2bytes(name, 4) if check(name, 4) + @name = name2bytes(name, 4) if check(name, 4) @info = num2bytes(info, 1) if check(info, 4) @other = num2bytes(other, 1) if check(other, 1) @shndx = num2bytes(shndx, 4) if check(shndx, 4) @@ -20,4 +20,16 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) private def bytes = [@name, @info, @other, @shndx, @value, @size] + def name2bytes(name, bytes) + case name + when String + name.bytes.reverse + when Array + name[0..bytes] + when Integer + num2bytes(name, bytes) + else + [0] * bytes + end + end end diff --git a/sig/vaporware/compiler/assembler/elf/section/symtab.rbs b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs index 1d4c584..369ff34 100644 --- a/sig/vaporware/compiler/assembler/elf/section/symtab.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/symtab.rbs @@ -1,5 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab - @name: Array[Integer] + @name: Array[Integer]? @info: Array[Integer] @other: Array[Integer] @shndx: Array[Integer] @@ -14,6 +14,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab private def desc!: (String) -> void private def bytes: () -> Array[Array[Integer]?] private def align!: (Array[Integer], Integer) -> void - private def num2bytes: (Integer, Integer) -> Array[Integer] - private def check: (Integer | Array[Integer], Integer) -> bool + private def num2bytes: (Integer?, Integer) -> Array[Integer] + private def check: ((String | Integer | Array[Integer])?, Integer) -> bool + private def name2bytes: ((String | Integer | Array[Integer])?, Integer) -> Array[Integer]? end diff --git a/test/vaporware/compiler/assembler/elf/section/test_symtab.rb b/test/vaporware/compiler/assembler/elf/section/test_symtab.rb index 01bb13b..ba296b3 100644 --- a/test/vaporware/compiler/assembler/elf/section/test_symtab.rb +++ b/test/vaporware/compiler/assembler/elf/section/test_symtab.rb @@ -3,8 +3,19 @@ class Vaporware::Compiler::Assembler::ELF::Section::TestSymtab < Test::Unit::TestCase def setup = @symtab = Vaporware::Compiler::Assembler::ELF::Section::Symtab.new - def test_set_values - binary = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding("ASCII-8BIT") - assert_equal(@symtab.build.size, binary.size) + + def test_default_values + binary = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding("ASCII-8BIT") + assert_equal(@symtab.build, binary) + end + + def test_main_value + binary = "\x01\x00\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding("ASCII-8BIT") + @symtab.set!(name: [1, 0, 2, 1]) + assert_equal(@symtab.build, binary) + @symtab.set!(name: 16908289) + assert_equal(@symtab.build, binary) + @symtab.set!(name: "\x01\x00\x02\x01".force_encoding("ASCII-8BIT")) + assert_equal(@symtab.build, binary) end end From 150a7676f57fb40b57332ca725f3ba7e48ce6ce8 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 21 Jul 2024 15:19:35 +0900 Subject: [PATCH 40/58] forget adding & for method calling --- lib/vaporware/compiler/assembler/elf/section/text.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 63a6d22..c150574 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -26,7 +26,7 @@ def assemble!(line) def build = @bytes.flatten.pack("C*") def size = build.bytesize - def align(val, bytes) = (val << [0] until @bytes.map(:bytesize).sum % bytes == 0) + def align(val, bytes) = (val << [0] until @bytes.map(&:bytesize).sum % bytes == 0) private From 7e388637024293cb0e51f75a8ad14ae6cdb64d74 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 22 Jul 2024 10:53:26 +0900 Subject: [PATCH 41/58] remove hand assemble sources from each --- lib/vaporware/compiler/assembler/elf/sections.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 180fb53..7e12e8d 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -2,6 +2,7 @@ class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| + HAND_ASSEMBLES = %i|text shstrtab| attr_reader *ATTRIBUTES def initialize @null = Vaporware::Compiler::Assembler::ELF::Section.new(type: :null) @@ -14,9 +15,5 @@ def initialize @shstrtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :shstrtab) end - def each(&block) - (ATTRIBUTES - [:shstrtab]).each do |t| - yield send(t) - end - end + def each(&block) = (ATTRIBUTES - HAND_ASSEMBLES).each { |t| yield send(t) } end From 872a78051ab2a1565cebe1d6834c43362c29f9cd Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Wed, 7 Aug 2024 09:38:49 +0900 Subject: [PATCH 42/58] set keyword arguments for section classes --- lib/vaporware/compiler/assembler/elf/section.rb | 4 ++-- lib/vaporware/compiler/assembler/elf/section/bss.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/data.rb | 5 ++--- lib/vaporware/compiler/assembler/elf/section/note.rb | 3 ++- lib/vaporware/compiler/assembler/elf/section/null.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/shstrtab.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/strtab.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/symtab.rb | 2 +- lib/vaporware/compiler/assembler/elf/section/text.rb | 2 +- lib/vaporware/compiler/assembler/elf/section_header.rb | 4 ++-- lib/vaporware/compiler/assembler/elf/sections.rb | 5 +++-- 11 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index 10e3fb1..f3ff502 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -10,12 +10,12 @@ class Vaporware::Compiler::Assembler::ELF::Section attr_reader :header, :body, :name - def initialize(type:) + def initialize(type:, options: {}) type_string = type.to_s.capitalize type_string = type_string.upcase if type_string == "Bss" section_name = type_string.downcase @name = "\0.#{section_name}\0" @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{section_name}!") - @body = Module.const_get("Vaporware::Compiler::Assembler::ELF::Section::#{type_string}").new + @body = Module.const_get("Vaporware::Compiler::Assembler::ELF::Section::#{type_string}").new(**options) end end diff --git a/lib/vaporware/compiler/assembler/elf/section/bss.rb b/lib/vaporware/compiler/assembler/elf/section/bss.rb index 942ce1b..cc30d58 100644 --- a/lib/vaporware/compiler/assembler/elf/section/bss.rb +++ b/lib/vaporware/compiler/assembler/elf/section/bss.rb @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section::BSS include Vaporware::Compiler::Assembler::ELF::Utils - def initialize = nil + def initialize(**opts) = nil def build = bytes.flatten.pack("C*") def set! = self diff --git a/lib/vaporware/compiler/assembler/elf/section/data.rb b/lib/vaporware/compiler/assembler/elf/section/data.rb index fe6ad04..1b5b4c3 100644 --- a/lib/vaporware/compiler/assembler/elf/section/data.rb +++ b/lib/vaporware/compiler/assembler/elf/section/data.rb @@ -1,8 +1,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Data include Vaporware::Compiler::Assembler::ELF::Utils - def initialize = nil + def initialize(**opts) = nil def build = bytes.flatten.pack("C*") def set! = self - private - def bytes = [] + private def bytes = [] end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index c63349c..0550158 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -6,12 +6,13 @@ def self.gnu_property note.build end - def initialize + def initialize(type: nil) @nsize = nil @dsize = nil @type = nil @name = nil @desc = nil + gnu_property! if type == :gnu end def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) diff --git a/lib/vaporware/compiler/assembler/elf/section/null.rb b/lib/vaporware/compiler/assembler/elf/section/null.rb index ea49ce3..3ff5c42 100644 --- a/lib/vaporware/compiler/assembler/elf/section/null.rb +++ b/lib/vaporware/compiler/assembler/elf/section/null.rb @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section::Null include Vaporware::Compiler::Assembler::ELF::Utils - def initialize = nil + def initialize(**opts) = nil def build = bytes.flatten.pack("C*") def set! = self private def bytes = [] diff --git a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb index f214def..e5ec246 100644 --- a/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/shstrtab.rb @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section::Shstrtab include Vaporware::Compiler::Assembler::ELF::Utils - def initialize = @name = [] + def initialize(**opts) = @name = [] def build = bytes.flatten.pack("C*") def set!(name:) = (@name << name!(name); self) diff --git a/lib/vaporware/compiler/assembler/elf/section/strtab.rb b/lib/vaporware/compiler/assembler/elf/section/strtab.rb index 8253008..eb57289 100644 --- a/lib/vaporware/compiler/assembler/elf/section/strtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/strtab.rb @@ -1,5 +1,5 @@ class Vaporware::Compiler::Assembler::ELF::Section::Strtab include Vaporware::Compiler::Assembler::ELF::Utils - def initialize(names = "\0main\0") = @names = names + def initialize(names = "\0main\0", **opts) = @names = names def build = @names.bytes.pack("C*") end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index ef53e11..a607e9f 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,6 +1,6 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab include Vaporware::Compiler::Assembler::ELF::Utils - def initialize + def initialize(**opts) @name = num2bytes(0, 4) @info = num2bytes(0, 1) @other = num2bytes(0, 1) diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index c150574..3f1777b 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -17,7 +17,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text SUB: 0x83, }.freeze - def initialize = @bytes = [] + def initialize(**opts) = @bytes = [] def assemble!(line) op, *operands = line.split(/\s+/).reject { |o| o.empty? }.map { |op| op.gsub(/,/, "") } diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index ac7b559..8223c56 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -34,10 +34,10 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) def text! = set!(flags: 0x06, addralign: 0x01) def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) - def data! = null! + def data! = null! # unsupported in the current implementation def symtab! = set! def strtab! = set! - def bss! = null! + def bss! = null! #unsupported in the current implementation def shstrtab! = set! private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize] diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 7e12e8d..1a7506f 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -2,14 +2,15 @@ class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| - HAND_ASSEMBLES = %i|text shstrtab| + HAND_ASSEMBLES = %i|text shstrtab note| attr_reader *ATTRIBUTES + def initialize @null = Vaporware::Compiler::Assembler::ELF::Section.new(type: :null) @text = Vaporware::Compiler::Assembler::ELF::Section.new(type: :text) @data = Vaporware::Compiler::Assembler::ELF::Section.new(type: :data) @bss = Vaporware::Compiler::Assembler::ELF::Section.new(type: :bss) - @note = Vaporware::Compiler::Assembler::ELF::Section.new(type: :note) + @note = Vaporware::Compiler::Assembler::ELF::Section.new(type: :note, options: {type: :gnu}) @symtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :symtab) @strtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :strtab) @shstrtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :shstrtab) From 3b23c2e0663809fa6a773354fe4f664f595d3de2 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 10 Aug 2024 20:20:01 +0900 Subject: [PATCH 43/58] add integration test --- test/vaporware/compiler/test_assembler.rb | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/vaporware/compiler/test_assembler.rb diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb new file mode 100644 index 0000000..f7ab25f --- /dev/null +++ b/test/vaporware/compiler/test_assembler.rb @@ -0,0 +1,49 @@ +require "vaporware" +require "test/unit" +require "tempfile" + +class Vaporware::Compiler::Assembler::ELFTest < Test::Unit::TestCase + def reference_binary = "" + + def test_to_elf + input_file = Tempfile.open(["amd64.s"]) + input_file.puts <<~AMD64ASM + .intel_syntax noprefix + .globl main +main: + push rbp + mov rbp, rsp + sub rsp, 0 + push 1 + push 2 + pop rdi + pop rax + add rax, rdi + push rax + push 3 + pop rdi + pop rax + imul rax, rdi + push rax + push 5 + push 4 + pop rdi + pop rax + sub rax, rdi + push rax + pop rdi + pop rax + cqo + idiv rdi + push rax + mov rsp, rbp + pop rbp + ret + AMD64ASM + input = input_file.path + + assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") + assembler.to_elf + binding.irb + end +end From a10bd59dbcba5b9887b4a15769bbeeffff174f95 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 12 Aug 2024 00:54:08 +0900 Subject: [PATCH 44/58] tmp --- lib/vaporware/compiler/assembler.rb | 12 ++++-------- lib/vaporware/compiler/assembler/elf/sections.rb | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 911d735..e64d8ee 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -29,27 +29,23 @@ def obj_file = @output def to_elf(input: @input, output: @output, debug: false) program_size = 0 read(input:) - elf_header = @elf_header.build offset = 0 section_headers = [] - name = [] + names = [] bins = [] @sections.each do |section| - name << section.name + names << name = section.name bin = section.body.build size = bin.bytesize offset += size - bin = section.body.align(bin, 8) bins << bin header = section.header header.set!(offset:) section_headers << header.build end - f = File.open(output, "wb") - ensure - f.close - f.path + @elf_header.set!(phoffset: offset, shnum: 6, entry: 3, shstrndx: 6, shoffset: offset) + [@elf_header.build, *bins] end def read(input: @input, text: @sections.text.body) diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 1a7506f..4afcffc 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -2,7 +2,7 @@ class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| - HAND_ASSEMBLES = %i|text shstrtab note| + HAND_ASSEMBLES = %i|text shstrtab note symtab strtab| attr_reader *ATTRIBUTES def initialize From 2d3681169efab176e989275ce24c3dd4ee1e97e7 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Tue, 13 Aug 2024 04:58:45 +0900 Subject: [PATCH 45/58] check header from integration file --- lib/vaporware/compiler/assembler.rb | 3 +- .../compiler/assembler/elf/header.rb | 29 +++++++++---------- test/vaporware/compiler/test_assembler.rb | 22 +++++++++++++- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index e64d8ee..5a190dc 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -8,7 +8,7 @@ class Vaporware::Compiler::Assembler def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(input:, output:).assemble - def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocator, debug: false) + def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocatable, debug: false) @input, @output = input, output @elf_header = ELF::Header.new(type:) @assembler = assembler @@ -44,7 +44,6 @@ def to_elf(input: @input, output: @output, debug: false) header.set!(offset:) section_headers << header.build end - @elf_header.set!(phoffset: offset, shnum: 6, entry: 3, shstrndx: 6, shoffset: offset) [@elf_header.build, *bins] end diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb index 94bf366..f663f6c 100644 --- a/lib/vaporware/compiler/assembler/elf/header.rb +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -2,30 +2,27 @@ class Vaporware::Compiler::Assembler::ELF::Header include Vaporware::Compiler::Assembler::ELF::Utils - IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ].freeze + IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].freeze ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze - def initialize(endian: :littel, type: :reloacatable, arch: :amd64) - @e_ident = IDENT - @type = ELF_FILE_TYPE[elf(type)] - @arch = arch(arch) + def initialize(endian: :littel, type: :rel, arc: :amd64) + @ident = IDENT + @type = num2bytes(ELF_FILE_TYPE[elf(type)], 2) + @arch = arch(arc) @version = num2bytes(1, 4) - @entry = nil - @phoffset = nil - @shoffset = nil - @flags = num2bytes(0, 4) + @entry = num2bytes(0x00, 8) + @phoffset = num2bytes(0x00, 8) + @shoffset = num2bytes(0x0220, 8) + @flags = num2bytes(0x00, 4) @ehsize = num2bytes(0x40, 2) @phsize = num2bytes(0x00, 2) @phnum = num2bytes(0x00, 2) @shentsize = num2bytes(0x40, 2) - @shnum = nil - @shstrndx = nil + @shnum = num2bytes(0x08, 2) + @shstrndx = num2bytes(0x07, 2) end - def build - raise Vaporware::Compiler::Assembler::ELF::Error, "input for the following variables: #{empties}" unless empties.empty? - bytes.flatten.pack("C*") - end + def build = bytes.flatten.pack("C*") def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil) @entry = num2bytes(entry, 8) if check(entry, 8) @@ -38,7 +35,7 @@ def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil) private def bytes = [ - @e_ident, @type, @machine, @version, @entry, @phoffset, + @ident, @type, @arch, @version, @entry, @phoffset, @shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize, @shnum, @shstrndx ] diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index f7ab25f..7cfa601 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -43,7 +43,27 @@ def test_to_elf input = input_file.path assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") - assembler.to_elf + header, null, text, data, bss, note, symtab, strtab, shstrtab = assembler.to_elf + r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab = readelf binding.irb + assert_equal(r_header, header.unpack("C*")) + assert_equal(r_null, null.unpack("C*")) + assert_equal(r_data, data.unpack("C*")) + assert_equal(r_text, text.unpack("C*")) + assert_equal(r_bss, bss.unpack("C*")) + end + + def readelf + [ + [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], + [], + [85, 72, 137, 229, 72, 131, 236, 32, 72, 137, 232, 72, 131, 232, 8, 80, 106, 0, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 16, 80, 106, 1, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 24, 80, 72, 137, 232, 72, 131, 232, 8, 80, 88, 72, 139, 0, 80, 72], + [], + [], + [137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 106, 1, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 88, 72, 139, 0, 80, 106, 9, 95, 88, 72, 57, 248, 15, 156, 192, 72, 15, 182, 192, 80, 88, 80, 72, 131, 248, 0, 15, 132, 144, 0, 0, 0, 72, 137,232, 72, 131, 232, 8, 80, 72, 137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 16, 80, 72, 137, 232, 72, 131, 232, 24, 80, 88, 72, 139, 0, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 24, 80, 72, 137, 232, 72, 131, 232, 8, 80, 88, 72, 139, 0, 80, 72, 137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 72, 137, 232, 72, 131, 232, 32, 80, 88, 72, 139, 0, 80, 106, 1, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 233, 72, 255, 255, 255, 72, 137, 232, 72, 131, 232, 24, 80, 88, 72, 139, 0, 80, 72, 137, 236, 93, 195, 0, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 46, 115, 121, 109, 116], + [97, 98, 0, 46, 115, 116], + [114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0, 46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ] end end From f523e05071f8d31fd1b0b5c1227d611e3f0db33e Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Mon, 19 Aug 2024 14:37:21 +0900 Subject: [PATCH 46/58] create test assembler file --- test/vaporware/compiler/amd64.s | 31 +++++++++++ test/vaporware/compiler/test_assembler.rb | 68 ++++++++--------------- 2 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 test/vaporware/compiler/amd64.s diff --git a/test/vaporware/compiler/amd64.s b/test/vaporware/compiler/amd64.s new file mode 100644 index 0000000..5a725d2 --- /dev/null +++ b/test/vaporware/compiler/amd64.s @@ -0,0 +1,31 @@ + .intel_syntax noprefix + .globl main +main: + push rbp + mov rbp, rsp + sub rsp, 0 + push 1 + push 2 + pop rdi + pop rax + add rax, rdi + push rax + push 3 + pop rdi + pop rax + imul rax, rdi + push rax + push 5 + push 4 + pop rdi + pop rax + sub rax, rdi + push rax + pop rdi + pop rax + cqo + idiv rdi + push rax + mov rsp, rbp + pop rbp + ret diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index 7cfa601..4da39d5 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -6,64 +6,44 @@ class Vaporware::Compiler::Assembler::ELFTest < Test::Unit::TestCase def reference_binary = "" def test_to_elf - input_file = Tempfile.open(["amd64.s"]) - input_file.puts <<~AMD64ASM - .intel_syntax noprefix - .globl main -main: - push rbp - mov rbp, rsp - sub rsp, 0 - push 1 - push 2 - pop rdi - pop rax - add rax, rdi - push rax - push 3 - pop rdi - pop rax - imul rax, rdi - push rax - push 5 - push 4 - pop rdi - pop rax - sub rax, rdi - push rax - pop rdi - pop rax - cqo - idiv rdi - push rax - mov rsp, rbp - pop rbp - ret - AMD64ASM - input = input_file.path + input = __dir__ + "/amd64.s" assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") - header, null, text, data, bss, note, symtab, strtab, shstrtab = assembler.to_elf - r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab = readelf - binding.irb + header, null, text, data, bss, note, symtab, strtab, shstrtab, *section_headers = assembler.to_elf + sh_null, sh_text, sh_data, sh_bss, sh_note, shsymtab, sh_strtab, sh_shstrtab = section_headers + r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab, *r_section_headers = readelf + r_sh_null, r_sh_text, * = r_section_headers assert_equal(r_header, header.unpack("C*")) assert_equal(r_null, null.unpack("C*")) assert_equal(r_data, data.unpack("C*")) - assert_equal(r_text, text.unpack("C*")) + # remove alignment bytes + assert_equal(r_text, text.unpack("C*")[..-7]) assert_equal(r_bss, bss.unpack("C*")) + assert_equal(r_note, note.unpack("C*")) + assert_equal(r_symtab, symtab.unpack("C*")) + assert_equal(r_strtab, strtab.unpack("C*")) + ref_shstrtab = r_shstrtab.pack("C*").split("\0").select { |str| str.size > 0 }.sort + assert(ref_shstrtab.zip(shstrtab.split("\0").select { |str| str.size > 0 }.sort).all? { |ref, act| ref =~ /#{act}/ }) + assert_equal(r_sh_null, sh_null.unpack("C*")) + assert_equal(r_sh_text, sh_text.unpack("C*")) end def readelf [ [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], [], - [85, 72, 137, 229, 72, 131, 236, 32, 72, 137, 232, 72, 131, 232, 8, 80, 106, 0, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 16, 80, 106, 1, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 24, 80, 72, 137, 232, 72, 131, 232, 8, 80, 88, 72, 139, 0, 80, 72], + [85, 72, 137, 229, 72, 131, 236, 0, 106, 1, 106, 2, 95, 88, 72, 1, 248, 80, 106, 3, 95, 88, 72, 15, 175, 199, 80, 106,5, 106, 4, 95, 88, 72, 41, 248, 80, 95, 88, 72, 153, 72, 247, 255, 80, 72, 137, 236, 93, 195], [], [], - [137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 106, 1, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 88, 72, 139, 0, 80, 106, 9, 95, 88, 72, 57, 248, 15, 156, 192, 72, 15, 182, 192, 80, 88, 80, 72, 131, 248, 0, 15, 132, 144, 0, 0, 0, 72, 137,232, 72, 131, 232, 8, 80, 72, 137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 16, 80, 72, 137, 232, 72, 131, 232, 24, 80, 88, 72, 139, 0, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 24, 80, 72, 137, 232, 72, 131, 232, 8, 80, 88, 72, 139, 0, 80, 72, 137, 232, 72, 131, 232, 16, 80, 88, 72, 139, 0, 80, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 72, 137, 232, 72, 131, 232, 32, 80, 72, 137, 232, 72, 131, 232, 32, 80, 88, 72, 139, 0, 80, 106, 1, 95, 88, 72, 1, 248, 80, 95, 88, 72, 137, 56, 87, 88, 233, 72, 255, 255, 255, 72, 137, 232, 72, 131, 232, 24, 80, 88, 72, 139, 0, 80, 72, 137, 236, 93, 195, 0, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 46, 115, 121, 109, 116], - [97, 98, 0, 46, 115, 116], - [114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0, 46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 109, 97, 105, 110, 0], + [0, 46, 115, 121, 109, 116, 97, 98, 0, 46, 115, 116, 114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0,46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0], + [0]*64, + [0, 0, 0, 27, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [], ] end end From b53ea98941c7c122fdc5361258904dd943ad9055 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 25 Aug 2024 00:20:28 +0900 Subject: [PATCH 47/58] set section headers --- lib/vaporware/compiler/assembler.rb | 49 +++++++++++++++---- .../compiler/assembler/elf/section.rb | 12 +++-- .../compiler/assembler/elf/section/symtab.rb | 10 ++-- .../compiler/assembler/elf/section_header.rb | 14 +++--- .../compiler/assembler/elf/sections.rb | 3 +- lib/vaporware/compiler/assembler/elf/utils.rb | 9 +++- sig/vaporware/compiler/assembler.rbs | 9 +++- .../compiler/assembler/elf/section.rbs | 8 +-- .../compiler/assembler/elf/section/note.rbs | 2 + test/vaporware/compiler/test_assembler.rb | 33 +++++++------ 10 files changed, 103 insertions(+), 46 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 5a190dc..dd878dd 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -28,33 +28,62 @@ def obj_file = @output def to_elf(input: @input, output: @output, debug: false) program_size = 0 - read(input:) + read!(input:) + init_assemble! - offset = 0 + offset = 0x40 section_headers = [] names = [] - bins = [] + bodies = { + null: nil, + text: nil, + data: nil, + bss: nil, + note: nil, + symtab: nil, + strtab: nil, + shstrtab: nil, + } + name_idx = 0 + padding = nil @sections.each do |section| - names << name = section.name + name = section.name + names << name + section.body.set!(name: names.join + "\0") if name == "\0.shstrtab" bin = section.body.build size = bin.bytesize - offset += size - bins << bin + bin << "\0" until (bin.bytesize % 8) == 0 if ["\0.text", "\0.shstrtab"].include?(name) + bodies[section.section_name.to_sym] = bin header = section.header - header.set!(offset:) + padding = bin.size - size if offset > 0x40 && size > 0 + if offset > 0x40 && size > 0 && padding > 0 + offset += padding + padding = nil + end + + header.set!(name: name_idx, offset:, size:) unless name == "" + offset += size section_headers << header.build + name_idx += name == "" ? 1 : name.size end - [@elf_header.build, *bins] + w = File.open(output, "wb") + w.write([@elf_header.build, *bodies.values, *section_headers].join) + w.close + [@elf_header.build, *bodies.values, *section_headers] end - def read(input: @input, text: @sections.text.body) + private + def init_assemble! = (note!; symtab!) + def read!(input: @input, text: @sections.text.body) read = { main: false } File.open(input, "r") do |r| r.each_line do |line| - read[:main] = /main:/.match(line) unless read[:main] + read[:main] = line.match(/main:/) unless read[:main] next unless read[:main] && !/main:/.match(line) text.assemble!(line) end end end + def note! = @sections.note.body.gnu_property! + def symtab! = @sections.symtab.body.set!(entsize: 0x18, name: 1, info: 0x10, other: 0, shndx: 1) end diff --git a/lib/vaporware/compiler/assembler/elf/section.rb b/lib/vaporware/compiler/assembler/elf/section.rb index f3ff502..57af7c3 100644 --- a/lib/vaporware/compiler/assembler/elf/section.rb +++ b/lib/vaporware/compiler/assembler/elf/section.rb @@ -9,13 +9,17 @@ require_relative "section_header" class Vaporware::Compiler::Assembler::ELF::Section - attr_reader :header, :body, :name + attr_reader :header, :body, :name, :section_name def initialize(type:, options: {}) type_string = type.to_s.capitalize type_string = type_string.upcase if type_string == "Bss" - section_name = type_string.downcase - @name = "\0.#{section_name}\0" - @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{section_name}!") + @section_name = type_string.downcase + @name = section_name == "null" ? "" : "\0.#{section_name}" + @header = Vaporware::Compiler::Assembler::ELF::SectionHeader.new.send("#{@section_name}!") @body = Module.const_get("Vaporware::Compiler::Assembler::ELF::Section::#{type_string}").new(**options) end + + def name=(name) + @name = name + end end diff --git a/lib/vaporware/compiler/assembler/elf/section/symtab.rb b/lib/vaporware/compiler/assembler/elf/section/symtab.rb index a607e9f..faf9b88 100644 --- a/lib/vaporware/compiler/assembler/elf/section/symtab.rb +++ b/lib/vaporware/compiler/assembler/elf/section/symtab.rb @@ -1,6 +1,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Symtab include Vaporware::Compiler::Assembler::ELF::Utils def initialize(**opts) + @entsize = [] @name = num2bytes(0, 4) @info = num2bytes(0, 1) @other = num2bytes(0, 1) @@ -9,17 +10,18 @@ def initialize(**opts) @size = num2bytes(0, 8) end - def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) + def set!(entsize: nil, name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) + @entsize = [0] * entsize unless entsize.nil? @name = name2bytes(name, 4) if check(name, 4) - @info = num2bytes(info, 1) if check(info, 4) + @info = num2bytes(info, 1) if check(info, 1) @other = num2bytes(other, 1) if check(other, 1) - @shndx = num2bytes(shndx, 4) if check(shndx, 4) + @shndx = num2bytes(shndx, 2) if check(shndx, 2) @value = num2bytes(value, 8) if check(value, 8) @size = num2bytes(size, 8) if check(size, 8) end private - def bytes = [@name, @info, @other, @shndx, @value, @size] + def bytes = [@entsize, @name, @info, @other, @shndx, @value, @size] def name2bytes(name, bytes) case name when String diff --git a/lib/vaporware/compiler/assembler/elf/section_header.rb b/lib/vaporware/compiler/assembler/elf/section_header.rb index 8223c56..6134ec9 100644 --- a/lib/vaporware/compiler/assembler/elf/section_header.rb +++ b/lib/vaporware/compiler/assembler/elf/section_header.rb @@ -32,13 +32,13 @@ def set!(name: nil, type: nil, flags: nil, addr: nil, end def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0) - def text! = set!(flags: 0x06, addralign: 0x01) - def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08) - def data! = null! # unsupported in the current implementation - def symtab! = set! - def strtab! = set! - def bss! = null! #unsupported in the current implementation - def shstrtab! = set! + def text! = set!(flags: 0x06, addralign: 0x01, addr: 0, type: 1, entsize: 0, link: 0, info: 0) + def data! = set!(type: 0x01, flags: 0x03, addralign: 1, addr: 0, info: 0, link: 0, entsize: 0) + def bss! = set!(type: 0x8, flags: 3, addralign: 1, addr: 0, info: 0, link: 0, entsize: 0) + def note! = set!(type: 0x07, flags: 0x02, size: 0x30, addralign: 0x08, addr: 0, link: 0, info: 0, entsize: 0) + def symtab! = set!(type: 2, info: 1, addr: 0, link: 6, entsize: 0x18, addralign: 8, flags: 0) + def strtab! = set!(type: 3, info: 0, addr: 0, link: 0, entsize: 0, addralign: 1, flags: 0) + def shstrtab! = set!(type: 3, info: 0, addr: 0, link: 0, entsize: 0, addralign: 1, flags: 0) private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize] end diff --git a/lib/vaporware/compiler/assembler/elf/sections.rb b/lib/vaporware/compiler/assembler/elf/sections.rb index 4afcffc..d4d6962 100644 --- a/lib/vaporware/compiler/assembler/elf/sections.rb +++ b/lib/vaporware/compiler/assembler/elf/sections.rb @@ -2,7 +2,6 @@ class Vaporware::Compiler::Assembler::ELF::Sections ATTRIBUTES = %i|null text data bss note symtab strtab shstrtab| - HAND_ASSEMBLES = %i|text shstrtab note symtab strtab| attr_reader *ATTRIBUTES def initialize @@ -16,5 +15,5 @@ def initialize @shstrtab = Vaporware::Compiler::Assembler::ELF::Section.new(type: :shstrtab) end - def each(&block) = (ATTRIBUTES - HAND_ASSEMBLES).each { |t| yield send(t) } + def each(&block) = ATTRIBUTES.each { |t| yield send(t) } end diff --git a/lib/vaporware/compiler/assembler/elf/utils.rb b/lib/vaporware/compiler/assembler/elf/utils.rb index 344e9e0..f19f011 100644 --- a/lib/vaporware/compiler/assembler/elf/utils.rb +++ b/lib/vaporware/compiler/assembler/elf/utils.rb @@ -1,5 +1,5 @@ module Vaporware::Compiler::Assembler::ELF::Utils - def build = bytes.flatten.pack("C*") + def build = (build_errors; bytes.flatten.pack("C*")) def size = build.bytesize def set! = (raise Vaporware::Compiler::Assembler::ELF::Error, "should be implementing #{self.class}") def empties = must_be_filled_section_fields @@ -14,4 +14,11 @@ def must_be_filled_section_fields = instance_variables.reject { |i| instance_var def num2bytes(val, bytes) = hexas(val, bytes).reverse def check(val, bytes) = ((val.is_a?(Array) && val.all? { |v| v.is_a?(Integer) } && val.size == bytes) || (val.is_a?(Integer) && (hexas(val, bytes).size == bytes))) def hexas(val, hex) = ("%0#{hex*2}x" % val).scan(/.{1,2}/).map { |v| v.to_i(16) }.then { |list| list.unshift(0) until list.size >= hex; list } + def build_errors + if bytes.any?(&:nil?) + errors = [] + bytes.each_with_index { |v, idx| errors << instance_variables[idx] if v.nil? } + raise Vaporware::Compiler::Assembler::ELF::Error, "unaccepted types: #{errors.join(",")}" + end + end end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index 05d3174..aa1db5a 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -14,5 +14,12 @@ class Vaporware::Compiler::Assembler def assemble: (?assembler: String, ?assembler_options: Array[String] | [], ?input: String, ?output: String, ?debug: bool) -> String def obj_file: () -> String def to_elf: (?input: String, ?output: String, ?debug: bool) -> void - def read: (?input: String, ?text: Text) -> void + + private def read!: (?input: String, ?text: Text) -> void + private def init_assemble!: () -> void + private def note!: () -> void + private def text!: () -> void + private def symtab!: () -> void + private def strtab!: () -> void + private def shstrtab!: () -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section.rbs b/sig/vaporware/compiler/assembler/elf/section.rbs index bcbf638..e3412b5 100644 --- a/sig/vaporware/compiler/assembler/elf/section.rbs +++ b/sig/vaporware/compiler/assembler/elf/section.rbs @@ -2,8 +2,10 @@ use Vaporware::Compiler::Assembler::ELF::SectionHeader use Vaporware::Compiler::Assembler::ELF::Section::* class Vaporware::Compiler::Assembler::ELF::Section - @header: SectionHeader - @body: Text | Null | Data | BSS | Symtab | Shstrtab | Strtab | Note - + attr_reader name: String + attr_reader header: SectionHeader + attr_reader body: Text | Null | Data | BSS | Symtab | Shstrtab | Strtab | Note + attr_reader section_name: String def initialize: (type: (String | Symbol)) -> void + def name=: (String) -> void end diff --git a/sig/vaporware/compiler/assembler/elf/section/note.rbs b/sig/vaporware/compiler/assembler/elf/section/note.rbs index 1bb7af6..fef8d46 100644 --- a/sig/vaporware/compiler/assembler/elf/section/note.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/note.rbs @@ -7,6 +7,8 @@ class Vaporware::Compiler::Assembler::ELF::Section::Note @name: Array[Integer]? @desc: Array[Integer]? + def initialize: (?type: String | Symbol) -> void + def set!: ( ?nsize: Integer?, ?dsize: Integer?, diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index 4da39d5..8a7f95b 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -12,7 +12,7 @@ def test_to_elf header, null, text, data, bss, note, symtab, strtab, shstrtab, *section_headers = assembler.to_elf sh_null, sh_text, sh_data, sh_bss, sh_note, shsymtab, sh_strtab, sh_shstrtab = section_headers r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab, *r_section_headers = readelf - r_sh_null, r_sh_text, * = r_section_headers + r_sh_null, r_sh_text, r_sh_data, r_sh_bss, r_sh_note, * = r_section_headers assert_equal(r_header, header.unpack("C*")) assert_equal(r_null, null.unpack("C*")) assert_equal(r_data, data.unpack("C*")) @@ -26,23 +26,28 @@ def test_to_elf assert(ref_shstrtab.zip(shstrtab.split("\0").select { |str| str.size > 0 }.sort).all? { |ref, act| ref =~ /#{act}/ }) assert_equal(r_sh_null, sh_null.unpack("C*")) assert_equal(r_sh_text, sh_text.unpack("C*")) + assert_equal(r_sh_data, sh_data.unpack("C*")) + assert_equal(r_sh_bss, sh_bss.unpack("C*")) + assert_equal(r_sh_note, sh_note.unpack("C*")) end def readelf [ - [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], - [], - [85, 72, 137, 229, 72, 131, 236, 0, 106, 1, 106, 2, 95, 88, 72, 1, 248, 80, 106, 3, 95, 88, 72, 15, 175, 199, 80, 106,5, 106, 4, 95, 88, 72, 41, 248, 80, 95, 88, 72, 153, 72, 247, 255, 80, 72, 137, 236, 93, 195], - [], - [], - [4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 109, 97, 105, 110, 0], - [0, 46, 115, 121, 109, 116, 97, 98, 0, 46, 115, 116, 114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0,46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0], - [0]*64, - [0, 0, 0, 27, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], # elf header + [], # null section + [85, 72, 137, 229, 72, 131, 236, 0, 106, 1, 106, 2, 95, 88, 72, 1, 248, 80, 106, 3, 95, 88, 72, 15, 175, 199, 80, 106,5, 106, 4, 95, 88, 72, 41, 248, 80, 95, 88, 72, 153, 72, 247, 255, 80, 72, 137, 236, 93, 195], # text section + [], # data section + [], # bss section + [4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], # note section + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0], # symtab section + [0, 109, 97, 105, 110, 0], # strtab section + [0, 46, 115, 121, 109, 116, 97, 98, 0, 46, 115, 116, 114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0,46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0], # shstrtab section + # section headers + [0]*64, # null + [1, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # text + [7, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # data + [13, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # bss + [18, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # note [], ] end From ec650880aa192f73473079c353d223ed56446ff1 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 25 Aug 2024 19:01:47 +0900 Subject: [PATCH 48/58] set paddings --- lib/vaporware/compiler/assembler.rb | 13 ++++++++----- test/vaporware/compiler/test_assembler.rb | 10 ++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index dd878dd..7cfb730 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -49,18 +49,18 @@ def to_elf(input: @input, output: @output, debug: false) @sections.each do |section| name = section.name names << name - section.body.set!(name: names.join + "\0") if name == "\0.shstrtab" + section.body.set!(name: names.join) if name == "\0.shstrtab" bin = section.body.build size = bin.bytesize bin << "\0" until (bin.bytesize % 8) == 0 if ["\0.text", "\0.shstrtab"].include?(name) + bin << "\0" until ((bin.bytesize + offset) % 8) == 0 if ["\0.shstrtab"].include?(name) bodies[section.section_name.to_sym] = bin header = section.header - padding = bin.size - size if offset > 0x40 && size > 0 - if offset > 0x40 && size > 0 && padding > 0 + if offset > 0x40 && size > 0 && padding&.>(0) offset += padding padding = nil end - + padding = bin.size - size if size > 0 header.set!(name: name_idx, offset:, size:) unless name == "" offset += size section_headers << header.build @@ -84,6 +84,9 @@ def read!(input: @input, text: @sections.text.body) end end end - def note! = @sections.note.body.gnu_property! + def note! + @sections.note.body.gnu_property! + @sections.note.name = "\0.note.gnu.property" + end def symtab! = @sections.symtab.body.set!(entsize: 0x18, name: 1, info: 0x10, other: 0, shndx: 1) end diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index 8a7f95b..21e7fa4 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -10,9 +10,9 @@ def test_to_elf assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") header, null, text, data, bss, note, symtab, strtab, shstrtab, *section_headers = assembler.to_elf - sh_null, sh_text, sh_data, sh_bss, sh_note, shsymtab, sh_strtab, sh_shstrtab = section_headers + sh_null, sh_text, sh_data, sh_bss, sh_note, sh_symtab, sh_strtab, sh_shstrtab = section_headers r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab, *r_section_headers = readelf - r_sh_null, r_sh_text, r_sh_data, r_sh_bss, r_sh_note, * = r_section_headers + r_sh_null, r_sh_text, r_sh_data, r_sh_bss, r_sh_note, r_sh_symtab, r_sh_strtab, r_sh_shstrtab = r_section_headers assert_equal(r_header, header.unpack("C*")) assert_equal(r_null, null.unpack("C*")) assert_equal(r_data, data.unpack("C*")) @@ -29,6 +29,9 @@ def test_to_elf assert_equal(r_sh_data, sh_data.unpack("C*")) assert_equal(r_sh_bss, sh_bss.unpack("C*")) assert_equal(r_sh_note, sh_note.unpack("C*")) + assert_equal(r_sh_symtab, sh_symtab.unpack("C*")) + assert_equal(r_sh_strtab, sh_strtab.unpack("C*")) + assert_equal(r_sh_shstrtab, sh_shstrtab.unpack("C*")) end def readelf @@ -48,6 +51,9 @@ def readelf [7, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # data [13, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # bss [18, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # note + [37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0], # symtab + [45, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # strtab + [53, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # shstrtab [], ] end From 87e299f14fd39b75ff3caa55a61c959b46c316fb Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 25 Aug 2024 21:50:38 +0900 Subject: [PATCH 49/58] fix elf header for section header offset bytes --- lib/vaporware/compiler/assembler.rb | 1 + lib/vaporware/compiler/assembler/elf/header.rb | 2 +- test/vaporware/compiler/test_assembler.rb | 8 +++----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 7cfb730..8e087c7 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -66,6 +66,7 @@ def to_elf(input: @input, output: @output, debug: false) section_headers << header.build name_idx += name == "" ? 1 : name.size end + @elf_header.set!(shoffset: offset + padding) w = File.open(output, "wb") w.write([@elf_header.build, *bodies.values, *section_headers].join) w.close diff --git a/lib/vaporware/compiler/assembler/elf/header.rb b/lib/vaporware/compiler/assembler/elf/header.rb index f663f6c..ee2756c 100644 --- a/lib/vaporware/compiler/assembler/elf/header.rb +++ b/lib/vaporware/compiler/assembler/elf/header.rb @@ -12,7 +12,7 @@ def initialize(endian: :littel, type: :rel, arc: :amd64) @version = num2bytes(1, 4) @entry = num2bytes(0x00, 8) @phoffset = num2bytes(0x00, 8) - @shoffset = num2bytes(0x0220, 8) + @shoffset = num2bytes(0x00, 8) @flags = num2bytes(0x00, 4) @ehsize = num2bytes(0x40, 2) @phsize = num2bytes(0x00, 2) diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index 21e7fa4..c76f00e 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -3,15 +3,13 @@ require "tempfile" class Vaporware::Compiler::Assembler::ELFTest < Test::Unit::TestCase - def reference_binary = "" - def test_to_elf input = __dir__ + "/amd64.s" assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") header, null, text, data, bss, note, symtab, strtab, shstrtab, *section_headers = assembler.to_elf sh_null, sh_text, sh_data, sh_bss, sh_note, sh_symtab, sh_strtab, sh_shstrtab = section_headers - r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab, *r_section_headers = readelf + r_header, r_null, r_text, r_data, r_bss, r_note, r_symtab, r_strtab, r_shstrtab, *r_section_headers = dumped_references r_sh_null, r_sh_text, r_sh_data, r_sh_bss, r_sh_note, r_sh_symtab, r_sh_strtab, r_sh_shstrtab = r_section_headers assert_equal(r_header, header.unpack("C*")) assert_equal(r_null, null.unpack("C*")) @@ -34,9 +32,9 @@ def test_to_elf assert_equal(r_sh_shstrtab, sh_shstrtab.unpack("C*")) end - def readelf + def dumped_references [ - [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], # elf header + [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], # elf header [], # null section [85, 72, 137, 229, 72, 131, 236, 0, 106, 1, 106, 2, 95, 88, 72, 1, 248, 80, 106, 3, 95, 88, 72, 15, 175, 199, 80, 106,5, 106, 4, 95, 88, 72, 41, 248, 80, 95, 88, 72, 153, 72, 247, 255, 80, 72, 137, 236, 93, 195], # text section [], # data section From f06c56dc92604e225fc82268a29a3e6dfd30f5bf Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 31 Aug 2024 13:06:45 +0900 Subject: [PATCH 50/58] remove unnecessary test --- test/vaporware/compiler/amd64.s => sample/assembler/plus.s | 0 test/vaporware/compiler/assembler/elf/test_header.rb | 5 ----- test/vaporware/compiler/test_assembler.rb | 4 +++- 3 files changed, 3 insertions(+), 6 deletions(-) rename test/vaporware/compiler/amd64.s => sample/assembler/plus.s (100%) diff --git a/test/vaporware/compiler/amd64.s b/sample/assembler/plus.s similarity index 100% rename from test/vaporware/compiler/amd64.s rename to sample/assembler/plus.s diff --git a/test/vaporware/compiler/assembler/elf/test_header.rb b/test/vaporware/compiler/assembler/elf/test_header.rb index 05fb486..2c6affd 100644 --- a/test/vaporware/compiler/assembler/elf/test_header.rb +++ b/test/vaporware/compiler/assembler/elf/test_header.rb @@ -3,11 +3,6 @@ class Vaporware::Compiler::Assembler::ELF::HeaderTest < Test::Unit::TestCase def setup = @elf_header = Vaporware::Compiler::Assembler::ELF::Header.new - def test_filled_fields - assert_equal(@elf_header.empties, [:@entry, :@phoffset, :@shoffset, :@shnum, :@shstrndx]) - @elf_header.set!(shoffset: 1) - assert_equal(@elf_header.empties, [:@entry, :@phoffset, :@shnum, :@shstrndx]) - end def test_build_elf_header # TODO: ELF Header Section binary diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index c76f00e..6a07321 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -1,10 +1,11 @@ require "vaporware" require "test/unit" require "tempfile" +require "pathname" class Vaporware::Compiler::Assembler::ELFTest < Test::Unit::TestCase def test_to_elf - input = __dir__ + "/amd64.s" + input = Pathname.pwd.join('sample', 'assembler', 'plus.s').to_s assembler = Vaporware::Compiler::Assembler.new(input:, output: "amd64.o") header, null, text, data, bss, note, symtab, strtab, shstrtab, *section_headers = assembler.to_elf @@ -30,6 +31,7 @@ def test_to_elf assert_equal(r_sh_symtab, sh_symtab.unpack("C*")) assert_equal(r_sh_strtab, sh_strtab.unpack("C*")) assert_equal(r_sh_shstrtab, sh_shstrtab.unpack("C*")) + File.delete("amd64.o") end def dumped_references From 50427a230f7cc021618a35409dbaea688ee206eb Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 31 Aug 2024 13:34:54 +0900 Subject: [PATCH 51/58] wrong setup and teadown --- test/vaporware/test_compiler.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 7e6e278..0a936dc 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -2,10 +2,14 @@ require "test/unit" class Vaporware::CompilerTest < Test::Unit::TestCase - def tear_down = File.delete("tmp") rescue File.delete(@generated) + def setup = @generated = ["tmp.s", "tmp.o"] + def teardown + File.delete("tmp") if File.exist?("tmp") + @generated.map { File.delete(_1) } + end def test_sample_plus @file = "sample/plus.rb" - @vaporware = Vaporware::Compiler.compile(@file, debug: true) + @vaporware = Vaporware::Compiler.compile(@file) IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(9, exit_code) @@ -49,7 +53,7 @@ def test_sample_while end def test_sample_call_method - @generated = "libtmp.so" + @generated = ["libtmp.so", "libtmp.so.o", "libtmp.so.s"] @file = "sample/method.rb" @vaporware = Vaporware::Compiler.compile(@file, dest: "./libtmp.so", shared: true) require './sample/fiddle.rb' From b397ffd9e408135010439fc43e2eab1481d15075 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 12 Sep 2024 10:18:59 +0900 Subject: [PATCH 52/58] add using assembler option and commented default linker --- exe/vaporware | 3 ++- test/vaporware/test_compiler.rb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exe/vaporware b/exe/vaporware index e2d639b..821c288 100755 --- a/exe/vaporware +++ b/exe/vaporware @@ -5,11 +5,12 @@ require "optparse" opt = OptionParser.new options = {} opt.on("-c", "--compiler[=VAL]", "this option is selecting compiler precompiled file, default: \"self\"") { |v| options[:compiler] = v } +opt.on("-a", "--assembler[=VAL]", "this option is selecting assembler assembler file, default: \"as\"") { |v| options[:assembler] = v } opt.on("-D", "--debug") { |v| options[:debug] = v } opt.on("-o", "--objects[=VAL]") { |v| options[:dest] = v } opt.on("--compiler-options[=VAL]", "compiler options") { |v| options[:compiler_options] = v.split(",") } opt.on("-s", "--shared-library") { |v| options[:shared] = v } -opt.on("-l", "--linker[=VAL]", "selecting linker: gold, lld, and mold.") { |v| options[:linker] = v } +opt.on("-l", "--linker[=VAL]", "selecting linker: gold, lld, and mold, default: \"gold\".") { |v| options[:linker] = v } begin opt.parse!(ARGV) diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 0a936dc..890b50d 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -5,11 +5,11 @@ class Vaporware::CompilerTest < Test::Unit::TestCase def setup = @generated = ["tmp.s", "tmp.o"] def teardown File.delete("tmp") if File.exist?("tmp") - @generated.map { File.delete(_1) } + @generated.map { File.delete(_1) if File.exist?(_1) } end def test_sample_plus @file = "sample/plus.rb" - @vaporware = Vaporware::Compiler.compile(@file) + @vaporware = Vaporware::Compiler.compile(@file, assembler: "self") IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(9, exit_code) From f8476fed0cc26faa4340abfe98aa616d9ee9b550 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Fri, 13 Sep 2024 10:55:28 +0900 Subject: [PATCH 53/58] fix error class for rbs --- .../compiler/assembler/elf/section/text.rb | 22 ++++++++++--------- sig/vaporware/compiler/assembler/elf.rbs | 3 +-- .../compiler/assembler/elf/section/text.rbs | 6 ++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 3f1777b..25a3020 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -43,7 +43,7 @@ def opecode(op, *operands) when "ret" [0xc3] else - raise Compiler::Assembler::ERROR + raise Vaporware::Compiler::Assembler::ELF::Error, "yet implemented operations: #{op}" end end @@ -55,7 +55,7 @@ def mov(op, operands) [0xec] else operands&.map { reg(_1) } - end + end # steep:ignore [OPECODE[op.upcase.to_sym], *reg] end @@ -74,7 +74,7 @@ def calc(op, operands) [ope_code, 0xec, *num.map { |n| n.to_i(16) }] in ["cqo"] [0x99] - end + end # steep:ignore end def push(operands) @@ -85,7 +85,7 @@ def push(operands) [0x50] else [0x6a, *operands.map { reg(_1) }] - end + end # steep:ignore end def pop(operands) @@ -94,21 +94,23 @@ def pop(operands) [0x58 + REGISTER_CODE[operands.first.upcase.to_sym]] in ["rbp"] [0x5d] - end + end # steep:ignore end def reg(r) case r - in "rsp" + when "rsp" 0xec - in "rbp" + when "rbp" 0x5e - in "rax" + when "rax" 0x29 - in "rdi" + when "rdi" 0xf8 - in /\d+/ + when /\d+/ ("%02x" % r).to_i(16) + else + raise Vaporware::Compiler::Assembler::ELF::Error, "yet implemented operand address: #{r}" end end end diff --git a/sig/vaporware/compiler/assembler/elf.rbs b/sig/vaporware/compiler/assembler/elf.rbs index ba3e3cb..dd644c5 100644 --- a/sig/vaporware/compiler/assembler/elf.rbs +++ b/sig/vaporware/compiler/assembler/elf.rbs @@ -1,4 +1,3 @@ class Vaporware::Compiler::Assembler::ELF - class Error - end + Error: singleton(StandardError) end diff --git a/sig/vaporware/compiler/assembler/elf/section/text.rbs b/sig/vaporware/compiler/assembler/elf/section/text.rbs index 26926ac..bc41571 100644 --- a/sig/vaporware/compiler/assembler/elf/section/text.rbs +++ b/sig/vaporware/compiler/assembler/elf/section/text.rbs @@ -3,18 +3,18 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text REGISTER_CODE: Hash[Symbol, Integer] OPECODE: Hash[Symbol, Integer] - @bytes: Array[untyped]? + @bytes: Array[untyped] - attr_reader size: Integer attr_reader offset: Integer def initialize: () -> void def assemble!: (String) -> void def align!: (Integer) -> void + def build: () -> String private - def opecode: ((String | Symbol), Array[String]) -> (Array[Integer] | nil) + def opecode: ((String | Symbol)?, *String) -> Array[Integer] def mov: ((String | Symbol), Array[String]) -> Array[Integer] def calc: ((String | Symbol), Array[String]) -> Array[Integer] def push: (Array[String]) -> Array[Integer] From a8607b95d73639e7f5b88af10507b3a9f4403f5f Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 15 Sep 2024 15:22:09 +0900 Subject: [PATCH 54/58] use self made note --- lib/vaporware/compiler/assembler.rb | 5 +---- .../compiler/assembler/elf/section/note.rb | 9 ++++----- test/vaporware/compiler/test_assembler.rb | 13 +++++++------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index 8e087c7..d6d9cb4 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -85,9 +85,6 @@ def read!(input: @input, text: @sections.text.body) end end end - def note! - @sections.note.body.gnu_property! - @sections.note.name = "\0.note.gnu.property" - end + def note! = @sections.note.body.null! def symtab! = @sections.symtab.body.set!(entsize: 0x18, name: 1, info: 0x10, other: 0, shndx: 1) end diff --git a/lib/vaporware/compiler/assembler/elf/section/note.rb b/lib/vaporware/compiler/assembler/elf/section/note.rb index 0550158..f273ce1 100644 --- a/lib/vaporware/compiler/assembler/elf/section/note.rb +++ b/lib/vaporware/compiler/assembler/elf/section/note.rb @@ -1,10 +1,8 @@ class Vaporware::Compiler::Assembler::ELF::Section::Note include Vaporware::Compiler::Assembler::ELF::Utils - def self.gnu_property - note = new - note.gnu_property! - note.build - end + + def self.gnu_property = new.gnu_property!.build + def self.null = new.null!.build def initialize(type: nil) @nsize = nil @@ -25,6 +23,7 @@ def set!(nsize: nil, dsize: nil, type: nil, name: nil, desc: nil) end def gnu_property! = set!(nsize: 0x04, dsize: 0x20, type: 0x05, name: "GNU", desc: %w(02 00 01 c0 04 00 00 00 00 00 00 00 00 00 00 00 01 00 01 c0 04 00 00 00 01 00 00 00 00 00 00 00).map { |val| val.to_i(16) }) + def null! = set!(nsize: 0, dsize: 0, type: 0, name: "NULL", desc: [0]) private diff --git a/test/vaporware/compiler/test_assembler.rb b/test/vaporware/compiler/test_assembler.rb index 6a07321..a979a77 100644 --- a/test/vaporware/compiler/test_assembler.rb +++ b/test/vaporware/compiler/test_assembler.rb @@ -36,12 +36,13 @@ def test_to_elf def dumped_references [ - [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], # elf header + [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 64, 0, 8, 0, 7, 0], # elf header [], # null section [85, 72, 137, 229, 72, 131, 236, 0, 106, 1, 106, 2, 95, 88, 72, 1, 248, 80, 106, 3, 95, 88, 72, 15, 175, 199, 80, 106,5, 106, 4, 95, 88, 72, 41, 248, 80, 95, 88, 72, 153, 72, 247, 255, 80, 72, 137, 236, 93, 195], # text section [], # data section [], # bss section - [4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], # note section + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 85, 76, 76, 0, 0, 0, 0],# + # [4, 0, 0, 0, 32, 0, 0, 0, 5, 0, 0, 0, 71, 78, 85, 0, 2, 0, 1, 192, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 192, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], # note section [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0], # symtab section [0, 109, 97, 105, 110, 0], # strtab section [0, 46, 115, 121, 109, 116, 97, 98, 0, 46, 115, 116, 114, 116, 97, 98, 0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0,46, 116, 101, 120, 116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 110, 111, 116, 101, 46, 103, 110, 117, 46, 112, 114, 111, 112, 101, 114, 116, 121, 0], # shstrtab section @@ -50,10 +51,10 @@ def dumped_references [1, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # text [7, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # data [13, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # bss - [18, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # note - [37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0], # symtab - [45, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # strtab - [53, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # shstrtab + [18, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # note + [24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0], # symtab + [32, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # strtab + [40, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # shstrtab [], ] end From c184dacadceaf7631dae6f14d2dbb3047cb88346 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 15 Sep 2024 18:13:22 +0900 Subject: [PATCH 55/58] migrate building elf file to ELF class from Assembler class --- lib/vaporware/compiler/assembler.rb | 68 +------------------- lib/vaporware/compiler/assembler/elf.rb | 80 +++++++++++++++++++++--- sig/vaporware/compiler/assembler.rbs | 16 +---- sig/vaporware/compiler/assembler/elf.rbs | 20 ++++++ 4 files changed, 97 insertions(+), 87 deletions(-) diff --git a/lib/vaporware/compiler/assembler.rb b/lib/vaporware/compiler/assembler.rb index d6d9cb4..ceba424 100644 --- a/lib/vaporware/compiler/assembler.rb +++ b/lib/vaporware/compiler/assembler.rb @@ -10,9 +10,8 @@ def self.assemble!(input, output = File.basename(input, ".*") + ".o") = new(inpu def initialize(input:, output: File.basename(input, ".*") + ".o", assembler: "as", type: :relocatable, debug: false) @input, @output = input, output - @elf_header = ELF::Header.new(type:) + @elf = ELF.new(type:, input:, output:, debug:) @assembler = assembler - @sections = ELF::Sections.new @debug = debug end @@ -20,71 +19,10 @@ def assemble(assembler: @assembler, assembler_options: [], input: @input, output if ["gcc", "as"].include?(assembler) IO.popen([assembler, *assembler_options, "-o", output, input].join(" ")).close else - to_elf(input:, output:) + to_elf(input:, output:, debug:) end output end def obj_file = @output - - def to_elf(input: @input, output: @output, debug: false) - program_size = 0 - read!(input:) - init_assemble! - - offset = 0x40 - section_headers = [] - names = [] - bodies = { - null: nil, - text: nil, - data: nil, - bss: nil, - note: nil, - symtab: nil, - strtab: nil, - shstrtab: nil, - } - name_idx = 0 - padding = nil - @sections.each do |section| - name = section.name - names << name - section.body.set!(name: names.join) if name == "\0.shstrtab" - bin = section.body.build - size = bin.bytesize - bin << "\0" until (bin.bytesize % 8) == 0 if ["\0.text", "\0.shstrtab"].include?(name) - bin << "\0" until ((bin.bytesize + offset) % 8) == 0 if ["\0.shstrtab"].include?(name) - bodies[section.section_name.to_sym] = bin - header = section.header - if offset > 0x40 && size > 0 && padding&.>(0) - offset += padding - padding = nil - end - padding = bin.size - size if size > 0 - header.set!(name: name_idx, offset:, size:) unless name == "" - offset += size - section_headers << header.build - name_idx += name == "" ? 1 : name.size - end - @elf_header.set!(shoffset: offset + padding) - w = File.open(output, "wb") - w.write([@elf_header.build, *bodies.values, *section_headers].join) - w.close - [@elf_header.build, *bodies.values, *section_headers] - end - - private - def init_assemble! = (note!; symtab!) - def read!(input: @input, text: @sections.text.body) - read = { main: false } - File.open(input, "r") do |r| - r.each_line do |line| - read[:main] = line.match(/main:/) unless read[:main] - next unless read[:main] && !/main:/.match(line) - text.assemble!(line) - end - end - end - def note! = @sections.note.body.null! - def symtab! = @sections.symtab.body.set!(entsize: 0x18, name: 1, info: 0x10, other: 0, shndx: 1) + def to_elf(input: @input, output: @output, debug: false) = @elf.build(input:, output:, debug:) end diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb index 3040799..e92ed5a 100644 --- a/lib/vaporware/compiler/assembler/elf.rb +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -1,12 +1,76 @@ -module Vaporware - class Compiler - class Assembler - class ELF - class Error < StandardError; end - class Section; end - class SectionHeader; end - module Utils; end +class Vaporware::Compiler::Assembler + class ELF + class Error < StandardError; end + class Section; end + class SectionHeader; end + module Utils; end + + def initialize(type:, input:, output:, debug:) + @input, @output = input, output + @header = Header.new(type:) + @sections = Sections.new + end + + def build(input: @input, output: @output, debug: false) + program_size = 0 + read!(input:) + init_assemble! + + offset = 0x40 + section_headers = [] + names = [] + bodies = { + null: nil, + text: nil, + data: nil, + bss: nil, + note: nil, + symtab: nil, + strtab: nil, + shstrtab: nil, + } + name_idx = 0 + padding = nil + @sections.each do |section| + name = section.name + names << name + section.body.set!(name: names.join) if name == "\0.shstrtab" + bin = section.body.build + size = bin.bytesize + bin << "\0" until (bin.bytesize % 8) == 0 if ["\0.text", "\0.shstrtab"].include?(name) + bin << "\0" until ((bin.bytesize + offset) % 8) == 0 if ["\0.shstrtab"].include?(name) + bodies[section.section_name.to_sym] = bin + header = section.header + if offset > 0x40 && size > 0 && padding&.>(0) + offset += padding + padding = nil + end + padding = bin.size - size if size > 0 + header.set!(name: name_idx, offset:, size:) unless name == "" + offset += size + section_headers << header.build + name_idx += name == "" ? 1 : name.size + end + @header.set!(shoffset: offset + padding) + w = File.open(output, "wb") + w.write([@header.build, *bodies.values, *section_headers].join) + w.close + [@header.build, *bodies.values, *section_headers] + end + + private + def init_assemble! = (note!; symtab!) + def read!(input: @input, text: @sections.text.body) + read = { main: false } + File.open(input, "r") do |r| + r.each_line do |line| + read[:main] = line.match(/main:/) unless read[:main] + next unless read[:main] && !/main:/.match(line) + text.assemble!(line) + end end end + def note! = @sections.note.body.null! + def symtab! = @sections.symtab.body.set!(entsize: 0x18, name: 1, info: 0x10, other: 0, shndx: 1) end end diff --git a/sig/vaporware/compiler/assembler.rbs b/sig/vaporware/compiler/assembler.rbs index aa1db5a..fac4245 100644 --- a/sig/vaporware/compiler/assembler.rbs +++ b/sig/vaporware/compiler/assembler.rbs @@ -1,25 +1,13 @@ -use Vaporware::Compiler::Assembler::ELF::Header -use Vaporware::Compiler::Assembler::ELF::Sections -use Vaporware::Compiler::Assembler::ELF::Section::Text - +use Vaporware::Compiler::Assembler::ELF class Vaporware::Compiler::Assembler @input: String @output: String @assembler: String - @elf_header: Header - @sections: Sections + @elf: ELF @debug: bool def initialize: (input: String, ?output: String, ?type: Symbol, ?debug: bool) -> void def assemble: (?assembler: String, ?assembler_options: Array[String] | [], ?input: String, ?output: String, ?debug: bool) -> String def obj_file: () -> String def to_elf: (?input: String, ?output: String, ?debug: bool) -> void - - private def read!: (?input: String, ?text: Text) -> void - private def init_assemble!: () -> void - private def note!: () -> void - private def text!: () -> void - private def symtab!: () -> void - private def strtab!: () -> void - private def shstrtab!: () -> void end diff --git a/sig/vaporware/compiler/assembler/elf.rbs b/sig/vaporware/compiler/assembler/elf.rbs index dd644c5..78a0346 100644 --- a/sig/vaporware/compiler/assembler/elf.rbs +++ b/sig/vaporware/compiler/assembler/elf.rbs @@ -1,3 +1,23 @@ +use Vaporware::Compiler::Assembler::ELF::Header +use Vaporware::Compiler::Assembler::ELF::Section::Text +use Vaporware::Compiler::Assembler::ELF::Sections + class Vaporware::Compiler::Assembler::ELF Error: singleton(StandardError) + + @input: String + @output: String + @header: Header + @sections: Sections + + def initialize: (type: Symbol, input: String, output: String, debug: bool) -> void + def build: (input: String, output: String, ?debug: bool) -> void + + private def read!: (?input: String, ?text: Text) -> void + private def init_assemble!: () -> void + private def note!: () -> void + private def text!: () -> void + private def symtab!: () -> void + private def strtab!: () -> void + private def shstrtab!: () -> void end From 384b7a186986905713dd569b950095a5b5d7f6f5 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 19 Sep 2024 17:32:16 +0900 Subject: [PATCH 56/58] add mov operation --- .../compiler/assembler/elf/section/text.rb | 12 +++- sample/assembler/variable.s | 63 +++++++++++++++++++ test/vaporware/test_compiler.rb | 2 +- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 sample/assembler/variable.s diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 25a3020..1235c60 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -14,6 +14,7 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text IDIV: 0xf7, IMUL: 0x0f, MOV: 0x89, + MOVR: 0x8B, SUB: 0x83, }.freeze @@ -26,7 +27,7 @@ def assemble!(line) def build = @bytes.flatten.pack("C*") def size = build.bytesize - def align(val, bytes) = (val << [0] until @bytes.map(&:bytesize).sum % bytes == 0) + def align(val, bytes) = (val << [0] until build.bytesize % bytes == 0) private @@ -49,10 +50,17 @@ def opecode(op, *operands) def mov(op, operands) reg = case operands + in ["rax", "rbp"] + [0xe8] in ["rbp", "rsp"] [0xe5] in ["rsp", "rbp"] [0xec] + in ["[rax]", "rdi"] + [0x38] + in ["rax", "[rax]"] + op = "MOVR" + [0x00] else operands&.map { reg(_1) } end # steep:ignore @@ -72,6 +80,8 @@ def calc(op, operands) [ope_code, 0xff] in ["sub", "rsp", *num] [ope_code, 0xec, *num.map { |n| n.to_i(16) }] + in ["sub", "rax", *num] + [ope_code, 0xe8, *num.map { |n| n.to_i(16) }] in ["cqo"] [0x99] end # steep:ignore diff --git a/sample/assembler/variable.s b/sample/assembler/variable.s new file mode 100644 index 0000000..7de5ca9 --- /dev/null +++ b/sample/assembler/variable.s @@ -0,0 +1,63 @@ + .intel_syntax noprefix + .globl main +main: + push rbp + mov rbp, rsp + sub rsp, 24 + mov rax, rbp + sub rax, 8 + push rax + push 1 + pop rdi + pop rax + mov [rax], rdi + push rdi + pop rax + mov rax, rbp + sub rax, 16 + push rax + push 2 + pop rdi + pop rax + mov [rax], rdi + push rdi + pop rax + mov rax, rbp + sub rax, 24 + push rax + mov rax, rbp + sub rax, 8 + push rax + pop rax + mov rax, [rax] + push rax + mov rax, rbp + sub rax, 16 + push rax + pop rax + mov rax, [rax] + push rax + pop rdi + pop rax + add rax, rdi + push rax + push 3 + pop rdi + pop rax + cqo + idiv rdi + push rax + pop rdi + pop rax + mov [rax], rdi + push rdi + pop rax + mov rax, rbp + sub rax, 24 + push rax + pop rax + mov rax, [rax] + push rax + mov rsp, rbp + pop rbp + ret diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 890b50d..97265c9 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -18,7 +18,7 @@ def test_sample_plus def test_sample_variable @file = "sample/variable.rb" - @vaporware = Vaporware::Compiler.compile(@file) + @vaporware = Vaporware::Compiler.compile(@file, assembler: "self") IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(1, exit_code) From ad77519864dc7cbd9854f1e37073ba66cb76098f Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 19 Sep 2024 23:48:42 +0900 Subject: [PATCH 57/58] support if condition opecodes --- lib/vaporware/compiler/assembler/elf.rb | 1 + .../compiler/assembler/elf/section/text.rb | 51 +++++++++++++---- sample/assembler/if.s | 55 +++++++++++++++++++ test/vaporware/test_compiler.rb | 2 +- 4 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 sample/assembler/if.s diff --git a/lib/vaporware/compiler/assembler/elf.rb b/lib/vaporware/compiler/assembler/elf.rb index e92ed5a..c0d42d3 100644 --- a/lib/vaporware/compiler/assembler/elf.rb +++ b/lib/vaporware/compiler/assembler/elf.rb @@ -66,6 +66,7 @@ def read!(input: @input, text: @sections.text.body) r.each_line do |line| read[:main] = line.match(/main:/) unless read[:main] next unless read[:main] && !/main:/.match(line) + next if /\.L.+:/.match(line) text.assemble!(line) end end diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 1235c60..05d4072 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -9,14 +9,16 @@ class Vaporware::Compiler::Assembler::ELF::Section::Text }.freeze OPECODE = { - ADD: 0x01, - CQO: 0x99, - IDIV: 0xf7, - IMUL: 0x0f, - MOV: 0x89, - MOVR: 0x8B, - SUB: 0x83, - }.freeze + ADD: [0x01], + CMP: [0x39], + CQO: [0x99], + IDIV: [0xf7], + IMUL: [0x0f], + MOV: [0x89], + MOVR: [0x8B], + MOVXZ: [0x0f, 0xb7], + SUB: [0x83], + }.freeze def initialize(**opts) = @bytes = [] @@ -35,12 +37,18 @@ def opecode(op, *operands) case op when "push" push(operands) - when "mov" + when "mov", "movzb" [PREFIX[:REX_W], *mov(op, operands)] when "sub", "add", "imul", "cqo", "idiv" [PREFIX[:REX_W], *calc(op, operands)] when "pop" pop(operands) + when "cmp" + [PREFIX[:REX_W], *cmp(op, operands)] + when "sete" + sete(op, operands) + when "je" + jump(op, operands) when "ret" [0xc3] else @@ -48,6 +56,10 @@ def opecode(op, *operands) end end + def jump(op, operands) + [0x74, 0x08] + end + def mov(op, operands) reg = case operands in ["rax", "rbp"] @@ -58,13 +70,16 @@ def mov(op, operands) [0xec] in ["[rax]", "rdi"] [0x38] + in ["rax", "al"] + op = "MOVXZ" + [0xc0] in ["rax", "[rax]"] op = "MOVR" [0x00] else operands&.map { reg(_1) } end # steep:ignore - [OPECODE[op.upcase.to_sym], *reg] + [OPECODE[op.upcase.to_sym], reg].flatten end def calc(op, operands) @@ -87,6 +102,22 @@ def calc(op, operands) end # steep:ignore end + def cmp(op, operands) + case operands + in ["rax", "rdi"] + [0x39, 0xf8] + in ["rax", "0"] + [0x83, 0xf8, 0x00] + end + end + + def sete(op, operands) + case operands + in ["al"] + [0x0f, 0x94, 0xc0] + end + end + def push(operands) case operands in ["rbp"] | ["rdi"] diff --git a/sample/assembler/if.s b/sample/assembler/if.s new file mode 100644 index 0000000..754f39f --- /dev/null +++ b/sample/assembler/if.s @@ -0,0 +1,55 @@ + .intel_syntax noprefix + .globl main +main: + push rbp + mov rbp, rsp + sub rsp, 16 + mov rax, rbp + sub rax, 8 + push rax + push 1 + pop rdi + pop rax + mov [rax], rdi + push rdi + pop rax + mov rax, rbp + sub rax, 16 + push rax + push 1 + pop rdi + pop rax + mov [rax], rdi + push rdi + pop rax + mov rax, rbp + sub rax, 8 + push rax + pop rax + mov rax, [rax] + push rax + mov rax, rbp + sub rax, 16 + push rax + pop rax + mov rax, [rax] + push rax + pop rdi + pop rax + cmp rax, rdi + sete al + movzb rax, al + push rax + pop rax + push rax + cmp rax, 0 + je .Lend0 + push 1 + pop rax + mov rsp, rbp + pop rbp + ret +.Lend0: + mov rsp, rbp + pop rbp + ret diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 97265c9..38fb8c0 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -27,7 +27,7 @@ def test_sample_variable def test_sample_if @file = "sample/if.rb" - @vaporware = Vaporware::Compiler.compile(@file) + @vaporware = Vaporware::Compiler.compile(@file, assembler: "self") IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(1, exit_code) From eea7449d08aa96bacc8ef556b525ef07c7559b3b Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Thu, 19 Sep 2024 23:59:41 +0900 Subject: [PATCH 58/58] support else jump --- .../compiler/assembler/elf/section/text.rb | 25 +++++++++++++---- sample/assembler/else.s | 27 +++++++++++++++++++ test/vaporware/test_compiler.rb | 2 +- 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 sample/assembler/else.s diff --git a/lib/vaporware/compiler/assembler/elf/section/text.rb b/lib/vaporware/compiler/assembler/elf/section/text.rb index 05d4072..848e54c 100644 --- a/lib/vaporware/compiler/assembler/elf/section/text.rb +++ b/lib/vaporware/compiler/assembler/elf/section/text.rb @@ -45,9 +45,9 @@ def opecode(op, *operands) pop(operands) when "cmp" [PREFIX[:REX_W], *cmp(op, operands)] - when "sete" + when "sete", "setl" sete(op, operands) - when "je" + when "je", "jmp" jump(op, operands) when "ret" [0xc3] @@ -57,7 +57,20 @@ def opecode(op, *operands) end def jump(op, operands) - [0x74, 0x08] + case op + when "je" + [0x74, *jaddr(operands)] + when "jmp" + [0xeb, *jaddr(operands)] + end + end + def jaddr(operands) + case operands + in [".Lend0"] + [0x08] + in [".Lelse0"] + [0x0a] + end end def mov(op, operands) @@ -112,9 +125,11 @@ def cmp(op, operands) end def sete(op, operands) - case operands - in ["al"] + case [op, operands] + in ["sete", ["al"]] [0x0f, 0x94, 0xc0] + in ["setl", ["al"]] + [0x0f, 0x9c, 0xc0] end end diff --git a/sample/assembler/else.s b/sample/assembler/else.s new file mode 100644 index 0000000..47334f6 --- /dev/null +++ b/sample/assembler/else.s @@ -0,0 +1,27 @@ + .intel_syntax noprefix + .globl main +main: + push rbp + mov rbp, rsp + sub rsp, 0 + push 0 + pop rax + push rax + cmp rax, 0 + je .Lelse0 + push 1 + pop rax + mov rsp, rbp + pop rbp + ret + jmp .Lend0 +.Lelse0: + push 2 + pop rax + mov rsp, rbp + pop rbp + ret +.Lend0: + mov rsp, rbp + pop rbp + ret diff --git a/test/vaporware/test_compiler.rb b/test/vaporware/test_compiler.rb index 38fb8c0..0173c90 100644 --- a/test/vaporware/test_compiler.rb +++ b/test/vaporware/test_compiler.rb @@ -36,7 +36,7 @@ def test_sample_if def test_sample_else @file = "sample/else.rb" - @vaporware = Vaporware::Compiler.compile(@file) + @vaporware = Vaporware::Compiler.compile(@file, assembler: "self") IO.popen("./tmp").close exit_code, handle_code = check_process($?.to_i) assert_equal(2, exit_code)