From e7e111e2b522170fc443cc712f36b5f67290e89b Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Wed, 24 Dec 2025 00:23:05 +0900 Subject: [PATCH 1/2] exclude ai agent docs --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2144d3d..d2d627d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /coverage/ /doc/ /pkg/ +/AGENTS.md /spec/reports/ /tmp/ /Gemfile.lock @@ -13,3 +14,5 @@ /.gem_rbs_collection /node_modules /package*.json +/*.md +!/README.md From d8167f38ff481e1ad6ac65f5ddb6cc775206fcbe Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 27 Dec 2025 12:15:24 +0900 Subject: [PATCH 2/2] add linker elf stubs --- lib/caotral/linker.rb | 7 +++ lib/caotral/linker/elf.rb | 7 +++ lib/caotral/linker/elf/header.rb | 60 ++++++++++++++++++++++ lib/caotral/linker/elf/section.rb | 13 +++++ lib/caotral/linker/elf/section/shstrtab.rb | 6 +++ lib/caotral/linker/elf/section/strtab.rb | 5 ++ lib/caotral/linker/elf/section/symtab.rb | 12 +++++ lib/caotral/linker/elf/section/text.rb | 4 ++ lib/caotral/linker/elf/section_header.rb | 37 +++++++++++++ lib/caotral/linker/elf/sections.rb | 18 +++++++ lib/caotral/linker/reader.rb | 29 +++++++++++ lib/caotral/linker/writer.rb | 8 +++ sig/caotral/linker.rbs | 17 ++++++ sig/caotral/linker/elf.rbs | 3 ++ sig/caotral/linker/reader.rbs | 5 ++ sig/caotral/linker/writer.rbs | 5 ++ 16 files changed, 236 insertions(+) create mode 100644 lib/caotral/linker/elf.rb create mode 100644 lib/caotral/linker/elf/header.rb create mode 100644 lib/caotral/linker/elf/section.rb create mode 100644 lib/caotral/linker/elf/section/shstrtab.rb create mode 100644 lib/caotral/linker/elf/section/strtab.rb create mode 100644 lib/caotral/linker/elf/section/symtab.rb create mode 100644 lib/caotral/linker/elf/section/text.rb create mode 100644 lib/caotral/linker/elf/section_header.rb create mode 100644 lib/caotral/linker/elf/sections.rb create mode 100644 lib/caotral/linker/reader.rb create mode 100644 lib/caotral/linker/writer.rb create mode 100644 sig/caotral/linker.rbs create mode 100644 sig/caotral/linker/elf.rbs create mode 100644 sig/caotral/linker/reader.rbs create mode 100644 sig/caotral/linker/writer.rbs diff --git a/lib/caotral/linker.rb b/lib/caotral/linker.rb index 9a73dd2..80c2a3f 100644 --- a/lib/caotral/linker.rb +++ b/lib/caotral/linker.rb @@ -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" @@ -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 diff --git a/lib/caotral/linker/elf.rb b/lib/caotral/linker/elf.rb new file mode 100644 index 0000000..75bcaae --- /dev/null +++ b/lib/caotral/linker/elf.rb @@ -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 diff --git a/lib/caotral/linker/elf/header.rb b/lib/caotral/linker/elf/header.rb new file mode 100644 index 0000000..ea1785f --- /dev/null +++ b/lib/caotral/linker/elf/header.rb @@ -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 diff --git a/lib/caotral/linker/elf/section.rb b/lib/caotral/linker/elf/section.rb new file mode 100644 index 0000000..d7b46cb --- /dev/null +++ b/lib/caotral/linker/elf/section.rb @@ -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 diff --git a/lib/caotral/linker/elf/section/shstrtab.rb b/lib/caotral/linker/elf/section/shstrtab.rb new file mode 100644 index 0000000..244e5e7 --- /dev/null +++ b/lib/caotral/linker/elf/section/shstrtab.rb @@ -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 diff --git a/lib/caotral/linker/elf/section/strtab.rb b/lib/caotral/linker/elf/section/strtab.rb new file mode 100644 index 0000000..f44e311 --- /dev/null +++ b/lib/caotral/linker/elf/section/strtab.rb @@ -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 diff --git a/lib/caotral/linker/elf/section/symtab.rb b/lib/caotral/linker/elf/section/symtab.rb new file mode 100644 index 0000000..de51dc8 --- /dev/null +++ b/lib/caotral/linker/elf/section/symtab.rb @@ -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 diff --git a/lib/caotral/linker/elf/section/text.rb b/lib/caotral/linker/elf/section/text.rb new file mode 100644 index 0000000..e3998f5 --- /dev/null +++ b/lib/caotral/linker/elf/section/text.rb @@ -0,0 +1,4 @@ +class Caotral::Linker::ELF::Section::Text + def initialize = @bytes = [] + def build = @bytes.flatten.pack("C*") +end diff --git a/lib/caotral/linker/elf/section_header.rb b/lib/caotral/linker/elf/section_header.rb new file mode 100644 index 0000000..98223ba --- /dev/null +++ b/lib/caotral/linker/elf/section_header.rb @@ -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 diff --git a/lib/caotral/linker/elf/sections.rb b/lib/caotral/linker/elf/sections.rb new file mode 100644 index 0000000..e84c4a4 --- /dev/null +++ b/lib/caotral/linker/elf/sections.rb @@ -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 diff --git a/lib/caotral/linker/reader.rb b/lib/caotral/linker/reader.rb new file mode 100644 index 0000000..ec36eed --- /dev/null +++ b/lib/caotral/linker/reader.rb @@ -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 diff --git a/lib/caotral/linker/writer.rb b/lib/caotral/linker/writer.rb new file mode 100644 index 0000000..689b84b --- /dev/null +++ b/lib/caotral/linker/writer.rb @@ -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 diff --git a/sig/caotral/linker.rbs b/sig/caotral/linker.rbs new file mode 100644 index 0000000..493aabe --- /dev/null +++ b/sig/caotral/linker.rbs @@ -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 diff --git a/sig/caotral/linker/elf.rbs b/sig/caotral/linker/elf.rbs new file mode 100644 index 0000000..a295925 --- /dev/null +++ b/sig/caotral/linker/elf.rbs @@ -0,0 +1,3 @@ +class Caotral::Linker::ELF + def initialize: () -> void +end diff --git a/sig/caotral/linker/reader.rbs b/sig/caotral/linker/reader.rbs new file mode 100644 index 0000000..689f5e4 --- /dev/null +++ b/sig/caotral/linker/reader.rbs @@ -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 diff --git a/sig/caotral/linker/writer.rbs b/sig/caotral/linker/writer.rbs new file mode 100644 index 0000000..fdc3288 --- /dev/null +++ b/sig/caotral/linker/writer.rbs @@ -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