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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/coverage/
/doc/
/pkg/
/AGENTS.md
/spec/reports/
/tmp/
/Gemfile.lock
Expand All @@ -13,3 +14,5 @@
/.gem_rbs_collection
/node_modules
/package*.json
/*.md
!/README.md
7 changes: 7 additions & 0 deletions lib/caotral/linker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def link(input: @input, output: @output, debug: @debug, shared: @shared) = IO.po

def link_command(input: @input, output: @output, debug: @debug, shared: @shared)
ld_path = []
return to_elf(input:, output:, debug:) if @linker == "self"

if @shared
ld_path << "--shared"
ld_path << "#{libpath}/crti.o"
Expand All @@ -36,4 +38,9 @@ def link_command(input: @input, output: @output, debug: @debug, shared: @shared)

def libpath = @libpath ||= File.dirname(Dir.glob("/usr/lib*/**/crti.o").last)
def gcc_libpath = @gcc_libpath ||= File.dirname(Dir.glob("/usr/lib/gcc/x86_64-*/*/crtbegin.o").last)

def to_elf(input: @input, output: @output, debug: @debug)
elf_obj = Caotral::Linker::Reader.new(input:, debug:).read
Caotral::Linker::Writer.new(elf_obj:, output:, debug:).write
end
end
7 changes: 7 additions & 0 deletions lib/caotral/linker/elf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Caotral::Linker::ELF
attr_reader :sections, :header
def initialize
@sections = Caotral::Linker::ELF::Sections.new
@header = Caotral::Linker::ELF::Header.new
end
end
60 changes: 60 additions & 0 deletions lib/caotral/linker/elf/header.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class Caotral::Linker::ELF::Header
include Caotral::Assembler::ELF::Utils
IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].freeze
ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze

def initialize(endian: :little, type: :rel, arc: :amd64)
@ident = IDENT
@type = num2bytes(ELF_FILE_TYPE[elf(type)], 2)
@arch = arch(arc)
@version = num2bytes(1, 4)
@entry = num2bytes(0x00, 8)
@phoffset = num2bytes(0x00, 8)
@shoffset = num2bytes(0x00, 8)
@flags = num2bytes(0x00, 4)
@ehsize = num2bytes(0x40, 2)
@phsize = num2bytes(0x00, 2)
@phnum = num2bytes(0x00, 2)
@shentsize = num2bytes(0x40, 2)
@shnum = num2bytes(0x08, 2)
@shstrndx = num2bytes(0x07, 2)
end

def build = bytes.flatten.pack("C*")

def set!(entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil)
@entry = num2bytes(entry, 8) if check(entry, 8)
@phoffset = num2bytes(phoffset, 8) if check(phoffset, 8)
@shoffset = num2bytes(shoffset, 8) if check(shoffset, 8)
@shnum = num2bytes(shnum, 2) if check(shnum, 2)
@shstrndx = num2bytes(shstrndx, 2) if check(shstrndx, 2)
end

private

def bytes = [
@ident, @type, @arch, @version, @entry, @phoffset,
@shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize,
@shnum, @shstrndx
]

def arch(machine)
case machine.to_s
in "amd64" | "x86_64" | "x64"
[0x3e, 0x00]
end
end

def elf(type)
case type.to_s
in "relocatable" | "rel"
:REL
in "exe" | "ex" | "exec"
:EXEC
in "shared" | "share" | "dynamic" | "dyn"
:DYN
else
:NONE
end
end
end
13 changes: 13 additions & 0 deletions lib/caotral/linker/elf/section.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Caotral::Linker::ELF::Section
attr_accessor :name
attr_reader :section_name, :header, :body
def initialize(type:, options: {})
type_string = type.to_s.capitalize
type_string = type_string.upcase if type_string == "Bss"
# section_name is extra information about section type
@section_name = type_string.downcase
# name is used in section header string table in elf file
@name = section_name == "null" ? "" : "\0.#{section_name}"
@header, @body = nil, nil
end
end
6 changes: 6 additions & 0 deletions lib/caotral/linker/elf/section/shstrtab.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Caotral::Linker::ELF::Section::Shstrtab
include Caotral::Assembler::ELF::Utils
def initialize(**opts) = @name = []
def build = bytes.flatten.pack("C*")
def set!(name:) = (@name << name!(name); self)
end
5 changes: 5 additions & 0 deletions lib/caotral/linker/elf/section/strtab.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Caotral::Linker::ELF::Section::Strtab
include Caotral::Assembler::ELF::Utils
def initialize(names = "\0main\0", **opts) = @names = names
def build = @names.bytes.pack("C*")
end
12 changes: 12 additions & 0 deletions lib/caotral/linker/elf/section/symtab.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Caotral::Linker::ELF::Section::Symtab
include Caotral::Assembler::ELF::Utils
def initialize(**opts)
@entsize = []
@name = num2bytes(0, 4)
@info = num2bytes(0, 1)
@other = num2bytes(0, 1)
@shndx = num2bytes(0, 2)
@value = num2bytes(0, 8)
@size = num2bytes(0, 8)
end
end
4 changes: 4 additions & 0 deletions lib/caotral/linker/elf/section/text.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Caotral::Linker::ELF::Section::Text
def initialize = @bytes = []
def build = @bytes.flatten.pack("C*")
end
37 changes: 37 additions & 0 deletions lib/caotral/linker/elf/section_header.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class Caotral::Linker::ELF::SectionHeader
include Caotral::Assembler::ELF::Utils
def initialize
@name = nil
@type = nil
@flags = nil
@addr = nil
@offset = nil
@size = nil
@link = nil
@info = nil
@addralign = nil
@entsize = nil
end

