最近在看《程序员的自我修养--链接,装载和库》,给以前的不扎实基础补课。因为我是缺什么看什么,所以在看到链接器一章时对glibc和MSVC CRT不慎清楚,遂search一波,尽量参考权威网站,且经过实际验证得出结论,若有问题,欢迎指正!
总述
参考Wikipedia,glibc全名GNU C Library,是GNU Project对C标准库的实现,但它也支持C++。它为GNU下的以Linux作为内核的系统提供内核库,也就是系统调用。其中包含了重要的API,包括ISO C11, POSIX, BSD等。这些API包括基础功能:open,read,write,malloc,printf,pthread_create,exit等。
MSVC CRT全名Microsoft Visual C++ C Runtime,是Windows下的C运行库。
说明:该文的glibc环境是Ubuntu16.04 64bit,
glibc
glibc在成为linux下的C 运行库之前,开发者们从Linux内核中分离出一部分代码,作为早期Linux下的C运行库,这个库又叫“Linux libc”。libc维护了5个版本,在1996年FSF发布了glibc 2.0,它支持POSIX标准、国际化、IPv6、64-位数据访问、多线程及改进了代码的可移植性。这时起,Linux开始采用glibc作为默认的C运行库,并且将2.x版本的glibc看作是Linux libc的后继版本。在Ubuntu中,glibc在/lib/x86_64-linux-gnu下的.so文件为libc.so.6,即第六个libc版本
而且在各个Linux发行版中,glibc往往被称为libc6。
glibc的发行版主要由两部分组成,一部分是头文件,比如stdio.h、stdlib.h等,在Ubuntu下,位于/usr/include/,
另外部分是库的二进制部分。主要包括C语言标准库,有静态和动态两个版本。动态库位于/lib/x86_64-linux-gnu/libc.so.6
静态库位于/usr/lib/libc.a,
除了上述头文件及标准库,还有辅助程序运行的运行库,即在执行你的代码过程中进行main引导以及返回处理的代码,它们是/usr/lib/crt1.o、/usr/lib/crti.o和/usr/lib/crtn.o。它们在静态链接时被调用,是程序启动运行的关键。
glibc启动文件
这几个文件具体源码分析见《程序员的自我修养--链接,装载和库》链接篇,这里只说明作用。
crt1.o里面包含程序入口函数_start,它负责调用__libc_start_main初始化libc并调用main函数进入真正的程序主体。这里反编译看下逻辑:
一开始它只作为C语言的运行库,后来C++出现,出现了必须在main()函数之前执行的全局/静态对象构造和必须在main()函数之后执行的全局/静态对象析构。运行库在每个目标文件中引入两个与初始化相关的段“.init”和“.finit”。运行库会保证所有位于这两个段中的代码会先于/后于main()函数执行,所以用它们来实现全局构造和析构就可以了。链接器在进行链接时,会把所有输入目标文件中的“.init”和“.finit”按照顺序收集起来,然后将它们合并成输出文件中的“.init”和“.finit”。
但是这两个输出的段中所包含的指令还需要一些辅助的代码来帮助它们启动(比如计算GOT之类的),于是引入了两个目标文件分别用来帮助实现初始化函数的crti.o和crtn.o,两者对应反编译函数如下:
除了全局对象构造和析构之外,.init和.finit还有其他的作用。由于它们的特殊性(在main之前/后执行),一些用户监控程序性能、调试等工具经常利用它们进行一些初始化和反初始化的工作。比如“__attribute__((section(“.init”)))”将函数放到.init段里面,但是要注意的是普通函数放在“.init”是会破坏它们的结构的,因为函数的返回指令使得_init()函数会提前返回,必须使用汇编指令,不能让编译器产生“ret”指令。(未验证)
暂时总结到这,还有MSVC CRT,等以后用到再说hh
参考:
1. C/C++运行库: https://blog.csdn.net/hpghy123456/article/details/5616030