GCC/G++调试添加编译选项-g作用与用法

reference:
https://blog.csdn.net/tanrui519521/article/details/79867221
http://c.biancheng.net/view/8153.html

1、问题

为什么调试的时候需要编译选项中添加 -g

默认编译生成的可执行文件是无法使用 gdb跟踪或调试的,因为可执行程序中没有可供 gdb 调试使用的特殊信息为了将必要的调试信息整合到可执行文件中,我们便需要用到 -g 选项,这样生成的可执行程序,倘若出现问题,便可以使用 gdb 找出问题具体出现的位置,便于问题的解决。

如下:

#gcc -g test.c·

“-g”标志是对程序进行调试性编译时常用的选项。我们需要给每一个需要调试的源文件都加上这个选项。它将使用特殊版本的C语言标准库完成编译和链接操作,给库函数加上程序调试方面的支持。编译器会把这些标志自动传给链接器。

加上-g选项以后,gcc在编译是会做以下额外的操作:

  1. 创建符号表,符号表包含了程序中使用的变量名称的列表。
  2. 关闭所有的优化机制,以便程序执行过程中严格按照原来的C代码进行。

2、例子:

#include <stdio.h>
int main ()
{
   unsigned long long int n, sum;
   n = 1;
   sum = 0;
   while (n <= 100)
   {
       sum = sum + n;
       n = n + 1;
   }
   return 0;
}

3、使用GDB的前期准备

通过前面的学习我们知道,GDB 的主要功能就是监控程序的执行流程。这也就意味着,只有当源程序文件编译为可执行文件并执行时,GDB 才会派上用场。

Linux发行版中,经常使用 GCC 编译 C、C++ 程序(有关 GCC 编译器,读者可猛击《GCC编译器》系统学习)。但需要注意的是,仅使用 gcc(或 g++)命令编译生成的可执行文件,是无法借助 GDB 进行调试的。

以 main.c 源文件为例,正常情况下,使用 GCC 编译该源代码的指令如下:

[root@bogon demo]# ls
main.c
[root@bogon demo]# gcc main.c -o main.exe
[root@bogon demo]# ls
main.c  main.exe

可以看到,这里已经生成了 main.c 对应的执行文件 main.exe,但值得一提的是,此文件不支持使用 GDB 进行调试。原因很简单,使用 GDB 调试某个可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等),而上面生成的 main.exe 则没有。

那么,如何生成符合 GDB 调试要求的可执行文件呢?很简单,只需要使用 gcc -g 选项编译源文件,即可生成满足 GDB 要求的可执行文件。仍以 main.c 源程序文件为例:

[root@bogon demo]# ls
main.c
[root@bogon demo]# gcc main.c -o main.exe -g
[root@bogon demo]# ls
main.c  main.exe

由此生成的 main.exe,即可使用 GDB 进行调试。

较早以前的 C 语言编译器也允许使用 -gg 选项来产生调试信息,但是现在版本的 GDB 不再支持这种格式产生的调试信息,所以不建议使用 -gg 选项。

值得一提的是,GCC 编译器支持 -O(等于同 -O1,优化生成的目标文件)和 -g 一起参与编译。GCC 编译过程对进行优化的程度可分为 5 个等级,分别为 O0~O4,O0 表示不优化(默认选项),从 O1 ~ O4 优化级别越来越高,O4 最高。

所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率

而相对于 -O -g 选项,对 GDB 调试器更友好的是 -Og 选项,-Og 对代码所做的优化程序介于 O0 ~ O1 之间,真正可做到“在保持快速编译和良好调试体验的同时,提供较为合理的优化级别”。

解决了如何生成满足 GDB 调试器要求的可执行文件,接下来正式学习 GDB 调试器的使用。

4、启动GDB调试器

在生成包含调试信息的 main.exe 可执行文件的基础上,启动 GDB 调试器的指令如下:

[root@bogon demo]# gdb main.exe
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
......
(gdb) 

注意,该指令在启动 GDB 的同时,会打印出一堆免责条款。通过添加 --silent(或者 -q、–quiet)选项,可将比部分信息屏蔽掉:

[root@bogon demo]#  gdb main.exe --silent
Reading symbols from main.exe...(no debugging symbols found)...done.
(gdb) 

无论使用以上哪种方式,最终都可以启动 GDB 调试器,启动成功的标志就是最终输出的 (gdb)。通过在 (gdb) 后面输入指令,即可调用 GDB 调试进行对应的调试工作。

GDB 调试器提供有大量的调试选项,可满足大部分场景中调试代码的需要。如表 1 所示,罗列了几个最常用的调试指令及各自的作用:

