它花了我比我希望的更长的时间,但是我终于有了补丁的第二个版本来构建经过测试的静态可执行文件,并且可以开始使用!通过减少所需的编译量(以牺牲一些纯度为代价),这组修补程序在第一个方面有了很大的改进。另外,我创建了一个系统,该系统可自动完成构建静态可执行文件的过程以及其他与发布相关的任务。
新的补丁集可以在我的SBCL分支的static-executable-v2分支上找到,也可以在https://www.timmons.dev/static/patches/sbcl/$VERSION/static-executable-support-v2.patch中找到可通过https://www.timmons.dev/static/patches/sbcl/$VERSION/static-executable-support-v2.patch.asc使用GPG密钥0x9ACF6934签名的分离签名。
您肯定要使用:sb-prelink-linkage-table功能(由修补程序新添加)来构建SBCL。您可能还需要:sb-linkable-runtime功能(已经存在,但是该补丁还在arm / arm64上启用了它)。
新的补丁程序使您可以使用更少的Lisp代码编译来构建静态可执行文件。
asdf-release-ops系统通过将静态可执行文件绑定到ASDF中来自动执行构建静态可执行文件的过程。
如果您需要复习什么是静态可执行文件或它们最适合的用例,请参考此主题之前的文章。
在我以前的补丁程序中,创建静态可执行文件的唯一方法是执行以下步骤:
确定您的代码所需的外国符号。最简单的方法是编译所有Lisp代码,然后从映像中转储信息。
从该外来符号列表中,创建一个C文件,其中包含对这些符号的引用填充数组。
使用此新文件重新编译SBCL核心和运行时,另外禁用libdl支持并链接到外部库。
用新的运行时(重新)编译所有Lisp代码(如果在步骤1中创建了映像,由于功能和内部版本ID不匹配,它将与新的运行时不兼容)。
在最一般的情况下,这涉及两次编译整个Lisp image。经过#lisp讨论后,我意识到有一种更好的方法。尽管先前的过程仍然有效,但新推荐的过程现在看起来像:
将您要制作的映像构建为静态可执行文件并保存。
从此映像中转储外部符号信息,并写入SBCL可用来预链接自身的C文件。
编译该C文件并将其链接到现有的sbcl.o文件中以创建新的运行时。 sbcl.o是对象形式的SBCL运行时,是使用:sb-linkable-runtime功能进行构建时创建的。
将步骤1中的映像加载到新的运行时中。因为构建ID和功能集相同,所以它将兼容!
此新过程可以大大减少使可执行文件所需的时间。另外,它还使您可以充分利用基于图像的开发功能。完全按照您的需要构建映像,然后将其转储,然后将其与自定义静态运行时配对以创建静态可执行文件,这是相当简单的。
对于此版本的补丁集,需要克服两个主要挑战。
首先,必须使SBCL内核对每个libdl函数都健壮,无条件地返回错误。由于我们希望功能集保持不变,因此我们无法使用#-os-provides-dlopen重新编译运行时。相反,我们利用了Musl libc允许您相对于libdl链接静态可执行文件这一事实,但是所有这些功能都是noop。这是我上面提到的“纯度”牺牲。
其次,由于我们正在重用图像,因此需要按照图像期望对符号进行精确排序的方式对预链接信息表(生成的Cfile)进行排序。棘手的是,某些库(例如cl-plus-ssl)在链接表中添加了始终未定义的符号。 cl-plus-ssl这样做是为了支持各种opensl版本。先前的补丁集无条件地滤除了未定义的符号,这在新方法中令人震惊地破坏了一切。
和以前一样,在应用补丁之后,您将在存储库的根目录中找到一个README.static-executable文件。您还将在README.static-executable中找到一个Dockerfile以及如何使用它的示例。
:sb-prelink-linkage-table功能在32位ARM + Musl libc> = 1.2上不起作用。 Musl在后台转换为64位时间,同时仍然与所有为32位时间编译的内容保持兼容性。
问题是它们如何保持向后兼容性。每次与时间相关的符号仍然存在,并在32位时间接口的顶部实现所有功能。但是,如果包括定义符号的标准头文件,或者通过dlsym查找符号,则实际上会得到一个指向该符号的64位时间版本的指针。我们不能使用dlsym(它在静态可执行文件中不起作用)。并且生成的C文件不包含任何标题。
如果有人有足够的动力在libc符号和定义它们的标头之间创建/查找完整,易于使用的映射并将其集成到预链接信息生成器中,则可以修复此问题。
:sb-prelink-linkage-table在Windows上有效,但会导致测试失败。根本问题是mingw64已经实现了自己的libm。它们的触发功能很快,但是在后台使用了不正确的指令(例如FSIN)。当进行预链接时,将使用这些不准确的实现,而不是使用dlsym查找符号时发现的更准确的实现(来自msvcrt.dll吗?)。
我很乐意收到有关此方法以及如何改进它的任何想法的反馈!如果您有任何建议,请给我留言(Freenode上的emimmons或Github / Gitlab上的daewok)。
我已经将静态可执行文件合并到CLPM中,并将从v0.4.0开始分发它们!我将继续在其他项目中推出静态可执行文件。
补丁集的各个部分现在已经足够牢固,我认为可以将其提交上游考虑。当前2.1.2冻结后,我将开始发送它们。