1. 一个C文件需要通过步骤变成一个可执行文件的?
要通过预处理、编译、汇编、连接这四步。
预处理: 处理包含“#”的那些命令。如“#include”、“define”宏定义展开、“ifdef”条件编译 等。最后输出到一个“.i”文件中等待进一步处理。file.c==>file.i
编译:把C代码翻译成汇编代码。file.i==>file.s
汇编:把汇编代码翻译成符合一定格式(ELF)的机器码。file.s==>file.o (即OBJ文件,目标文件)
链接:链接就是将上步生成的OBJ文件和系统库中的OBJ文件、库文件链接起来。file.o ==> 应用程序
(1)动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。ggc -o 默认为动态链接。
(2)静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过静态链接生成的程序体积较大。gcc -static -o 则为静态链接,产生的应用程序文件因包含静态库,所以占的存储空间会比较多。
2. gcc的使用方法:
gcc [选项] 文件名
gcc常用选项:
-v:查看gcc编译器的版本,显示gcc执行时的详细过程
-o <file> Place the output into <file>
指定输出文件名为file,这个名称不能跟源文件名同名
-E Preprocess only; do not compile, assemble or link
只预处理,不会编译、汇编、链接
-S Compile only; do not assemble or link
只编译,不会汇编、链接
-c Compile and assemble, but do not link
编译和汇编,不会链接
//==================================================
当需要通过gcc得到一个应用程序时,可以用以下指令:
gcc -c -o hello.o hello.c
gcc -o hello hello.o
gcc会对.c文件默认进行预处理操作,-c再来指明了编译、汇编,从而得到.o文件
再通过gcc -o hello hello.o将.o文件进行链接,得到可执行应用程序。
3. gcc编译的规范
应该分别编译,最后链接,如果把所有的文件都用一条指令编译链接。若之后对其中某个文件进行了修改,若还是只用一条指令编译链接,再编译的话,就又会把编译过的文件再编译一遍,会很耗时。eg. gcc -o program a.c b.c
正确的做法是把分别编译最后链接。 这样就只需要重新编译修改过的文件,再将.o文件重新链接即可。
eg. gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o
4. Makefile的语法
(1) 作用
作用是组织和管理程序。
规则:
目标文件 : 依赖文件
【TAB键】 + 命令
注:当目标文件不存在,或某个依赖文件比目标文件“新”,则执行“命令”。
//========================================================================
(2) 通配符
当目标文件过多时,为了不一一罗列,我们需要使用通配符: %.o
eg. %.o : %.c
gcc -c -o $@ $<
$@ 表示目标文件
$< 表示第1个依赖文件
$^ 表示所有依赖文件
//=======================================================================
(3)假想目标:. PHONY (格式 .PHONY: xxx)
PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能。
如果编写一个规则,并不产生目标文件,则其命令在每次make 该目标时都执行。例如:
clean:
rm *.o temp
因为"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。如果目录中出现了"clean"文件,则规则失效了:没有依赖文件,文件"clean"始终是最新的,命令永远不会 执行;为避免这个问题,可使用".PHONY"指明该目标。如:
.PHONY : clean
这样执行"make clean"会无视"clean"文件存在与否。
已知phony 目标并非是由其它文件生成的实际文件,make 会跳过隐含规则搜索。这就是声明phony 目标会改善性能的原因,即使你并不担心实际文件存在与否。
完整的例子如下:
.PHONY : clean
clean :
rm *.o temp
//=======================================================================
(4) 即时变量、延时变量, export
简单变量(即时变量) :
A := xxx # A的值即刻确定,在定义时即确定。(即前面的变量不能使用后面的变量,只能使用前面已定义好了的变量,定了就不能变了,不能反悔。)
B = xxx # B的值使用到时才确定
:= # 即时变量
= # 延时变量
?= # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
+= # 附加, 它是即时变量还是延时变量取决于前面的定义
//=======================================================================
5. Makefile函数
a. $(foreach var,list,text)
这个函数的意思是,把参数<list>;中的单词逐一取出放到参数<var>;所指定的变量中,然后再执行< text>;所包含的表达式。每一次<text>;会返回一个字符串,循环过程中,<text>;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
所以,<var>;最好是一个变量名,<list>;可以是一个表达式,而<text>;中一般会使用<var>;这个参数来依次枚举<list>;中的单词。举个例子:
eg. names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
b. $(filter pattern...,text) # 在text中取出符合patten格式的值
$(filter-out pattern...,text) # 在text中取出不符合patten格式的值
c. $(wildcard pattern) # pattern定义了文件名的格式,
# wildcard取出其中存在的文件
d. $(patsubst pattern,replacement,$(var)) # 从列表中取出每一个变量 var
# 如果符合pattern
# 则替换为replacement
6. Makefile生成依赖
当一个.c文件包含很多的头文件时,很难在依赖关系中将所有的头文件都列出来。如果不列出依赖的头文件来的话,当该头文件被修改了的话,重新编译的时候并不会认为.c文件更新了。因此吧所有的依赖都写入.d文件来代替自己逐个罗列。
gcc -M c.c // 打印出依赖
gcc -M -MF c.d c.c // 把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d // 编译c.o, 把依赖写入文件c.d
gcc -c -o $@ $< -MD -MF .$@.d //编译成目标文件,并生成目标文件的依赖文件,前面加“.”表示生成隐藏的.d文件。ls -a 表示显示隐藏文件。所以以后编译用这条指令就可以了,具有普遍意义。
7. 一个Makefile实例
objs = a.o b.o c.o
/*为了不产生迭代的问题,故用了即时变量 :=*/
dep_files := $(patsubst %, .%.d, $(objs)) /*列表objsz中的变量,替换为.%.d字符串,再将其赋值给dep_files*/
dep_files := $(wildcard $(dep_files)) /*判断路径内是否包含有dep_files描述的的文件,如有则取出再赋值给dep_files*/
test: $(objs)
gcc -o test $^
/*ifneq(ARG1,ARG2),ifnotequal判断变量depfiles是否为空,若非空则包含dep_files变量,即依赖文件 */
ifneq ($(dep_files),)
include $(dep_files)
endif
%.o : %.c
gcc -c -o $@ $< -MD -MF .$@.d //编译且生成依赖文件,依赖文件为隐藏文件
clean:
rm *.o test
distclean:
rm $(dep_files) //移除依赖文件.d
.PHONY: clean //假想目标clean 即使文件中有同名文件clean也不影响编译
8. CFLAGS
CFLAGS 表示用于 C 编译器的选项。
eg. CFLAGS = -Werror //编译的时候都会把warning认为是error。
CFLAGS = -Iinclude //指定include后面的路径为gcc寻找头文件的默认路径。
arm-linux-gcc 编译选项
- -o 编译及链接,会生成一个exe可执行文件
- -Wall 指定产生全部的警告信息
- -O/-O2/-O3 数字越高,代表优化的更多,可以使生成的执行文件的提高执行效率
- -c 编译不链接,会生成一个*.obj文件,若后面加了-o,则表示指定输出文件名称
- -static 静态链接,生成的文件会非常大, 好处在于不需要动态链接库,也可以运行
- -S 只激活预处理和编译,就是指把文件编译成为汇编代码
例如:
arm-linux-gcc -c -o led.o led.c :编译不链接
arm-linux-gcc -o led led.c :编译以及链接
arm-linux-ld 连接选项
- -Ttext 0x00000000 指代码段头地址为0x00000000
- -T链接脚本 指使用链接脚本来进行更复杂的地址设置,包括了代码段,数据段,bss段等
- -o 后面指的将多个文件连接在一起,生成一个obj文件,上面的名称是led_elf。
- -pie 生成动态链接地址段,一般在新版uboot里会看到
arm-linux-objcopy 复制选项,支持格式转换
- -O binary 用来指定生成文件按照后面的格式来输出,其中binary是指生成二进制(.bin)文件。
- -S 不从源文件中复制重定位信息和符号信息到目标文件中去
arm-linux-objdump 反编译选项
- -D 反编译所有段
- -b binary 指定反编译目标文件格式
- -m ram 指定反编译目标文件所需的架构,这里是ram架构
转载自:https://www.cnblogs.com/lifexy/p/7065175.html