好的,这只是一个有趣的练习,但是对于一些较旧的Linux系统来说编译程序不是太难,或者可以吗?
我可以访问所有运行Linux的几个古老系统,也许看看它们在负载下的表现会很有趣。举个例子,我们想用Eigen做一些线性代数,This post是一个很好的只有头的库。有没有机会在目标系统上编译它?
user@ancient:~ $ uname -a
Linux local 2.2.16 #5 Sat Jul 8 20:36:25 MEST 2000 i586 unknown
user@ancient:~ $ gcc --version
egcs-2.91.66
也许不是......所以让我们在当前系统上编译它。以下是我的尝试,主要是失败的。任何更多的想法都非常受欢迎。
使用-m32 -march=i386
user@ancient:~ $ ./a.out
BUG IN DYNAMIC LINKER ld.so: dynamic-link.h: 53: elf_get_dynamic_info: Assertion `! "bad dynamic tag"' failed!
使用-m32 -march=i386 -static
进行编译:在所有相当新的内核版本上运行,但如果它们稍微陈旧且出现众所周知的错误消息则会失败
user@ancient:~ $ ./a.out
FATAL: kernel too old
Segmentation fault
这是一个glibc
错误,它支持的最小内核版本,例如我的系统上的内核2.6.4:
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
statically linked, for GNU/Linux 2.6.4, not stripped
自己编译glibc
,支持最老的内核。 link order更详细地描述了它,但基本上就像这样
wget ftp://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.bz2
tar -xjf glibc-2.14.tar.bz2
cd glibc-2.14
mkdir build; cd build
../configure --prefix=/usr/local/glibc_32 \
--enable-kernel=2.0.0 \
--with-cpu=i486 --host=i486-linux-gnu \
CC="gcc -m32 -march=i486" CXX="g++ -m32 -march=i486"
make -j 4
make intall
不确定--with-cpu
和--host
选项是否做了什么,最重要的是强制使用编译器标志-m32 -march=i486
进行32位构建(不幸的是-march=i386
bails一段时间后出错,并--enable-kernel=2.0.0
使库与旧内核兼容。在configure
期间,我收到了警告
WARNING: minimum kernel version reset to 2.0.10
我想,仍然可以接受。有关使用不同内核更改的内容列表,请参阅./sysdeps/unix/sysv/linux/kernel-features.h
。
好的,所以让我们链接新编译的glibc
库,稍微有点乱,但是这里有:
$ export LIBC_PATH=/usr/local/glibc_32
$ export LIBC_FLAGS=-nostdlib -L${LIBC_PATH} \
${LIBC_PATH}/crt1.o ${LIBC_PATH}/crti.o \
-lm -lc -lgcc -lgcc_eh -lstdc++ -lc \
${LIBC_PATH}/crtn.o
$ g++ -m32 -static prog.o ${LIBC_FLAGS} -o prog
由于我们正在进行静态编译,this post很重要,可能需要一些试验和错误,但基本上我们可以了解gcc
给链接器的选项:
$ g++ -m32 -static -Wl,-v file.o
注意,crtbeginT.o
和crtend.o
也与我不需要的程序相关联,所以我将它们排除在外。输出还包括类似--start-group -lgcc -lgcc_eh -lc --end-group
的行,表示库之间的相互依赖关系,请参阅{{3}}。我刚刚在-lc
命令行中提到gcc
两次,这也解决了相互依赖。
是的,努力工作得到了回报,现在我得到了
$ file ./prog
./prog: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
statically linked, for GNU/Linux 2.0.10, not stripped
很棒我想,现在在旧系统上尝试:
user@ancient:~ $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault
这又是来自glibc
的{{1}}错误消息。我无法理解细节并放弃。
在新系统./nptl/sysdeps/i386/tls.h
上编译并链接旧系统。哇,这实际上适用于C和简单的C ++程序(不使用C ++对象),至少对于我测试过的少数几个。这并不太令人惊讶,因为g++ -c -m32 -march=i386
所需要的只是libc
(也许是一些数学),其界面没有改变但是printf
的界面现在非常不同。 / p>
使用旧版linux系统和gcc版本2.95设置虚拟框。然后编译gcc版本4.x.x ...对不起,但现在太懒了......
???
答案 0 :(得分:9)
找到了错误消息的原因:
user@ancient $ ./prog
set_thread_area failed when setting up thread-local storage
Segmentation fault
这是因为glibc
对函数进行系统调用,该函数仅在内核2.4.20之后可用。在某种程度上,它可以被视为glibc
的错误,因为当它至少需要内核2.4.20时它错误地声称与内核2.0.10兼容。
细节:
./glibc-2.14/nptl/sysdeps/i386/tls.h
[...]
/* Install the TLS. */ \
asm volatile (TLS_LOAD_EBX \
"int $0x80\n\t" \
TLS_LOAD_EBX \
: "=a" (_result), "=m" (_segdescr.desc.entry_number) \
: "0" (__NR_set_thread_area), \
TLS_EBX_ARG (&_segdescr.desc), "m" (_segdescr.desc)); \
[...]
_result == 0 ? NULL \
: "set_thread_area failed when setting up thread-local storage\n"; })
[...]
这里主要的是,它调用汇编函数int 0x80
,它是对linux内核的系统调用,它根据eax
的值决定要做什么,该值设置为
在这种情况下__NR_set_thread_area
并在
$ grep __NR_set_thread_area /usr/src/linux-2.4.20/include/asm-i386/unistd.h
#define __NR_set_thread_area 243
但不在任何早期的内核版本中。
所以好消息是“3.用--enable-kernel=2.0.0
编译glibc”可能会生成在所有Linux内核上运行的可执行文件> = 2.4.20。
tls
(线程本地存储),但glibc 2.14无法实现,尽管它是以configure
提供的选项。
答案 1 :(得分:3)
你无法在原始系统上编译它的原因可能与内核版本无关(它可能,但2.2通常不够老,因为它是大多数代码的绊脚石)。问题是工具链是古老的(至少是编译器)。但是,没有什么能阻止您使用已安装的egcs
构建更新版本的G ++。一旦你完成了这个问题,你也可能会遇到glibc
的问题,但你至少应该做到这一点。
你应该做的事情看起来像这样:
egcs
gcc
重建最新的GCC ld
现在您拥有一个精心构建的现代编译器和(大部分)工具链,可用于构建示例应用程序。如果运气不好,您可能还需要构建更新版本的glibc
,但这个是您的问题 - 工具链 - 而不是内核。