本文最后更新于 395 天前,其中的信息可能已经有所发展或是发生改变。
内容目录
注意: 下文验证的内容,使用的是这里的func.h、test.c
func.h
#include <stdio.h>
void func()
{
#ifdef TEST
printf("TEST = %s\n", TEST);
#endif
return;
}
test.c
#include <stdio.h>
#include "func.h"
int g_global = 0;
int g_test = 1;
int main(int argc, char *argv[])
{
func();
printf("&g_global = %p\n", &g_global);
printf("&g_test = %p\n", &g_test);
printf("&func = %p\n", &func);
printf("&main = %p\n", &main);
return 0;
}
0x01 预处理指令: gcc -E test.c -o test.i
test.i
# 1 "test.c" // 1 :表示后文部分是test.c的第一行展开内容
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
// ..... 此处省略800余行 .....
# 2 "test.c" 2 // 第一个2 : 表示后文部分是test.c的第二行展开内容, 第二个2表示:之前包含的语句结束了
# 1 "func.h" 1 // 第一个1 :从这里开始,展开的是func.h第一行展开的内容
void func()
{
//打印语句被删掉了
return;
}
# 3 "test.c" 2 // 3:后面部分来自test.c的第三行之后的部分,2:表示从之前包含的头文件里退出了
int g_global = 0;
int g_test = 1;
int main(int argc, char *argv[])
{
func();
printf("&g_global = %p\n", &g_global);
printf("&g_test = %p\n", &g_test);
printf("&func = %p\n", &func);
printf("&main = %p\n", &main);
return 0;
}
0x02 编译指令 : gcc -S test.i -o test.s
Linux下生成的汇编代码
test.s
.file "test.c"
.text
.globl func
.type func, @function
func:
pushl %ebp
movl %esp, %ebp
popl %ebp
ret
.size func, .-func
.globl g_global
.bss
.align 4
.type g_global, @object
.size g_global, 4
g_global:
.zero 4
.globl g_test
.data
.align 4
.type g_test, @object
.size g_test, 4
g_test:
.long 1
.section .rodata
.LC0:
.string "&g_global = %p\n"
.LC1:
.string "&g_test = %p\n"
.LC2:
.string "&func = %p\n"
.LC3:
.string "&main = %p\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
call func
movl $.LC0, %eax
movl $g_global, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC1, %eax
movl $g_test, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC2, %eax
movl $func, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC3, %eax
movl $main, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
.section .note.GNU-stack,"",@progbits
0x03 汇编指令 : gcc -c test.s -o test.o
0x04 生成映射文件: gcc -Wl,-Map=test.map test.c
全局变量的地址和存放的位置可以在映射文件中找到;
全局变量和函数的地址在编译结束后就已经决定了,不需要运行的时候动态决定;
0x05 宏定义 gcc -D’TEST="test"’ test.c
看一下预处理文件
0x06 获取系统头文件路径 : gcc -v file.c
查看自己包含的头文件路径对不对,这个也很关键
0x07 获取目标的完整依赖关系:gcc -M test.c
0x08 获取目标的部分依赖关系: gcc -MM test.c
Makefile中会用到
“-MM”就很清晰,.o依赖.c和.h
0x09 指定库文件和库文件搜索路径 : gcc test.c -L. -lfunc
“-L”: 指定库文件搜索路径
“-l”: 指定库文件
编译test.c的时候依赖func库,到当前目录下搜索("-L.")
创建一个库文件,来链接,步骤:
- 把func.h名字修改为func.c, test.c中不包含func.h
- func.c编译成func.o
- func.o打包成库文件libfunc.a
- 使用这个库文件 “-L.”
注意:
静态库文件名的命名规范:以lib为前缀,紧接着跟静态库名,扩展名为.a。
例如:我们将创建的静态库名为hello,则静态库文件名就是libhello.a