Ruby通过FFI调用共享库时libapi.so符号无法识别的问题
Alright, let's break down why this error happens when using Ruby FFI but works perfectly with direct C calls, and walk through the fixes.
Root Cause
When you compile a C executable that calls start_client from libapi.so, the linker adds a dependency on libapi.so to the executable. So when you run the C program, the dynamic linker automatically loads libapi.so and resolves the start_client symbol without issue.
But if your web_client.so was compiled without linking against libapi.so, it won’t have that dependency recorded. When FFI loads web_client.so, the dynamic linker can’t find start_client because libapi.so isn’t loaded into the process space.
Solution 1: Fix Your Makefile to Link libapi.so Properly
The cleanest fix is to ensure web_client.so explicitly links against libapi.so during compilation. Update your Makefile rule for web_client.so to include the necessary library flags:
# Example Makefile rule web_client.so: web_client.c gcc -fPIC -shared -o $@ $< -L/path/to/libapi_directory -lapi
Let’s break down the key flags:
-fPIC: Generates position-independent code (required for shared libraries to work across different memory addresses).-shared: Tells the compiler to output a shared library instead of a standalone executable.-L/path/to/libapi_directory: Points to the folder wherelibapi.solives (skip this if it’s in a standard system path like/usr/lib).-lapi: Links againstlibapi.so(the linker looks forlibapi.sowhen given-lapi).
After rebuilding, run ldd /path/to/web_client.so — you should see libapi.so listed as a dependency. Now when FFI loads web_client.so, the dynamic linker will automatically load libapi.so and resolve the start_client symbol.
Solution 2: Explicitly Load libapi.so in Your Ruby FFI Code
If you can’t modify the web_client.so compilation (or prefer not to), you can load libapi.so directly in your Ruby code before loading web_client.so. This ensures the start_client symbol is present in the process space when web_client.so is loaded.
Here’s how to adjust your Client.rb:
require 'ffi' # First, load libapi.so to make its symbols available to the process module LibAPI extend FFI::Library ffi_lib '/path/to/libapi.so' # Declare the function signature for start_client (adjust params/return type to match your actual code) attach_function :start_client, [], :void end # Now load web_client.so — it can resolve start_client since LibAPI is already loaded module WebClient extend FFI::Library ffi_lib '/path/to/web_client.so' attach_function :send_from_web, [], :void # Adjust signature to match your function end class Client def self.send_from_web WebClient.send_from_web end end
Solution 3: Ensure libapi.so is Findable by the Dynamic Linker
If libapi.so lives in a non-standard directory, you might need to tell the dynamic linker where to find it when running your Ruby script. Set the LD_LIBRARY_PATH environment variable before executing the script:
export LD_LIBRARY_PATH=/path/to/libapi_directory:$LD_LIBRARY_PATH ruby Client.rb
This makes the dynamic linker search the specified directory for libapi.so when loading shared libraries.
内容的提问来源于stack exchange,提问作者Daniel Mur

