linux上编译release并剥离调试信息
十月 22, 2024 [C/C++] #C++ #Linux #obsidian #编译链接 #调试linux上编译release并剥离调试信息
本文讲的是不编译完全release的可执行文件,而是使用cmake的RelWithDebugInfo(附加调试信息的编译,它的效果下面会说明)或者Debug编译也可以生成release版本的可执行文件进行调试.
详细的 cmake 使用会在下一篇文章里讲.
这里先记下Debug, Release 和 RelWithDebugInfo的区别(对比我手上的gcc 4.8.2编译出的不同版本得出的结论)
Debug 等同于 -O0 -g 编译选项, 不优化代码, 附加调试信息
Release 等同于 -O3 编译选项, 最大化优化代码, 但不包含调试信息
RelWithDebugInfo 等同于 -O2 -g 编译选项, 中等程度优化代码, 并且包含调试信息.
可以自定义使用-O3 -g 的编译选项,来生成最接近Release的代码(至于-ggdb的选项,加了以后二进制只大了8个字节,目测无所谓)
下面的方法只对-g编译有效,方法有两种.
man手册里推荐的方法
objcopy –only-keep-debug foo foo.dbg 把调试信息(即debug section)dump到单独的文件里.
objcopy –strip-debug foo 这里只剔除调试信息,符号信息仍保留(用户依然可以用nm查看到各种函数符号,我推荐用–strip-all,连同符号信息一起删除,生成的可执行文件更小,不影响调试)
objcopy –add-gnu-debuglink=foo.dbg foo 为foo添加.gnu-debuglink section, 这个会让二进制增加近100字节的样子.
这样分离了Debug Section后,就可以单独分发release版本的foo,调试时只需讲foo.dbg放在和foo同一目录下即可直接gdb foo进行调试.
如果不希望Release的最终二进制文件包含.gnu-debuglink段,那么可以直接省略第三步,且当成另一种方法,方便叙述.
同一
strip foo 这个效果等同于objcopy –strip-all foo,剔除所有符号和调试信息, 即为release版本.
方法二所不同的调试方法是需先加载dbg文件, gdb foo.dbg, 然后attach(或者exec)到foo进程,这样不要求.dbg和执行程序在同一个目录下,并且适合调试已经启动的进程,例如服务器.
测试
测试demo:CMakeLists.txt
命令执行的时机由如下参数决定: 1.PRE_BUILD - 命令将会在其他依赖项执行前执行 2.PRE_LINK - 命令将会在其他依赖项执行完后执行 3.POST_BUILD - 命令将会在目标构建完后执行。
cmake_minimum_required(VERSION 3.5)
# set the project name
project(test_EXE)
#SET(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_C_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-g -O3 -DNDEBUG")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_executable(test_EXE
test.cpp
)#定义命令用于从release中剔除符号表和调试信息
add_custom_command(TARGET test_EXE
POST_BUILD
COMMAND objcopy --only-keep-debug ${EXECUTABLE_OUTPUT_PATH}/test_EXE ${EXECUTABLE_OUTPUT_PATH}/test_EXE.debug
COMMAND objcopy --strip-all ${EXECUTABLE_OUTPUT_PATH}/test_EXE
COMMAND objcopy --add-gnu-debuglink=${EXECUTABLE_OUTPUT_PATH}/test_EXE.debug ${EXECUTABLE_OUTPUT_PATH}/test_EXE
COMMENT "**** test cmake command: run obj copy"
)
#在构建过程中执行自定义命令,文件安装;可自行替换可以运行install(FILES ${EXECUTABLE_OUTPUT_PATH}/test_EXE.debug DESTINATION ${EXECUTABLE_OUTPUT_PATH})
#include <stdio.h>
#include <iostream>
#include <chrono>
#include <thread>
void PrintInfo()
{
printf("load fun print info..........\n");
}
int addfun(int a ,int b)
{
printf("will run a+b:%d\n",(a+b));
return a+b;
}
int main(void)
{
int a=3,b=5;
PrintInfo();
int c = addfun(a,b);
printf("-----print a+b ok\n");
while(1)
{
static int c =0;
static int d =1;
addfun(c,d);
c++;
d++;
std::this_thread::sleep_for(std::chrono::seconds(1)); //Ãë
}
return 0;
}
测试的时候使用cmake -DCMAKE_BUILD_TYPE=Release生成Makefile,然后输入make命令生成exe;注意这些操作要在root下,否则会出现无法调试,如continue的时候出现 the program is not running等;
自测程序如果top 找不到Pid,请用pidof命令查找
我们先看没有符号表的,就是在文件夹中只有test_EXE,没有调试debug文件的:
可以看到pidof test_EXE,然后gdb attach pid后,打断点根本不会停;
然后我们把刚刚一起编译的test_EXE.debug放到同一个目录下 (不用重启程序,因为实际情况下,一般都是项目运行中如果出错,你重启程序编译debug调试,或者重启添加debug符号,现象有可能就无法出现了,我们就是要在出错的release版本下,添加符号直接调试)

综上就可以同时编译release以及剥离出来的debug调试信息,如果现场遇到需要调试的情况就可以直接把debug文件放进运行目录,在线调试,而不必停止程序的运行;