Skip to content

Commit 16205d5

Browse files
committed
add _start stub and handle PC32/PLT32 relocations
1 parent ef657fb commit 16205d5

File tree

4 files changed

+60
-29
lines changed

4 files changed

+60
-29
lines changed

lib/caotral/linker/writer.rb

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
require_relative "elf/program_header"
2+
23
module Caotral
34
class Linker
45
class Writer
6+
include Caotral::Assembler::ELF::Utils
57
ALLOW_SECTIONS = %w(.text .strtab .shstrtab).freeze
68
R_X86_64_PC32 = 2
9+
R_X86_64_PLT32 = 4
10+
ALLOW_RELOCATION_TYPES = [R_X86_64_PC32, R_X86_64_PLT32].freeze
711
RELOCATION_SECTION_NAMES = [".rela.text", ".rel.text"].freeze
812
attr_reader :elf_obj, :output, :entry, :debug
913
def self.write!(elf_obj:, output:, entry: nil, debug: false)
@@ -19,33 +23,45 @@ def write
1923
ph = Caotral::Linker::ELF::ProgramHeader.new
2024
text_section = @elf_obj.sections[".text"]
2125
rel_sections = @elf_obj.sections.select { RELOCATION_SECTION_NAMES.include?(it.section_name) }
26+
start_bytes = [0xe8, *[0] * 4, 0x48, 0x89, 0xc7, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05]
2227
symtab = @elf_obj.sections[".symtab"]
2328
symtab_body = symtab.body
29+
old_text = text_section.body
30+
main_sym = symtab_body.find { |sym| sym.name_string == "main" }
31+
text_offset = 0x1000
32+
base_addr = 0x400000
33+
align = 0x1000
34+
vaddr = base_addr + text_offset
35+
paddr = base_addr + text_offset
36+
start_len = start_bytes.length
37+
start_addr = base_addr + text_offset
38+
main_offset = main_sym.value + start_len
39+
start_bytes[1, 4] = num2bytes((main_offset - 5), 4)
40+
start_bytestring = start_bytes.pack("C*")
41+
text_section.body = start_bytestring + old_text
42+
type, flags = 1, 5
43+
filesz = text_section.body.bytesize
44+
memsz = filesz
45+
2446
rel_sections.each do |rel|
2547
target = @elf_obj.sections[rel.header.info]
2648
bytes = target.body.dup
2749
rel.body.each do |entry|
28-
next unless entry.type == R_X86_64_PC32
50+
next unless ALLOW_RELOCATION_TYPES.include?(entry.type)
51+
a = entry.type == R_X86_64_PC32 ? 4 : 0
2952
sym = symtab_body[entry.sym]
30-
sym_addr = sym.value + target.header.addr
31-
offset = entry.offset
32-
addend = entry.addend? ? entry.addend : bytes[offset, 4].unpack1("l<")
33-
value = sym_addr + addend - (target.header.addr + offset)
34-
bytes[offset, 4] = [value].pack("l<")
53+
target_addr = target == text_section ? vaddr : target.header.addr
54+
sym_addr = sym.value + target_addr + (target == text_section ? start_len : 0)
55+
sym_offset = entry.offset + (target == text_section ? start_len : 0)
56+
sym_addend = entry.addend? ? entry.addend : bytes[sym_offset, 4].unpack1("l<")
57+
value = sym_addr + sym_addend - (target_addr + sym_offset + a)
58+
bytes[sym_offset, 4] = [value].pack("l<")
3559
end
3660
target.body = bytes
3761
end
38-
filesz = text_section.header.size
39-
memsz = filesz
40-
offset = text_offset = 0x1000
41-
base_addr = 0x400000
42-
align = 0x1000
43-
vaddr = base_addr + text_offset
44-
paddr = base_addr + text_offset
45-
type, flags = 1, 5
4662
header.set!(entry: @entry || base_addr + text_offset)
47-
ph.set!(type:, offset:, vaddr:, paddr:, filesz:, memsz:, flags:, align:)
48-
text_section.header.set!(addr: vaddr, offset: text_offset)
63+
ph.set!(type:, offset: text_offset, vaddr:, paddr:, filesz:, memsz:, flags:, align:)
64+
text_section.header.set!(size: text_section.body.bytesize, addr: vaddr, offset: text_offset)
4965
f.write(@elf_obj.header.build)
5066
f.write(ph.build)
5167
gap = [text_offset - f.pos, 0].max

