在linux上进行编译调试

1.相关疑问

1. 为什么在代码里使用了一个未定义过的函数(如add()),在编译阶段不会报错,在链接阶段会报错呢?

答:先说几个代码编译的结论:

  • 单个\.c源文件文件被编译成机器码文件时,源文件中的所有变量名以及数组名都会变成地址偏移量;
  • 类型信息都会变成指令的长度(int\-\>subl, 地址\-\>subg);
  • 循环会变成goto的方式实现;
  • 函数的调用变成了使pc指针移动到被调方函数的地址;

由以上第四条可知,函数的调用依赖于被调函数的地址,编译后的文件可以使用nm xxx.o来查看所有函数的地址和函数名的对应情况,在链接之前可以看到此时有的函数名还没有对应的地址,而将这些函数名和地址关联起来的操作就是链接阶段要做的事,在链接阶段无法为主调方找到被调方函数地址时就会报链接错误。

2. gcc编译器

1. 预处理指令

  • #include:引入头文件

  • #define:定义宏常量和宏函数

  • #if:根据指定条件判断是否编译后续代码块(可作为测试代码开关

    c 复制代码
    // 使用示例:
    #define DEBUG 1
    #if DEBUG
        // Debugging code
        printf("Debug mode is active.\n");
    #endif
  • #ifdef:判断某个标识符是否已经定义(可作为测试代码开关

    c 复制代码
    #ifdef ON //表示如果定义了ON,或命令行编译时用-D传了ON,就执行下面的输出
        printf("ON is defined!\n");
    #else
        printf("ON is not defined!\n");
    #endif
        printf("main exit\n");

    可以在命令行定义预处理器宏gcc -o test test.c -DON

  • #ifndef:判断某个标识符是否没有已经定义(可用于头文件保护

    c 复制代码
    #ifndef TEST_H
        #define TEST_H
    #endif
  • #endif:结束条件编译的代码块

2. gcc常用指令

补充

列出所有函数:nm test.o

查看可执行程序链接了哪些库:ldd test

  • 预处理:

    c 复制代码
    gcc -E test.c -o test.i
  • 编译:

    c 复制代码
    gcc -S test.c -o test.s
  • 汇编:

    c 复制代码
    // 方式一:从汇编文件到机器码文件
    as test.s -o test.o
    // 方式二:从预处理文件到机器码文件
    gcc-13 -C test.i -o test.o
    // 方式三:从源文件到机器码文件
    gcc-13 -C test.c -o test.o
  • 链接:gcc -o test test.c 输出可执行文件

    c 复制代码
    // 方式一:编译+链接(从源文件到可执行文件)
    gcc -o test test.c
    // 方式二:链接
    gcc -o test test.o
    // 方式三:指定链接路径
    gcc test1.o -o test1 -ladd
    // 还可以使用ld命令可以调用静态链接器
    ld test.o [其他系统库文件] -o test
  • 定义宏:

    c 复制代码
    gcc test1.o -o test1 -D DEBUG
  • 制定优化级别:

    c 复制代码
    gcc test1.o -o test1 -O0 // 推荐O1级别
  • 为gdb补充符号信息:

    c 复制代码
    gcc test1.o -o test1 -O0 -g // 要用gdb调试时最好指定不优化,避免机器码和代码不一致影响调试
  • 提示警告:

    c 复制代码
    gcc test1.o -o test1 -Wall // 注意Wall是大写W
  • 指定头文件搜路径:

    c 复制代码
    gcc test1.o -o test1 -I "../h" //默认从当前路径下搜索

3. gdb调试

补充

用gdb调试时,传递命令行参数的两种方式:

  • 方式一:进入gdb后:用set args xxx
  • 方式二:进入gdb前:使用gdb --args test xxx启动

