working

您所在的位置:网站首页 hcl连接crt working

working

2023-05-13 00:03| 来源: 网络整理| 查看: 265

文章标题:学习总结:C Runtime

作者:汪辰 联系方式:[email protected] / [email protected] 1. 参考 2. 什么是 C Runtime 3. 认识一下这些 crt*.o 3.1. crt0.o/crt1.o 3.2. Scrt1.o 3.3. gcrt1.o/Mcrt1.o 3.4. crti.o/crtn.o 3.5. crtbegin.o/crtend.o 3.6. crtbeginS.o/crtbeginT.o/crtendS.o 4. 链接命令的例子 4.1. Static Executable 4.2. Dynamic Executable 4.3. Position-independent Executable (PIE) 4.4. Shared Object 5. Android 系统的 CRT

希望通过一篇文章把那些 crtXXX 相关的东西都整理一下,以前在脑子里面一直感觉乱哄哄的。

1. 参考 [Ref 1]《程序员的自我修养-链接、装载与库》 [Ref 2] Mini FAQ about the misc libc/gcc crt files. 2. 什么是 C Runtime

链接过程中除了有我们的源码文件对应的 object 文件、共享库、静态库外,在命令行的前后还会出现很多 crt*.o 的文件,这些文件之所以都带有 crt 的前缀,就是因为 crt 是 C RunTime 的缩写。这些文件链接进入我们的可执行文件后在运行期间起到帮助我们在启动时初始化程序运行环境,结束时清理运行环境的作用。

3. 认识一下这些 crt*.o

注意 这些 crt 文件中,crtbegin*.o 和 crtend*.o 不是 c 库的一部分,而是作为 GCC 的一部分提供的,其他都是由 C 库提供。具体原因下面会说明。

3.1. crt0.o/crt1.o

(参考 [Ref 1] 11.2.3 章节 P342)这些文件都是类似的东西,内部都是实现了 "Entry point",即 _start 函数(有关 "Entry point",可以参考另一篇总结《静态链接可执行程序的入口分析》) 之所以出现了 0、1,完全是历史原因造成的。一开始叫 crt.o,由于当时有些链接器对链接时目标文件和库的顺序有依赖性,crt.o 这个文件必须被放在链接器命令行中的所有输入文件中的第一个,为了强调这一点,crt.o 被更名为 crt0.o,表示它是链接时输入的第一个文件。后来由于 c++ 的出现以及为了支持全局构造或者析构函数实现(有关这个概念可以参考另一篇总结 《Global Constructors/Destructors》),crt0.o 也进行了升级,变成了 crt1.o。crt0.o 和 crt1.o 之间的区别在于:crt0.o 是早期的,不支持 .init 和 .finit 的启动代码,而 crt1.o 是改进后支持 .init 和 .finit 的。

但无论是早期的 crt0.o 还是目前使用的 crt1.o,这些 CRT 对象都包含 _start 符号,该符号负责引导程序的初始执行。这些 CRT 文件由 C 库提供。

3.2. Scrt1.o

当我们指定创建 PIE 的可执行程序时,采用 Scrt1.o 代替 crt1.o

有关 PIE 的概念,可以参考另一篇总结 《学习笔记:Position-Independent Executables》

在 musl(v1.2.3)中并不区分 Scrt1.o 和 crt1.o,两者内容是相同的。

$ cat crt/Scrt1.c #include "crt1.c" 3.3. gcrt1.o/Mcrt1.o

和 profiling 相关,譬如我们在 GCC 使用 -pg 时会涉及,不常见。不展开了。

这个是 glibc 特有的,musl 没有。

3.4. crti.o/crtn.o

参考 《Global Constructors/Destructors》,crti.o 中定义了 _init() 和 _fini() 的 prolog,crtn.o 中定义了 _init() 和 _fini() 的 epilog。

但需要注意的是以上描述并不总是成立。譬如,因为 _init()/_fini() 这套实现方案已经逐渐被新的 ARCH 所放弃,所以并不是所有的 ARCH 的 crti.o/crtn.o 中都会提供 _init() 和 _fini() 的 prolog/epilog。譬如针对 riscv,glibc 中这两个文件里面就没有相关实现,我们可以用 nm 和 objdump -dr 自己看一下。 musl (v1.2.3)也是类似。general 提供了空的 crt/crti.c 和 crt/crtn.c,但对一些 legacy 的 ARCH 则实现在自己的汇编文件里,譬如 crt/aarch64/crti.s 和 crt/aarch64/crtn.s。

3.5. crtbegin.o/crtend.o

同样是为了支持 C++ 的全局构造或者析构(参考另一篇总结 《Global Constructors/Destructors》),用来实现 ctors/dtor 的相关处理,需要注意的是这些文件由 GCC 提供,而不是作为 c 库的一部分,原因是在设计上,crtbegin.o/crtend.o 实现的内容完全是为了 C++ 服务的,而 c 库作为 C 语言的 runtime,并不关心 C++,也无需了解 C++ 的这些构造析构相关的特性。

