GCC:用于静态链接到pthread的--whole-archive配方在最新的gcc版本中停止工作 [英] GCC: --whole-archive recipe for static linking to pthread stopped working in recent gcc versions

查看:301
本文介绍了GCC:用于静态链接到pthread的--whole-archive配方在最新的gcc版本中停止工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Linux上,针对pthread的静态链接是一个困难的话题.

Static linking against pthread is a difficult topic on Linux. It used to work to wrap -lpthread as -Wl,--whole-archive -lpthread -Wl,--no-whole-archive (the details can be found in this answer).

结果是(用于pthread的)符号强但不弱.自从Ubuntu 18.04左右(在gcc 5.4.0和gcc 7.4.0之间)以来,这种行为似乎已经改变,pthread符号现在始终以弱符号结尾,而与--whole-archive选项无关.

The effect was that symbols (for pthread) were strong, not weak. Since around Ubuntu 18.04 (between gcc 5.4.0 and gcc 7.4.0) that behavior seemed to have changed, and pthread symbols now always end up as weak symbols independent of the --whole-archive option.

因此,-whole-archive配方停止工作.我的问题的目的是了解工具链中最近发生了哪些变化(编译器,链接器,标准库),以及可以采取哪些措施来恢复旧的行为.

Because of that, the -whole-archive recipe stopped working. The intention of my question is to understand what has changed recently in the toolchain (compiler, linker, standard libray), and what can be done to get the old behavior back.

示例:

#include <mutex>

int main(int argc, char **argv) {
  std::mutex mutex;
  mutex.lock();
  mutex.unlock();
  return 0;
}

在以下所有示例中,都使用了相同的编译命令:

In all following examples, the same compilation command was used:

g++ -std=c++11 -Wall -static simple.cpp  -Wl,--whole-archive -lpthread  -Wl,--no-whole-archive

之前,使用-static进行编译时,pthread符号(例如,pthread_mutex_lock)很强(由T "rel =" nofollow noreferrer> nm ),但现在它们很弱(W):

Before, when compiling with -static, pthread symbols (e.g., pthread_mutex_lock) were strong (marked as T by nm), but now they are weak (W):

Ubuntu 14.04:docker run --rm -it ubuntu:14.04 bash

Ubuntu 14.04: docker run --rm -it ubuntu:14.04 bash

$ apt-get update
$ apt-get install g++

$ g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4

$ nm a.out | grep pthread_mutex_lock
0000000000408160 T __pthread_mutex_lock
00000000004003e0 t __pthread_mutex_lock_full
0000000000408160 T pthread_mutex_lock

Ubuntu 16.04:docker run --rm -it ubuntu:16.04 bash

Ubuntu 16.04: docker run --rm -it ubuntu:16.04 bash

$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609

$ nm a.out | grep pthread_mutex_lock
00000000004077b0 T __pthread_mutex_lock
0000000000407170 t __pthread_mutex_lock_full
00000000004077b0 T pthread_mutex_lock

Ubuntu 18.04:docker run --rm -it ubuntu:18.04 bash

Ubuntu 18.04: docker run --rm -it ubuntu:18.04 bash

$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0

$ nm ./a.out  | grep pthread_mutex_lock
0000000000407010 T __pthread_mutex_lock
00000000004069d0 t __pthread_mutex_lock_full
0000000000407010 W pthread_mutex_lock

总结一下:

  • Ubuntu 14.04& 16.04:T pthread_mutex_lock(强符号)
  • Ubuntu 18.04:W pthread_mutex_lock(弱符号)
  • Ubuntu 14.04 & 16.04: T pthread_mutex_lock (strong symbol)
  • Ubuntu 18.04: W pthread_mutex_lock (weak symbol)

