diff --git a/lib/caotral/linker.rb b/lib/caotral/linker.rb index c27e6f5d..60c4c082 100644 --- a/lib/caotral/linker.rb +++ b/lib/caotral/linker.rb @@ -64,7 +64,7 @@ def to_elf(inputs: @inputs, output: @output, debug: @debug, shared: @shared, exe Caotral::Linker::Layout.new(elf:, shared:, executable:, pie:).apply! elf_obj = Caotral::Linker::Finalizer.new(elf:, metadata:, shared:, executable:, pie:, debug:).apply! - Caotral::Linker::Writer.new(elf_obj:, output:, debug:, shared:, executable:, pie:).write + Caotral::Linker::Writer.new(elf_obj:, output:).write File.chmod(0755, output) if executable output end diff --git a/lib/caotral/linker/layout.rb b/lib/caotral/linker/layout.rb index bcea0f51..0f883a97 100644 --- a/lib/caotral/linker/layout.rb +++ b/lib/caotral/linker/layout.rb @@ -79,10 +79,15 @@ def layout_section_headers! @file_offset += @elf.sections.sum { |section| section.header.build.bytesize } end def finalize_program_headers! + text = @elf.find_by_name(".text") + load_bias = text.nil? ? 0 : text.header.addr - text.header.offset + phdr = @elf.program_headers.any? { |ph| ph.type == :PHDR } @elf.program_headers.each do |ph| case ph.type when :PHDR - offset, vaddr, paddr, align = 64, 64, 64, 8 + offset, align = 64, 8 + vaddr = load_bias + offset + paddr = vaddr filesz = @elf.program_headers.size * 56 memsz = filesz flags = Caotral::Binary::ELF::ProgramHeader::PF[:R] @@ -91,13 +96,13 @@ def finalize_program_headers! next unless text allocated_sections = @elf.sections.select { |s| s.header.allocated? } - segment_start = @pie ? 0 : text.header.offset + segment_start = phdr ? 0 : allocated_sections.map { |s| s.header.offset }.min segment_end = allocated_sections.map { |s| s.header.offset + s.header.size }.max next unless segment_end offset = segment_start - vaddr = @pie ? 0 : text.header.addr + vaddr = load_bias + offset paddr = vaddr filesz = segment_end - segment_start memsz = filesz @@ -106,7 +111,8 @@ def finalize_program_headers! when :INTERP interp = @elf.find_by_name(".interp") raise Caotral::Linker::Error, "Missing .interp section" unless interp - offset, vaddr, paddr, align = interp.header.offset, 0, 0, 1 + header = interp.header + offset, vaddr, paddr, align = header.offset, header.addr, header.addr, 1 filesz = interp.header.size memsz = filesz flags = Caotral::Binary::ELF::ProgramHeader::PF[:R] @@ -117,7 +123,7 @@ def finalize_program_headers! offset, vaddr, paddr, align = header.offset, header.addr, header.addr, header.addralign filesz = header.size memsz = filesz - flags = Caotral::Binary::ELF::ProgramHeader::PF[:R] + flags = Caotral::Binary::ELF::ProgramHeader::PF[:RW] when :GNU_STACK offset, vaddr, paddr, align = 0, 0, 0, 0 filesz = 0 diff --git a/lib/caotral/linker/writer.rb b/lib/caotral/linker/writer.rb index 136965f2..876e4805 100644 --- a/lib/caotral/linker/writer.rb +++ b/lib/caotral/linker/writer.rb @@ -3,67 +3,26 @@ module Caotral class Linker class Writer - include Caotral::Binary::ELF::Utils - RELOCATION_SECTION_NAMES = [".rela.text", ".rela.dyn", ".rela.data", ".rela.plt"].freeze - attr_reader :elf_obj, :output, :debug, :program_headers - def self.write!(elf_obj:, output:, debug: false, executable: true, shared: false) - new(elf_obj:, output:, debug:, shared:, executable:).write - end - - def initialize(elf_obj:, output:, debug: false, executable: true, shared: false, pie: false) - @elf_obj, @output, @debug, @executable, @shared, @pie = elf_obj, output, debug, executable, shared, pie - @program_headers = elf_obj.program_headers - @write_sections = elf_obj.sections - end + def self.write!(elf_obj:, output:) = new(elf_obj:, output:).write + def initialize(elf_obj:, output:) = (@elf_obj, @output = elf_obj, output) def write - f = File.open(@output, "wb") - - f.write(@elf_obj.header.build) - program_headers.each { |ph| f.write(ph.build) } + File.open(@output, "wb") do |file| - write_elf_sections(file: f) - - # relocation - rel_sections.each { |rel| write_section(file: f, section: rel) } - write_section(file: f, section: shstrtab_section) - shoffset = @elf_obj.header.shoffset - f.seek(shoffset) - write_section_headers(file: f) - output - ensure - f.close if f - end - - private - def write_elf_sections(file:) - write_section(file:, section: text_section) + file.write(@elf_obj.header.build) + @elf_obj.program_headers.each { |ph| file.write(ph.build) } - write_section(file:, section: rodata_section) if rodata_section - write_section(file:, section: data_section) if data_section + write_elf_sections(file:) - if plt_section - write_section(file:, section: plt_section) - raise Caotral::Binary::ELF::Error, "missing .got.plt for .plt" if got_plt_section.nil? - write_section(file:, section: got_plt_section) + file.seek(@elf_obj.header.shoffset) + write_section_headers(file:) end - - write_shared_dynamic_sections(file:) if dynamic? - write_section(file:, section: symtab_section) - write_section(file:, section: strtab_section) - end - - def write_shared_dynamic_sections(file:) - write_section(file:, section: interp_section) if interp_section - write_section(file:, section: dynstr_section) if dynstr_section - write_section(file:, section: dynsym_section) if dynsym_section - write_section(file:, section: hash_section) if dynamic? - write_section(file:, section: dynamic_section) if dynamic_section + @output end - def write_section_headers(file:) - @write_sections.each { |section| file.write(section.header.build) } - end + private + def write_elf_sections(file:) = @elf_obj.sections.each { |section| write_section(file:, section:) } + def write_section_headers(file:) = @elf_obj.sections.each { |section| file.write(section.header.build) } def write_section(file:, section:) return unless section @@ -71,25 +30,6 @@ def write_section(file:, section:) file.seek(section.header.offset) file.write(section.build) end - - def dynamic? = (@shared || @pie) - - def text_section = @text_section ||= @write_sections.find { |s| ".text" === s.section_name.to_s } - def rel_sections = @rel_sections ||= @write_sections.select { |s| RELOCATION_SECTION_NAMES.include?(s.section_name.to_s) } - 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 } - def dynstr_section = @dynstr_section ||= @write_sections.find { |s| ".dynstr" === s.section_name.to_s } - def dynsym_section = @dynsym_section ||= @write_sections.find { |s| ".dynsym" === s.section_name.to_s } - def dynamic_section = @dynamic_section ||= @write_sections.find { |s| ".dynamic" === s.section_name.to_s } - def interp_section = @interp_section ||= @write_sections.find { |s| ".interp" === s.section_name.to_s } - def rela_dyn_section = @rela_dyn_section ||= @write_sections.find { |s| ".rela.dyn" === s.section_name.to_s } - def data_section = @data_section ||= @write_sections.find { |s| ".data" === s.section_name.to_s } - def rodata_section = @rodata_section ||= @write_sections.find { |s| ".rodata" === s.section_name.to_s } - def hash_section = @hash_section ||= @write_sections.find { |s| ".hash" === s.section_name.to_s } - def plt_section = @plt_section ||= @write_sections.find { |s| ".plt" === s.section_name.to_s } - def got_plt_section = @got_plt_section ||= @write_sections.find { |s| ".got.plt" === s.section_name.to_s } - def rela_plt_section = @rela_plt_section ||= @write_sections.find { |s| ".rela.plt" === s.section_name.to_s } end end end diff --git a/sig/caotral/linker/writer.rbs b/sig/caotral/linker/writer.rbs index edb23d38..a0a16554 100644 --- a/sig/caotral/linker/writer.rbs +++ b/sig/caotral/linker/writer.rbs @@ -1,19 +1,8 @@ class Caotral::Linker::Writer @elf_obj: Caotral::Binary::ELF @output: String - @debug: bool - @executable: bool - @shared: bool - @pie: bool - @program_headers: Array[Caotral::Binary::ELF::ProgramHeader] - @write_sections: Array[Caotral::Binary::ELF::Section] - attr_reader elf_obj: Caotral::Binary::ELF - attr_reader output: String - attr_reader debug: bool - attr_reader program_headers: Array[Caotral::Binary::ELF::ProgramHeader] - - def self.write!: (elf_obj: Caotral::Binary::ELF, output: String, ?debug: bool, ?executable: bool, ?shared: bool) -> String - def initialize: (elf_obj: Caotral::Binary::ELF, output: String, ?debug: bool, ?executable: bool, ?shared: bool, ?pie: bool) -> void + def self.write!: (elf_obj: Caotral::Binary::ELF, output: String) -> String + def initialize: (elf_obj: Caotral::Binary::ELF, output: String) -> void def write: () -> String end diff --git a/test/caotral/linker/integration/needed_test.rb b/test/caotral/linker/integration/needed_test.rb index 9765c5e4..93fa7a39 100644 --- a/test/caotral/linker/integration/needed_test.rb +++ b/test/caotral/linker/integration/needed_test.rb @@ -35,7 +35,7 @@ def test_needed_in_executable Caotral::Linker.link!(inputs: [@input], output: @output, linker: "self", pie: true, needed: ["libc.so.6"], executable: true) IO.popen(["./#{@output}"]).close - pid, exit_code = check_process($?.to_i) + _pid, exit_code = check_process($?.to_i) assert_equal(0, exit_code) end @@ -91,7 +91,7 @@ def test_start_in_executable io = IO.popen(["./#{@output}"]) stdout = io.read io.close - pid, exit_code = check_process($?.to_i) + _pid, exit_code = check_process($?.to_i) assert_equal("hello, world!!!\n", stdout) assert_equal(0, exit_code) end diff --git a/test/caotral/linker/integration/symbol_remap_test.rb b/test/caotral/linker/integration/symbol_remap_test.rb index eddbbcef..0d9cf729 100644 --- a/test/caotral/linker/integration/symbol_remap_test.rb +++ b/test/caotral/linker/integration/symbol_remap_test.rb @@ -25,7 +25,7 @@ def test_remap_external_symbols assert_not_include(dynsym_names, "symbol_remap_external_b.c") assert_empty(elf.find_by_name(".rela.plt").body) _o, _e, status = Open3.capture3("./#{execute}") - ec, hc = check_process(status.to_i) + ec, _hc = check_process(status.to_i) assert_equal(ec, 42) end @@ -44,7 +44,7 @@ def test_remap_external_symbols_using_glibc assert_include(dynsym_names, "value_from_b") assert_include(dynsym_names, "puts") _o, _e, status = Open3.capture3("./#{execute}") - ec, hc = check_process(status.to_i) + ec, _hc = check_process(status.to_i) assert_equal(ec, 42) end end diff --git a/test/caotral/linker/layout_test.rb b/test/caotral/linker/layout_test.rb index ba616518..8df6d789 100644 --- a/test/caotral/linker/layout_test.rb +++ b/test/caotral/linker/layout_test.rb @@ -85,7 +85,7 @@ def test_layout_with_dynamic assert_equal(dynamic.header.size, elf.program_headers[1].filesz) assert_equal(dynamic.header.size, elf.program_headers[1].memsz) assert_equal(dynamic.header.addralign, elf.program_headers[1].align) - assert_equal(:R, elf.program_headers[1].flags) + assert_equal(:RW, elf.program_headers[1].flags) end def test_layout_sections diff --git a/test/caotral/linker/writer_test.rb b/test/caotral/linker/writer_test.rb index 734dd6ce..7b077a96 100644 --- a/test/caotral/linker/writer_test.rb +++ b/test/caotral/linker/writer_test.rb @@ -19,7 +19,7 @@ def teardown File.delete("relocated_exec") if File.exist?("relocated_exec") end def test_write - written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write.o", debug: false) + written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write.o") read_written_elf = Caotral::Binary::ELF::Reader.read!(input: written_output, debug: false) assert_equal @elf_obj.header.shoffset, read_written_elf.header.shoffset assert_equal 7, read_written_elf.sections.size @@ -27,7 +27,7 @@ def test_write end def test_execute_written - Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write", debug: false) + Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write") File.chmod(0755, "./write") IO.popen("./write").close exit_code, handle_code = check_process($?.to_i) @@ -44,7 +44,7 @@ def test_relocation_write_and_execute metadata = builder.linker_metadata Caotral::Linker::Layout.new(elf:, shared: false, pie: false, executable: true).apply! Caotral::Linker::Finalizer.new(elf:, metadata:, shared: false, pie: false, executable: true, debug: false).apply! - Caotral::Linker::Writer.write!(elf_obj: elf, output: "relocated_exec", debug: false) + Caotral::Linker::Writer.write!(elf_obj: elf, output: "relocated_exec") File.chmod(0755, "./relocated_exec") IO.popen("./relocated_exec").close exit_code, _handle_code = check_process($?.to_i)