time() 函数段错误与 g++ -static 在 Centos 6 上编译,在 Debian 10 上编译 运行
time() Function Segfaults with g++ -static Compiled on Centos 6 and Run on Debian 10
伙计们!我正在编写必须安装并 运行 尽可能多的 Linux 软件,但我必须能够在单个 Jenkins 从站上编译它。
现在这主要是有效的。但是我遇到了 运行 的情况,即某些事物的特殊组合会在 Debian 10 上产生段错误,但不会在我支持的 Linux 的任何其他版本中产生。我已经能够在 3 个不同的应用程序(其中一些已经工作多年)中重现这一点,包括我在下面列出的简化原型。
// g++ -g -o ttt -static tt.cpp
// The above compile of this code on Centos 6 will produce a segfault
// when run on Debian 10, but not on any other tested flavor of Linux.
// Dozens of them. The version of g++ is 4.7.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int _argc, char* _argv[])
{
srand(time(0));
printf("success\n");
return 0;
}
我在 Debian 10 上使用 gdb 运行我的 3 个应用程序中的每一个都发现,在这些条件下它会出现段错误。
- 它们必须使用 -static 标志进行编译。如果我不使用 -static,它可以正常工作,无论它是在哪种风格下编译的。
- 他们必须调用 time() 函数。我怎么称呼它并不重要,但它必须被称呼。我尝试了通常的嫌疑人,比如传递 NULL 指针和传递真实指针。当应用程序静态编译时它总是出现段错误。
- 它们必须在 Centos 6 上编译,运行 在 Debian 10 上编译。如果我在 Debian 10 上静态编译,原型可以正常工作。
所以这是我工作的限制条件。
- 我必须在一个 Linux 从机上编译,因为我只分发一个二进制文件。跟踪多个二进制文件以及哪个二进制文件执行什么 Linux 风格并不是一个真正的选择。
- 我必须静态编译,否则它会在 Linux.
的其他受支持版本上造成不兼容
- 我不得不使用旧的 Linux 风格,也是为了兼容性。它不一定是 Centos,但它必须生成将 运行 在 Centos 以及许多其他版本上运行的二进制文件。
- 为了代码兼容性,我还必须使用 g++ 4.7。
在您的回答中,我希望有某种代码技巧。也许是 time() 函数的一个好的、可靠的替代品。或者对与 Debian 10 兼容的另一种 Linux 风格的建议。
谁能够解释为什么像 time() 这样基本的、无处不在的函数在 Debian 9 上完全兼容,但只有在 Centos 上静态编译时才会在 Debian 10 上出现段错误6...
编辑:
Centos 6 服务器上的 strace:
execve("./ttt", ["./ttt"], [/* 37 vars */]) = 0
uname({sys="Linux", node="testcent6", ...}) = 0
brk(0) = 0x238c000
brk(0x238d180) = 0x238d180
arch_prctl(ARCH_SET_FS, 0x238c860) = 0
brk(0x23ae180) = 0x23ae180
brk(0x23af000) = 0x23af000
gettimeofday({1585687633, 358976}, NULL) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f682c82f000
write(1, "success\n", 8success
) = 8
exit_group(0) = ?
+++ exited with 0 +++
Debian 10 服务器上的 strace:
execve("./ttt", ["./ttt"], 0x7fff0430dfd0 /* 18 vars */) = 0
uname({sysname="Linux", nodename="deletemedebian10", ...}) = 0
brk(NULL) = 0x1f6f000
brk(0x1f70180) = 0x1f70180
arch_prctl(ARCH_SET_FS, 0x1f6f860) = 0
brk(0x1f91180) = 0x1f91180
brk(0x1f92000) = 0x1f92000
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xffffffffff600400} ---
+++ killed by SIGSEGV +++
Segmentation fault
可执行文件正在尝试使用 vsyscall 接口来实现用于 time
函数的系统调用。
这个接口已经被弃用很长时间了,取而代之的是 vdso。前一阵子完全放弃了,但仍然可以模拟。
Debian 10 似乎禁用了 vsyscall 仿真,这是出于安全原因,因为它可能使攻击更容易。您应该能够通过在启动时传递内核命令行选项 vsyscall=emulate
来重新启用仿真,当然,如果这是一个选项,也会产生上述的安全影响。
CentOS 6 上的glibc 版本好像是2.12,太旧了,无法使用vdso。因此,要为较新的内核配置编译兼容的二进制文件,您至少需要 glibc 2.14。我不知道这是否可以很容易地安装在 CentOS 上,或者它是否可以与它附带的内核一起正常工作。
您还应该考虑您是否真的需要一个完全静态的二进制文件。您可以 link 除了 libc 之外的所有内容都是静态的。
伙计们!我正在编写必须安装并 运行 尽可能多的 Linux 软件,但我必须能够在单个 Jenkins 从站上编译它。
现在这主要是有效的。但是我遇到了 运行 的情况,即某些事物的特殊组合会在 Debian 10 上产生段错误,但不会在我支持的 Linux 的任何其他版本中产生。我已经能够在 3 个不同的应用程序(其中一些已经工作多年)中重现这一点,包括我在下面列出的简化原型。
// g++ -g -o ttt -static tt.cpp
// The above compile of this code on Centos 6 will produce a segfault
// when run on Debian 10, but not on any other tested flavor of Linux.
// Dozens of them. The version of g++ is 4.7.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int _argc, char* _argv[])
{
srand(time(0));
printf("success\n");
return 0;
}
我在 Debian 10 上使用 gdb 运行我的 3 个应用程序中的每一个都发现,在这些条件下它会出现段错误。
- 它们必须使用 -static 标志进行编译。如果我不使用 -static,它可以正常工作,无论它是在哪种风格下编译的。
- 他们必须调用 time() 函数。我怎么称呼它并不重要,但它必须被称呼。我尝试了通常的嫌疑人,比如传递 NULL 指针和传递真实指针。当应用程序静态编译时它总是出现段错误。
- 它们必须在 Centos 6 上编译,运行 在 Debian 10 上编译。如果我在 Debian 10 上静态编译,原型可以正常工作。
所以这是我工作的限制条件。
- 我必须在一个 Linux 从机上编译,因为我只分发一个二进制文件。跟踪多个二进制文件以及哪个二进制文件执行什么 Linux 风格并不是一个真正的选择。
- 我必须静态编译,否则它会在 Linux. 的其他受支持版本上造成不兼容
- 我不得不使用旧的 Linux 风格,也是为了兼容性。它不一定是 Centos,但它必须生成将 运行 在 Centos 以及许多其他版本上运行的二进制文件。
- 为了代码兼容性,我还必须使用 g++ 4.7。
在您的回答中,我希望有某种代码技巧。也许是 time() 函数的一个好的、可靠的替代品。或者对与 Debian 10 兼容的另一种 Linux 风格的建议。
谁能够解释为什么像 time() 这样基本的、无处不在的函数在 Debian 9 上完全兼容,但只有在 Centos 上静态编译时才会在 Debian 10 上出现段错误6...
编辑:
Centos 6 服务器上的 strace:
execve("./ttt", ["./ttt"], [/* 37 vars */]) = 0
uname({sys="Linux", node="testcent6", ...}) = 0
brk(0) = 0x238c000
brk(0x238d180) = 0x238d180
arch_prctl(ARCH_SET_FS, 0x238c860) = 0
brk(0x23ae180) = 0x23ae180
brk(0x23af000) = 0x23af000
gettimeofday({1585687633, 358976}, NULL) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f682c82f000
write(1, "success\n", 8success
) = 8
exit_group(0) = ?
+++ exited with 0 +++
Debian 10 服务器上的 strace:
execve("./ttt", ["./ttt"], 0x7fff0430dfd0 /* 18 vars */) = 0
uname({sysname="Linux", nodename="deletemedebian10", ...}) = 0
brk(NULL) = 0x1f6f000
brk(0x1f70180) = 0x1f70180
arch_prctl(ARCH_SET_FS, 0x1f6f860) = 0
brk(0x1f91180) = 0x1f91180
brk(0x1f92000) = 0x1f92000
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xffffffffff600400} ---
+++ killed by SIGSEGV +++
Segmentation fault
可执行文件正在尝试使用 vsyscall 接口来实现用于 time
函数的系统调用。
这个接口已经被弃用很长时间了,取而代之的是 vdso。前一阵子完全放弃了,但仍然可以模拟。
Debian 10 似乎禁用了 vsyscall 仿真,这是出于安全原因,因为它可能使攻击更容易。您应该能够通过在启动时传递内核命令行选项 vsyscall=emulate
来重新启用仿真,当然,如果这是一个选项,也会产生上述的安全影响。
CentOS 6 上的glibc 版本好像是2.12,太旧了,无法使用vdso。因此,要为较新的内核配置编译兼容的二进制文件,您至少需要 glibc 2.14。我不知道这是否可以很容易地安装在 CentOS 上,或者它是否可以与它附带的内核一起正常工作。
您还应该考虑您是否真的需要一个完全静态的二进制文件。您可以 link 除了 libc 之外的所有内容都是静态的。