GNU gcc编译&gdb调试


前言


1.gcc

(1)gcc常用参数

gcc用法可以man gcc查找,基本用法就在开头:

gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-Wpedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] [@file] infile...

-c:只编译不链接,生成目标文件.o
-S:生成汇编代码
-E:只预编译,注意默认打印预编译内容,不会输出文件,要生成文件:gcc -E target.c -o target.i
-g:包含调试信息,生成的可执行文件中带有调试信息,用于gdb调试
-o:指定目标输出文件,-o后面跟随的就是输出的文件名,文件类型不随文件名而更改,只随编译选项更改,如gcc -c -o mian main.c 他生成的文件名虽然叫main,但是他的类型为.o文件。
-Idir:大写的i,搜索头文件路径
-lxxx:小写的L,搜索库
-Ldir:搜索库路径

一些常用选项:
-D        使用-D name[=definition]预定义名为name的宏,若不指定值则默认宏的内容为1
-static      指定静态链接(默认是动态链接)
-march      指定目标平台的体系结构,如-march=armv4t,常用于交叉编译
-mtune      指定目标平台的CPU以便GCC优化,如-mtune=arm9tdmi,常用于交叉编译
-w        不生成任何警告信息。
-Wall      生成所有警告信息。
-On       (n为0~7)优化等级,一般在最终发布时使用
-nostdinc     不使用标准头文件
-nostdin C++   
-nostdlib
-include     添加头文件路径
-fPIC      生成位置无关码

(2)静态库,动态库编译

基本用法参考:https://blog.csdn.net/weixin_44705391/article/details/113797249

常用参数:
-static:静态编译
-shared:生成动态库、进行动态编译
-Ldir
-fPIC:位置无关码,多用于生成动态库文件

关于生成静态链接库的ar指令详解:
man ar得到详细说明:
ar其实是备份压缩命令,可以将几个文件归档为一个文件中,我们很多时候用来生成静态库

·指令参数
-d 删除备存文件中的成员文件。
-m 变更成员文件在备存文件中的次序。
-p 显示备存文件中的成员文件内容。
-q 将成员文件添加到备存文件末端。
-r 将文件插入备存文件中。
-s 等价于运行ranlib。
-t 显示备存文件中所包含的文件。
-x 自备存文件中取出成员文件。
·选项参数
a<成员文件> 将文件插入备存文件中指定的成员文件之后。
b<成员文件> 将文件插入备存文件中指定的成员文件之前。
c 建立备存文件。
f 为避免过长的文件名不兼容于其他系统的ar指令指令,因此可利用此参数,截掉要放入备存文件中过长的成员文件名称。
i<成员文件> 将文件插入备存文件中指定的成员文件之前。
o 保留备存文件中文件的日期。
s 若备存文件中包含了对象模式,可利用此参数建立备存文件的符号表。
S 不产生符号表。
u 只将日期较新文件插入备存文件中。
v 程序执行时显示详细的信息。
V 显示版本信息。
例子:ar -rv librich.a main.c rich.c可得到librich.a,ar -p librich.a可查看其内容就是main.c和rich.c的组合
vi其内容为:

!<arch>
main.c/         0           0     0     644     145       `
#include "rich.h"
int main(void){
    int money = 1;
    money = be_rich(money);
}

rich.c/         0           0     0     644     79        `
#include "rich.h"
int rich =10;
int be_rich(int money){
    return money+rich;
}

我们可以看到只不过多了以下内容来表征其是归档文件和各个文件的顺序以及大小等属性。

