如何链接到特定的 glibc 版本?
当我在 Ubuntu Lucid 10.04 PC 上编译某些内容时,它会与 glibc 链接。 Lucid 使用 glibc 2.11。当我在另一台装有较旧 glibc 的 PC 上运行此二进制文件时,该命令失败,提示没有 glibc 2.11...
据我所知 glibc 使用符号版本控制。我可以强制 gcc 链接到特定的符号版本吗?
在我的具体使用中,我尝试为ARM编译一个gcc交叉工具链。
When I compile something on my Ubuntu Lucid 10.04 PC it gets linked against glibc. Lucid uses 2.11 of glibc. When I run this binary on another PC with an older glibc, the command fails saying there's no glibc 2.11...
As far as I know glibc uses symbol versioning. Can I force gcc to link against a specific symbol version?
In my concrete use I try to compile a gcc cross toolchain for ARM.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您是正确的,glibc 使用符号版本控制。如果您好奇,此处描述了 glibc 2.1 中引入的符号版本控制实现,并且是Sun 符号版本控制方案的扩展 在这里。
一种选择是静态链接您的二进制文件。这可能是最简单的选择。
您还可以在 chroot 构建环境中构建二进制文件,或使用 glibc-new => glibc-旧交叉编译器。
根据 http://www.trevorpounds。 com 博客文章 链接到旧版本符号 (glibc),只要使用有效的符号,就可以强制将任何符号链接到旧版本符号与最初用于定义版本化符号的
.symver
伪操作相同。以下示例摘自 博客文章。以下示例使用 glibc 的实际路径,但确保它链接到较旧的 2.2.5 版本。
You are correct in that glibc uses symbol versioning. If you are curious, the symbol versioning implementation introduced in glibc 2.1 is described here and is an extension of Sun's symbol versioning scheme described here.
One option is to statically link your binary. This is probably the easiest option.
You could also build your binary in a chroot build environment, or using a glibc-new => glibc-old cross-compiler.
According to the http://www.trevorpounds.com blog post Linking to Older Versioned Symbols (glibc), it is possible to to force any symbol to be linked against an older one so long as it is valid by using the same
.symver
pseudo-op that is used for defining versioned symbols in the first place. The following example is excerpted from the blog post.The following example makes use of glibc’s realpath, but makes sure it is linked against an older 2.2.5 version.
设置 1:在没有专用 GCC 的情况下编译您自己的 glibc 并使用它
由于仅使用符号版本控制 hack 似乎不可能做到这一点,让我们更进一步,自己编译 glibc。
此设置可能有效并且速度很快,因为它不会重新编译整个 GCC 工具链,而只是 glibc。
但它并不可靠,因为它使用 glibc 提供的主机 C 运行时对象,例如
crt1.o
、crti.o
和crtn.o
。这在以下位置提到: https://sourceware.org /glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location 这些对象会进行 glibc 所依赖的早期设置,因此如果事情以奇妙且极其微妙的方式崩溃,我不会感到惊讶。要获得更可靠的设置,请参阅下面的设置 2。
构建 glibc 并在本地安装:
设置 1:验证构建
test_glibc.c
使用
test_glibc.sh
编译并运行:程序输出预期的:
命令改编自 https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21 #Compile_against_glibc_in_an_installed_location 但
--sysroot
导致它失败:所以我删除了它。
ldd
输出确认我们刚刚构建的ldd
和库实际上正在按预期使用:gcc
编译调试输出显示我的使用了主机运行时对象,这很糟糕,如前所述,但我不知道如何解决它,例如它包含:设置 1:修改 glibc
现在让我们修改 glibc:
然后重新编译并重新安装 glibc,然后重新编译并重新运行我们的程序:
我们看到
hacked
按预期打印了几次。这进一步证实了我们实际上使用了我们编译的 glibc 而不是主机。
在 Ubuntu 18.04 上测试。
设置 2:crosstool-NG 原始设置
这是设置 1 的替代方案,它是我迄今为止实现的最正确的设置:据我观察,一切都是正确的,包括 C 运行时对象,例如
crt1 .o
、crti.o
和crtn.o
。在此设置中,我们将编译一个完整的专用 GCC 工具链,该工具链使用我们想要的 glibc。
这种方法的唯一缺点是构建时间会更长。但我不会冒险用更少的东西来进行生产设置。
crosstool-NG 是一组脚本,可以为我们从源代码下载和编译所有内容,包括 GCC、glibc 和 binutils。
是的,GCC 构建系统太糟糕了,我们需要一个单独的项目。
此设置并不完美,因为 crosstool-NG 不支持在没有额外内容的情况下构建可执行文件
-Wl
标志,这感觉很奇怪,因为我们已经构建了 GCC 本身。但一切似乎都正常,所以这只是一个不便。获取 crosstool-NG 并配置它:
我能看到的唯一强制选项是使其与您的主机内核版本匹配以使用正确的内核标头。使用以下命令查找您的主机内核版本:
它显示:
所以在
menuconfig
中我这样做:操作系统
Linux 版本
所以我选择:
这是第一个相同或更旧的版本。它必须更旧,因为内核是向后兼容的。
现在您可以使用以下命令进行构建:
然后等待大约三十分钟到两个小时进行编译。
设置 2:可选配置
我们使用
./ct-ng x86_64-unknown-linux-gnu
生成的.config
具有:要更改它,请在
menuconfig< /code> do:
C-library
glibc 版本
保存
.config
,然后继续构建。或者,如果您想使用自己的 glibc 源,例如使用最新 git 中的 glibc,请继续 像这样:
路径和其他选项
尝试标记为“实验”的功能
:设置为 trueC-library
glibc 来源
自定义位置
:说是自定义位置
自定义源位置
:指向包含 glibc 源的目录,其中 glibc 被克隆为:
设置 2:测试它
一旦您构建了所需的工具链,请使用以下命令进行测试:
一切似乎都正常与设置 1 一样,只不过现在使用了正确的运行时对象:
设置 2:高效 glibc 重新编译尝试失败
使用 crosstool-NG 似乎不可能,如下所述。
如果你只是重新构建;
那么您对自定义 glibc 源位置的更改就会被考虑在内,但它会从头开始构建所有内容,使其无法用于迭代开发。
如果我们这样做:
它很好地概述了构建步骤:
因此,我们看到 glibc 步骤与多个 GCC 步骤交织在一起,最值得注意的是
libc_start_files
出现在cc_core_pass_2
之前,这可能是与 cc_core_pass_1 一起使用时最昂贵的步骤。为了仅构建一个步骤,您必须首先在初始构建的
.config
选项中设置“保存中间步骤”:路径和其他选项
调试 crosstool-NG
保存中间步骤
,然后您可以尝试:
但不幸的是,需要的
+
如所述:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536基本上仍然使重建太慢而无法进行开发,并且我不知道如何在不修补 crosstool-NG 的情况下克服这个问题。
此外,从
libc
步骤开始似乎没有从自定义源位置
再次复制源,进一步使该方法无法使用。额外奖励:stdlibc++
如果您也对 C++ 标准库感兴趣,那么额外奖励:如何编辑和重新构建 GCC libstdc++ C++ 标准库源?
Setup 1: compile your own glibc without dedicated GCC and use it
Since it seems impossible to do just with symbol versioning hacks, let's go one step further and compile glibc ourselves.
This setup might work and is quick as it does not recompile the whole GCC toolchain, just glibc.
But it is not reliable as it uses host C runtime objects such as
crt1.o
,crti.o
, andcrtn.o
provided by glibc. This is mentioned at: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Those objects do early setup that glibc relies on, so I wouldn't be surprised if things crashed in wonderful and awesomely subtle ways.For a more reliable setup, see Setup 2 below.
Build glibc and install locally:
Setup 1: verify the build
test_glibc.c
Compile and run with
test_glibc.sh
:The program outputs the expected:
Command adapted from https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location but
--sysroot
made it fail with:so I removed it.
ldd
output confirms that theldd
and libraries that we've just built are actually being used as expected:The
gcc
compilation debug output shows that my host runtime objects were used, which is bad as mentioned previously, but I don't know how to work around it, e.g. it contains:Setup 1: modify glibc
Now let's modify glibc with:
Then recompile and re-install glibc, and recompile and re-run our program:
and we see
hacked
printed a few times as expected.This further confirms that we actually used the glibc that we compiled and not the host one.
Tested on Ubuntu 18.04.
Setup 2: crosstool-NG pristine setup
This is an alternative to setup 1, and it is the most correct setup I've achieved far: everything is correct as far as I can observe, including the C runtime objects such as
crt1.o
,crti.o
, andcrtn.o
.In this setup, we will compile a full dedicated GCC toolchain that uses the glibc that we want.
The only downside to this method is that the build will take longer. But I wouldn't risk a production setup with anything less.
crosstool-NG is a set of scripts that downloads and compiles everything from source for us, including GCC, glibc and binutils.
Yes the GCC build system is so bad that we need a separate project for that.
This setup is only not perfect because crosstool-NG does not support building the executables without extra
-Wl
flags, which feels weird since we've built GCC itself. But everything seems to work, so this is only an inconvenience.Get crosstool-NG and configure it:
The only mandatory option that I can see, is making it match your host kernel version to use the correct kernel headers. Find your host kernel version with:
which shows me:
so in
menuconfig
I do:Operating System
Version of linux
so I select:
which is the first equal or older version. It has to be older since the kernel is backwards compatible.
Now you can build with:
and now wait for about thirty minutes to two hours for compilation.
Setup 2: optional configurations
The
.config
that we generated with./ct-ng x86_64-unknown-linux-gnu
has:To change that, in
menuconfig
do:C-library
Version of glibc
save the
.config
, and continue with the build.Or, if you want to use your own glibc source, e.g. to use glibc from the latest git, proceed like this:
Paths and misc options
Try features marked as EXPERIMENTAL
: set to trueC-library
Source of glibc
Custom location
: say yesCustom location
Custom source location
: point to a directory containing your glibc sourcewhere glibc was cloned as:
Setup 2: test it out
Once you have built he toolchain that you want, test it out with:
Everything seems to work as in Setup 1, except that now the correct runtime objects were used:
Setup 2: failed efficient glibc recompilation attempt
It does not seem possible with crosstool-NG, as explained below.
If you just re-build;
then your changes to the custom glibc source location are taken into account, but it builds everything from scratch, making it unusable for iterative development.
If we do:
it gives a nice overview of the build steps:
therefore, we see that there are glibc steps intertwined with several GCC steps, most notably
libc_start_files
comes beforecc_core_pass_2
, which is likely the most expensive step together withcc_core_pass_1
.In order to build just one step, you must first set the "Save intermediate steps" in
.config
option for the intial build:Paths and misc options
Debug crosstool-NG
Save intermediate steps
and then you can try:
but unfortunately, the
+
required as mentioned at: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536and basically still makes the rebuild too slow to be feasible for development, and I don't see how to overcome this without patching crosstool-NG.
Furthermore, starting from the
libc
step didn't seem to copy over the source again fromCustom source location
, further making this method unusable.Bonus: stdlibc++
A bonus if you're also interested in the C++ standard library: How to edit and re-build the GCC libstdc++ C++ standard library source?
使用-static链接。当您使用-static链接时,链接器将库嵌入到可执行文件中,因此可执行文件会更大,但它可以在具有旧版本glibc的系统上执行,因为程序将使用它自己的库而不是系统的库。
Link with -static. When you link with -static the linker embeds the library inside the executable, so the executable will be bigger, but it can be executed on a system with an older version of glibc because the program will use it's own library instead of that of the system.
在我看来,最懒的解决方案(特别是如果您不依赖最新的前沿 C/C++ 功能或最新的编译器功能)尚未提及,所以这里是:
只需在系统上构建您仍然想支持最古老的 GLIBC。
如今,使用 chroot、KVM/Virtualbox 或 docker 等技术实际上很容易做到这一点,即使您真的不想直接在任何操作系统上使用这样的旧发行版。个人电脑。详细来说,为了使您的软件具有最大的可移植性,我建议您遵循以下步骤:
只需选择您的沙箱/虚拟化/...任何毒药,然后使用它为自己获取一个虚拟的较旧的 Ubuntu LTS 并使用默认情况下,gcc/g++ 就在那里。这会自动将您的 GLIBC 限制为该环境中可用的 GLIBC。
避免依赖于基础库之外的外部库:例如,您应该动态链接底层系统内容,例如 glibc、libGL、libxcb/X11/wayland 等、libasound/libpulseaudio,如果您使用的话,可能还需要 GTK+,但否则最好使用 GTK+如果可以的话,静态链接外部库/运送它们。特别是大多数独立的库,如图像加载器、多媒体解码器等,如果您静态发布它们,则可以减少其他发行版上的损坏(例如,如果仅存在于不同的主要版本中的某个位置,则可能会导致损坏)。
通过这种方法,您可以获得旧的 GLIBC 兼容二进制文件,无需任何手动符号调整,无需执行完全静态的二进制文件(这可能会破坏更复杂的程序,因为 glibc 讨厌这样做,这可能会导致您的许可问题),并且无需设置任何自定义工具链、任何自定义 glibc 副本或其他任何内容。
In my opinion, the laziest solution (especially if you don't rely on latest bleeding edge C/C++ features, or latest compiler features) wasn't mentioned yet, so here it is:
Just build on the system with the oldest GLIBC you still want to support.
This is actually pretty easy to do nowadays with technologies like chroot, or KVM/Virtualbox, or docker, even if you don't really want to use such an old distro directly on any pc. In detail, to make a maximum portable binary of your software I recommend following these steps:
Just pick your poison of sandbox/virtualization/... whatever, and use it to get yourself a virtual older Ubuntu LTS and compile with the gcc/g++ it has in there by default. That automatically limits your GLIBC to the one available in that environment.
Avoid depending on external libs outside of foundational ones: like, you should dynamically link ground-level system stuff like glibc, libGL, libxcb/X11/wayland things, libasound/libpulseaudio, possibly GTK+ if you use that, but otherwise preferrably statically link external libs/ship them along if you can. Especially mostly self-contained libs like image loaders, multimedia decoders, etc can cause less breakage on other distros (breakage can be caused e.g. if only present somewhere in a different major version) if you statically ship them.
With that approach you get an old-GLIBC-compatible binary without any manual symbol tweaks, without doing a fully static binary (that may break for more complex programs because glibc hates that, and which may cause licensing issues for you), and without setting up any custom toolchain, any custom glibc copy, or whatever.
使用
zig cc
< /a> 作为您的编译器,可以针对特定版本的 glibc。例如,以
glibc 2.5
为目标,这也适用于使用
zig c++
的C++zig cc
基于clang的顶部,所以它是一个漂亮的如果您使用 clang 或 gcc,则替换率接近下降。Using
zig cc
as your compiler, it's possible to target a specific version of glibc.For example, to target
glibc 2.5
This also works for C++ too using
zig c++
zig cc
is based on top of clang, so it's a pretty close to a drop in replacement if you are using either clang or gcc.另一种方法是丢弃版本信息并让链接器默认为它所拥有的任何版本。
为此,您可能需要查看PatchELF1:
这非常如果您手头没有源代码(或者很难理解代码2),这很有用。
1 尽管
patchelf
在许多发行版上都可用,但它们很可能已经过时。--clear-symbol-version
标志是在0.12
版本中添加的,不幸的是,它并没有完全删除版本要求。在合并之前,您需要从此合并请求手动编译。
2仅供参考,我正在交叉编译LuaJIT。
An alternative is to just discard the version info and let the linker default to whatever version it has.
To do this, you may want to check out PatchELF1:
This is extremely useful if you don't have the source at hand (or have a hard time understanding the code2).
1 Although
patchelf
is available on many distributions, they are very likely to be outdated.The
--clear-symbol-version
flag was added in version0.12
, which, unfortunately, does not fully remove version requirements.You will need to compile manually from this merge request before it gets merged.
2 FYI, I was cross-compiling LuaJIT.
这个仓库:
https://github.com/wheybags/glibc_version_header
提供了一个头文件,负责处理中描述的详细信息接受的答案。
基本上:
-include /path/to/header.h
到您的编译器标志中-D_REENTRANT
我还添加了链接器标志:
-static-libgcc -static-libstdc++ -pthread
但这些取决于您的应用程序的要求。
This repo:
https://github.com/wheybags/glibc_version_header
provides a header file that takes care of the details described in the accepted answer.
Basically:
-include /path/to/header.h
to your compiler flags-D_REENTRANT
if you're linking pthreadI also add the linker flags:
-static-libgcc -static-libstdc++ -pthread
But those are dependent on your app's requirements.