Skip to content
Merged
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
10 changes: 10 additions & 0 deletions lib/caotral/binary/elf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
require_relative "elf/section/strtab"
require_relative "elf/section/symtab"
require_relative "elf/section_header"
require_relative "elf/symtab_methods"

require_relative "elf/reader"

module Caotral
Expand All @@ -29,6 +31,14 @@ def select_by_name(section_name) = @sections.select { |s| section_name == s.sect
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 } }
def entry_symbol
symtab = find_by_name(".symtab")
raise Caotral::Binary::ELF::Error, "missing .symtab section!!!" unless symtab
raise Caotral::Binary::ELF::Error, "invalid .symtab section!!!" unless symtab.respond_to?(:find_defined_symbol)
symtab.find_defined_symbol("_start") || symtab.find_defined_symbol("main")
end

def entry_start? = entry_symbol&.name_string == "_start"
end
end
end
6 changes: 5 additions & 1 deletion lib/caotral/binary/elf/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class ELF
class Reader
attr_reader :context
def self.read!(input:, debug: false, linker_options: []) = new(input:, debug:, linker_options:).read
SymbolTable = Caotral::Binary::ELF::SymbolTable
Comment thread
katsyoshi marked this conversation as resolved.

def initialize(input:, debug: false, linker_options: [])
@input = decision(input)
Expand Down Expand Up @@ -99,7 +100,9 @@ def read
size = sym_bin[16, 8].unpack1("Q<")
name_string = @context.sections[section.header.link]&.body&.lookup(name).to_s

Caotral::Binary::ELF::Section::Symtab.new.set!(name:, info:, other:, shndx:, value:, size:, name_string:)
symtab = Caotral::Binary::ELF::Section::Symtab.new
symtab.set!(name:, info:, other:, shndx:, value:, size:, name_string:)
symtab
end
when :rel, :rela
rela = type == :rela
Expand All @@ -126,6 +129,7 @@ def read
when :progbits
body_bin
end
section.extend(SymbolTable) if [:symtab, :dynsym].include?(type)
end

strtab = @context.find_by_name(".strtab")
Expand Down
9 changes: 9 additions & 0 deletions lib/caotral/binary/elf/symtab_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Caotral
module Binary
class ELF
module SymbolTable
def find_defined_symbol(symbol_name) = body.find { |sym| sym.name_string == symbol_name && sym.shndx != 0 }
end
end
end
end
3 changes: 2 additions & 1 deletion lib/caotral/linker.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative "binary/elf/reader"

