Skip to content

Commit 2e34465

Browse files
authored
Merge pull request #34 from katsyoshi/shared-library-output
support ET_DYN output in self linker
2 parents 7916aa5 + dd02a7b commit 2e34465

6 files changed

Lines changed: 64 additions & 40 deletions

File tree

lib/caotral/binary/elf/header.rb

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ class Header
77
IDENT = [0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].freeze
88
IDENT_STR = IDENT.pack("C*").freeze
99
ELF_FILE_TYPE = { NONE: 0, REL: 1, EXEC: 2, DYN: 3, CORE: 4 }.freeze
10+
ELF_TARGET_MACHINE = { AMD64: 62 }.freeze
11+
TYPE = ELF_FILE_TYPE
12+
ITYPE = TYPE.invert.freeze
13+
MACHINE = ELF_TARGET_MACHINE
14+
IMACHINE = MACHINE.invert.freeze
1015

11-
def initialize(endian: :little, type: :rel, arc: :amd64)
16+
def initialize(endian: :little, type: :REL)
1217
@ident = IDENT
13-
@type = num2bytes(ELF_FILE_TYPE[elf(type)], 2)
14-
@arch = arch(arc)
18+
@type = num2bytes(ELF_FILE_TYPE[type], 2)
19+
@arch = num2bytes(62, 2) # target: e_machine EM_x86_64
1520
@version = num2bytes(1, 4)
1621
@entry = num2bytes(0x00, 8)
1722
@phoffset = num2bytes(0x00, 8)
@@ -27,8 +32,9 @@ def initialize(endian: :little, type: :rel, arc: :amd64)
2732

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

30-
def set!(type: nil, entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil, phsize: nil, phnum: nil, ehsize: nil)
35+
def set!(type: nil, arch: nil, entry: nil, phoffset: nil, shoffset: nil, shnum: nil, shstrndx: nil, phsize: nil, phnum: nil, ehsize: nil)
3136
@type = num2bytes(type, 2) if check(type, 2)
37+
@arch = num2bytes(arch, 2) if check(arch, 2)
3238
@entry = num2bytes(entry, 8) if check(entry, 8)
3339
@phoffset = num2bytes(phoffset, 8) if check(phoffset, 8)
3440
@phsize = num2bytes(phsize, 2) if check(phsize, 2)
@@ -48,33 +54,14 @@ def shentsize = @shentsize.pack("C*").unpack1("S<")
4854
def shnum = @shnum.pack("C*").unpack1("S<")
4955
def shstrndx = @shstrndx.pack("C*").unpack1("S<")
5056
def shoffset = @shoffset.pack("C*").unpack1("Q<")
57+
def type = ITYPE[@type.pack("C*").unpack1("S<")]
58+
def arch = IMACHINE[@arch.pack("C*").unpack1("S<")]
5159