表 1 GDB常用的调试指令

调试指令作 用
(gdb) break xxx or (gdb) b xxx在源代码指定的某一行设置断点,其中 xxx 用于指定具体打断点的位置。
(gdb) run or (gdb) r执行被调试的程序,其会自动在第一个断点处暂停执行。
(gdb) continue or (gdb) c当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束。
(gdb) next or (gdb) n令程序一行代码一行代码的执行。
(gdb) print xxx or (gdb) p xxx打印指定变量的值,其中 xxx 指的就是某一变量名。
(gdb) list or (gdb) l显示源程序代码的内容,包括各行代码所在的行号。
(gdb) quit or (gdb) q终止调试。

如上所示,每一个指令既可以使用全拼,也可以使用其首字母表示。另外,表 1 中罗列的指令仅是冰山一角,GDB 还提供有大量的选项,可以通过 help 选项来查看。有关 help 选项的具体用法,读者可阅读《GDB查看命令》一节,这里不再做具体赘述。

仍以 main.exe 可执行程序为例,接下来为读者演示表 1 中部分选项的功能和用法:

(gdb) l               <-- 显示带行号的源代码
 #include <stdio.h>
 int main ()
 {
     unsigned long long int n, sum;
     n = 1;
     sum = 0;
     while (n <= 100)
     {
         sum = sum + n;
         n = n + 1;
(gdb)        <-- 默认情况下,l 选项只显示 10 行源代码,如果查看后续代码,安装 Enter 回车即可                                                               
     }
     return 0;
 }
(gdb) b 7               <-- 在第 7 行源代码处打断点
Breakpoint 1 at 0x400504: file main.c, line 7.
(gdb) r                   <-- 运行程序,遇到断点停止
Starting program: /home/mozhiyan/demo1/main.exe

Breakpoint 1, main () at main.c:7
7     while (n <= 100)
Missing separate debuginfos, use: debuginfo-install glibc-2.17-55.el7.x86_64
(gdb) p n               <-- 查看代码中变量 n 的值
$1 = 1                   <-- 当前 n 的值为 1,$1 表示该变量所在存储区的名称
(gdb) b 12             <-- 在程序第 12 行处打断点
Breakpoint 2 at 0x40051a: file main.c, line 12.
(gdb) c                  <-- 继续执行程序
Continuing.

Breakpoint 2, main () at main.c:12
12     return 0;
(gdb) p n               <-- 查看当前 n 变量的值
$2 = 101               <-- 当前 n 的值为 101
(gdb) q                  <-- 退出调试
A debugging session is active.

Inferior 1 [process 3080] will be killed.

Quit anyway? (y or n) y       <-- 确实是否退出调试,y 为退出,n 为不退出
[root@bogon demo]#

转自:GCC/G++调试添加编译选项-g作用与用法_gcc -g_limanjihe的博客-CSDN博客 

