diff --git a/lib/caotral.rb b/lib/caotral.rb index 6d5ab81..7a11bdb 100644 --- a/lib/caotral.rb +++ b/lib/caotral.rb @@ -14,7 +14,7 @@ def compile!(input:, assembler: "as", linker: "ld", output: "tmp", debug: false, execf = "#{basename}#{File.extname(d)}" compile(input:, output: basename+".s", debug:, shared:) assemble(input: basename+".s", output: basename+".o", assembler:, debug:, shared:) - link(input: basename+".o", output: execf, linker:, debug:, shared:) + link(input: [basename+".o"], output: execf, linker:, debug:, shared:) end def compile(input:, output: "tmp.s", debug: false, shared: false) Caotral::Compiler.compile!(input:, output:, debug:) @@ -23,6 +23,7 @@ def assemble(input:, output: "tmp.o", debug: false, shared: false, assembler: "a Caotral::Assembler.assemble!(input:, output:, debug:, assembler:, shared:) end def link(input:, output: "tmp", linker: "ld", debug: false, shared: false) - Caotral::Linker.link!(input:, output:, linker:, debug:, shared:) + inputs = Array === input ? input : [input] + Caotral::Linker.link!(inputs:, output:, linker:, debug:, shared:) end end diff --git a/lib/caotral/binary/elf.rb b/lib/caotral/binary/elf.rb index 0836008..e859004 100644 --- a/lib/caotral/binary/elf.rb +++ b/lib/caotral/binary/elf.rb @@ -25,6 +25,7 @@ def find_by_name(section_name) = @sections.find { |s| section_name == s.section_ def select_by_name(section_name) = @sections.select { |s| section_name == s.section_name } def index(section_name) = @sections.index { |s| section_name == s.section_name } def select_by_names(section_names) = @sections.select { |section| section_names.any? { |name| name === section.section_name.to_s } } + def without_sections(names) = @sections.reject { |s| names.any? { |name| name === s.section_name.to_s } } end end end diff --git a/lib/caotral/binary/elf/section/rel.rb b/lib/caotral/binary/elf/section/rel.rb index 4c0f520..5e17fe2 100644 --- a/lib/caotral/binary/elf/section/rel.rb +++ b/lib/caotral/binary/elf/section/rel.rb @@ -14,7 +14,7 @@ def initialize(addend: true) def set!(offset: nil, info: nil, addend: nil) @offset = num2bytes(offset, 8) if check(offset, 8) @info = num2bytes(info, 8) if check(info, 8) - @addend = num2bytes(addend, 8) if check(addend, 8) + @addend = [addend].pack("q<").unpack("C*") if check(addend, 8) self end @@ -23,7 +23,7 @@ def offset = @offset.pack("C*").unpack1("Q<") def info = @info.pack("C*").unpack1("Q<") def addend raise "No addend field in this REL entry" unless addend? - @addend.pack("C*").unpack1("Q<") + @addend.pack("C*").unpack1("q<") end def sym = @info.pack("C*").unpack1("Q<") >> 32 def type = @info.pack("C*").unpack1("Q<") & 0xffffffff diff --git a/lib/caotral/binary/elf/section/symtab.rb b/lib/caotral/binary/elf/section/symtab.rb index 06c7b0f..c6f5676 100644 --- a/lib/caotral/binary/elf/section/symtab.rb +++ b/lib/caotral/binary/elf/section/symtab.rb @@ -32,6 +32,9 @@ def set!(name: nil, info: nil, other: nil, shndx: nil, value: nil, size: nil) def name_offset = @name.pack("C*").unpack1("L<") def value = @value.pack("C*").unpack1("Q<") def info = @info.pack("C*").unpack1("C") + def shndx = @shndx.pack("C*").unpack1("S<") + def bind = info >> 4 + def type = info & 0x0f private def bytes = [@name, @info, @other, @shndx, @value, @size] end diff --git a/lib/caotral/linker.rb b/lib/caotral/linker.rb index ea5764d..58f97c8 100644 --- a/lib/caotral/linker.rb +++ b/lib/caotral/linker.rb @@ -5,21 +5,21 @@ module Caotral class Linker - def self.link!(input:, output: "a.out", linker: "mold", debug: false, shared: false) = new(input:, output:, linker:, debug:, shared:).link + def self.link!(inputs:, output: "a.out", linker: "mold", debug: false, shared: false) = new(inputs:, output:, linker:, debug:, shared:).link - def initialize(input:, output: "a.out", linker: "mold", linker_options: [], shared: false, debug: false) - @input, @output, @linker = input, output, linker + def initialize(inputs:, output: "a.out", linker: "mold", linker_options: [], shared: false, debug: false) + @inputs, @output, @linker = inputs, output, linker @options = linker_options @debug, @shared = debug, shared end - def link(input: @input, output: @output, debug: @debug, shared: @shared) - return to_elf(input:, output:, debug:) if @linker == "self" + def link(inputs: @inputs, output: @output, debug: @debug, shared: @shared) + return to_elf(inputs:, output:, debug:) if @linker == "self" IO.popen(link_command).close end - def link_command(input: @input, output: @output, debug: @debug, shared: @shared) + def link_command(inputs: @inputs, output: @output) ld_path = [] if @shared @@ -39,7 +39,7 @@ def link_command(input: @input, output: @output, debug: @debug, shared: @shared) ld_path << "#{libpath}/libc.so" ld_path << "#{libpath}/crtn.o" - cmd = [@linker, "-o", @output, "-m", "elf_x86_64", *@options, *ld_path, @input].join(' ') + cmd = [@linker, "-o", @output, "-m", "elf_x86_64", *@options, *ld_path, *inputs].join(' ') puts cmd if @debug cmd end @@ -47,10 +47,11 @@ def link_command(input: @input, output: @output, debug: @debug, shared: @shared) 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) - def to_elf(input: @input, output: @output, debug: @debug) - elf_obj = Caotral::Binary::ELF::Reader.new(input:, debug:).read - builder = Caotral::Linker::Builder.new(elf_obj:) + def to_elf(inputs: @inputs, output: @output, debug: @debug) + elf_objs = inputs.map { |input| Caotral::Binary::ELF::Reader.new(input:, debug:).read } + builder = Caotral::Linker::Builder.new(elf_objs:) builder.resolve_symbols + elf_obj = builder.build Caotral::Linker::Writer.new(elf_obj:, output:, debug:).write end end diff --git a/lib/caotral/linker/builder.rb b/lib/caotral/linker/builder.rb index 928353f..67981b6 100644 --- a/lib/caotral/linker/builder.rb +++ b/lib/caotral/linker/builder.rb @@ -6,25 +6,213 @@ class Linker class Builder SYMTAB_BIND = { locals: 0, globals: 1, weaks: 2, }.freeze BIND_BY_VALUE = SYMTAB_BIND.invert.freeze + RELOCATION_SECTION_NAMES = [".rela.text", ".rel.text"].freeze attr_reader :symbols - def initialize(elf_obj:) - @elf_obj = elf_obj + def initialize(elf_objs:) + @elf_objs = elf_objs @symbols = { locals: Set.new, globals: Set.new, weaks: Set.new } end + + def build + raise Caotral::Binary::ELF::Error, "no ELF objects to link" if @elf_objs.empty? + elf = Caotral::Binary::ELF.new + elf_obj = @elf_objs.first + first_text = elf_obj.find_by_name(".text") + null_section = Caotral::Binary::ELF::Section.new( + body: nil, + section_name: "", + header: Caotral::Binary::ELF::SectionHeader.new + ) + text_section = Caotral::Binary::ELF::Section.new( + body: String.new, + section_name: ".text", + header: Caotral::Binary::ELF::SectionHeader.new + ) + strtab_section = Caotral::Binary::ELF::Section.new( + body: Caotral::Binary::ELF::Section::Strtab.new("\0".b), + section_name: ".strtab", + header: Caotral::Binary::ELF::SectionHeader.new + ) + symtab_section = Caotral::Binary::ELF::Section.new( + body: [], + section_name: ".symtab", + header: Caotral::Binary::ELF::SectionHeader.new + ) + shstrtab_section = Caotral::Binary::ELF::Section.new( + body: Caotral::Binary::ELF::Section::Strtab.new("\0".b), + section_name: ".shstrtab", + header: Caotral::Binary::ELF::SectionHeader.new + ) + rel_sections = [] + elf.header = elf_obj.header.dup + strtab_names = [] + text_offsets = {} + text_offset = 0 + sym_by_elf = Hash.new { |h, k| h[k] = [] } + @elf_objs.each do |elf_obj| + text = elf_obj.find_by_name(".text") + unless text.nil? + text_section.body << text.body + text_offsets[elf_obj.object_id] = text_offset + size = text.body.bytesize + text_offset += size + end + strtab = elf_obj.find_by_name(".strtab") + strtab.body.names.split("\0").each { |name| strtab_names << name } unless strtab.nil? + symtab = elf_obj.find_by_name(".symtab") + base_index = nil + unless symtab.nil? + base_index = symtab_section.body.size + symtab.body.each_with_index do |st, index| + sym = Caotral::Binary::ELF::Section::Symtab.new + name, info, other, shndx, value, size = st.build.unpack("L> 4 } + local_count = symtab_section.body.count { |sym| (sym.info >> 4) == SYMTAB_BIND[:locals] } + + symtab_section.header.set!( + type: 2, + flags: 0, + link: elf.sections.index(strtab_section), + info: local_count, + addralign: 8, + entsize: 24 + ) + + elf.sections << symtab_section + + rel_sections.each { |s| elf.sections << s.dup } + + shstrtab_section.header.set!( + type: 3, + flags: 0, + addralign: 1, + entsize: 0 + ) + shstrtab_section_names = elf.sections.map(&:section_name).join("\0") + "\0" + shstrtab_section.body.names = shstrtab_section_names + elf.sections << shstrtab_section + + @elf_objs.first.without_sections([".text", ".strtab", ".symtab", ".shstrtab", /\.rela?\./]).each do |section| + elf.sections << section.dup + end + section_map = Hash.new { |h, k| h[k] = {} } + @elf_objs.each do |elf_obj| + elf_obj.sections.each_with_index do |section, index| + newndx = elf.sections.index { |s| s.section_name == section.section_name } + section_map[elf_obj][index] = newndx unless newndx.nil? + end + end + + old_ids = old_syms.map(&:object_id) + + resolved_index = {} + symtab_section.body.each_with_index do |sym, index| + old_index = old_ids.index(sym.object_id) + name = sym.name_string + next if name.empty? || sym.shndx == 0 || sym.bind != 1 + resolved_index[name] ||= index + end + + sym_by_elf.each do |elf_obj, syms| + syms.each do |sym| + next if sym.shndx == 0 + shndx = section_map[elf_obj][sym.shndx] + sym.set!(shndx:) + end + end + elf.select_by_names(RELOCATION_SECTION_NAMES).each do |rel_section| + rel_section.body.each do |rel| + orig_sym = old_syms[rel.sym] + next if orig_sym.nil? + name = orig_sym.name_string + new_index = resolved_index[name] + next if new_index.nil? + rel.set!(info: (new_index << 32) | rel.type) + end + + rel_section.header.set!( + type: rel_type(rel_section), + flags: 0, + link: elf.sections.index(symtab_section), + info: ref_index(elf, rel_section.section_name), + addralign: 8, + entsize: rel_entsize(rel_section) + ) + end + + elf + end def resolve_symbols - @elf_obj.find_by_name(".symtab").body.each do |symtab| - name = symtab.name_string - next if name.empty? - info = symtab.info - bind = BIND_BY_VALUE.fetch(info >> 4) - if bind == :globals && @symbols[bind].include?(name) - raise Caotral::Binary::ELF::Error,"cannot add into globals: #{name}" + @elf_objs.each do |elf_obj| + elf_obj.find_by_name(".symtab").body.each do |symtab| + name = symtab.name_string + next if name.empty? + info = symtab.info + bind = BIND_BY_VALUE.fetch(info >> 4) + if bind == :globals && @symbols[bind].include?(name) && symtab.shndx != 0 + raise Caotral::Binary::ELF::Error,"cannot add into globals: #{name}" + end + @symbols[bind] << name end - @symbols[bind] << name end @symbols end + + private + def ref_index(elf_obj, section_name) + raise Caotral::Binary::ELF::Error, "invalid section name: #{section_name}" if section_name.nil? + ref = elf_obj.select_by_names(section_name.split(".").filter { |sn| !sn.empty? && sn != "rel" && sn != "rela" }.map { "." + it }).first + elf_obj.index(ref.section_name) + end + + def rel_type(section) = section.section_name&.start_with?(".rela.") ? 4 : 9 + def rel_entsize(section) = section.section_name&.start_with?(".rela.") ? 24 : 16 end end end diff --git a/lib/caotral/linker/writer.rb b/lib/caotral/linker/writer.rb index 01aad8b..122a97d 100644 --- a/lib/caotral/linker/writer.rb +++ b/lib/caotral/linker/writer.rb @@ -21,10 +21,8 @@ def write phoffset, phnum, phsize, ehsize = 64, 1, 56, 64 header = @elf_obj.header.set!(type: 2, phoffset:, phnum:, phsize:, ehsize:) ph = Caotral::Binary::ELF::ProgramHeader.new - text_section = @elf_obj.find_by_name(".text") - rel_sections = @elf_obj.sections.select { RELOCATION_SECTION_NAMES.include?(it.section_name) } start_bytes = [0xe8, *[0] * 4, 0x48, 0x89, 0xc7, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05] - symtab = @elf_obj.find_by_name(".symtab") + symtab = symtab_section symtab_body = symtab.body old_text = text_section.body main_sym = symtab_body.find { |sym| sym.name_string == "main" } @@ -41,19 +39,20 @@ def write type, flags = 1, 5 filesz = text_section.body.bytesize memsz = filesz - + text_section.header.set!(size: text_section.body.bytesize, addr: vaddr, offset: text_offset) rel_sections.each do |rel| - target = @elf_obj.sections[rel.header.info] + target = @write_sections[rel.header.info] bytes = target.body.dup rel.body.each do |entry| next unless ALLOW_RELOCATION_TYPES.include?(entry.type) - a = entry.type == R_X86_64_PC32 ? 4 : 0 sym = symtab_body[entry.sym] + next if sym.nil? || sym.shndx == 0 target_addr = target == text_section ? vaddr : target.header.addr - sym_addr = sym.value + target_addr + (target == text_section ? start_len : 0) - sym_offset = entry.offset + (target == text_section ? start_len : 0) + sym_addr = sym.shndx >= 0xff00 ? sym.value : @write_sections[sym.shndx].then { |st| st.header.addr + sym.value } + sym_addr += start_len + sym_offset = entry.offset + start_len sym_addend = entry.addend? ? entry.addend : bytes[sym_offset, 4].unpack1("l<") - value = sym_addr + sym_addend - (target_addr + sym_offset + a) + value = sym_addr + sym_addend - (target_addr + sym_offset) bytes[sym_offset, 4] = [value].pack("l<") end target.body = bytes @@ -61,36 +60,58 @@ def write header.set!(entry: @entry || base_addr + text_offset) ph.set!(type:, offset: text_offset, vaddr:, paddr:, filesz:, memsz:, flags:, align:) - text_section.header.set!(size: text_section.body.bytesize, addr: vaddr, offset: text_offset) f.write(@elf_obj.header.build) f.write(ph.build) gap = [text_offset - f.pos, 0].max f.write("\0" * gap) f.write(text_section.body) - shstrtab = @elf_obj.find_by_name(".shstrtab") - shstrtab_offset = f.pos - f.write(shstrtab.body.names) - shstrtab.header.set!(offset: shstrtab_offset, size: shstrtab.body.names.bytesize) + symtab_offset = f.pos + text_index = write_section_index(".text") + symtab_section.body.each do |sym| + next unless sym.shndx == text_index + sym.set!(value: sym.value + vaddr + start_len) + end + symtab_section.body.each { |sym| f.write(sym.build) } + symtab_entsize = symtab_section.body.first&.build&.bytesize.to_i + symtab_size = f.pos - symtab_offset + symtab_section.header.set!(offset: symtab_offset, size: symtab_size, entsize: symtab_entsize) + strtab_offset = f.pos + f.write(strtab_section.body.build) + strtab_section.header.set!(offset: strtab_offset, size: strtab_section.body.names.bytesize) + + rel_sections.each do |rel| + rel_offset = f.pos + rel.body.each { |entry| f.write(entry.build) } + rel_size = f.pos - rel_offset + entsize = rel.body.first&.build&.bytesize.to_i + rel.header.set!(offset: rel_offset, size: rel_size, entsize:) + end + offset = f.pos + names = @write_sections.map { |s| s.section_name.to_s } + if names.last != ".shstrtab" + raise Caotral::Binary::ELF::Error, "section header string table must be the last section" + end + shstrtab_section.body.names = names.uniq.join("\0") + "\0" + shstrtab_section.header.set!(offset:, size: shstrtab_section.body.names.bytesize) + f.write(shstrtab_section.body.names) shoffset = f.pos shstrndx = write_section_index(".shstrtab") strtabndx = write_section_index(".strtab") symtabndx = write_section_index(".symtab") shnum = @write_sections.size @elf_obj.header.set!(shoffset:, shnum:, shstrndx:) - names = @elf_obj.find_by_name(".shstrtab").body + names = shstrtab_section.body @write_sections.each do |section| header = section.header lookup_name = section.section_name name_offset = names.offset_of(lookup_name) - name, info, link, entsize = (name_offset.nil? ? 0 : name_offset), header.info, header.link, 0 - if header.type == :symtab - info = section.body.size - link = strtabndx - entsize = header.entsize.nonzero? || 24 - elsif [:rel, :rela].include?(header.type) + name, info, entsize = (name_offset.nil? ? 0 : name_offset), header.info, header.entsize + link = header.link + link = strtabndx if section.section_name == ".symtab" + if [:rel, :rela].include?(header.type) link = symtabndx - info = ref_index(section) + info = ref_index(section.section_name) end header.set!(name:, info:, link:, entsize:) f.write(section.header.build) @@ -110,16 +131,24 @@ def write_order_sections write_order << @elf_obj.find_by_name(".text") write_order << @elf_obj.find_by_name(".symtab") write_order << @elf_obj.find_by_name(".strtab") - write_order.concat(@elf_obj.select_by_names([/\.rel\./, /\.rela\./])) + write_order.concat(@elf_obj.select_by_names(RELOCATION_SECTION_NAMES)) write_order << @elf_obj.find_by_name(".shstrtab") write_order.compact end def write_section_index(section_name) = @write_sections.index { it.section_name == section_name } - def ref_index(section) - section_name = section.section_name - ref = @elf_obj.select_by_names(section_name.split(".").filter { |sn| !sn.empty? && sn != "rel" && sn != "rela" }.map { "." + it }).first + def ref_index(section_name) + ref_name = section_name.split(".").filter { |sn| !sn.empty? && sn != "rel" && sn != "rela" } + ref_name = "." + ref_name.join(".") + ref = @write_sections.find { |s| s.section_name == ref_name } + raise Caotral::Binary::ELF::Error, "cannot find reference section for #{section_name}" if ref.nil? write_section_index(ref.section_name) end + + def text_section = @text_section ||= @write_sections.find { |s| ".text" === s.section_name.to_s } + def rel_sections = @rel_sections ||= @write_sections.select { RELOCATION_SECTION_NAMES.include?(it.section_name) } + def symtab_section = @symtab_section ||= @write_sections.find { |s| ".symtab" === s.section_name.to_s } + def strtab_section = @strtab_section ||= @write_sections.find { |s| ".strtab" === s.section_name.to_s } + def shstrtab_section = @shstrtab_section ||= @write_sections.find { |s| ".shstrtab" === s.section_name.to_s } end end end diff --git a/sample/C/multi-file-link-a.c b/sample/C/multi-file-link-a.c new file mode 100644 index 0000000..bf7759e --- /dev/null +++ b/sample/C/multi-file-link-a.c @@ -0,0 +1 @@ +int foo() { return 42; } diff --git a/sample/C/multi-file-link-b.c b/sample/C/multi-file-link-b.c new file mode 100644 index 0000000..bd5eb92 --- /dev/null +++ b/sample/C/multi-file-link-b.c @@ -0,0 +1,2 @@ +#include "multifile.h" +int main() { return foo(); } diff --git a/sample/C/multifile.h b/sample/C/multifile.h new file mode 100644 index 0000000..5a2ca62 --- /dev/null +++ b/sample/C/multifile.h @@ -0,0 +1 @@ +int foo(void); diff --git a/sig/caotral/binary/elf.rbs b/sig/caotral/binary/elf.rbs index df74cdc..8a0df98 100644 --- a/sig/caotral/binary/elf.rbs +++ b/sig/caotral/binary/elf.rbs @@ -11,4 +11,5 @@ class Caotral::Binary::ELF def select_by_name: (String | Symbol) -> Array[Caotral::Binary::ELF::Section] def index: (String | Symbol) -> Integer? def select_by_names: (Array[String | Regexp]) -> Array[Caotral::Binary::ELF::Section] + def without_section: (String | Symbol) -> Array[Caotral::Binary::ELF::Section] end diff --git a/sig/caotral/compiler/linker.rbs b/sig/caotral/compiler/linker.rbs index 7fbf9de..e69fc64 100644 --- a/sig/caotral/compiler/linker.rbs +++ b/sig/caotral/compiler/linker.rbs @@ -1,13 +1,13 @@ class Caotral::Compiler::Linker - @input: String + @inputs: Array[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 initialize: (inputs: Array[String], ?output: String, ?linker: String, ?linker_options: Array[String], ?shared: bool, ?debug: bool) -> void + def link: (inputs: Array[String], ?output: String, ?shared: bool, ?debug: bool) -> void def link_command: () -> String def libpath: () -> String diff --git a/sig/caotral/linker.rbs b/sig/caotral/linker.rbs index f87cd68..d3971f9 100644 --- a/sig/caotral/linker.rbs +++ b/sig/caotral/linker.rbs @@ -1,17 +1,17 @@ class Caotral::Linker - @input: String + @inputs: Array[String] @output: String @linker: String @options: Array[String] @shared: bool @debug: bool - def self.link!: (input: String, ?output: String, ?linker: String, ?debug: bool, ?shared: bool) -> void - 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 self.link!: (inputs: Array[String], ?output: String, ?linker: String, ?debug: bool, ?shared: bool) -> void + def initialize: (inputs: Array[String], ?output: String, ?linker: String, ?linker_options: Array[String], ?shared: bool, ?debug: bool) -> void + def link: (inputs: Array[String], ?output: String, ?shared: bool, ?debug: bool) -> void - def link_command: (input: String, ?output: String, ?shared: bool, ?debug: bool) -> String + def link_command: (inputs: Array[String], ?output: String, ?shared: bool, ?debug: bool) -> String def libpath: () -> String def gcc_libpath: () -> String - def to_elf: (input: String, ?output: String, ?debug: bool) -> String + def to_elf: (inputs: Array[String], ?output: String, ?debug: bool) -> String end diff --git a/test/caotral/linker/multi-link_test.rb b/test/caotral/linker/multi-link_test.rb new file mode 100644 index 0000000..73ca3a5 --- /dev/null +++ b/test/caotral/linker/multi-link_test.rb @@ -0,0 +1,31 @@ +require_relative "../../test_suite" + +class Caotral::Linker::MultiFileLinkingTest < Test::Unit::TestCase + include TestProcessHelper + + def setup + @outputs = ["a.o", "b.o", "multifile"].freeze + @inputs = ["a", "b"].freeze + end + + def teardown + # @outputs.each { |output| File.delete(output) if File.exist?(output) } + end + + def test_link_multi_files + inputs = [] + output = @outputs.last + @inputs.each do |x| + path = Pathname.new("sample/C/multi-file-link-#{x}.c").to_s + o = "%s.o" % x + IO.popen(["gcc", "-o", o, "-c", "%s" % path]).close + inputs << o + end + Caotral::Linker.link!(inputs:, output:, linker: "self") + File.chmod(0755, "./#{output}") + IO.popen("./#{output}").close + exit_code, handle_code = check_process($?.to_i) + assert_equal(exit_code, 42) + assert_equal(handle_code, 0) + end +end diff --git a/test/caotral/linker_test.rb b/test/caotral/linker_test.rb index 1c117c2..e145e29 100644 --- a/test/caotral/linker_test.rb +++ b/test/caotral/linker_test.rb @@ -3,15 +3,15 @@ class Caotral::LinkerTest < Test::Unit::TestCase def test_librarie - linker = Caotral::Linker.new(input: "tmp.o") + linker = Caotral::Linker.new(inputs: ["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 = Caotral::Linker.new(input: "tmp.o", output: "tmp") + linker = Caotral::Linker.new(inputs: ["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 = Caotral::Linker.new(input: "tmp.o", output: "tmp", shared: true) + linker = Caotral::Linker.new(inputs: ["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_suite.rb b/test/test_suite.rb new file mode 100644 index 0000000..1dc1eb3 --- /dev/null +++ b/test/test_suite.rb @@ -0,0 +1,12 @@ +require "caotral" +require "test/unit" +require "pathname" + +module TestProcessHelper + private + def check_process(status) + exit_code = status >> 8 + handle_code = status & 0x7f + [exit_code, handle_code] + end +end