【问题标题】:time() Function Segfaults with g++ -static Compiled on Centos 6 and Run on Debian 10time() 函数 Segfaults 与 g++ -static 在 Centos 6 上编译并在 Debian 10 上运行
【发布时间】:2020-03-31 00:34:55
【问题描述】:

伙计们!我正在编写必须在尽可能多的 Linux 版本上安装和运行的软件,但我必须能够在单个 Jenkins slave 上编译它。

现在这主要是可行的。但是我遇到了一种情况,即特殊组合会在 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 个应用程序,我发现它在这些条件下会出现段错误。

  1. 必须使用 -static 标志编译它们。如果我不使用 -static,它可以正常工作,无论编译哪种风格。
  2. 他们必须调用 time() 函数。我如何称呼它并不重要,但它必须被调用。我尝试了通常的疑点,例如传递 NULL 指针和传递真实指针。静态编译应用程序时总是会出现段错误。
  3. 它们必须在 Centos 6 上编译并在 Debian 10 上运行。如果我在 Debian 10 上静态编译,原型可以正常工作。

所以这是我工作的限制条件。

  1. 我必须在一个 Linux 从站上编译,因为我只分发一个二进制文件。跟踪多个二进制文件以及哪个二进制文件适用于哪种 Linux 风格并不是真正的选择。
  2. 我必须静态编译,否则会在其他受支持的 Linux 版本上造成不兼容。
  3. 为了兼容性,我必须使用较旧的 Linux 版本。它不一定是 Centos,但它必须生成可在 Centos 以及许多其他版本上运行的二进制文件。
  4. 为了代码兼容性,我还必须使用 g++ 4.7。

在您的回答中,我希望有某种代码技巧。也许是 time() 函数的良好、可靠的替代品。或者对与 Debian 10 兼容的另一种 Linux 风格的建议。

谁能解释为什么像 time() 这样的基本的、无处不在的函数在 Debian 9 上完全兼容,但只有在 Centos 6 上静态编译时才会在 Debian 10 上出现段错误。 ..

编辑:

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

【问题讨论】:

    标签: c++ linux debian


    【解决方案1】:

    可执行文件正在尝试使用 vsyscall 接口来实现用于time 函数的系统调用。

    这个接口已经被 vdso 弃用很长时间了。它在不久前被完全删除,但仍然可以模拟。

    Debian 10 似乎禁用了 vsyscall 仿真,这是出于安全原因,因为它可能使攻击更容易。您应该能够通过在启动时传递内核命令行选项vsyscall=emulate 来重新启用仿真,当然,如果这是一个选项的话,还有提到的安全影响。

    CentOS 6 上的 glibc 版本好像是 2.12,太旧了,无法使用 vdso。因此,要为较新的内核配置编译兼容的二进制文件,您至少需要 glibc 2.14。我不知道这是否可以很容易地安装在 CentOS 上,或者它是否可以与它附带的内核一起正常工作。

    您还应该考虑是否真的需要完全静态的二进制文件。您可以静态链接除 libc 之外的所有内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 2017-12-20
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多