sample/C/rel_text.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
extern int foo(int);
2-
int foo(int x) { return x + 1; }
1+
extern int foo(void);
2+
int foo(void) { return 0; }
33

44
int main(void) {
55
asm volatile(
6-
".long foo-.-4\n"
7-
".reloc .-4, R_X86_64_PC32, foo"
6+
"jmp 1f\n"
7+
".long 0\n"
8+
".reloc .-4, R_X86_64_PC32, foo\n"
9+
"1:\n"
810
);
9-
return 0;
11+
return foo();
1012
}

test/caotral/linker/reader_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def test_read
99
assert_equal elf_obj.sections.size, 8
1010
assert_equal elf_obj.sections[0].section_name, "null"
1111
shstrtab = elf_obj.sections[elf_obj.header.shstrndx]
12-
assert_equal shstrtab.section_name, "shstrtab"
12+
assert_equal shstrtab.section_name, ".shstrtab"
1313
assert_equal shstrtab.body.names, "\0.text\0.data\0.bss\0.note\0.symtab\0.strtab\0.shstrtab\0"
1414
assert_equal elf_obj.sections[1].section_name, ".text"
1515
assert_equal elf_obj.sections[1].header.size, 61

test/caotral/linker/writer_test.rb

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,39 @@ def setup
77
end
88
def teardown
99
File.delete("plus.o") if File.exist?("plus.o")
10-
File.delete("written.o") if File.exist?("written.o")
11-
File.delete("written_exec") if File.exist?("written_exec")
10+
File.delete("write.o") if File.exist?("write.o")
11+
File.delete("write") if File.exist?("write")
12+
File.delete("relocatable.o") if File.exist?("relocatable.o")
13+
File.delete("relocated_exec") if File.exist?("relocated_exec")
1214
end
1315
def test_write
14-
written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "written.o", debug: false)
16+
written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write.o", debug: false)
1517
read_written_elf = Caotral::Linker::Reader.read!(input: written_output, debug: false)
1618
assert_equal @elf_obj.header.shoffset.pack("C*").unpack("Q<").first, read_written_elf.header.shoffset.pack("C*").unpack("Q<").first
17-
assert_equal @elf_obj.sections.size, read_written_elf.sections.size
19+
assert_equal 4, read_written_elf.sections.size
1820
assert_equal 0x401000, read_written_elf.header.entry.pack("C*").unpack("Q<").first
1921
end
2022

2123
def test_execute_written
22-
written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "written_exec", debug: false)
23-
File.chmod(0755, "./written_exec")
24-
IO.popen("./written_exec").close
24+
written_output = Caotral::Linker::Writer.write!(elf_obj: @elf_obj, output: "write", debug: false)
25+
File.chmod(0755, "./write")
26+
IO.popen("./write").close
2527
exit_code, handle_code = check_process($?.to_i)
2628
assert_equal(9, exit_code)
2729
assert_equal(0, handle_code)
2830
end
2931

32+
def test_relocation_write_and_execute
33+
IO.popen("gcc -c -fno-pic -fno-pie -o relocatable.o sample/C/rel_text.c").close
34+
elf_obj = Caotral::Linker::Reader.read!(input: "relocatable.o", debug: false)
35+
Caotral::Linker::Writer.write!(elf_obj:, output: "relocated_exec", debug: false)
36+
File.chmod(0755, "./relocated_exec")
37+
IO.popen("./relocated_exec").close
38+
exit_code, _handle_code = check_process($?.to_i)
39+
assert_equal(0, exit_code)
40+
end
41+
42+
private
3043
def check_process(status)
3144
exit_code = status >> 8
3245
handle_code = status & 0x7f

0 commit comments

Comments
 (0)