在构建期间覆盖使用automake / libtool构建的共享库中的安装路径,以便在无法使用DYLD_LIBRARY_PATH时进行测试

时间:2017-07-07 20:52:02

标签: macos automake libtool

概要

从OS X 10.11开始,启用系统完整性保护时,动态链接器环境变量不会传递给子进程。在一个生成两个库的项目中,一个依赖于另一个库,链接到库的测试代码(围绕可执行文件的shell包装器)无法运行,因为安装路径被硬连线到库中,库尚未安装,并且动态链接器将尝试使用库中的硬连线路径来查找它们。由于DYLD_LIBRARY_PATH未传递给子进程(例如,由测试包装器脚本运行的可执行文件),因此无法指示链接器查找其他位置。

详情

该项目由两个库组成,一个是共享库,另一个是dlopen'能够加载共享库的模块。

以下是automake摘要:

#-------------------
# shared library
lib_LTLIBRARIES           += %D%/liblua_udunits2.la

%C%_liblua_udunits2_la_CFLAGS = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_liblua_udunits2_la_SOURCES = %D%/lua_udunits2.c

#-------------------
# dlopen'able module

luaexec_LTLIBRARIES = %D%/udunits2.la

%C%_udunits2_la_CFLAGS  = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_udunits2_la_LIBADD  = $(UDUNITS2_LIBS) %D%/liblua_udunits2.la
%C%_udunits2_la_SOURCES = %D%/udunits2.c
%C%_udunits2_la_LDFLAGS = -L%D% -module

这会产生

  • liblua_udunits2.dylib
  • udunits2.so

当链接器构建udunits2.so时,它会记录liblua_udunits2.dylib中找到的安装名称,

% otool -D lua_udunits2/.libs/liblua_udunits2.dylib

lua_udunits2/.libs/liblua_udunits2.dylib:
/usr/local/lib/liblua_udunits2.0.dylib

进入udunits2.so

% otool -L lua_udunits2/.libs/udunits2.0.so

lua_udunits2/.libs/udunits2.0.so:
/usr/local/lib/libudunits2.0.dylib (compatibility version 2.0.0, current version 2.0.0)
/usr/lib/libexpat.1.dylib (compatibility version 7.0.0, current version 8.0.0)
/usr/local/lib/liblua_udunits2.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)

测试代码是一个shell脚本,它调用加载udunits2.so的可执行文件,然后尝试加载liblua_udunits2.dylib。这失败了,因为它使用liblua_udunits2.dylib的安装名称,但尚未安装。

我通过添加

解决了以前版本的OSX问题
TESTS_ENVIRONMENT += DYLD_LIBRARY_PATH="$(top_builddir)/$(PACKAGE_NAME)/.libs"

表示测试代码。

但是,自OS X 10.11(El Capitan)以来,如果启用了系统完整性保护,则在某些情况下dynamic linker environment variables are not passed to subprocesses。在我的情况下,测试代码是一个调用可执行文件的脚本,因此DYLD_LIBRARY_PATH不会传递给可执行文件,我无法覆盖库加载路径。 (该脚本是我的代码外部的第三方测试框架的一部分,因此修改它以显式设置DYLD_LIBRARY_PATH不是一个选项。)

问题

有没有办法让automake / libtool使用构建目录路径来创建库,以便可以继续进行测试,然后在安装过程中使用安装路径重新链接它们?

1 个答案:

答案 0 :(得分:0)

这样做最不具干扰性的方法似乎是使用install_name_tool(正如我在问题中的评论中所提到的那样),希望只是轻微依赖于automake如何实施的东西。

我的解决方案是创建一个依赖于dlopen能力模块的目标,并将动态库依赖关系的路径从安装目录更改为构建目录。这允许链接模块的测试成功加载动态库。安装后,依赖关系路径将恢复到安装路径。

修改后的Makefile.am如下所示:

#-------------------
# shared library
lib_LTLIBRARIES           += %D%/liblua_udunits2.la

%C%_liblua_udunits2_la_VERSION_MAJOR = 0
%C%_liblua_udunits2_la_VERSION_MINOR = 0

%C%_liblua_udunits2_la_CFLAGS = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_liblua_udunits2_la_SOURCES = %D%/lua_udunits2.c
%C%_liblua_udunits2_la_LDFLAGS = -version-info $(%C%_liblua_udunits2_la_VERSION_MAJOR):$(%C%_liblua_udunits2_la_VERSION_MINOR)


#-------------------
# dlopen'able module

luaexec_LTLIBRARIES = %D%/udunits2.la

%C%_udunits2_la_VERSION_MAJOR = 0
%C%_udunits2_la_VERSION_MINOR = 0

%C%_udunits2_la_CFLAGS  = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_udunits2_la_LIBADD  = $(UDUNITS2_LIBS) %D%/liblua_udunits2.la
%C%_udunits2_la_SOURCES = %D%/udunits2.c
%C%_udunits2_la_LDFLAGS = -L%D% -module -version-info $(%C%_udunits2_la_VERSION_MAJOR):$(%C%_udunits2_la_VERSION_MINOR)

if HOST_OS_IS_DARWIN

noinst_DATA = %D%/fix_install_name

%C%_DYLIB_NAME  = liblua_udunits2.$(%C%_liblua_udunits2_la_VERSION_MAJOR).dylib
%C%_MODULE_NAME = udunits2.$(%C%_udunits2_la_VERSION_MAJOR).so

%C%_LIBSDIR = $(abs_builddir)/%D%/.libs/

%D%/fix_install_name : %D%/udunits2.la
    install_name_tool               \
        -change                 \
        $(libdir)/$(%C%_DYLIB_NAME)     \
        $(%C%_LIBSDIR)/$(%C%_DYLIB_NAME)    \
        $(%C%_LIBSDIR)/$(%C%_MODULE_NAME)
    touch $@

install-data-hook :
    install_name_tool                   \
        -change                     \
        $(%C%_LIBSDIR)/$(%C%_DYLIB_NAME)            \
        $(libdir)/$(%C%_DYLIB_NAME)         \
        $(DESTDIR)$(luaexecdir)/$(%C%_MODULE_NAME)

endif

注意事项:

  • 现在明确指定了库版本,以便确定性地知道动态库和dlopen的模块的名称
  • 代码使用库在.libs
  • 中暂存的事实
  • 代码明确使用.so.dylib扩展名。我不相信有这种方法可以从libtool中获取这些信息。