require_relative "binary/elf"
require_relative "linker/builder"
require_relative "linker/layout"
require_relative "linker/finalizer"
Expand Down
63 changes: 48 additions & 15 deletions lib/caotral/linker/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Caotral
class Linker
class Builder
include Caotral::Binary::ELF::Utils
NULL_BIN = "\x00".b.freeze
NOP_BIN = "\x90".b.freeze
REL_TYPES = Caotral::Binary::ELF::Section::Rel::TYPES
DYNAMIC_TAGS = Caotral::Binary::ELF::Section::Dynamic::TAG_TYPES
SYMTAB_BIND = { locals: 0, globals: 1, weaks: 2, }.freeze
Expand All @@ -15,6 +17,7 @@ class Builder
SHT = Caotral::Binary::ELF::SectionHeader::SHT
SHF = Caotral::Binary::ELF::SectionHeader::SHF
SectionDynamic = Caotral::Binary::ELF::Section::Dynamic
SymbolTableMethods = Caotral::Binary::ELF::SymbolTable
UNSUPPORTED_REL_TYPES = [
REL_TYPES[:AMD64_GOTPCREL],
REL_TYPES[:AMD64_GOTPCRELX],
Expand Down Expand Up @@ -99,6 +102,7 @@ def build
section_name: ".symtab",
header: Caotral::Binary::ELF::SectionHeader.new
)
symtab_section.extend(SymbolTableMethods)
shstrtab_section = Caotral::Binary::ELF::Section.new(
body: Caotral::Binary::ELF::Section::Strtab.new("\0".b),
section_name: ".shstrtab",
Expand Down Expand Up @@ -135,35 +139,55 @@ def build
got_plt_offsets = {}
text_offset = 0
rodata_offset = 0
data_offset = 0
bss_offset = 0
got_plt_offset = 24
bss_present = false
text_addralign = 1
rodata_addralign = 1
data_addralign = 1
bss_addralign = 1
data_offset = 0
got_plt_offset = 24
sym_by_elf = Hash.new { |h, k| h[k] = [] }
dynstr, dynsym = build_shared_dynamic_sections if dynamic?

start_len = 0 if @elf_objs.any? { |elf| elf.find_by_name(".symtab").body.find { |sym| sym.name_string == "_start" } }
start_len = 0 if @executable && @elf_objs.any? { |elf| elf.entry_start? }

@elf_objs.each do |elf_obj|
text = elf_obj.find_by_name(".text")
unless text.nil?
alignment = [text.header.addralign, 1].max
aligned_offset = align_up(start_len + text_offset, alignment) - start_len
padding_size = aligned_offset - text_offset

text_section.body << (NOP_BIN * padding_size)
text_offsets[elf_obj.object_id] = aligned_offset
text_section.body << text.body
text_offsets[elf_obj.object_id] = text_offset
size = text.body.bytesize
text_offset += size
text_offset = aligned_offset + text.build.bytesize
text_addralign = [text_addralign, alignment].max
end
rodata = elf_obj.find_by_name(".rodata")
unless rodata.nil?
alignment = [rodata.header.addralign, 1].max
aligned_offset = align_up(rodata_offset, alignment)
padding_size = aligned_offset - rodata_offset

rodata_section.body << (NULL_BIN * padding_size)
rodata_offsets[elf_obj.object_id] = aligned_offset
rodata_section.body << rodata.body
rodata_offsets[elf_obj.object_id] = rodata_offset
rodata_offset += rodata.body.bytesize
rodata_offset = aligned_offset + rodata.build.bytesize
rodata_addralign = [rodata_addralign, alignment].max
end
data = elf_obj.find_by_name(".data")
unless data.nil?
alignment = [data.header.addralign, 1].max
aligned_offset = align_up(data_offset, alignment)
padding_size = aligned_offset - data_offset

data_section.body << (NULL_BIN * padding_size)
data_offsets[elf_obj.object_id] = aligned_offset
Comment thread
katsyoshi marked this conversation as resolved.
data_section.body << data.body
data_offsets[elf_obj.object_id] = data_offset
data_offset += data.body.bytesize
data_offset = aligned_offset + data.build.bytesize
data_addralign = [data_addralign, alignment].max
end
bss = elf_obj.find_by_name(".bss")
unless bss.nil?
Expand Down Expand Up @@ -257,8 +281,13 @@ def build
elsif UNSUPPORTED_REL_TYPES.include?(rel.type)
raise Caotral::Binary::ELF::Error, "unsupported relocation type: #{rel.type_name}"
end
offset = rel.offset + text_offsets.fetch(elf_obj.object_id, 0)
offset += start_len if section.section_name.to_s.start_with?(".rela.text", ".rel.text")

offset = case section.section_name.to_s
when ".rela.text", ".rel.text"
rel.offset + start_len + text_offsets.fetch(elf_obj.object_id, 0)
when ".rela.data", ".rel.data"
rel.offset + data_offsets.fetch(elf_obj.object_id, 0)
end
addend = rel.addend? ? rel.addend : nil
new_rel = Caotral::Binary::ELF::Section::Rel.new(addend: rel.addend?)
info = (sym << 32) | rel.type
Expand All @@ -273,8 +302,8 @@ def build
strtab_section.body.names = strtab_names.to_a.sort.join("\0") + "\0"
sections << null_section

_start = symtab_section.body.find { |sym| sym.name_string == "_start" }
entry_sym = _start || symtab_section.body.find { |sym| sym.name_string == "main" }
_start = symtab_section.find_defined_symbol("_start")
entry_sym = _start || symtab_section.find_defined_symbol("main")
Comment thread
katsyoshi marked this conversation as resolved.

raise Caotral::Binary::ELF::Error, "main or _start function not found" if @executable && entry_sym.nil?
if _start.nil?
Expand All @@ -289,12 +318,14 @@ def build
addr: vaddr,
offset: exec_text_offset,
size: text_section.body.bytesize,
addralign: 16
addralign: text_addralign
Comment thread
katsyoshi marked this conversation as resolved.
)

sections << text_section
strtab_section.header.set!(type: 3, flags: 0, addralign: 1, entsize: 0)
rodata_section.header.set!(addralign: rodata_addralign)
sections << rodata_section
data_section.header.set!(addralign: data_addralign)
sections << data_section
if dynamic?
sections << plt_section
Expand Down Expand Up @@ -497,6 +528,8 @@ def build_shared_dynamic_sections
header: Caotral::Binary::ELF::SectionHeader.new
)

