本文将逐渐转移
GCC
常用选项
-Og 执行所有不影响调试体验的优化[4]
-Wall 使能所有的警告
-ffunction-sections 将每个函数放入自己的段中
-fdata-sections 将每个数据放入自己的段中
-x assembler-with-cpp 其格式为:-x language。为输入文件指定语言(而不是让编译器根据后缀选择默认语言)
-u_scanf_float 在scanf中使用f%
-u_printf_float 在printf中使用f%
预处理
-E Preprocess only; do not compile, assemble or link.
-E 仅预处理; 不要编译、汇编或链接。
gcc -E main.c -o main.i
编译
-S Compile only; do not assemble or link.
-S 仅编译; 不要汇编或链接。
gcc -S main.c -o main.s
汇编
-c Compile and assemble, but do not link.
-c 编译和汇编,但不要链接。
gcc -c main.c -o main.o
链接
链接文件
编译命令
gcc main.o -o main
指定头文件路径
https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gcc.pdf 第265页
gcc main.c -I inc -o main
生成依赖关系
https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gcc.pdf 第252页
gcc -MM main.c -I inc/
输出:
main.o: main.c inc/inc.h
readelf工具
添加代码:
static const char __attribute__((section(".test.str"))) str[] = "hello word!\n";
执行: readelf -S xxx
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08000000 0000b8 000040 00 A 0 0 1
[ 2] .text PROGBITS 08000040 0000f8 00f33c 00 AX 0 0 8
[ 3] .rodata PROGBITS 0800f37c 00f434 004190 00 A 0 0 4
[ 4] .test.str PROGBITS 0801350c 0135c4 00000d 00 A 0 0 4
0801350c(Addr):若可被加载,则对应虚拟地址。如下为bin文件的地址,如被加载则为虚拟地址0801350c,物理地址一般和虚拟地址一样。
0135c4(Off):在文件中的偏移地址,对.bss段而言则无意义。如下为elf文件的地址。
00000d(Size):表示该段的大小,str字符串0x0d字节。
Flg:表示权限,RE表示只读,RW表示可读可写。
Align:表示映射到虚拟地址后的对齐方式。
objdump工具
执行: objdump --section=.test.str -s main
-s --full-contents:显示指定section的完整内容。默认所有的非空section都会被显示。[3]
Contents of section .test.str:
0798 68656c6c 6f31210a 00000000 00000000 hello1!.........
07a8 68656c6c 6f32210a 00000000 00000000 hello2!.........
07b8 68656c6c 6f33210a 00 hello3!..
执行:objdump -j .text -S main
反汇编main中的text段内容,并尽可能用源代码形式表示。
参考文献
[1] 计算机系统基础目标文件格式
https://www.codenong.com/cs106511100/
[2] readelf(1) — Linux manual page
https://www.man7.org/linux/man-pages/man1/readelf.1.html
[3] objdump命令详解
https://blog.csdn.net/qq_41683305/article/details/105375214
[4] gcc“你好,世界!” 具有常见的命令行选项
https://www.nhooo.com/note/qa54zx.html