<think>嗯,用户现在遇到了一个新的错误,是关于链接器找不到`-lnl-genl-3`和`-lnl-3`这两个库的问题。之前他们的问题是关于`pcap_loop`检测失败,现在可能是在解决那个问题的过程中,又遇到了新的依赖项缺失的问题。需要先理解这两个库是什么,为什么会找不到。 首先,`-lnl-genl-3`和`-lnl-3`指的是libnl-genl-3和libnl-3,属于Netlink通信库,用于Linux内核和用户空间之间的通信。在交叉编译时,链接器找不到这些库,说明目标平台的系统库中可能没有安装这些库,或者交叉编译工具链的路径没有正确配置。 接下来,用户提供的错误信息中的路径是`/opt/hisi-linux/x86-arm/arm-himix410-linux/...`,看起来是海思的交叉编译工具链。需要确认交叉编译环境是否安装了libnl库,或者是否正确指定了库的路径。 可能的原因有几个:首先是交叉编译的sysroot中没有这些库;其次是编译tcpdump或libpcap时没有正确配置依赖项;或者是在配置过程中错误地启用了某些需要libnl的功能,但实际上目标平台并不需要这些功能。 解决方案方面,首先应该检查交叉编译工具链的sysroot目录下是否存在这些库。如果不存在,可能需要安装对应的交叉编译版本的libnl库。或者,如果用户不需要Netlink支持,可以在配置时禁用相关选项,比如在编译libpcap时加上`--without-libnl`。 另外,用户可能在之前的步骤中正确编译了libpcap,但此时在链接tcpdump时遇到了新的依赖问题,说明libpcap可能依赖了这些库,而交叉编译环境中没有。因此,需要确认libpcap的编译配置是否正确,是否不必要地启用了libnl的支持。 如果用户无法获取这些库,可能需要调整配置参数,避免依赖它们。例如,在配置libpcap时使用`--without-libnl`来禁用对libnl的依赖,从而避免链接这些库。或者在编译tcpdump时,确保链接器能够找到正确的库路径,通过设置LDFLAGS环境变量来指定库的位置。 此外,用户可能需要检查他们的交叉编译工具链是否完整安装,是否有缺失的库文件,或者是否需要安装额外的软件包来支持这些依赖。 总结来说,解决步骤可能包括:确认libnl库是否存在,安装或交叉编译libnl库,或者在配置时禁用相关功能,调整链接路径等。需要根据用户的具体情况选择合适的方法,并指导用户逐步排查。</think>--- ### 一、错误解析 你遇到的链接错误信息表明:交叉编译工具链在尝试链接 `libnl-genl-3` 和 `libnl-3` 时失败。这两个库属于 **Linux Netlink 通信库**,常用于网络协议栈开发。错误的核心原因是: ```bash /opt/hisi-linux/.../bin/ld: cannot find -lnl-genl-3 /opt/hisi-linux/.../bin/ld: cannot find -lnl-3 ``` **根本问题**: 交叉编译环境中的链接器(`arm-himix410-linux-gcc`)未找到目标平台(ARM)的 `libnl` 库,或未正确配置库搜索路径。 --- ### 二、系统性解决方案 #### 步骤 1:验证交叉编译环境中的 libnl 库 1. **检查交叉编译工具链的 sysroot 路径** 根据错误中的路径 `/opt/hisi-linux/x86-arm/arm-himix410-linux/...`,定位到交叉编译的 sysroot 目录: ```bash export SYSROOT=/opt/hisi-linux/x86-arm/arm-himix410-linux/target ``` 检查是否存在 `libnl` 库: ```bash ls $SYSROOT/usr/lib/libnl-3* # 期望输出:libnl-3.so、libnl-genl-3.so 等 ``` 2. **若无库文件**: 需要手动为 ARM 平台交叉编译 `libnl`,步骤如下: ```bash # 下载源码 git clone https://github.com/thom311/libnl.git cd libnl # 配置交叉编译 ./configure \ --host=arm-himix410-linux \ --prefix=$SYSROOT/usr \ --disable-static make && make install ``` #### 步骤 2:调整 tcpdump/libpcap 编译参数 若已确认 `libnl` 存在,但链接器仍无法找到,需显式指定库路径: ```bash # 在编译 tcpdump 前设置环境变量 export LDFLAGS="-L$SYSROOT/usr/lib -Wl,-rpath-link=$SYSROOT/usr/lib" export LIBS="-lnl-3 -lnl-genl-3" ``` #### 步骤 3:禁用非必要依赖(备用方案) 如果目标平台无需 Netlink 支持,可在编译 `libpcap` 时关闭此功能: ```bash # 重新配置 libpcap cd /path/to/libpcap-src ./configure \ --host=arm-himix410-linux \ --prefix=$SYSROOT/usr \ --without-libnl # 关键参数 make clean && make install ``` --- ### 三、错误复现调试 1. **手动测试链接** 尝试独立编译一个最小测试程序,验证 `libnl` 是否可用: ```c // test.c #include <netlink/netlink.h> int main() { struct nl_sock *sk = nl_socket_alloc(); return 0; } ``` 编译命令: ```bash arm-himix410-linux-gcc test.c -o test -lnl-3 -lnl-genl-3 ``` 2. **检查动态库依赖** 使用交叉编译工具链的 `readelf` 检查生成的二进制文件: ```bash arm-himix410-linux-readelf -d /path/to/test | grep NEEDED ``` --- ### 四、根本原因扩展 - **海思工具链的局限性**:某些厂商提供的工具链可能未包含完整动态库,需手动补充。 - **隐式依赖问题**:`libpcap` 可能通过 `--enable-remote` 或 DPDK 支持间接依赖 `libnl`。 --- ### 五、替代方案 1. **静态链接**: 强制静态链接 `libnl`,避免运行时依赖: ```bash export LDFLAGS="-static -L$SYSROOT/usr/lib" ``` 2. **使用供应商提供的库**: 联系海思技术支持,获取适配该工具链的 `libnl` 预编译包。 --- 通过上述步骤,可系统性解决 `libnl` 链接失败问题。核心在于确保交叉编译环境中库的完整性和路径正确性。若需进一步优化,建议通过 `strace` 跟踪编译过程以观察文件访问行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值