diff --git a/lib/caotral/binary/elf.rb b/lib/caotral/binary/elf.rb index 8a652b2..1145b54 100644 --- a/lib/caotral/binary/elf.rb +++ b/lib/caotral/binary/elf.rb @@ -24,6 +24,15 @@ def [](idx) = @sections[idx] def find_by_name(section_name) = @sections.find { section_name == it.section_name } def select_by_name(section_name) = @sections.select { section_name == it.section_name } def index(section_name) = @sections.index { section_name == it.section_name } + def select_by_names(section_names) + @sections.select do |section| + next true if section.section_name.nil? + section_names.any? do |name| + re = Regexp === name + re ? section.section_name.match(name) : section.section_name == name + end + end + end end end end diff --git a/lib/caotral/binary/elf/section_header.rb b/lib/caotral/binary/elf/section_header.rb index 3532d12..77f6aa4 100644 --- a/lib/caotral/binary/elf/section_header.rb +++ b/lib/caotral/binary/elf/section_header.rb @@ -46,6 +46,7 @@ def size = @size.pack("C*").unpack1("Q<") def type = SHT_BY_VALUE[@type.pack("C*").unpack1("L<")] def info = @info.pack("C*").unpack1("L<") def addr = @addr.pack("C*").unpack1("Q<") + def link = @link.pack("C*").unpack1("L<") private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize] end diff --git a/lib/caotral/linker/writer.rb b/lib/caotral/linker/writer.rb index ce74edf..9eefea2 100644 --- a/lib/caotral/linker/writer.rb +++ b/lib/caotral/linker/writer.rb @@ -4,7 +4,7 @@ module Caotral class Linker class Writer include Caotral::Binary::ELF::Utils - ALLOW_SECTIONS = %w(.text .strtab .shstrtab).freeze + ALLOW_SECTIONS = [".text", ".strtab", ".shstrtab", ".symtab", /\.rela\./, /\.rel\./].freeze R_X86_64_PC32 = 2 R_X86_64_PLT32 = 4 ALLOW_RELOCATION_TYPES = [R_X86_64_PC32, R_X86_64_PLT32].freeze @@ -15,6 +15,7 @@ def self.write!(elf_obj:, output:, entry: nil, debug: false) end def initialize(elf_obj:, output:, entry: nil, debug: false) @elf_obj, @output, @entry, @debug = elf_obj, output, entry, debug + @write_sections = @elf_obj.select_by_names(ALLOW_SECTIONS) end def write f = File.open(@output, "wb") @@ -70,17 +71,28 @@ def write shstrtab_offset = f.pos f.write(shstrtab.body.names) shstrtab.header.set!(offset: shstrtab_offset, size: shstrtab.body.names.bytesize) - write_sections = @elf_obj.sections.select { ALLOW_SECTIONS.include?(it.section_name) || it.section_name.nil? } shoffset = f.pos - shstrndx = write_sections.index { it.section_name == ".shstrtab" } - shnum = write_sections.size + 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 - write_sections.each do |section| + @write_sections.each do |section| + header = section.header lookup_name = section.section_name name_offset = names.offset_of(lookup_name) - section.header.set!(name: name_offset) if name_offset + 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) + link = symtabndx + info = ref_index(section) + end + header.set!(name:, info:, link:, entsize:) f.write(section.header.build) end @@ -90,6 +102,14 @@ def write ensure f.close if f end + + private + 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" }).first + write_section_index(ref.section_name) + end end end end diff --git a/test/caotral/linker/writer_test.rb b/test/caotral/linker/writer_test.rb index 3b554fb..9c588dc 100644 --- a/test/caotral/linker/writer_test.rb +++ b/test/caotral/linker/writer_test.rb @@ -16,7 +16,7 @@ def test_write written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write.o", debug: false) 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 4, read_written_elf.sections.size + assert_equal 5, read_written_elf.sections.size assert_equal 0x401000, read_written_elf.header.entry end