dynsym_section.extend(SymbolTableMethods)

dynsym_section.header.set!(info: 1, type: SHT[:dynsym], flags: SHF[:ALLOC], addralign: 8, entsize: 24)

[dynstr_section, dynsym_section,]
Expand Down
13 changes: 11 additions & 2 deletions lib/caotral/linker/layout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def apply!

private
def classify_load_sections!
allocated_sections = @elf.sections.select { |s| s.header.allocated? }
read_only_sections = allocated_sections.select { |s| !s.header.execinstr? && !s.header.writable? }
executable_sections = allocated_sections.select { |s| s.header.execinstr? && !s.header.writable? }
writable_sections = allocated_sections.select { |s| !s.header.execinstr? && s.header.writable? }
Expand Down Expand Up @@ -64,7 +63,14 @@ def build_program_header(type:, flags: 0)
def layout_sections!
@file_offset = header_end
text = @elf.find_by_name(".text")
@load_bias = text ? text.header.addr - text.header.offset : 0
entry_offset = 0
@load_bias = 0
if text
entry_offset = @elf.header.entry - text.header.addr
requested_bias = text.header.addr - text.header.offset
max_alignment = allocated_sections.map { |s| [s.header.addralign, 1].max }.max
@load_bias = align_up(requested_bias, max_alignment)
end

@load_sections.each do |flags, sections|
next if sections.empty?
Expand All @@ -90,6 +96,8 @@ def layout_sections!
addr = @load_bias + section.header.offset
section.header.set!(addr:)
end
@elf.header.set!(entry: text.header.addr + entry_offset) if @executable && text
@elf
end
def layout_section_headers!
shstrtab = @elf.find_by_name(".shstrtab")
Expand Down Expand Up @@ -184,6 +192,7 @@ def header_end
raise Error, "must have ELF header" unless Caotral::Binary::ELF::Header === @elf.header
@elf.header.build.bytesize + @elf.program_headers.sum { |ph| ph.build.bytesize }
end
def allocated_sections = @elf.sections.select { |s| s.header.allocated? }
end
end
end
7 changes: 7 additions & 0 deletions sample/assembler/aligned_data_relocation.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.data
.balign 32
.globl aligned_data_reference
.type aligned_data_reference, @object
aligned_data_reference:
.long prefix_data
.size aligned_data_reference, .-aligned_data_reference
25 changes: 25 additions & 0 deletions sample/assembler/aligned_sections.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.text
.balign 32
.globl aligned_function
.type aligned_function, @function
aligned_function:
mov aligned_rodata(%rip), %eax
add aligned_data(%rip), %eax
ret
.size aligned_function, .-aligned_function

.section .rodata
.balign 32
.globl aligned_rodata
.type aligned_rodata, @object
aligned_rodata:
.long 40
.size aligned_rodata, .-aligned_rodata

.data
.balign 32
.globl aligned_data
.type aligned_data, @object
aligned_data:
.long 2
.size aligned_data, .-aligned_data
21 changes: 21 additions & 0 deletions sample/assembler/alignment_prefix.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.text
.globl main
.type main, @function
main:
call aligned_function
ret
.size main, .-main

.section .rodata
.globl prefix_rodata
.type prefix_rodata, @object
prefix_rodata:
.byte 0x11
.size prefix_rodata, .-prefix_rodata

.data
.globl prefix_data
.type prefix_data, @object
prefix_data:
.byte 0x22
.size prefix_data, .-prefix_data
12 changes: 12 additions & 0 deletions sample/assembler/undefined_start.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.text
.globl main
.type main, @function
main:
mov $42, %eax
ret
.size main, .-main

.weak _start

.data
.long _start
2 changes: 2 additions & 0 deletions sig/caotral/binary/elf.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ class Caotral::Binary::ELF
def index: (String | Symbol) -> Integer?
def select_by_names: (Array[String | Regexp]) -> Array[Caotral::Binary::ELF::Section]
def without_sections: (Array[String | Regexp]) -> Array[Caotral::Binary::ELF::Section]
def entry_symbol: () -> Caotral::Binary::ELF::Section::Symtab?
def entry_start?: () -> bool
end
3 changes: 3 additions & 0 deletions sig/caotral/binary/elf/symtab_methods.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Caotral::Binary::ELF::SymbolTable
def find_defined_symbol: (String) -> Caotral::Binary::ELF::Section::Symtab?
end
Loading
Loading