1. 常用指令

  1. 用-g生成可执行文件:gcc --o test test.c -O0 --g

  2. 进入调试器:gdb test

  3. 显示代码:

    • 默认显示:l/list //默认显示10行
    • 显示指定信息:l/list [文件名:]行号or函数名 //[]表示可有可无
  4. 运行:r/run

  5. 退出:q/quit

  6. 断点相关:

    • 设置断点:b/break 4 //在第四行设置断点
    • 列出断点:i b/info break // 查看断点号
    • 删除断点:delete 断点号
    • 停止断点:disable 断点号
    • 激活断点:enable 断点号
  7. 跳转:

    • 下一步不进入函数:n/next //F10
    • 下一步进入函数:s/step //F11
    • 到下一个断点:c/continue
    • 跳出当前函数:finsh
  8. 查看信息:

    • 打印变量:p/print
    • 自动显示的表达式内容:display 表达式 (自动显示内存:display /1xw &i``)
    • 停止显示某个表达式:undisplay 表达式 或者 undisplay 编号(通过info display查看编号)
    • 查看调用堆栈:b t/back trace
    • 查看内存:x<n/f/u> //n表示内存的长度 f表示内存的格式 u表示内存的单位
    • 清空信息: Ctrl + L 组合键,或者输入 shell clear 命令(shell命令用于调用系统断点shell

2. 调试core文件

  1. 启用 core 文件自动生成
    • 查看core文件是否存在限制:ulimit -a
    • 关闭限制:ulimit -c unlimited
  2. 运行程序并生成 core 文件
    • 如果未生成,则使用man core查看帮助文档解决;
  3. 使用 GDB 调试 core 文件:gdb test core;
  4. 查看调试信息
    • 查看调用栈:bt
    • 查看栈帧信息:frame 0
    • 显示寄存器的值:info registers
    • 分析内存:x

4. 静态库和动态库

库文件其实就是别人造好的"轮子",别人写好并编译好后给用户使用的二进制代码。

静态库文件在程序的链接阶段被需要,而动态库文件在程序运行过程中也被需要。

1. 静态库

在程序链接阶段,库文件会被打包进最终的可执行程序。

gcc编译时,如果存在同名的动态(.so)和静态(.a)库文件,默认是使用动态文件,此时要使用静态库文件就必须要使用-static参数指定优先静态链接,如果找不对应的\.a文件会链接失败。

特点:省心,但体积大且更新不方便。

打包命令

sh 复制代码
// 1. 得到编译后的机器码文件
gcc -c add.c -o add.o 
// 2. 生成静态库文件(库文件必须以lib开头)
ar crsv libadd.a add.o
// 3. 将静态库文件移动到"/usr/lib"或"/usr/local/lib"下,推荐前者
sudo mv libadd.a /usr/lib
// 4. 使用即可
gcc main.o -o main -ladd

2. 动态库

链接阶段不打包进程序,在程序运行时加载。

特点:体积小、更新方便(动态更新),但容易存在依赖问题。

打包命令

sh 复制代码
// 1. 生成位置无关的目标代码,使用-fpid(Position Independent Code)
gcc -c add.c -o add.o -fpic
// 2. 生成动态库文件(库文件必须以lib开头)
gcc -shared add.o -o libadd.so
// 3. 将静态库文件移动到"/usr/lib"或"/usr/local/lib"下,推荐前者
sudo mv libadd.so /usr/lib
// 4. 使用即可
gcc main.o -o main -ladd

3. 产品动态更新

原理:利用版本号和软连接实现

  • 第一步:生成带版本好的.so动态库文件,如:libadd.so.0.0.0、libadd.so.0.1.0
  • 第二步:建立最新版本的软连接,sudo ln -s libadd.so.0.1.0 libadd.so
相关推荐
饮啦冰美式1 分钟前
22.04Ubuntu---ROS2使用rclcpp编写节点
linux·运维·ubuntu
wowocpp1 分钟前
ubuntu 22.04 server 安装 和 初始化 LTS
linux·运维·ubuntu
Huaqiwill3 分钟前
Ubuntun搭建并行计算环境
linux·云计算
wclass-zhengge5 分钟前
Netty篇(入门编程)
java·linux·服务器
Lign173147 分钟前
ubuntu unrar解压 中文文件名异常问题解决
linux·运维·ubuntu
vip4511 小时前
Linux 经典面试八股文
linux
大霞上仙1 小时前
Ubuntu系统电脑没有WiFi适配器
linux·运维·电脑
孤客网络科技工作室2 小时前
VMware 虚拟机使用教程及 Kali Linux 安装指南
linux·虚拟机·kali linux
颇有几分姿色3 小时前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
AndyFrank3 小时前
mac crontab 不能使用问题简记
linux·运维·macos