FFmpeg编译过程分析
作者:罗上文,微信:Loken1,公众号:FFmpeg弦外之音
在 编译链接基础知识 一章 的 《Linux环境编译多个C程序文件》一文提过,无论多么庞大的 C/C++ 项目,无论这个项目里面有多少个代码文件,整个 build 过程就只有两步。编译跟链接。
1,编译,把 所有 .c 文件都编译成 .o 文件,例如 aa.c
编译成 aa.o
,bb.c
编译成 bb.o
在这一步,这里所有的 .c 文件都是独立编译的,没有关联。一些 .c 文件里面可能会 include 一些头文件,头文件里面有一些函数声明,结构体定义,或者变量定义之类的。
不过头文件的只是函数声明而已,不是函数的实现,编译器知道函数的声明有什么作用呢?
这里需要先普及一个知识点,函数的使用其实分两步。
- 传参。
- 跳转到函数地址,执行函数里面的代码。里面的代码能获取到上一步传进来的参数。
首先,传参这一步 跟函数的实现没有关系的,例如 aa.c
调用了 bb.c
里面的一个函数 echo
,代码如下:
void echo(char *str1,char *str2){
printf("str is %s,%s \n",str1,str2);
}
通常第一个参数 会放在 eax
寄存器传递。echo 函数里面的汇编代码,他只管直接取 eax
寄存器即可。eax
寄存器就是 str
变量。
至于是谁把 这个参数 放进去 eax
,这个参数值是多少,echo 函数不管的,那是调用者的事情,也就是 aa.c
的事情。
所以 aa.c
为什么 include 头文件,为什么要知道 echo
函数的声明?
这是因为 aa.o
里面要先把参数移动到合适的寄存器,再跳转到 echo 函数的地址。aa.o
可能会执行以下指令来分配寄存器,如下:
mov $111,%eax
mov $222,%ebx
call 0000000
111 是 str1 字符串的指针地址。222 是 str2 的地址。由于 aa.o
不知道 echo 函数的真正地址,所以编译器只能占个位置,填 00 ,编译器还会在另一个地方记录了这个位置,记录这个符号位置的000000内容是需要修改的。然后链接器二次扫描的时候就会修正地址。
重复一下重点,各个 .c 文件都是独立编译的,之间没有关联,编译器不需要知道函数的实现,只需要知道函数的声明即可。
2,链接,把编译器生成的 .o
文件全部链接在一起,到这一步的时候,之前那些 .h
头文件已经完全没有用了。
链接器主要的工作就是把 各个 .o 文件之间的函数地址修正,把各个段合并。PE
或者 ELF
, .o
或者 .obj
都是基于段结构的二进制文件。
上面讲解了 C/C++ 项目的 build 过程。FFmpeg 也是一个 C 语言项目,所以 FFmpeg 也符合上面这些原理过程。
FFmpeg 的编译链接过程主要由两个文件来实现。
1,configure ,这是一个shell脚本,主要检测当前编译环境,生成 config.mak 文件。
2,makefile,这是一个 makefile 文件,会引入 configure 生成的 config.mak 文件 来做一些自定义的编译。
扩展知识:不是每个 C/C++ 项目的 configure 脚本都会生成 makefile 文件,FFmpeg 项目就是个例外,他的 makefile 文件一开始就存在的,生成的是 config.mak 文件。
下面就开启本章的内容,分析 configure 跟 makefile 的逻辑。
感谢 NETINT(镕铭微电子) 赞助《FFmpeg原理》免费版一书的服务器费用,下面是 VPU 产品介绍