问题描述
今天在使用C++编写代码生成可执行文件时,发现一个之前没有遇到过的问题。具体地,编译生成的可执行文件,在都还没有进入main函数中前就发生了段错误运行崩溃的情况,使用gdb进入调试模式,也没有其他有效的提示信息,只有以下类似的输出信息:
#0 0x0000000000000001 in ?? ()
#1 0x00007fffffffe6fa in ?? ()
#2 0x0000000000000000 in ?? ()
网上搜相关问题,有的说是静态变量静态资源相关的,还有的说是库链接的问题,但我的工程出现的情况跟这两种应该都不符合
问题复现
-
复现文件
现使用以下简单的代码复现下该问题// test.cpp #include <iostream> int main() { std::cout << "Test." << std::endl; return 0; }
代码很简单,man函数中只做了一个标准输出的操作。对应地,写一个简单的CMakeLists.txt
# CMakeLists.txt cmake_minimum_required(VERSION 2.8) set(CMAKE_CXX_FLAGS "$ENV{CMAKE_CXX_FLAGS} -std=c++14 -g -O0 -shared -fPIC") add_executable(test test.cpp)
在CMakeList中,指明了使用C++14,为了可以调试,添加了编译选项
-g
和-O0
,还有两个编译选项-shared
和-fPIC
后面介绍 -
编译生成可执行文件
依次执行以下命令编译 & 生成可执行文件testmkdir build && cd build && cmake .. && make
这一步没啥问题,编译期间也没有报啥错误,生成了可执行文件test
-
运行可执行文件
一切正常的话,会在终端输出
Test.
,但是实际运行的时候,发现直接发生了段错误,甚至都没有进入main函数中,使用gdb调试也没发现有效的特别的提示信息
但是如果我直接使用g++工具编译,又发现可执行文件是可正常执行的g++ -std=c++14 -o test ../test.cpp
注意到使用g++编译时,除了指定C++14,没有添加其他编译选项,说明问题出在了编译选项上。
观察上面添加的所有编译选项,-g
与-O0
都是比较常规的选项,不会出现啥问题,那出问题的选项就可能出在了-shared
和-fPIC
选项上。CMakeLists.txt中去掉这两个编译选项,成功编译并执行cmake_minimum_required(VERSION 2.8) set(CMAKE_CXX_FLAGS "$ENV{CMAKE_CXX_FLAGS} -std=c++14 -g -O0") add_executable(test test.cpp)
原因分析
生成可执行文件的CMakeLists.txt我是拷贝自生成动态库的文件夹中,而动态库生成时为了生成与位置无关的代码,达到真正意义上的代码共享。生成与位置无关的代码的动态库时,需要在编译前添加编译选项-shared·
和-fPIC
但当将-shared·
和-fPIC
选项用于编译可执行文件时,编译器将会生成一个与可执行文件不兼容的输出文件,其中包含了共享库的相关信息。当尝试运行这个输出文件时,操作系统会遇到不符合可执行文件格式的数据,从而导致运行时错误和崩溃。
更多-shared·
和-fPIC
选项相关信息,可自行网上搜索相关资料,这里不在多说明