如何转储使用osicat

时间:2019-03-15 13:03:05

标签: linker shared-libraries common-lisp dlopen quicklisp

我有一个简单的通用lisp服务器程序,该程序使用osicat库与posix文件系统接口。我需要执行此操作,因为系统创建了文件的符号链接,并使用POSIX stat元数据,而在可移植Lisp中,这些操作都不是直接可以做的。

我正在使用quicklisp管理依赖项,而我将所有这些都固定在一个有效的发行版中。该应用程序可在CCL和SBCL之间移植,我倾向于在前者中构建它并使用后者进行部署。我使用asdf defsystem声明了应用程序的依赖关系,并且可以使用quicklisp加载它,以便从本地项目轻松开发。

对于部署,我只是使用一些有趣的剧本,它们在远程复制了开发人员环境(例如,设置quicklisp,将代码推送到本地项目中,用完用户主目录),但确实很麻烦。最近,随着它变得更加稳定,我一直在使用sb-ext:save-lisp-and-die和一个简单的编译脚本来对其进行编译。这意味着我得到了一个可执行文件,它可以像服务器一样运行,具有服务管理脚本和一个匿名用户帐户。

这一直很好,所以我最近将这一步移到了下一个级别,并且我正在使用编译脚本来构建.deb软件包,因此我可以将所有内容捆绑到可重定位的二进制文件中。这也可以完成,但是无法从原始构建主机中重定位生成的二进制文件。他们拒绝启动,并且似乎试图动态加载osicat的共享库

Unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
Mar 15 12:47:14 annie [479]:                                     {10005C05B3}>:
Mar 15 12:47:14 annie [479]:   Error opening shared object "libosicat.so":
Mar 15 12:47:14 annie [479]:   libosicat.so: cannot open shared object file: No such file or directory.

该图像似乎希望在原始构建树的quicklisp档案中找到它

(ERROR "Error opening ~:[runtime~;shared object ~:*~S~]:~%  ~A." "/home/builder/buil...quicklisp/dists/quicklisp/software/osicat-20180228-git/posix/libosicat.so
(SB-SYS:DLOPEN-OR-LOSE #S(SB-ALIEN::SHARED-OBJECT :PATHNAME #P"

因此在源代码中四处寻找,我意识到,当quicklisp获取osicat并执行其构建操作时,它会编译该DLL来包装它与系统库的接口,而不仅仅是直接将ffi封装给它们-可能是因为它使用的是cffi groveller,我对cffi尚不甚了解(尚未)。很好,但是不是使用系统链接程序链接到.so,而是尝试从固定路径dlopen进行save-image的访问,该路径不是很可移植,并且会破坏source: Hello World target: lo Hello World lo // No match: He != lo Hello World lo // No match: el != lo Hello World lo // No match: ll != lo Hello World lo // Match: lo != lo 的用途

在这一点上我有些困惑,但是在我进一步深入研究QL和cffi构建之前,我想知道是否缺少一些构建或编译配置,这会使它在更静态的环境中进行引导时尚或影响包装图书馆的生产。理想情况下,我只希望可以将一个blob包裹在安装程序中,然后将其链接到系统库,但是如果我必须部署一些其他工件,那么可能就可以了。我不知道如何使自动生成的共享对象出现在受控制的路径上。

尽管如此,我还是可以为posix调用编写一个.so,并将其分发到应用程序中,然后尝试直接使用FFI。那会有点痛苦,所以我宁愿不这样做。

2 个答案:

答案 0 :(得分:2)

也许您可以在SBCL上使用sb-posix:symlinksb-posix:fstat,通过功能切换来消除osicat依赖性。

答案 1 :(得分:2)

是的,当转储的映像启动时,它正在尝试重新加载共享库。如您所见,如果映像不是在转储映像的计算机上启动的,则该映像不起作用。

这几乎是static-program-op要解决的问题。像这样的简单系统定义应该可以帮助您编译静态程序:

(defsystem "foo"
  :defsystem-depends-on ("cffi-grovel")
  :build-operation "static-program-op" ; "asdf" package is implied
  :build-pathname "foo" ; path of the generated binary
  :entry-point "foo:main" ; function to use as the entry point
  ;; ... everything else ...
  )

如果您的系统依赖于grovel文件(由:cffi-wrapper-file:c-file:o-file定义),例如osicat提供的文件,则它将这些文件静态链接到转储的映像

但是,这并不完美。

从本质上讲,仍然存在一些问题。有些是由CFFI本身固定在上游的(例如,不重新加载静态嵌入式库的共享库),有些则有些困难。 (例如,SBCL的默认编译选项默认情况下不允许您使用static-program-op。这在SBCL的Debian版本中已得到修复,但其他发行版的响应速度较慢。)

这显然是整个社区都遇到的一个问题,并且有一些图书馆可以为您提供帮助:

  • 已经存在一段时间的第一个是Deploy。采取的方法是将转储的图像和库嵌入到存档中,并对二进制文件进行重新排列,以便从提取到的任何位置加载它们。
  • 我之所以偏向于第二个是linux-packaging。它采用通过扩展来修复static-program-op的方法,但是需要您构建自定义的SBCL。但是,它生成.deb.rpm之类的分发软件包,以便能够指定系统共享库的依赖项(例如,如果您依赖sqlite,它将找出提供它的软件包并添加它作为.deb中的依赖项。我强烈建议您查看.gitlab-ci.yml作为示例。

我建议阅读这两个库的网页以做出选择,它们都有其优点和缺点。 <joke>显然,linux-packaging是更好的选择。</joke>