在更复杂的示例中,这可能会导致分段错误.例如,在此代码中(可以在

In a more complex example, this can lead to Segmentation faults. For example, in this code (the unmodified file can be found here):

#include <pthread.h>
#include <thread>
#include <cstring>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex mutex;

void myfunc(int i) {
    mutex.lock();
    std::cout << i << " " << std::this_thread::get_id() << std::endl << std::flush;
    mutex.unlock();
}

int main(int argc, char **argv) {
    std::cout << "main " << std::this_thread::get_id() << std::endl;
    std::vector<std::thread> threads;
    unsigned int nthreads;

    if (argc > 1) {
        nthreads = std::strtoll(argv[1], NULL, 0);
    } else {
        nthreads = 1;
    }

    for (unsigned int i = 0; i < nthreads; ++i) {
        threads.push_back(std::thread(myfunc, i));
    }
    for (auto& thread : threads) {
        thread.join();
    }
}

尝试生成静态二进制文件失败,例如:

Attempts to produce a static binary failed, for example:

$ g++ thread_get_id.cpp -Wall -std=c++11 -O3 -static -pthread -Wl,--whole-archive -lpthread -Wl,--no-whole-archive
$ ./a.out
Segmentation fault (core dumped)

我尝试删除-O3,切换到clang++,切换到Gold链接程序,等等.但是它总是崩溃.根据我的理解,静态二进制文件崩溃的原因是基本功能(例如pthread_mutex_lock)不会以强符号结尾.因此,它们在最终二进制文件中丢失,从而导致运行时错误.

I tried to drop -O3, switching to clang++, switch to the Gold linker, etc. But it always crashes. From my understanding the reason for the crashes in the static binary is that essential functions (such as pthread_mutex_lock) do not end up as strong symbols. Thus, they are missing in the final binary, leading to runtime errors.

除了Ubuntu 18.04,我还可以在带有gcc 10.0.0的Arch Linux上重现相同的行为. 但是,在Ubuntu 14.04和16.04上,可以创建和执行静态二进制文件,而不会出现任何错误.

Apart from Ubuntu 18.04, I could also reproduce the same behavior on Arch Linux with gcc 10.0.0. However, on Ubuntu 14.04 and 16.04, the static binaries could be created and executed without any errors.

问题:

  • What changed in the build toolchain (between gcc 5.4.0 and gcc 7.4.0)? (Wild guess: I saw a pthread cleanup for C11 that falls in that time. Maybe that is the reason?)
  • Is it a regression, or is the old workaround no longer correct.
  • If it is no regression, what should be done instead to allow static linking to pthread?

推荐答案

新的解决方法:-Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive

正如Federico指出的那样,添加-lrt可以防止崩溃.整个问题几乎可以肯定与librt有关,后者是Realtime Extensions库.它的计时功能(例如clock_gettimeclock_nanosleep)用于实现线程.

As pointed out by Federico, adding -lrt prevents the crash. The whole issue is almost certainly related to librt, which is the Realtime Extensions library. Its timing functions (e.g., clock_gettime, clock_nanosleep) are used to implement threads.

在Ubuntu 16.04和18.04之间,glibc也发生了与这些功能相关的更改.我无法弄清楚细节,但是代码中有注释:

Between Ubuntu 16.04 and 18.04, there were changes in glibc related to these functions as well. I could not figure out the details, but there are comments in the code:

/* clock_nanosleep在2.17版中移至libc; 旧的二进制文件可能期望它在librt中具有的符号版本. */

/* clock_nanosleep moved to libc in version 2.17; old binaries may expect the symbol version it had in librt. */

还提供了较新的提交消息:

Also for a newer commit message:

提交79a547b162657b3fa34d31917cc29f0e7af19e4c
作者:Adhemerval Zanella
日期:2019年11月5日星期二19:59:36 +0000

commit 79a547b162657b3fa34d31917cc29f0e7af19e4c
Author: Adhemerval Zanella
Date: Tue Nov 5 19:59:36 2019 +0000

nptl:将nanosleep实现移至libc

nptl: Move nanosleep implementation to libc

在x86_64-linux-gnu和powerpc64le-linux-gnu上进行了检查.我也检查了 每个受影响的ABI的libpthread.so .gnu.version_d条目,以及 它们都包含必需的版本(包括体系结构的版本) 会导出__nanosleep和其他版本.

Checked on x86_64-linux-gnu and powerpc64le-linux-gnu. I also checked the libpthread.so .gnu.version_d entries for every ABI affected and all of them contains the required versions (including for architectures which exports __nanosleep with a different version).

总结起来,解决方法是添加-lrt.请注意,在某些示例中(此处不是),顺序是相关的.从gcc中的测试和其他讨论中,我得到的印象是,首先针对librt进行链接会导致较少的问题,然后在pthread之后进行链接. (在一个示例中,似乎只有-lpthread -lrt -lpthread起作用了,但不清楚为什么.)

To sum it up, the workaround is to add -lrt. Note that in some examples (not here), the ordering is relevant. From the tests in gcc and some other discussion, I got the impression that first linking against librt causes less problems then linking after pthread. (In one example, only -lpthread -lrt -lpthread seemed to have worked, but it is not clear why.)

这篇关于GCC:用于静态链接到pthread的--whole-archive配方在最新的gcc版本中停止工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