From d823f1c1cdd96a0f81f78a4376aa46eb27a4afb5 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 11 Jan 2026 11:04:23 +0900 Subject: [PATCH 1/2] adjust symtab's section header --- lib/caotral/binary/elf/section_header.rb | 1 + lib/caotral/linker/writer.rb | 13 +++++++++++-- test/caotral/linker/writer_test.rb | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) 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..1edcdf0 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 = %w(.text .strtab .shstrtab .symtab).freeze R_X86_64_PC32 = 2 R_X86_64_PLT32 = 4 ALLOW_RELOCATION_TYPES = [R_X86_64_PC32, R_X86_64_PLT32].freeze @@ -73,14 +73,23 @@ def write 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" } + strtabndx = write_sections.index { it.section_name == ".strtab" } + shnum = write_sections.size @elf_obj.header.set!(shoffset:, shnum:, shstrndx:) names = @elf_obj.find_by_name(".shstrtab").body 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 + end + header.set!(name:, info:, link:, entsize:) f.write(section.header.build) 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 From a145bf1f507e114eae035ea3de6ac45a9e526e8f Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sun, 11 Jan 2026 12:27:16 +0900 Subject: [PATCH 2/2] support rel/rela section in linker --- lib/caotral/binary/elf.rb | 9 +++++++++ lib/caotral/linker/writer.rb | 25 ++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) 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/linker/writer.rb b/lib/caotral/linker/writer.rb index 1edcdf0..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 .symtab).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,16 +71,15 @@ 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" } - strtabndx = write_sections.index { it.section_name == ".strtab" } - - 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) @@ -88,6 +88,9 @@ def write 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) @@ -99,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