linux上编译release并剥离调试信息

使用 objcopy:

1转载出处:

原文作者:不插电码农

本文讲的是不编译完全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和执行程序在同一个目录下,并且适合调试已经启动的进程,例如服务器.

2 测试

测试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命令查找 

编译exe:

 sudo 运行:

 我们先看没有符号表的,就是在文件夹中只有test_EXE,没有调试debug文件的:

可以看到pidof test_EXE,然后gdb attach pid后,打断点根本不会停;

 然后我们把刚刚一起编译的test_EXE.debug放到同一个目录下(不用重启程序,因为实际情况下,一般都是项目运行中如果出错,你重启程序编译debug调试,或者重启添加debug符号,现象有可能就无法出现了,我们就是要在出错的release版本下,添加符号直接调试)

 调试:

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

posted on 2024-03-17 22:06  邗影  阅读(194)  评论(0编辑  收藏  举报

导航