52-
private
53-
def bytes = [
60+
private def bytes = [
5461
@ident, @type, @arch, @version, @entry, @phoffset,
5562
@shoffset, @flags, @ehsize, @phsize, @phnum, @shentsize,
5663
@shnum, @shstrndx
5764
]
58-
59-
def arch(machine)
60-
case machine.to_s
61-
in "amd64" | "x86_64" | "x64"
62-
[0x3e, 0x00]
63-
end
64-
end
65-
66-
def elf(type)
67-
case type.to_s
68-
in "relocatable" | "rel"
69-
:REL
70-
in "exe" | "ex" | "exec"
71-
:EXEC
72-
in "shared" | "share" | "dynamic" | "dyn"
73-
:DYN
74-
else
75-
:NONE
76-
end
77-
end
7865
end
7966
end
8067
end

lib/caotral/binary/elf/reader.rb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@ def read
1818
header = @bin.read(0x40)
1919
ident = header[0, 16]
2020
raise "Not ELF file" unless ident == Caotral::Binary::ELF::Header::IDENT_STR
21-
22-
entry = header[24, 8].unpack("Q<").first
23-
phoffset = header[32, 8].unpack("Q<").first
24-
shoffset = header[40, 8].unpack("Q<").first
25-
shentsize = header[58, 2].unpack("S<").first
26-
shnum = header[60, 2].unpack("S<").first
27-
shstrndx = header[62, 2].unpack("S<").first
28-
@context.header.set!(entry:, phoffset:, shoffset:, shnum:, shstrndx:)
21+
22+
type = header[16, 2].unpack1("S<")
23+
arch = header[18, 2].unpack1("S<")
24+
entry = header[24, 8].unpack1("Q<")
25+
phoffset = header[32, 8].unpack1("Q<")
26+
shoffset = header[40, 8].unpack1("Q<")
27+
shentsize = header[58, 2].unpack1("S<")
28+
shnum = header[60, 2].unpack1("S<")
29+
shstrndx = header[62, 2].unpack1("S<")
30+
@context.header.set!(type:, arch:, entry:, phoffset:, shoffset:, shnum:, shstrndx:)
2931

3032
@bin.pos = shoffset
3133
shnum.times do |i|

lib/caotral/linker/builder.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ class Builder
1212
RELOCATION_SECTION_NAMES = [".rela.text", ".rel.text"].freeze
1313
ALLOW_RELOCATION_TYPES = [R_X86_64_PC32, R_X86_64_PLT32].freeze
1414

15-
attr_reader :symbols, :executable, :debug
15+
attr_reader :symbols
1616

1717
def initialize(elf_objs:, executable: true, debug: false, shared: false)
1818
@elf_objs = elf_objs
1919
@executable, @debug, @shared = executable, debug, shared
2020
@symbols = { locals: Set.new, globals: Set.new, weaks: Set.new }
21+
sharing_object!
2122
end
2223

2324
def build
@@ -52,6 +53,10 @@ def build
5253
start_bytes = [0xe8, *[0] * 4, 0x48, 0x89, 0xc7, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05]
5354
exec_text_offset = 0x1000
5455
base_addr = 0x400000
56+
unless @executable
57+
start_bytes = []
58+
base_addr = 0
59+
end
5560
vaddr = base_addr + exec_text_offset
5661
start_len = start_bytes.length
5762
sections = []
@@ -108,9 +113,9 @@ def build
108113
sections << null_section
109114

110115
main_sym = symtab_section.body.find { |sym| sym.name_string == "main" }
111-
raise Caotral::Binary::ELF::Error, "main function not found" if executable && main_sym.nil?
116+
raise Caotral::Binary::ELF::Error, "main function not found" if @executable && main_sym.nil?
112117
main_offset = main_sym.nil? ? 0 : main_sym.value + start_len
113-
start_bytes[1, 4] = num2bytes((main_offset - 5), 4)
118+
start_bytes[1, 4] = num2bytes((main_offset - 5), 4) if @executable
114119
text_section.body.prepend(start_bytes.pack("C*"))
115120

116121
text_section.header.set!(
@@ -226,7 +231,7 @@ def build
226231
target.body = bytes
227232
end
228233

229-
sections = sections.reject { |section| RELOCATION_SECTION_NAMES.any? { |name| name === section.section_name.to_s } } if executable
234+
sections = sections.reject { |section| RELOCATION_SECTION_NAMES.any? { |name| name === section.section_name.to_s } } if @executable
230235
sections.each { |section| elf.sections << section }
231236

232237
elf
@@ -258,6 +263,7 @@ def ref_index(sections, section_name)
258263

259264
def rel_type(section) = section.section_name&.start_with?(".rela.") ? 4 : 9
260265
def rel_entsize(section) = section.section_name&.start_with?(".rela.") ? 24 : 16
266+
def sharing_object! = (@executable = false if @shared)
261267
end
262268
end
263269
end

lib/caotral/linker/writer.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ def initialize(elf_obj:, output:, entry: nil, debug: false, executable: true, sh
1616
@elf_obj, @output, @entry, @debug, @executable, @shared = elf_obj, output, entry, debug, executable, shared
1717
@write_sections = write_order_sections
1818
end
19+
1920
def write
2021
f = File.open(@output, "wb")
2122
phoffset, phnum, phsize, ehsize = 64, 1, 56, 64
22-
header = @elf_obj.header.set!(type: 2, phoffset:, phnum:, phsize:, ehsize:)
23+
file_type = @shared ? :DYN : :EXEC
24+
e_type = Caotral::Binary::ELF::Header::TYPE[file_type]
25+
26+
header = @elf_obj.header.set!(type: e_type, phoffset:, phnum:, phsize:, ehsize:)
2327
ph = Caotral::Binary::ELF::ProgramHeader.new
2428
text_offset = text_section.header.offset
2529
align = 0x1000
@@ -28,8 +32,9 @@ def write
2832
type, flags = 1, 5
2933
filesz = text_section.body.bytesize
3034
memsz = filesz
35+
entry = @shared ? 0 : (@entry || vaddr)
3136

32-
header.set!(entry: @entry || vaddr)
37+
header.set!(entry:)
3338
ph.set!(type:, offset: text_offset, vaddr:, paddr:, filesz:, memsz:, flags:, align:)
3439
f.write(@elf_obj.header.build)
3540
f.write(ph.build)

sample/C/shared-object.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int foo() { return 42; }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require_relative "../../test_suite"
2+
3+
class Caotral::Linker::SharedObjectLinkingTest < Test::Unit::TestCase
4+
include TestProcessHelper
5+
def setup
6+
@inputs = ["shared.o"]
7+
@output = "libshared.so"
8+
path = Pathname.new("sample/C/shared-object.c").to_s
9+
IO.popen(["gcc", "-fPIC", "-c", "-o", @inputs[0], "%s" % path]).close
10+
end
11+
12+
def teardown
13+
File.delete(@inputs[0]) if File.exist?(@inputs[0])
14+
File.delete(@output) if File.exist?(@output)
15+
end
16+
17+
def test_link_shared_object
18+
Caotral::Linker.link!(inputs: @inputs, output: @output, linker: "self", shared: true, executable: false)
19+
elf = Caotral::Binary::ELF::Reader.read!(input: @output, debug: false)
20+
assert_equal(:DYN, elf.header.type)
21+
assert_equal(:AMD64, elf.header.arch)
22+
end
23+
end

0 commit comments

Comments
 (0)