3.6. crtbeginS.o/crtbeginT.o/crtendS.o

当我们需要链接生成的是 static executables 时,需要用 crtbeginT.o 代替 crtbegin.o,但crtend.o 不变。

当我们需要链接生成的是 shared objects 或者是 PIE 时,需要用 crtbeginS.o 代替 crtbegin.o,用 crtendS.o 代替 crtend.o。

4. 链接命令的例子

注:下面例子中为方便阅读,对长行做了回车换行处理。

4.1. Static Executable $ gcc hello.c -v -static ...... /usr/lib/gcc/x86_64-linux-gnu/9/collect2 ...... -static ...... /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginT.o ...... /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o ......

简化一下,针对 Static Executable 的场景,典型的链接顺序为:crt1.o crti.o crtbeginT.o [-L paths] [user objects] [gcc libs] [C libs] [gcc libs] crtend.o crtn.o

4.2. Dynamic Executable $ gcc -no-pie test.c -v ...... /usr/lib/gcc/x86_64-linux-gnu/9/collect2 ...... /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o ...... /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o

简化一下,针对 Dynamic Executable 的场景,典型的链接顺序为:crt1.o crti.o crtbeginS.o [-L paths] [user objects] [gcc libs] [C libs] [gcc libs] crtend.o crtn.o

4.3. Position-independent Executable (PIE) $ gcc hello.c -v ...... /usr/lib/gcc/x86_64-linux-gnu/9/collect2 ...... -pie ...... /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o ...... /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o ......

简化一下,针对 PIE 的场景,典型的链接顺序为:Scrt1.o crti.o crtbeginS.o [-L paths] [user objects] [gcc libs] [C libs] [gcc libs] crtendS.o crtn.o

4.4. Shared Object $ gcc -fPIC -shared foo.c -o libfoo.so -v ...... /usr/lib/gcc/x86_64-linux-gnu/9/collect2 ...... -shared ...... -o libfoo.so /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o ...... /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o

简化一下,针对制作 so 的场景,典型的链接顺序为:crti.o crtbeginS.o [-L paths] [user objects] [gcc libs] [C libs] [gcc libs] crtendS.o crtn.o

注意这里没有 crt1.o。

5. Android 系统的 CRT

以上的总结都是基于 Unix-like 上的那套,后面我们姑且将其称之为 “Unix-like 系统” 吧,Android 系统没有采用 crt0.o, crt1.o, crti.o, crtn.o, crtbegin.o, crtend.o 这些 CRT 文件及其文件的命名方式,而是采用了 android 自己的一套 CRT。总结在这里:

先罗列一下 Android 系统中和 CRT 有关的 object 文件,每个文件后面的括号中是 libc/Android.bp 文件中对应的 "cc_object" 的 "name":

crtbrand.o("crtbrand"): 从代码上看,只是定义了一个新的 section,并且在其中加入了一些特殊的字符串标识 android(体会以下 “brand” 的含义)。严格讲 crtbrand.o 并不属于我们常说的 CRT,bionic 中创建这个文件只是一个临时的中间文件,这个文件作为一个公共的素材会被提前链接进入 crtbegin_so.o/crtbegin_static.o/crtbegin_dynamic.o 中。 crtbegin_static.o("crtbegin_static"):类似于 “Unix-like 系统” 中的 crt1.o,用于和 crtend.o 一起制作 Static Executable,典型链接顺序:clang++ crtbegin_static.o ... xxx.a ... crtend.o -o a.out ...... -static。 crtbegin_dynamic.o("crtbegin_dynamic"):用于和 crtend.o 一起制作 Dynamic Executable。典型链接顺序:clang++ crtbegin_dynamic.o ... libc.so libm.so libdl.so crtend.o -o a.out ... -dynamic-linker,/system/bin/linker64 crtbegin_so.o("crtbegin_so"): crtbegin_so.o 和 crtend_so.o 用于链接生成 so 库,典型链接顺序:clang++ crtbegin_so.o ... libc.so ... crtend_so.o -o libxxx.so -shared -Wl,-soname,libxxx.so -Wl,--Bsymbolic-functions -Wl,--version-script,libxxx.riscv64.map crtend.o("crtend_android"):见上 crtbegin_static.o/crtbegin_dynamic.o 的描述。 crtend_so.o("crtend_so"):见上 crtbegin_so.o 的描述。

注意,这些 CRT 文件在 aosp 的构建中不会被添加到 system image 中,只是在构建过程中被链接器所使用。但这些 CRT 文件会跟着 ndk 发布,会随 ndk 发布的 crt 库文件包括:

crtbegin_dynamic.o crtbegin_so.o crtbegin_static.o crtend_android.o:注意这个其实就是 crtend.o, android 里给 crtend.o 换了一个名字,为了避免和 ndk 中的 gcc 自带的 crtend.o 发生冲突。 crtend_so.o


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3