6

如果我的问题不准确,我深表歉意,因为我没有很多 Linux 相关经验。我目前正在从头开始构建 Linux(主要遵循 linuxfromscratch.org 7.3 版的指南)。我遇到了以下问题:当我构建一个可执行文件时,它会获得一个名为 ELF 解释器的硬编码路径。

readelf -l program

显示类似

[Requesting program interpreter: /lib/ld-linux.so.2]

我跟踪这个库 ld-linux-so.2 成为 glibc 的一部分。我对这种行为不太满意,因为它使二进制文件非常不可移植 - 如果我更改 /lib/ld-linux.so.2 的位置,可执行文件不再工作,我发现的唯一“修复”是使用 patchelf NixOS 的实用程序将硬编码路径更改为另一个硬编码路径。出于这个原因,我想链接到 ld 库的静态版本,但没有生成。所以这是我的问题,你能否解释一下我如何构建 glibc 以便它生成一个静态版本的 ld-linux.so.2,我稍后可以链接到我的可执行文件。我不完全理解这个 ld 库的作用,但我认为这是加载其他动态库(或至少 glibc.so)的部分。我想动态链接我的可执行文件,但我希望动态链接器本身静态内置于其中,因此它们不会依赖于硬编码路径。或者,我希望能够使用类似于 LD_LIBRARY_PATH 的环境变量设置解释器的路径,也许是 LD_INTERPRETER_PATH。目标是能够生成可移植的二进制文件,无论目录结构是什么,它都可以在具有相同 ABI 的任何平台上运行。

一些可能相关的背景:我正在使用 Slackware 14 x86 来构建 i686 编译器工具链,所以总的来说它都是 x86 主机和目标。我正在使用 glibc 2.17 和 gcc 4.7.x。

4

2 回答 2

12

我希望能够使用类似于 LD_LIBRARY_PATH 的环境变量设置解释器的路径,也许是 LD_INTERPRETER_PATH。

这根本不可能。仔细(并多次)阅读execve(2)elf(5)ld.so(8)手册页以及 Linux ABIELF规范。而且内核代码也在做execve.

ELF 解释器负责动态链接。它必须是文件层次结构中某个固定位置的文件(技术上是静态链接的 ELF 共享库)(通常是/lib/ld.so.2or/lib/ld-linux.so.2/lib64/ld-linux-x86-64.so.2

1990 年代的旧a.out格式有一个内置的动态链接器,部分在旧的 Linux 1.x 内核中实现。它没有那么灵活,也没有那么强大。

通过这种(原则上)任意动态链接器路径,内核能够拥有各种动态链接器。但大多数系统只有一个。这是参数化动态链接器的好方法。如果您想尝试另一个,请将其安装在文件系统中并生成提及该路径的 ELF 可执行文件。

付出极大的痛苦和努力,您可能会创建自己的ld.so类似动态链接器来实现您的愿望,但该链接器仍然必须是位于文件树中某个固定LD_INTERPRETER_PATH位置的 ELF 共享库。

如果您想要一个不需要任何文件的系统(在某些预定义的和连接的位置,如/lib/ld.so, /dev/null, /sbin/init...),您需要静态构建其所有可执行二进制文件。您可能希望(但当前的 Linux 发行版通常不这样做)拥有一些静态链接的可执行文件(例如/sbin/init/bin/sash...),这将使您能够修复损坏到没有任何动态链接器的系统。

顺便说一句,/sbin/init-or /bin/sh- 路径连接在内核本身内部。您可以在引导加载时将一些参数传递给内核 - 例如使用 GRUB - 以覆盖默认值。所以即使是内核也想要一些文件在这里!

正如我所评论的,您可能会查看MUSL-Libc以获取替代 Libc 实现(提供其自己的动态链接器)。另请阅读有关VDSOASLR以及initrd的信息。

在实践中,接受现代 Linux 和 Unix 期待一些非空文件系统的事实......请注意,动态链接和共享库是一个巨大的进步(在 1990 年代的 Linux 内核和发行版中要痛苦得多)。

或者,定义自己的二进制格式,然后创建内核模块或binfmt_misc条目来处理它。

顺便说一句,大多数(或全部)Linux 都是免费软件,因此您可以改进它(但这需要几个月或多年的工作时间)。请通过发布来分享您的改进。

另请阅读Drepper 的 Hwo to Write Shared Libraries论文;还有这个问题

于 2013-07-29T12:26:23.807 回答
1

我遇到了同样的问题。就我而言,我想将我的应用程序与安装的系统不同的 GLIBC 捆绑在一起。由于 ld-linux.so 必须匹配 GLIBC 版本,我不能简单地使用相应的 GLIBC 部署我的应用程序。问题是我无法在没有所需 GLIBC 版本的旧安装上运行我的应用程序。

加载器解释器的路径可以用 --dynamic-linker=/path/to/interp 修改。但是,这需要在编译时设置,因此需要将我的应用程序安装在该位置(或者至少我需要在该位置部署与我的 GLIBC 一起使用的 ld-linux.so,这与简单的xcopy 部署。

所以需要一个与 -rpath 选项可以处理的 $ORIGIN 选项等效的选项。这将允许完全动态部署。

鉴于缺少动态解释器路径(在运行时),有两种选择:

a) 在可执行文件启动之前使用 patchelf 修改路径。b) 使用可执行文件作为参数直接调用 ld-linux.so。

这两个选项都不像可执行文件本身中已编译的 $ORIGIN 路径那样“集成”。

于 2016-09-21T03:39:10.317 回答