eclipse/cdt:-fPIC引起的线程局部变量(__thread)的SIGSEGV异常问题

最近在 ubuntu下用eclipse Neon.3 (4.6.3) 调试一个C工程时遇到一个好奇怪的问题:

一个应用程序A,调用一个静态库B,静态库中用__thread定义了线程局部变量(TLS,thread local storage),在eclipse跟踪进B的函数,代码执行到访问TLS变量时,程序直接就崩溃了,报了SIGSEGV错误异常(无效的内存引用),但是不跟踪直接运行代码是没有问题的。

以下是lib B的代码 testlib2.c

#include 

#include "testlib2.h"

static __thread int tls_v = 12345;   
void test_tls(){
	printf("%d\n",tls_v);
}

对应的头文件testlib2.h

#ifndef TESTLIB2_H_
#define TESTLIB2_H_
void test_tls();
#endif /* TESTLIB2_H_ */

应用程序A代码

#include "testlib2.h"
int main(void) {
	test_tls();
	return EXIT_SUCCESS;
}

如下图,代码执行到读取tls_v变量的时候就直接崩溃了,如果调试时如果不跟踪进test_tls(),程序也能正常执行。
当我把lib B改为动态库时代码,调试正常。

eclipse/cdt:-fPIC引起的线程局部变量(__thread)的SIGSEGV异常问题_第1张图片

百思不得其解啊,没办法网上仔细翻了关于线程局部变量的相关资料。以前只了解thread local storage的基本概念,知道它是线程独享的变量,并没有深入去研究。通过这次的问题,知道线程局部变量有4种访问模型
General Dynamic (GD)Local Dynamic (LD)Initial Executable (IE)Local Executable (LE),关于这4种模型的说明参见下面oracle的文章

Thread-Local Storage Access Models
https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter8-20.html

我们只需要知道这4种模型分类代表不同的tls变量访问能力。一般来说,程序员在编译自己的c/c++代码时是不用关心这个问题的。

然而编译器在编译代码时针对这种不同的访问模型会生成不同的代码。参见下面的关于gcc编译选项的gnu官方手册(《3.16 Options for Code Generation Conventions》)中关于-ftls-model选项的说明
-ftls-model选项用于指定tls变量的访问模型,引起我关注不是如何用它来设置tls-model,而是说明中的最后一行文字(如下图红框标):如果指定了-fpictls-model的默认值为General Dynamic (GD)否则为Initial Executable (IE)
eclipse/cdt:-fPIC引起的线程局部变量(__thread)的SIGSEGV异常问题_第2张图片

看到这里我想到了我的静态库B在编译时指定了-fPIC选项。于是我去掉-fPIC选项重新编译,再跟踪可以通过了。如下图,可以看出,没有-fPIC选项时生成的汇编代码与前面有-fPIC选项时是不一样的。

eclipse/cdt:-fPIC引起的线程局部变量(__thread)的SIGSEGV异常问题_第3张图片

虽然到目前为止,我还不知道为什么eclipse下对-fPIC选项编译的静态库中的TLS调试会造成异常,但总算知道这个问题产生的条件,后续开发中就可以避免了。

导致SIGSEGV异常问题出现是在几个条件下都具备的情况下发生的:
1.静态库中使用__thread变量
2.静态库编译使用了-fPIC选项
3.eclipse调试跟踪静态库的代码

参考资料

《3.16 Options for Code Generation Conventions》

《Thread-Local Storage Access Models》

《Thread-local storage》

你可能感兴趣的:(开发工具,c/c++/c++11)