Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/caotral/binary/elf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions lib/caotral/binary/elf/section_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 26 additions & 6 deletions lib/caotral/linker/writer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -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

Expand All @@ -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)
Comment on lines +109 to +111

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve relocation target section with correct name

When writing an ELF that actually contains relocation sections (e.g., .rela.text or .rel.text), ref_index derives the target name by splitting on . and filtering out rel/rela, which yields "text" rather than ".text". Because select_by_names always includes nil section names, the first match is usually the null section, so write_section_index returns 0 and sh_info for the relocation points to section 0 instead of the referenced section. Tools that read relocations via sh_info will misinterpret or reject these sections. Consider preserving the leading dot (e.g., ".text") and/or filtering out nil sections when resolving the reference.

Useful? React with 👍 / 👎.

end
end
end
end
2 changes: 1 addition & 1 deletion test/caotral/linker/writer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down