【操作系统API】
在计算机操作系统发展早期,各种操作系统的API使用方式都不同,频繁学习不同的API非常耗费精力,为此人们制定了统一的API规则,称为POSIX(可移植操作系统接口),它规定了API需要提供哪些功能、每种功能对应的函数和全局变量使用方式,开源操作系统的API基本上都遵循此规则。
API与C标准库深度融合,很多功能是由两者共同提供的,不分彼此,C标准库中的stdlib.h文件提供了很多看似应该由API提供的功能。
● mask
使用二进制位记录某种功能是否生效的数据称为mask,中文翻译为掩码,一般值为1表示生效、值为0表示不生效,有些功能使用一个二进制位记录生效状态,有些功能使用多个二进制位的组合记录生效状态,多个同类型mask指定的生效功能可以使用按位或运算进行整合。
● API函数执行结果
若执行出错后需判断出错具体原因,可以使用errno.h文件中的全局变量errno,其存储了API函数执行结果的具体原因,每一种执行结果使用一个数值表示,每个数值表示的含义不方便直接观察,可以使用stdio.h文件中的perror函数,perror会自动读取errno的值并输出其表示的含义。
注意:errno会随下次执行API函数而被修改,所以判断出错原因之前不能执行其它API函数。
【GCC编译器】
将源代码制作为一个ELF文件需要经过三个步骤:编译、汇编、连接,三个步骤分别使用以下三种工具完成:
1.编译器,将高级语言代码转换为对应的汇编语言代码。
2.汇编器,将汇编语言代码转换为对应的指令数据、数学数据,GNU项目的汇编器为gas。
3.连接器,将编译后的文件添加必要的节组成ELF文件,GNU项目的连接器为ld。
gcc是编译器,只有编译功能,没有汇编与连接功能,但是因为编译、汇编、连接通常需要一起使用,所以在使用gcc编译代码时默认自动调用汇编器、连接器制作为ELF文件。
讯享网
● 预处理
1.将 #include 连接的文件内容复制到本文件中。
2.将使用 #define 定义的宏代码转换为具体的代码。
● GCC操作文件的类型
gcc通过文件后缀名自动确定文件类型,常用后缀如下:
cc,c++语言代码文件
cpp,c++语言代码文件
s,汇编语言代码文件
o,可重定向文件
so,动态连接库文件
out,可执行文件
● 常用参数
-o,小写字母o,指定编译后的文件名称,示例:gcc ~/a.c -o ~/ali.out,~/a.c为源代码文件路径,~/ali.out为编译后的文件路径,若不指定编译后的可执行文件存储路径,则默认存储路径为编译器工作目录,默认名称为a.out。
-O,大写字符O,设置编译优化级别,有4种级别,使用编号0-3表示,0表示不优化,3表示最高优化,默认为0级优化,级别值紧邻参数之后,示例:gcc -O2 ~/a.c。
------------------
-E,只进行预处理,不编译代码。
-S,只进行预处理、编译,不进行汇编、连接。
-c,只进行预处理、编译、汇编,不进行连接,用于制作可重定向文件。
-fpic,与-c组合使用,用于制作动态连接库使用的可重定向文件。
------------------
-static,调用连接库时使用静态库版本,而非动态库,编译后的程序体积会大很多。
-share,调用连接库时尽量使用动态库版本,生成的文件体积更小。
------------------
-g,添加调试功能相关数据,方便调试器调试程序,若不添加此参数则使用调试器调试程序时某些功能不可用,同时注意调试程序时不应该使用-O参数开启优化。
-s,编译后的程序不保存全局成员名称。
------------------
-masm,指定汇编代码的语法类型,比如在高级语言中内嵌汇编代码、或使用-S参数只编译代码时,都可以使用此参数,默认使用AT&T语法,若使用英特尔语法则应该添加参数 -masm=intel。
-std,设置C语言、C++语言的版本,比如:c89、c99、c11、c17、c++98、c++03、c++11、c++14、c++17,具体的版本值使用=符号赋值,示例:-std=c99,-std=c++03。
------------------
-w,不生成任何警告信息。
-Wall,生成所有警告和错误信息(数组越界访问除外),若不指定此参数则只显示必要的警告信息,比如设置了返回值的函数没有为返回值赋值,此时不使用此参数不会有警告。
-fpermissive,将不符合语法规则的某些代码改为可以编译,但是会生成警告信息,比如C++代码使用变量指针存储一个常量数据的地址,默认禁止编译,添加此参数即可编译,注意并非针对所有错误代码,有些错误不可忽略。
-fsanitize=address,检查源代码中各种内存错误访问相关信息,原理是在代码中添加各种检查代码,若出错则会在终端输出错误信息,并自动终止程序,一般用于调试程序。
------------------
-Idir,指定用户头文件的额外搜索路径,也就是在 #include 指令中使用""指定文件并使用相对路径时额外搜索的路径,参数值紧邻参数之后,示例:gcc -Idir/home/ali ~/a.c。
-I,指定所有类型头文件的额外搜索路径,使用 <> 和 "" 符号指定文件并使用相对路径时都会生效。
-L,指定静态连接库的额外搜索路径。
------------------
-v,查看gcc属性,包括gcc版本号。
【gdb调试器】
1.设置程序断点,程序执行到此处时会暂停。
2.程序单步执行,程序每执行一条指令就暂停。
3.查询程序执行时某些变量的值。
4.程序执行时临时修改某些变量的值。
使用gdb时首先在终端执行gdb程序,参数设置为要调试程序的路径,启动gdb后输入控制指令执行不同的功能,常用控制指令如下:
------------------
set args,设置被调试程序的main函数参数,示例:set args ali xyy,这里传入了2个参数,分别是ali和xyy。
list,显示程序源代码,示例:list 1,从第一行开始显示源代码,默认每次显示10行,之后按enter键显示下10行,示例:list main,显示main函数的源代码。
------------------
break/b,添加一个断点,可以使用代码行数或函数名指定断点添加处,程序执行到断点处会暂停,示例:break 9,在第9行添加一个断点。
info break/b,查询所有断点。
delete/d,取消一个断点,通过断点编号指定要删除的断点,示例:delete 1,取消编号为1的断点,示例:delete 1-5,取消编号1到5的断点。
disable,禁用指定编号的断点,示例:disable 1。
enable,启用指定编号的断点,示例:enable 1。
------------------
run/r,执行程序开始调试。
continue/c,在断点处恢复执行。
next/n,程序暂停后,单步执行下一条指令,若遇到跳转函数指令则忽略,执行之后的指令。
step/s,程序暂停后,单步执行下一条指令,若遇到跳转函数指令则进入函数,若跳转的函数是动态库函数则会产生错误。
finish/fin,程序暂停后,执行到下一个断点处,若本函数没有断点则执行到本函数末尾,并返回到上一级函数暂停。
until,跳转到源代码指定行数执行。
------------------
display,设置一个跟踪变量,每次暂停后自动查询此变量的值并输出,示例:display a,自动跟踪变量a。
undisplay,取消一个跟踪变量,需要使用跟踪变量编号指定,而不是变量名,示例:undisplay 1,取消编号为1的跟踪变量。
print/p,手动查询指定变量的值,示例:print a,查询变量a的值。
info locals,查询所有局部变量的值。
info reg,查询所有寄存器的值。
set var,修改指定变量的值,示例:set var a=0。
------------------
backtrace/bt,查询本函数调用者的执行顺序,也就是向前查询都有哪些函数执行。
call,跳转到指定函数执行一次并返回,同时输出此函数的返回值,示例:call ali(),跳转到ali函数执行,若有参数则需要同时为参数赋值。
disass,反汇编指定函数,示例:disass ali,反汇编ali函数。
------------------
quit/q,退出gdb。
注:有些控制指令可以使用简写,比如上述中的break/b,表示break可以简写为b。

