From 296ae418583b7fd04d5847319e1a905763e6d3e5 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 18 Apr 2026 20:10:23 +0900 Subject: [PATCH 1/2] add start-based tests for glibc linking --- test/caotral/linker/needed_test.rb | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/caotral/linker/needed_test.rb b/test/caotral/linker/needed_test.rb index fe0ac94..b45345b 100644 --- a/test/caotral/linker/needed_test.rb +++ b/test/caotral/linker/needed_test.rb @@ -38,4 +38,44 @@ def test_needed_in_executable pid, exit_code = check_process($?.to_i) assert_equal(0, exit_code) end + + def test_start_in_dynamic_section + @input = "start.o" + @output = "start" + compile_path = Pathname.new("sample/C/start.c").to_s + IO.popen(["gcc", "-fPIC", "-c", "-o", @input, "%s" % compile_path]).close + + Caotral::Linker.link!(inputs: [@input], output: @output, linker: "self", pie: true, needed: ["libc.so.6"], executable: false) + + elf = Caotral::Binary::ELF::Reader.read!(input: @output, debug: false) + dynstr = elf.find_by_name(".dynstr") + dynsym = elf.find_by_name(".dynsym") + needed = elf.find_by_name(".dynamic").body.select(&:needed?) + got_plt = elf.find_by_name(".dynamic").body.select(&:plt_got?) + jmp_rel = elf.find_by_name(".dynamic").body.select(&:jmp_rel?) + dynsym_names = dynsym.body.map(&:name_string) + assert_equal(1, needed.size) + assert_equal(dynstr.body.lookup(needed.first.un), "libc.so.6") + assert_include(dynsym_names, "_exit") + assert_include(dynsym_names, "_start") + assert_equal(1, got_plt.size) + assert_equal(1, jmp_rel.size) + end + + def test_start_in_executable + @input = "start.o" + @output = "start" + + compile_path = Pathname.new("sample/C/start.c").to_s + IO.popen(["gcc", "-fPIC", "-c", "-o", @input, "%s" % compile_path]).close + + Caotral::Linker.link!(inputs: [@input], output: @output, linker: "self", pie: true, needed: ["libc.so.6"], executable: true) + + io = IO.popen(["./#{@output}"]) + stdout = io.read + io.close + pid, exit_code = check_process($?.to_i) + assert_equal("hello, world!!!\n", stdout) + assert_equal(0, exit_code) + end end From 0b9f417628f6594970a7bc8b0bb313a6d481ff58 Mon Sep 17 00:00:00 2001 From: "MATSUMOTO, Katsuyoshi" Date: Sat, 18 Apr 2026 21:28:38 +0900 Subject: [PATCH 2/2] add glibc linker test for start and shared objects --- test/caotral/linker/needed_test.rb | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/caotral/linker/needed_test.rb b/test/caotral/linker/needed_test.rb index b45345b..2facfa3 100644 --- a/test/caotral/linker/needed_test.rb +++ b/test/caotral/linker/needed_test.rb @@ -39,6 +39,23 @@ def test_needed_in_executable assert_equal(0, exit_code) end + def test_needed_in_shared_object + @output = "libneeded.so" + compile_path = Pathname.new("sample/C/needed.c").to_s + IO.popen(["gcc", "-fPIC", "-c", "-o", @input, "%s" % compile_path]).close + + Caotral::Linker.link!(inputs: [@input], output: @output, linker: "self", needed: ["libc.so.6"], executable: false, shared: true) + + elf = Caotral::Binary::ELF::Reader.read!(input: @output, debug: false) + dynstr = elf.find_by_name(".dynstr") + dynsym = elf.find_by_name(".dynsym") + needed = elf.find_by_name(".dynamic").body.select(&:needed?) + + assert_equal(1, needed.size) + assert_equal(dynstr.body.lookup(needed.first.un), "libc.so.6") + assert_include(dynsym.body.map(&:name_string), "puts") + end + def test_start_in_dynamic_section @input = "start.o" @output = "start" @@ -78,4 +95,28 @@ def test_start_in_executable assert_equal("hello, world!!!\n", stdout) assert_equal(0, exit_code) end + + def test_start_in_shared_object + @input = "start.o" + @output = "libstart.so" + + compile_path = Pathname.new("sample/C/start.c").to_s + IO.popen(["gcc", "-fPIC", "-c", "-o", @input, "%s" % compile_path]).close + + Caotral::Linker.link!(inputs: [@input], output: @output, linker: "self", needed: ["libc.so.6"], executable: false, shared: true) + + elf = Caotral::Binary::ELF::Reader.read!(input: @output, debug: false) + dynstr = elf.find_by_name(".dynstr") + dynsym = elf.find_by_name(".dynsym") + needed = elf.find_by_name(".dynamic").body.select(&:needed?) + got_plt = elf.find_by_name(".dynamic").body.select(&:plt_got?) + jmp_rel = elf.find_by_name(".dynamic").body.select(&:jmp_rel?) + dynsym_names = dynsym.body.map(&:name_string) + assert_equal(1, needed.size) + assert_equal(dynstr.body.lookup(needed.first.un), "libc.so.6") + assert_include(dynsym_names, "_exit") + assert_include(dynsym_names, "_start") + assert_equal(1, got_plt.size) + assert_equal(1, jmp_rel.size) + end end