!< arch >
main.c/         0           0     0     644     145       `

我们还可以通过其他的命令对归档文件进行添加,删除等操作。

(3)其他命令(嵌入式交叉编译常用)

ld
链接,生成.elf可执行文件

选项:

-b <input-format>:指定目标代码输入文件的格式
-Bstatic:只使用静态库
-Bdynamic:只使用动态库
-Bsymbolic:把引用捆绑到共享库中的全局符号
-c <MRI-commandfile>,--mri-script=<MRI-commandfile>:为与MRI链接器兼容,ld接受由MRI命令语言编写的脚本文件
--cref:创建跨引用表
-d,-dc,-dp:即使指定了可重定位的输出文件(使用-r),也会为公共符号分配空间。脚本命令“FORCE_COMMON_ALLOCATION”具有相同的效果
-defsym:在输出文件中创建指定的全局符号
-demangle:在错误消息中还原符号名称
-e <entry>:使用指定的符号作为程序的初始执行点
-E,--export-dynamic:对于ELF格式文件,创建动态链接的可执行文件时,把所有符号添加到动态符号表
-f <name>,--auxiliary=<name>:对于ELF格式共享对象,设置 DT_AUXILIARY 名称
-F <name>,--filter=<name>:对于ELF格式共享对象,设置 DT_FILTER 名称。这告诉动态链接器,正在创建的共享对象的符号表应该用作共享对象名称的符号表的筛选器。
-g:被忽略。用于提供和其他工具的兼容性
-h:对于ELF格式共享对象,设置 DT_SONAME 名称
-I<file>,--dynamic-linker=<file>:指定动态链接器。这仅在生成动态链接的ELF可执行文件时才有意义。默认的动态链接器通常是正确的,除非您知道正在做什么,否则不要使用该选项。
-l <namespec>,--library=<namespec>:把指定的库文件添加到要链接的文件清单
-L <searchdir>,--library-path=searchdir:把指定的路径添加添加到搜索库的目录清单
-M,--print-map:显示链接映射,用于诊断目的
-Map=<mapfile>:	将链接映射输出到指定的文件
-m <emulation>:	模拟指定的链接器
-N,--omagic:	指定读取/写入文本和数据段
-n,--nmagic:	关闭节的页面对齐,并禁用对共享库的链接。如果输出格式支持Unix样式的幻数,则将输出标记为"NMAGIC"
-noinhibit-exec:生成输出文件,即使出现非致命链接错误。通常,如果链接器在链接过程中遇到错误,它将不会生成输出文件。
-no-keep-memory:ld通常在内存中缓存输入文件的符号表来优化内存使用速度。此选项告诉ld不要缓存符号表。当链接大型可执行文件时,如果ld耗尽内存空间,则可能需要使用该选项
-O <level>:对于非零的优化等级,ld将优化输出。此操作会比较耗时,应该在生成最终的结果时使用。
-o <output>,--output=<output>:指定输出文件的名称
-oformat=<output-format>:指定输出文件的二进制格式
-R <filename>,--just-symbols=<filename>:从指定的文件读取符号名称和地址
-r,--relocatable:生成可重定位的输出(称为部分连接)
-rpath=<dir>:把指定的目录添加到运行时库搜索路径
-rpath-link=<dir>:指定搜索运行时共享库的目录
-S,--strip-debug:忽略来自输出文件的调试器符号信息
-s,--strip-all:忽略来自输出文件的所有符号信息
-shared,-Bshareable:创建共享库
-split-by-file[=size]:为每个目标文件在输出文件中创建额外的段大小达到size。size默认为1
-split-by-reloc[=count]:按照指定的长度在输出文件中创建额外的段
--section-start=<sectionname>=<org>:在输出文件中指定的地址定位指定的段
-T <scriptfile>,--script=<scriptfile>:使用scriptfile作为链接器脚本。此脚本将替换ld的默认链接器脚本(而不是添加到其中),因此脚本必须指定输出文件所需的所有内容。如果当前目录中不存在脚本文件,“ld”会在-L选项指定的目录中查找
-Ttext=<org>:使用指定的地址作为文本段的起始点
-Tdata=<org>:使用指定的地址作为数据段的起始点
-Tbss=<org>:使用指定的地址作为bss段的起始点
-t,--trace:在处理输入文件时显示它们的名称
-u <symbol>,--undefined=<symbol>:强制指定符号在输出文件中作为未定义符号
-v,-V,--version:显示ld版本号
-warn-common:当一个通用符号和另一个通用符号结合时发出警告
-warn-constructors:如果没有使用任何全局构造器,则发出警告
-warn-once:对于每个未定义的符号只发出一次警告
-warn-section-align:如果为了对齐而改动了输出段地址,则发出警告
--whole-archive:对于指定的存档文件,在存档中包含所有文件
-X,--discard-locals:删除所有本地临时符号
-x,--discard-al:删除所有本地符号

例子:arm-linux-ld -Tlink.lds -o BL2.elf main.o start.o target.o

objcopy
复制一个目标文件的内容到另一个文件中,可以使用不同于源文件的格式来输出目的文件,即可以进行格式转换。多用于从.elf生成.bin镜像文件,可烧录。
input-file、out-file
选项:

input-file、out-file
参数input-file和outfile分别表示输入目标文件(源目标文件)和输出目标文件(目的目标文件)。如果在命令行中没有明确地指定outfile,objcopy将创建一个临时文件来存放目标结果,然后使用input-file的名字重命名这个临时文件(此时,原来的input-file将被覆盖)。

-I bfdname或--input-target=bfdname
用来指明源文件的格式,bfdname是BFD库中描述的标准格式名。如果不指明源文件格式,objcopy会自己去分析源文件的格式,然后去和BFD中描述的各种格式比较,从而得知源文件的目标格式名。

-O bfdname或--output-target=bfdname
使用指定的格式来输出文件,bfdname是BFD库中描述的标准格式名。

-F bfdname或--target=bfdname
同时指明源文件、目的的文件的格式。将源目标文件中的内容复制到目的目标文件的过程中,只进行复制不做格式转换,源目标文件是什么格式,目的目标文件就是什么格式

-R sectionname或--remove-section=sectionname
从输出文件中删掉所有名为sectionname的段。该选项可以多次使用

-S或--strip-all
不从源文件中复制重定位信息和符号信息到目标文件中去。

-g或--strip-debug
不从源文件中复制调试符号到目标文件中去

例子:
生成.bin

objcopy -O binary -R .note -R .comment -S *.elf  *.bin

#使用 -O binary (或–out-target=binary) 输出为原始的二进制文件
#使用 -R .note (或–remove-section) 输出文件中不要.note这个section,缩小了文件尺寸
#使用 -R .comment(或–remove-section) 输出文件中不要.comment这个section,缩小了文件尺寸
#使用 -S (或 --strip-all)输出文件中不要重定位信息和符号信息,缩小了文件尺寸
或者可以使用命令:

objcopy -O binary  --gap-fill 0xff  *.elf  *.bin

#–gap-fill 0xff 指定使用“0xff”填充段与段间的空闲区域

生成HEX命令:

objcopy -O ihex...

objdump
从.elf反汇编成.dis文件,其实就是.S文件
(gcc -S可以生成.S文件,但是这个命令是生成整个工程的汇编文件,用于分析或反向工程)

objdump -f test
显示test的文件头信息

objdump -d test
反汇编test中的需要执行指令的那些section

objdump -D test
与-d类似,但反汇编test中的所有section

objdump -h test
显示test的Section Header信息

objdump -x test
显示test的全部Header信息

objdump -s test
除了显示test的全部Header信息,还显示他们对应的十六进制文件代码

可以将打印的文件输出到文本文件中:
objdump -D BL2.elf > BL2_elf.dis

2.gdb

debug步骤:
1.debug模式编译
生成程序加 -g 选项:g++ -g hello.cpp -o hello
gdb hello即可开始 调试,run即可运行程序
2.打上断点
gdb选项都可以简写为首字母:
l list 列出文件内容
b break 打上断点
d delete 删除断点

打断点的几种方式:
b 函数名
b 行号
b 文件名:行号
b 行号 if 条件

查看断点:
i b 或者 info break
3.运行调试
r run
c continue
q quit
4.单步调试
n next (step over:如果正在调一个子函数,就会运行完子函数,不会进入到子函数内部去)
s step(step into:会进入到子函数中)
f finish(step return:运行完直接退出当前函数)
5.继续运行
continue
6.打印和监控值
p print
watch 变量(监控一个变量的值变化,此选项不能简写)

wi :进入控制台

造成segment fault,产生core dump的可能原因:
1.内存访问越界
1)由于使用错误的下标,导致数组访问越界
2)搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
3)使用strcpy,strcat,sprintf,strcmp,strcasecmp等字符串操作函数,将目标字符串读、写爆。应该使用srtncpy,srtlcpy,strncat,snprintf,strncmp,strncasecmp等函数防止读写越界。

2.多线程程序使用了线程不安全的函数

3.多线程读写的数据未加锁保护
对于会被多个线程同时访问的全局数据,应该注意加锁保护

4.非法指针
1)使用空指针
2)随意使用指针转换
一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将他转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问他。因为这种结构内部有其内存对齐规则。

5.堆栈溢出
不要使用大的局部变量

设置core dump的文件大小不受限制:
vi /etc/security/limits.conf或者
gedit /etc/security/limits.conf将

#*               soft    core            0

改为

#*               soft    core            unlimited
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值