0x01 gcc
gcc工具readelf
解析elf文件结构
gcc参数
-c 编译和汇编,但不链接
ex:gcc -c main.c 输出main.o文件,.o文件不可执行
-o <file> 对.o进行链接
ex:gcc -o main main.o 将main.o链接为可执行文件,输出main
-g 如果想用调试器执行一个可执行文件, 在用gcc编译时必须加上-g选项,加上-g选项以后,gcc在编译时会做以下额外的操作:
- 创建符号表,符号表包含了程序中使用的变量名称的列表。
- 关闭所有的优化机制,以便程序执行过程中严格按照原来的C代码进行
- -w 关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。
- -Wall:开启所有警告(可以理解为warinig all),使用它能够使GCC产生尽可能多的警告信息。(非常推荐使用该选项)
- gcc -S test.c 生成汇编文件
- ar rcs libtest.a test.o 生成静态链接库文件
gcc -o test test.o -L. -ltest 链接静态库文件和.o文件生成可执行文件
gcc -o test test.o -L/your/library/path -l:libmylib.a注意上面的说明中红框标注的内容,如果
-l:filename
格式指定一个文件名,连接程序直接去找这个文件名了,不会再像使用-lname
时将name扩展成lib<name>.a
格式的文件名.
所以使用-l:libpng.a
这样的形式来指定连接库,就指定了静态连接png
库。
当然如果库的位置不在gcc默认搜索路径中,要用-L
参数另外指定搜索库的路径,否则连接程序不知道该从哪里找到filename
。编译代码到共享库中,并使用操作系统的预加载功能。当执行/usr/bin下的被测应用时,如果系统库和我们创建的库中有相同的函数,会使用我们库中的函数。
gcc -shared -fPIC -o test.so test.c LD_PRELOAD=./test.so /usr/bin/target
- --cc=clang --extra-cflags='-O2 -g3 -fsanitize=address -fno-omit-frame-pointer -Wno-error' --extra-ldflags='-O2 -g3 -fsanitize=address -fno-omit-frame-pointer -Wno-error' --enable-debug --prefix=/home/cyber/VulnResearch/ffmpeg/clean/out_2
- 流程示例
1.预处理
gcc -E test.c < test.i
2.编译,生成汇编
gcc -S test.i -o test.s
3.汇编,生成机器码
gcc -c test.s -o test.o
4.链接,生成执行程序
gcc test.o -o test
每行汇编对应一行机器码
例子:
gcc -g -c test.c
objdump -d -M intel -S test.o
gcc之addressSanitizer
https://www.jianshu.com/p/3a2df9b7c353
gcc 4.8以上支持addressSanitizer,其能检测的错误类型
用-fsanitize=address选项编译和链接你的程序;
用-fno-omit-frame-pointer编译,以在错误消息中添加更好的堆栈跟踪。
增加-O1以获得更好的性能(最基础的优化,分为1,2,3级)
简单源码示例:
#include <stdlib.h>
char leaktest(){
char* x =(char*)malloc(10*sizeof(char*));
return x[5];
}
int main(){
leaktest();
return 0;
}
编译:gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g leaktest.c -o leaktest
makefile
1、默认情况下,make 会以第一条规则作为其“终极目标”。
2、make在执行命令时会检测依赖文件的时间戳:
若依赖文件不存在或者依赖文件的时间戳比目标文件新,则执行依赖文件对应的命令。
若依赖文件的时间戳比目标文件老,则忽略依赖文件对应的命令。
3、make
目标文件的依赖文件是按照从左到右的顺序生成的
1、makefile文件基本书写规则
TARGET... : PREREQUISITES...
COMMAND
TARGET:规则目标,可以为文件名或动作名
PREREQUISITES:规则依赖
COMMAND:命令行,必须以[TAB]开始,由shell执行
ex:
1 #this is a makefile example
2
3 main:main.o
4 gcc -o main main.o
5
6 main.o:main.c
7 gcc -c main.c
使用:
2、变量使用
${变量名}
$(变量名)
$单字符变量名
,Makefile有三个非常有用的变量。分别是$@
(目标文件),$^
(所有的依赖文件),$<
(第一个依赖文件)
参考:https://www.cnblogs.com/baiduboy/p/6849587.html
ex:
# 变量定义
objects = program.o foo.o utils.o
program : $(objects) #在依赖中引用变量
gcc -o program ${objects} #在命令中引用变量
$(objects) : defs.h #在目标中引用变量
3、变量的分类和赋值
根据变量定义时所使用的赋值操作符的不同,可以将变量分成两种类型(或者说是两种风格):
递归展开式变量
和直接展开式变量
;
使用赋值操作符
=
、+=
和?=
定义的变量都是递归展开式变量,使用赋值操作符:=
定义的变量为直接展开式变量 。两种变量类型的的最根本区别在于:变量值的求值时机,递归式变量的求值时机在于变量被引用时,直接展开式的求值时机在于变量被定义时。
4、makefile选项
- CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I /usr/include -I /path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
- LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-static -L /usr/lib -L /path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。
- LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv
5、make参数
1、make -j4 用4核同时编译,默认1核,可以不用加这个...
2、make -n/--just-print/--dry-run/--recon 只检查Makefile的命令,输出组合后的结果,不执行
cmake
你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 [[1]](https://www.hahack.com/codes/cmake/#fn1)。
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
- 编写 CMake 配置文件 CMakeLists.txt 。
- 执行命令
cmake PATH
或者ccmake PATH
生成 Makefile ,ccmake
和cmake
的区别在于前者提供了一个交互式的界面。。其中,PATH
是 CMakeLists.txt 所在的目录。 使用
make
命令进行编译。mkdir bld
cd bld
cmake .. (CMakeLists.txt在上一级目录)
make
0x02 clang
clang参数
clang使用libfuzzer进行fuzz所需参数
-fsanitize-coverage=trace-pc-guard(编译器在每条边界插入代码,每条边界的guard都不同。), trace-cmp(编译器将在比较指令和switch语句周围插入额外的工具),trace-gep(LLVM GEP指令(捕获数组索引)),trace-div(编译器将检测整型除法指令(以捕获除法的正确参数))
参考文档:https://www.anquanke.com/post/id/181936
0x03 实际场景
1、汇编代码如何生成可执行文件
生成可执行文件的过程通常需要以下几个步骤:
编写汇编代码文件,保存为后缀名为.asm的文件。
使用汇编器(assembler)将汇编代码文件转换成目标文件(object file),一般以.o为后缀名。
使用链接器(linker)将目标文件和所需的库文件链接起来,生成可执行文件。
具体操作可以根据使用的汇编器和链接器不同而有所不同。例如,使用GNU汇编器(Gas)和链接器(ld)可以通过以下命令生成可执行文件:
as -o your_file.o your_file.asm
ld -o your_executable your_file.o
2、如何查看汇编指定的机器码
要查看汇编指令的机器码,可以使用汇编编译器将汇编代码编译成目标文件,然后使用目标文件的反汇编工具来查看机器码。
- objdump:这是一个常用的 GNU 工具,可以用来查看目标文件和可执行文件的反汇编代码。例如,要查看一个名为 test 的可执行文件的反汇编代码,可以在终端中运行以下命令:
objdump -d test
这将显示 test 可执行文件的所有反汇编代码,包括每个汇编指令的机器码。
3、分两步才可以编译,为什么?
gcc -c -I/home/dmdba/dmdbms/include odbc_dml.c -o odbc_dml.o
gcc -o odbc_dml odbc_dml.o -L/home/dmdba/dmdbms/bin -ldodbc
合成一步报错.. gcc -I/home/dmdba/dmdbms/include -L/home/dmdba/dmdbms/bin -ldodbc odbc_dml.c -o odbc_dml.o
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。