Ruby:如何用FFI::Library卸载或指定.so库?(Ethon+libcurl场景)
我使用Ethon gem加载libcurl库,gem内的加载代码如下:
ffi_lib_flags :now, :global ffi_lib ['libcurl.so', 'libcurl.so.4'] extend Ethon::Curls::Functions # 从libcurl绑定函数
我编写的猴补丁代码:
ffi_lib_flags :now, :global ffi_lib '/home/myuser/libs/libcurl.so' attach_function :easy_impersonate, :curl_easy_impersonate, [:pointer, :string, :int], :int # 绑定自定义libcurl中的函数 extend Ethon::Curls::Functions # 从自定义libcurl绑定默认函数
FFI能找到curl_easy_impersonate函数,但调用时返回错误码48(根据libcurl文档,该错误码表示未找到对应函数)。不过如果直接修改gem中的ffi_lib ['libcurl.so', 'libcurl.so.4']为ffi_lib '/home/myuser/libs/libcurl.so',调用该函数会返回0并正常工作。推测原因是FFI优先使用了gem加载的系统默认libcurl库。
核心问题:是否可以通过FFI卸载已加载的库,或者显式指定函数调用时使用的库?
补充要求:希望通过猴补丁实现,不想fork该gem(会增加生产环境维护成本)。已尝试移除附加的方法、通过ffi_libraries方法查找libcurl库对象并移除,但未找到相关痕迹。
方法1:替换Ethon的库加载路径(推荐)
直接修改Ethon内部的库加载配置,让它优先加载你的自定义libcurl,而不是系统库。可以通过猴补丁覆盖Ethon的ffi_lib调用:
module Ethon module Curls module Functions remove_const(:LIBPATH) if defined?(LIBPATH) LIBPATH = ['/home/myuser/libs/libcurl.so'] ffi_lib LIBPATH end end end # 重新绑定所有函数(包括你的自定义函数) module Ethon module Curls module Functions attach_function :easy_impersonate, :curl_easy_impersonate, [:pointer, :string, :int], :int end end end
原理是直接替换Ethon内部存储库路径的常量,让后续的函数绑定都指向你的自定义库。
方法2:显式指定函数的库来源
FFI允许在attach_function时通过:library参数指定使用哪个库对象。你可以先获取自定义库的对象,再绑定函数时指定它:
# 加载自定义库并获取库对象 custom_lib = FFI::Library.new custom_lib.ffi_lib_flags :now, :global custom_lib.ffi_lib '/home/myuser/libs/libcurl.so' # 绑定自定义函数时指定库 attach_function :easy_impersonate, :curl_easy_impersonate, [:pointer, :string, :int], :int, library: custom_lib # 重新绑定Ethon的默认函数到自定义库 module Ethon module Curls module Functions # 先移除已绑定的方法 instance_methods.each do |m| remove_method(m) if m.to_s.start_with?('curl_') end # 从自定义库重新绑定所有函数 custom_lib.extend(self) end end end
这种方式强制让所有函数都使用你加载的自定义库,避免FFI的优先级问题。
方法3:调整系统库加载优先级(环境变量方式)
在启动Ruby进程前,设置LD_LIBRARY_PATH(Linux)或DYLD_LIBRARY_PATH(macOS),让系统优先加载你的自定义libcurl目录:
export LD_LIBRARY_PATH=/home/myuser/libs:$LD_LIBRARY_PATH ruby your_script.rb
这种方式不需要修改代码,但需要确保部署环境的环境变量配置正确,适合无法修改代码的场景。
内容的提问来源于stack exchange,提问作者Вячеслав

