diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb index 09262c43ff..6137c487c6 100644 --- a/ext/fiddle/lib/fiddle.rb +++ b/ext/fiddle/lib/fiddle.rb @@ -58,7 +58,36 @@ module Fiddle # # See Fiddle::Handle.new for more. def dlopen library - Fiddle::Handle.new library + begin + Fiddle::Handle.new(library) + rescue DLError => error + case RUBY_PLATFORM + when /linux/ + case error.message + when /\A(\/.+?): (?:invalid ELF header|file too short)/ + # This may be a linker script: + # https://sourceware.org/binutils/docs/ld.html#Scripts + path = $1 + else + raise + end + else + raise + end + + File.open(path) do |input| + input.each_line do |line| + case line + when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/ + # TODO: Should we support multiple files? + return dlopen($1) + end + end + end + + # Not found + raise + end end module_function :dlopen diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb index 8751d96920..2792897074 100644 --- a/test/fiddle/test_fiddle.rb +++ b/test/fiddle/test_fiddle.rb @@ -14,4 +14,38 @@ class TestFiddle < Fiddle::TestCase end end + def test_dlopen_linker_script_input_linux + omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") + if Dir.glob("/usr/lib/*/libncurses.so").empty? + omit("libncurses.so is needed") + end + # libncurses.so uses INPUT() on Debian GNU/Linux + # $ cat /usr/lib/x86_64-linux-gnu/libncurses.so + # INPUT(libncurses.so.6 -ltinfo) + handle = Fiddle.dlopen("libncurses.so") + begin + assert_equal("libncurses.so", + File.basename(handle.file_name, ".*")) + ensure + handle.close + end + end + + def test_dlopen_linker_script_group_linux + omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") + # libc.so uses GROUP() on Debian GNU/Linux + # $ cat /usr/lib/x86_64-linux-gnu/libc.so + # /* GNU ld script + # Use the shared library, but some functions are only in + # the static library, so try that secondarily. */ + # OUTPUT_FORMAT(elf64-x86-64) + # GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-x86-64.so.2 ) ) + handle = Fiddle.dlopen("libc.so") + begin + assert_equal("libc.so", + File.basename(handle.file_name, ".*")) + ensure + handle.close + end + end end if defined?(Fiddle)