def build = bytes.flatten.pack("C*")

def set!(name: nil, type: nil, flags: nil, addr: nil,
offset: nil, size: nil, link: nil, info: nil,
addralign: nil, entsize: nil)
@name = num2bytes(name, 4) if check(name, 4)
@type = num2bytes(type, 4) if check(type, 4)
@flags = num2bytes(flags, 8) if check(flags, 8)
@addr = num2bytes(addr, 8) if check(addr, 8)
@offset = num2bytes(offset, 8) if check(offset, 8)
@size = num2bytes(size, 8) if check(size, 8)
@link = num2bytes(link, 4) if check(link, 4)
@info = num2bytes(info, 4) if check(info, 4)
@addralign = num2bytes(addralign, 8) if check(addralign, 8)
@entsize = num2bytes(entsize, 8) if check(entsize, 8)
self
end

def null! = set!(name: 0, type: 0, flags: 0, addr: 0, offset: 0, size: 0, link: 0, info: 0, addralign: 0, entsize: 0)

private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize]
end
18 changes: 18 additions & 0 deletions lib/caotral/linker/elf/sections.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Caotral::Linker::ELF::Sections
include Enumerable

def initialize = @sections = []
def each(&block) = @sections.each(&block)
def add(section) = @sections << section
alias << add
def [](index)
case index
when Integer
@sections[index]
when String, Symbol
@sections.find { _1.section_name.to_s == index.to_s }
else
raise ArgumentError, "Invalid index type: #{index.class}"
end
end
end
29 changes: 29 additions & 0 deletions lib/caotral/linker/reader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Caotral::Linker::Reader
attr_reader :sections
def self.read!(input:, debug: false, linker_options: [])
new(input:, debug:, linker_options:).read
end

def initialize(input:, debug: false, linker_options: [])
@input = decision(input)
@bin = @input.read
@context = Caotral::Linker::ELF.new
end

def read
@context
ensure
@input.close
end

private

def decision(input)
case input
when String, Pathname
File.open(File.expand_path(input.to_s), "rb")
else
raise ArgumentError, "wrong input type"
end
end
end
8 changes: 8 additions & 0 deletions lib/caotral/linker/writer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Caotral::Linker::Writer
attr_reader :elf_obj, :output, :debug
def self.write!(elf_obj:, output:, debug: false)
new(elf_obj:, output:, debug:).write
end
def initialize(elf_obj:, output:, debug: false) = @elf_obj, @output, @debug = elf_obj, output, debug
def write = output
end
17 changes: 17 additions & 0 deletions sig/caotral/linker.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Caotral::Linker
@input: String
@output: String
@linker: String
@options: Array[String]
@shared: bool
@debug: bool

def self.link!: (input: String, ?output: String, ?linker: String, ?debug: bool, ?shared: bool) -> void
def initialize: (input: String, ?output: String, ?linker: String, ?linker_options: Array[String], ?shared: bool, ?debug: bool) -> void
def link: (input: String, ?output: String, ?shared: bool, ?debug: bool) -> void

def link_command: (input: String, ?output: String, ?shared: bool, ?debug: bool) -> String
def libpath: () -> String
def gcc_libpath: () -> String
def to_elf: (input: String, ?output: String, ?debug: bool) -> void
end
3 changes: 3 additions & 0 deletions sig/caotral/linker/elf.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Caotral::Linker::ELF
def initialize: () -> void
end
5 changes: 5 additions & 0 deletions sig/caotral/linker/reader.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Caotral::Linker::Reader
def self.read!: (input: String, ?debug: bool, ?linker_options: Array[String]) -> Caotral::Linker::ELF
def initialize: (input: String, ?debug: bool, ?linker_options: Array[String]) -> void
def read: () -> Caotral::Linker::ELF
end
5 changes: 5 additions & 0 deletions sig/caotral/linker/writer.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Caotral::Linker::Writer
def self.write!: (elf_obj: Caotral::Linker::ELF, output: String, ?debug: bool) -> String
def initialize: (elf_obj: Caotral::Linker::ELF, output: String, ?debug: bool) -> void
def write: () -> String
end