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
4 changes: 3 additions & 1 deletion lib/caotral/binary/elf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_relative "elf/program_header"
require_relative "elf/section"
require_relative "elf/section/dynamic"
require_relative "elf/section/hash"
require_relative "elf/section/rel"
require_relative "elf/section/strtab"
require_relative "elf/section/symtab"
Expand All @@ -14,9 +15,10 @@ module Caotral
module Binary
class ELF
include Enumerable
attr_reader :sections
attr_reader :sections, :program_headers
attr_accessor :header
def initialize
@program_headers = []
@sections = []
@header = nil
end
Expand Down
28 changes: 28 additions & 0 deletions lib/caotral/binary/elf/program_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@ module Binary
class ELF
class ProgramHeader
include Caotral::Binary::ELF::Utils
PF_X = 1
PF_W = 2
PF_R = 4
PF = {
RWX: PF_R | PF_W | PF_X,
RW: PF_R | PF_W,
RX: PF_R | PF_X,
WX: PF_W | PF_X,
R: PF_R,
W: PF_W,
X: PF_X,
NOP: 0,
}.freeze
PF_BY_V = PF.invert.freeze
PT = {
NULL: 0,
LOAD: 1,
DYNAMIC: 2,
INTERP: 3,
}.freeze
PT_BY_V = PT.invert.freeze
def initialize
@type = num2bytes(0, 4)
@flags = num2bytes(0, 4)
Expand All @@ -27,6 +48,13 @@ def set!(type: nil, flags: nil, offset: nil, vaddr: nil, paddr: nil, filesz: nil
@align = num2bytes(align, 8) if check(align, 8)
self
end

def type = PT_BY_V[@type.pack("C*").unpack1("L<")]
def flags = PF_BY_V[@flags.pack("C*").unpack1("L<")]
def offset = @offset.pack("C*").unpack1("Q<")
def filesz = @filesz.pack("C*").unpack1("Q<")
def memsz = @memsz.pack("C*").unpack1("Q<")

private def bytes = [@type, @flags, @offset, @vaddr, @paddr, @filesz, @memsz, @align]
end
end
Expand Down
30 changes: 29 additions & 1 deletion lib/caotral/binary/elf/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,28 @@ def read
entry = header[24, 8].unpack1("Q<")
phoffset = header[32, 8].unpack1("Q<")
shoffset = header[40, 8].unpack1("Q<")
phsize = header[54, 2].unpack1("S<")
phnum = header[56, 2].unpack1("S<")
shentsize = header[58, 2].unpack1("S<")
shnum = header[60, 2].unpack1("S<")
shstrndx = header[62, 2].unpack1("S<")
@context.header.set!(type:, arch:, entry:, phoffset:, shoffset:, shnum:, shstrndx:)
@context.header.set!(type:, arch:, entry:, phoffset:, phsize:, phnum:, shoffset:, shnum:, shstrndx:)

@bin.pos = phoffset
phnum.times do |i|
ph_entry = @bin.read(phsize)
type = ph_entry[0, 4].unpack1("L<")
flags = ph_entry[4, 4].unpack1("L<")
offset = ph_entry[8, 8].unpack1("Q<")
vaddr = ph_entry[16, 8].unpack1("Q<")
paddr = ph_entry[24, 8].unpack1("Q<")
filesz = ph_entry[32, 8].unpack1("Q<")
memsz = ph_entry[40, 8].unpack1("Q<")
align = ph_entry[48, 8].unpack1("Q<")
ph = Caotral::Binary::ELF::ProgramHeader.new
ph.set!(type:, flags:, offset:, vaddr:, paddr:, filesz:, memsz:, align:)
@context.program_headers.push(ph)
end

@bin.pos = shoffset
shnum.times do |i|
Expand Down Expand Up @@ -93,6 +111,16 @@ def read
addend = rela ? rel_bin[16, 8].unpack1("q<") : nil
Caotral::Binary::ELF::Section::Rel.new(addend: rela).set!(offset:, info:, addend:)
end
when :dynamic
dyn_entsize = section.header.entsize
dyn_entsize = 16 if dyn_entsize.zero?
count = body_bin.bytesize / dyn_entsize
count.times.map do |i|
dyn_bin = body_bin.byteslice(i * dyn_entsize, dyn_entsize)
tag = dyn_bin[0, 8].unpack1("Q<")
un = dyn_bin[8, 8].unpack1("Q<")
Caotral::Binary::ELF::Section::Dynamic.new.set!(tag:, un:)
end
when :progbits
body_bin
end
Expand Down
14 changes: 13 additions & 1 deletion lib/caotral/binary/elf/section/dynamic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ class ELF
class Section
class Dynamic
include Caotral::Binary::ELF::Utils
TAG_TYPES = { NULL: 0 }.freeze
TAG_TYPES = {
NULL: 0,
HASH: 4,
STRTAB: 5,
SYMTAB: 6,
RELA: 7,
RELASZ: 8,
RELAENT: 9,
STRSZ: 10,
SYMENT: 11,
TEXTREL: 22,
}.freeze
TAG_TYPES_BY_V = TAG_TYPES.invert.freeze

def initialize
@tag = num2bytes(0, 8)
Expand Down
20 changes: 20 additions & 0 deletions lib/caotral/binary/elf/section/hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require "caotral/binary/elf/utils"
module Caotral
module Binary
class ELF
class Section
class Hash
include Caotral::Binary::ELF::Utils
def initialize(nchain:, nbucket: 1)
@nbucket = num2bytes(nbucket, 4)
@nchain = num2bytes(nchain, 4)
@bucket = Array.new(nbucket, num2bytes(0, 4))
@chain = Array.new(nchain, num2bytes(0, 4))
end

private def bytes = [@nbucket, @nchain, *@bucket, *@chain]
end
end
end
end
end
12 changes: 12 additions & 0 deletions lib/caotral/binary/elf/section/rel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ class ELF
class Section
class Rel
include Caotral::Binary::ELF::Utils
TYPES = {
AMD64_NONE: 0,
AMD64_64: 1,
AMD64_PC32: 2,
AMD64_GOT32: 3,
AMD64_PLT32: 4,
AMD64_COPY: 5,
AMD64_GLOB_DAT: 6,
}.freeze
TYPES_BY_V = TYPES.invert.freeze

def initialize(addend: true)
@offset = num2bytes(0, 8)
@info = num2bytes(0, 8)
Expand All @@ -27,6 +38,7 @@ def addend
end
def sym = @info.pack("C*").unpack1("Q<") >> 32
def type = @info.pack("C*").unpack1("Q<") & 0xffffffff
def type_name = TYPES_BY_V[type]
def addend? = !!@addend

private def bytes = addend? ? [@offset, @info, @addend] : [@offset, @info]
Expand Down
6 changes: 6 additions & 0 deletions lib/caotral/binary/elf/section_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class ELF
class SectionHeader
include Caotral::Binary::ELF::Utils
SHT = { null: 0, progbits: 1, symtab: 2, strtab: 3, rela: 4, hash: 5, dynamic: 6, note: 7, nobits: 8, rel: 9, shlib: 10, dynsym: 11, }.freeze
SHF = {
WRITE: 0x1,
ALLOC: 0x2,
EXECINSTR: 0x4,
}.freeze
SHT_BY_VALUE = SHT.invert.freeze

def initialize
Expand Down Expand Up @@ -47,6 +52,7 @@ 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<")
def addralign = @addralign.pack("C*").unpack1("Q<")

private def bytes = [@name, @type, @flags, @addr, @offset, @size, @link, @info, @addralign, @entsize]
end
Expand Down
18 changes: 9 additions & 9 deletions lib/caotral/linker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@

module Caotral
class Linker
def self.link!(inputs:, output: "a.out", linker: "mold", debug: false, shared: false, executable: true)
new(inputs:, output:, linker:, debug:, shared:, executable:).link
def self.link!(inputs:, output: "a.out", linker: "mold", debug: false, shared: false, executable: true, pie: false)
new(inputs:, output:, linker:, debug:, shared:, executable:, pie:).link
end

def initialize(inputs:, output: "a.out", linker: "mold", linker_options: [], executable: true, shared: false, debug: false)
def initialize(inputs:, output: "a.out", linker: "mold", linker_options: [], executable: true, shared: false, pie: false, debug: false)
@inputs, @output, @linker = inputs, output, linker
@options = linker_options
@executable, @debug, @shared = executable, debug, shared
@executable, @debug, @shared, @pie = executable, debug, shared, pie
end

def link(inputs: @inputs, output: @output, debug: @debug, shared: @shared, executable: @executable)
return to_elf(inputs:, output:, debug:, shared:, executable:) if @linker == "self"
def link(inputs: @inputs, output: @output, debug: @debug, shared: @shared, executable: @executable, pie: @pie)
return to_elf(inputs:, output:, debug:, shared:, executable:, pie:) if @linker == "self"

IO.popen(link_command).close
Comment on lines +18 to 21

Choose a reason for hiding this comment

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

P1 Badge Honor pie flag for non-self linkers

When linker is not "self" (the default path), the pie flag is ignored and the code just runs link_command without adding a -pie/--pie option. This means callers using Linker.link!(..., pie: true, linker: "mold") still get a non-PIE executable, which defeats the new feature outside the self-linker path. The issue is triggered whenever pie: true is combined with a non-self linker.

Useful? React with 👍 / 👎.

end
Expand Down Expand Up @@ -49,12 +49,12 @@ def link_command(inputs: @inputs, output: @output)
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(inputs: @inputs, output: @output, debug: @debug, shared: @shared, executable: @executable)
def to_elf(inputs: @inputs, output: @output, debug: @debug, shared: @shared, executable: @executable, pie: @pie)
elf_objs = inputs.map { |input| Caotral::Binary::ELF::Reader.new(input:, debug:).read }
builder = Caotral::Linker::Builder.new(elf_objs:, debug:, shared:, executable:)
builder = Caotral::Linker::Builder.new(elf_objs:, debug:, shared:, executable:, pie:)
builder.resolve_symbols
elf_obj = builder.build
Caotral::Linker::Writer.new(elf_obj:, output:, debug:, shared:, executable:).write
Caotral::Linker::Writer.new(elf_obj:, output:, debug:, shared:, executable:, pie:).write
File.chmod(0755, output) if executable
output
end
Expand Down
Loading