这是设置1的一个替代方案,也是我迄今为止实现的最正确的设置:据我所知,一切都是正确的,包括C运行时对象,如crt1.o、crti.o和crtn.o。 在这个设置中,我们将编译一个完整的专用GCC工具链,它使用我们想要的glibc。 这种方法唯一的缺点是构建需要更长的时间,但我不会冒险用更少的东西来进行生产设置。 crosstool-NG是一组脚本,可以为我们下载和编译源代码中的所有内容,包括GCC、glibc和binutils。 是的,GCC构建系统是如此糟糕,我们需要一个单独的项目。 这个设置并不完美,因为crosstool-NG does not support building the executables without extra -Wl flags,这感觉很奇怪,因为我们已经构建了GCC本身。但一切似乎都工作,所以这只是一个不便。 获取crosstool-NG并对其进行配置:
Available build steps, in order:
- companion_tools_for_build
- companion_libs_for_build
- binutils_for_build
- companion_tools_for_host
- companion_libs_for_host
- binutils_for_host
- cc_core_pass_1
- kernel_headers
- libc_start_files
- cc_core_pass_2
- libc
- cc_for_build
- cc_for_host
- libc_post_cc
- companion_libs_for_target
- binutils_for_target
- debug
- test_suite
- finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.
6条答案
按热度按时间0yg35tkg1#
您是正确的,因为glibc使用符号版本控制。如果您好奇,glibc 2.1中引入的符号版本控制实现被描述为here,并且是Sun的符号版本控制方案的扩展,被描述为here。
一个选择是静态链接你的二进制文件,这可能是最简单的选择。
你也可以在chroot编译环境中编译你的二进制文件,或者使用glibc-new =〉glibc-old 交叉编译器。
根据http://www.trevorpounds.com博客文章***Linking to Older Versioned Symbols (glibc)***,可以强制任何符号与旧符号链接,只要它是有效的,使用的 *
.symver
* 伪操作与最初定义版本化符号时使用的操作相同。下面的示例使用glibc的realpath,但确保它链接到较早的2.2.5版本。
c7rzv4ha2#
设置1:在没有专用GCC的情况下编译您自己的glibc并使用它
既然只使用符号版本控制技巧似乎是不可能的,那么让我们更进一步,自己编译glibc。
这个设置可能会工作并且很快,因为它不重新编译整个GCC工具链,只重新编译glibc。
但它并不可靠,因为它使用宿主C运行时对象,如glibc提供的
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,而不是宿主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 does not support building the executables without extra
-Wl
flags,这感觉很奇怪,因为我们已经构建了GCC本身。但一切似乎都工作,所以这只是一个不便。获取crosstool-NG并对其进行配置:
我能看到的唯一强制选项是使其与您的主机内核版本匹配以使用正确的内核头文件。
它告诉我:
所以在
menuconfig
中我做了:Operating System
个Version of linux
所以我选择:
这是第一个相同或较旧的版本。它必须是较旧的,因为内核是向后兼容的。
现在,您可以使用以下内容进行构建:
且现在等待大约三十分钟到两个小时以进行编译。
设置2:可选配置
我们使用
./ct-ng x86_64-unknown-linux-gnu
生成的.config
具有:要更改此设置,请在
menuconfig
中执行以下操作:C-library
个保存
.config
,然后继续构建。或者,如果你想使用你自己的glibc源代码,例如,使用最新git中的glibc,请继续like this:
Paths and misc options
个Try features marked as EXPERIMENTAL
:设置为真C-library
个Custom location
:说是Custom location
个Custom source location
:指向包含glibc源代码的目录其中glibc被克隆为:
设置2:测试一下
构建了所需的工具链后,请使用以下工具进行测试:
除了现在使用了正确的运行时对象之外,一切看起来都像在Setup 1中一样:
型
设置2:尝试有效重新编译glibc失败
如下所述,crosstool-NG似乎不可能出现这种情况。
如果你只是重建;
那么您对自定义glibc源代码位置的更改将被考虑在内,但是它从头开始构建所有内容,这使得它不能用于迭代开发。
如果我们这样做:
它给出了构建步骤的一个很好的概述:
存储器
因此,我们看到glibc步骤与几个GCC步骤交织在一起,最值得注意的是
libc_start_files
出现在cc_core_pass_2
之前,cc_core_pass_2
可能是与cc_core_pass_1
一起开销最大的步骤。为了只构建一个步骤,您必须首先为初始构建设置
.config
选项中的“保存intermediate steps”:Paths and misc options
然后您可以尝试:
但不幸的是,需要
+
,如以下位置所述:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536但是请注意,在中间步骤重新启动会将安装目录重置为该步骤中的状态。也就是说,您将得到一个重建的libc -但不会使用此libc构建最终编译器(因此,也不会生成像libstdc++这样的编译器库)。
并且基本上仍然使重建太慢,不适于开发,我不知道如何在不修补crosstool-NG的情况下克服这个问题。
此外,从
libc
开始的步骤似乎没有再次从Custom source location
复制源代码,这进一步使该方法不可用。额外奖励:stdlibc++
如果您对C标准库也感兴趣,那么还有一个额外的好处:如何编辑和重新构建GCC libstdc C++标准库源代码?
huus2vyu3#
使用**-static链接。当使用-static**链接时,链接器将库嵌入到可执行文件中,因此可执行文件会更大,但它可以在使用旧版本glibc的系统上执行,因为程序将使用自己的库,而不是系统的库。
2mbi3lxu4#
在我看来,最懒惰的解决方案(特别是如果你不依赖最新的C/C++特性,或者最新的编译器特性)还没有被提到,所以这里有:
只需在您仍希望支持的最旧GLIBC的系统上构建即可。
这实际上是很容易做到的技术,如chroot,或KVM/Virtualbox,或docker,即使你真的不想使用这样一个旧的发行版直接在任何个人电脑上.详细地说,使您的软件的最大可移植二进制我建议以下步骤:
1.只要选择你的沙盒/虚拟化/......什么的毒药,用它给你自己弄一个虚拟的旧版Ubuntu LTS,然后用它默认的gcc/g++编译,这会自动地把你的GLIBC限制在那个环境中可用的那个。
1.避免依赖基础库之外的外部库:比如,你应该动态链接底层系统的东西,比如glibc、libGL、libxcb/X11/wayland things、libasound/libpulseaudio,如果你使用GTK+的话,但是如果可以的话,最好静态链接外部库/将它们沿着发送。特别是大多数自包含的库,比如图像加载器、多媒体解码器,等可以导致较少的破损对其他发行版(破损可以造成,例如,如果只存在于某处在不同的主要版本),如果你静态船舶他们。
通过这种方法,您可以得到一个与旧GLIBC兼容的二进制文件,而无需进行任何手动符号调整,无需进行完全静态的二进制文件(对于更复杂的程序,这可能会中断,因为glibc不喜欢这样做,这可能会导致您的许可问题),也无需设置任何自定义工具链、任何自定义glibc副本或其他任何东西。
qq24tv8q5#
另一种方法是丢弃版本信息,让链接器默认为它所拥有的任何版本。
为此,您可能需要 checkout PatchELF 1:
如果你手头没有源代码(或者很难理解代码2),这是非常有用的。
1虽然
patchelf
在许多发行版中都有,但它们很可能已经过时了。--clear-symbol-version
标志是在0.12
版本中添加的,不幸的是,它并没有完全删除版本要求。在合并this merge request之前,您需要手动编译它。2仅供参考,我正在交叉编译LuaJIT。
qgelzfjb6#
本回购协议:
https://github.com/wheybags/glibc_version_header
提供了一个头文件,用于处理已接受答案中描述的详细信息。
基本上:
1.下载您要链接的相应GCC的header
1.将
-include /path/to/header.h
添加到编译器标志中1.如果要链接pthread,可能还需要添加
-D_REENTRANT
我还添加了链接器标志:
-static-libgcc -static-libstdc++ -pthread
但这取决于应用的要求。