GCC-GDB-Makefile学习笔记

First Post:

Last Update:

Word Count:
2.2k

Read Time:
8 min

GCC

编译过程

a.c ->预处理-> a.i ->汇编-> a.s ->编译-> a.o ->链接-> a.out
gcc -E -o a.i a.c (预处理)
gcc -S -o a.s a.i (编译)
gcc -c -o a.o a.s (汇编)
gcc -o a.out a.o (链接)

后辍文件详解:
< .c > C语言源码文件
< .a > 目标文件构成的库文件
< .C / .cc /.cxx > C++ 源码文件
< .h > 头文件
< .o > 编译后的目标文件
< .s > 是汇编语言源代码文件

编译选项:
< -o file_name > 指定输出文件
< -O > 对程序进行优化编译,链接
< -O2 > 对程序进行更好的优化编译,链接
< -O3 > 最佳优化
< -g > 对程序加入调试 (gdb 调试)
< -I dir_name > 将所指出的目录作为编译器寻找头文件的路径
< -Wall > 生成所有警告信息
< -w > 不生成任何警告信息

< -D/-DMACRO > 定义MACRO宏,等效于在程序中使用#define MACRO

(应用场景:可以控制debug的输出)

    GDB

layout asm —汇编界面
b *addr
查看内存命
可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
x/<n/f/u>
n、f、u是可选的参数。
n是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义。
f 表示显示的格式,参见下面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。
当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
表示一个内存地址。
注意:严格区分n和u的关系,n表示单元个数,u表示每个单元的大小。
n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示输出三个单位,u表示按十六进制显示。
输出格式
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。
例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。
要做到这样,你可以使用GDB的数据显示格式:

x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。

f 按浮点数格式显示变量。

静态库

1.命名规则:
lib+库名+.a
2.制作静态库:
生成对应的.o文件 .c—> .o (gcc -c a.c b.c c.c ….)
将生成的.a文件打包 ar rcs 生成静态库名 lib1.o lib2.o ….. (ar src liblogan.a ./*.o)
3.发布和使用静态库:
1发布静态库: 把头文件移动到include里,把.a文件移动到lib里
2头文件
4.使用接口:
1源文件里导入头文件,gcc main.c -I ./include lib/lib.a (后面导入lib文件) (把main.c 放第一位)
2正式用法: gcc main.c -Iinclude -L lib -l logan -o run (logan 去头去尾: liblogan.a)
-I 指定头文件路径 (可以无空格)
-L 指定lib目录
-l 指定lib目录下的lib文件
5.查看静态库文件:
nm lib.a (nm 还可以查看.out .o)
nm 里
T 代表代码区
B b D W t r
6.缺点:
1若使用静态库里的某一个函数,则编译时会将整个.o文件连接到可执行文件中,占用空间(以.o文件为单位)
2库文件发生改变,需重新编译
7.优点:
1发布程序的时候,不需要提供对应的库

​ 2Load程序的时候,速度快

共享库

1.命名规则:
lib+库名+.so
2.制作共享库:
1生成与位置无关的.o (gcc -fPIC -c .c)
2将.o打包成共享库(动态库) (gcc -shared -o liblogan.so ./
.o)
3.发布和使用共享库:
1发布共享库: 把.so文件移动到lib里
2使用共享库:
在编译目标代码时只需导入动态链接库就可以使用(单个)
一* gcc main.c lib/libprint.so -o run

使用:(gcc main.c -L lib -l print -o run)会导致在运行时无法链接
需使用以下方法进行链接

1*使用环境变量来导入lib.so
echo $LD_LIBRARY_PATH
export LD_LIBRARY_PATH=./lib (临时测试用)

2*修改配置文件来实现永久修改:(永久)(不常用)
vim ~/.bashrc
找到 LD_LIBRARY_PATH=...然后进行修改

3*修改动态链接器的配置文件实现链接lib(永久)
     1->找到动态链接器的配置文件
    sudo vim /etc/ld.so.conf
     2->动态路径些到ld.so.conf里
     3->更新  sudo ldconfig -v
3查看可执行程序的链接库: ldd file  (print share object dependencis)
4动态库的优缺点:
    优点:
    体积小
    更新不需重新编译程序,实现了模块化(函数接口不变的情况下)
    缺点:
    需要把动态库提供给用户
    加载速度相对较慢

gdb调试

命令:
l ----list 查看源码
  l 10 查看10行
  查看其他文件:
  l a.c:20 表示查看 a.c文件里的20的行内容
  l a.c:function_name 查看 a.c文件里fun函数

b ----breakpoint 下断点
break---------->
条件断点:
b 15 if i==15 表示在15行变量i=15时停下
查看断点信息:
i b-------information of break
info break---->
d----删除断点
info break

p ----print 打印
print------>

ptype----查看变量类型

diplay----display----追踪变量(循环里可运用)
undisplay----去掉追踪变量的值
用之前需获取变量ID
使用 info display 获取
然后 undisplay var_ID

r ----run运行
start------>
vmmap ----查看内存分布
n----单布调试(绕过函数体)
c----继续执行(遇到断点停下)
s----(单步)(可进入函数体)
u----跳出单次循环
finish----跳出当前进入的函数

gdb调试的layout使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
layout:用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
layout src:显示源代码窗口
layout asm:显示汇编窗口
layout regs:显示源代码/汇编和寄存器窗口
layout split:显示源代码和汇编窗口
layout next:显示下一个layout
layout prev:显示上一个layout
Ctrl + L:刷新窗口

Ctrl + x,再按1:单窗口模式,显示一个窗口

Ctrl + x,再按2:双窗口模式,显示两个窗口

Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。

查看栈指针SP的位置

p $sp

查看该进程的内存映射

i proc mapping

Makefile

Makefile规则

TARGET....: DEPENDEDS...
COMMAND
...
...
在执行动作命令之前会判断所DEPENDEDS条件是满足,若不满足,则执行依赖项
clean规则会执行 -$(RM)$(TARGET)$(OBJS)命令
clean用于清除中间生成文件

注意:COMMAND之前必须有TAB键,不能使用空格来代替

make命令执行的时候会根据文件的时间搓来判断是否执行相关的命令

匹配模式

例:

1
2
main.o:main.c add/add.h sub/sub.h
gcc -c -o main.o main.c -Iadd -Isub

简便方法实现相同功能:

1
2
main.o:%o:%c
gcc -c$< -o $@

%o:%c表示将TARGET域的.o拓展名替换为.c,即main.o 替换为main.c.
$<表示依赖项的结果,即main.c
$@表示TARGET域的名称,即 main.o

函数:

$(wildcard ./*.c) —获取./下的所有.c文件
$(patsubst %.o,%.c,a.o) —将.o替换为.c

!在命令前加’-‘,若命令执行失败,则忽略该命令,继续往下执行.

若在当前目录下存在一个’clean’文件,则就无法使用make clean来实现清除的目的

需要在Makefile中声明伪目标.
具体实现:

1
2
.PHONY:clean
clean:
打赏点小钱
支付宝 | Alipay
微信 | WeChat