【反汇编相关命令】
● 反汇编ELF文件
-h,显示文件头中的节说明信息。
-x,显示文件头中的所有属性信息。
-d,反汇编指令数据节,转换为汇编代码,包括init、plt、text、fini这四个节。
-D,反汇编所有节,转换为汇编代码,对于不完整的ELF文件可以使用此参数,自行判断哪些数据是指令。
-s,反汇编所有节,转换为16进制数据,同时显示数据对应的ASCII字符编码(若没有对应的字符编码则显示.符号)。
-M,设置汇编代码使用的语法规则,可以设置为AT&T语法、intel语法,默认使用AT&T语法。
● 查看ELF文件属性
使用readelf命令查看ELF文件的文件头、各种节属性,常用参数:
-h,查看文件头信息。
-l,查看程序头信息。
-S,查看节头信息。
-s,查看所有数据名、函数名。
-r,查看rela节信息。
-d,查看dynamic节信息。
-a,查看ELF文件的所有信息。
使用strings命令查询ELF文件中存储的字符串,这些字符串可能包含了程序功能的重要信息,示例:strings ~/a.out
使用ldd命令查询ELF文件使用了哪些动态库,示例:ldd ~/a.out
● 查看ELF文件内容
xxd、od、hexdump命令都可以查看ELF文件数据,这里以hexdump为例介绍。
常用参数:
-c,以ASCII字符方式显示。
-b,以8进制数据、单字节一组的方式显示。
-o,以8进制数据、双字节一组的方式显示。
-d,以10进制数据、双字节一组的方式显示。
-x,以16进制数据、双字节一组的方式显示。
-C,以16进制数据、双字节一组的方式显示,同时显示数据对应的ASCII字符。
-n,读取指定的文件长度(单位字节)。
讯享网
● 修改ELF文件内容
修改ELF文件数据可以使用hexedit命令,它使用16进制数据的方式进行修改,若操作系统没有自带此程序,可使用如下命令下载安装:sudo apt install hexedit,若有兴趣也可以通过API中的读写文件函数自行实现类似程序,更方便的方式是使用拥有图形界面的IDA进行ELF文件的反汇编以及数据修改,IDA官网有学习使用的免费版本(不能商用),具体使用方式这里不详细介绍。
【make】
make常用参数如下:
-f,指定make脚本文件的路径,若不指定则在工作目录中查找makefile文件。
-n,输出需要执行的操作,但是不真正的执行这些操作,用于检查makefile文件。
-j,指定使用CPU的几个核心进行工作,示例:make -j 4,使用4个核心。
-k,出现错误后继续执行,而非停止。
makefile脚本文件代码的基础编写规则如下。
● 注释
使用#符号设置一行注释。
● 创建行为
第一行用于设置行为名称以及此行为需要使用的文件和其它行为,action1为行为名,:符号之后编写此行为需要使用的文件(可省略)以及调用的其它行为(不可省略),上述代码指定了3个源代码文件。
行为名之后的行编写此行为执行的命令,每行命令开头需要使用tab键输入一个制表符。
★ 使用 @ 符号
默认情况下make会在终端输出执行了哪些命令,在命令前添加@符号则不输出。
★ 使用 - 符号
默认情况下遇到无法执行的命令make会终止执行,剩余命令都不会执行,在命令前添加-符号则命令执行出错时忽略,继续执行之后的命令。
● 使用多个行为
执行多个命令时往往需要将命令分类,放在不同的行为中,make默认从第一个行为开始执行,并且只执行第一个行为,其他行为需要被调用才能执行,并且被调用的行为会首先执行,就像C语言中的其它函数必须直接或间接的被main函数调用才能执行一样。
make从action1行为开始执行,此行为又调用了action2行为,action2会首先执行,之后再执行action1,最后的clean行为不会执行,需要在终端执行make时手动调用此行为执行,示例:make clean
● 使用字符串变量
定义行为时可以使用字符串指定行为属性,比如行为名称、行为使用文件、行为执行的命令。
★ 字符串赋值运算符
=运算符,直接赋值,多次使用=赋值时以最后一次赋值为准。
:=运算符,引用另一个字符串现在的值进行赋值。
?=运算符,有条件赋值,若变量没有赋值则进行赋值,若变量已经赋值则不进行赋值。
+=运算符,增加字符串中的字符,新增字符与原有字符使用空格分隔,常用于为执行命令添加参数。
★ 嵌套调用字符串
● 条件语句
● 函数
★ 使用 wildcard 函数查询文件名
★ 使用 patsubst 函数修改字符串
参数2,指定修改后的值
参数3,提供需要修改的字符串
★ 使用 foreach 函数循环处理字符串
参数2,需要处理的字符串。
参数3,每次循环对字符串的操作。
上述代码中,foreach会遍历字符串s1,每次循环会以空格作为分割符取其中一段内容,并将其临时存储到s2中,之后为此段内容添加.o,循环处理完s1的所有分段后将处理后的分段组合为一个字符串并返回。
★ 使用 dir 函数读取目录
● 进入子目录执行 make
同时还可以向子目录内的makefile脚本文件传递一个字符串变量。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/2574.html