Libgcc

来自osdev
跳到导航 跳到搜索

GNU Compiler Collection在代码生成过程中使用了一个名为libgcc的特殊库,其中包含共享代码,每次重复这些代码都会降低效率,还包含辅助辅助程序例程和运行时支持。 其确切内容取决于特定的目标、配置,甚至取决于命令行选项。 GCC无条件地假设它可以安全地发出对它认为合适的 libgcc 符号的调用,因此GCC编译的所有代码都必须与 libgcc 链接。 当你链接到GCC时,默认情况下会自动包含该库,并且无需进一步操作即可使用它。 然而,由于显而易见的原因,内核通常不与标准用户空间libc链接,而是与-nodefaultlibs(由-nostdlib隐含)链接,这将禁用与libc和libgcc的自动链接。 这是一个问题,因为gcc仍然认为它可以使用 libgcc,你需要与之链接。

如何构建libgcc

正文: GCC Cross-Compiler

你需要在构建 GCC Cross-Compiler 时调用 all-target-libgccinstall-target-libgcc make target,以构建和安装 libgcc 以及你的交叉-编译器。 名为libgcc.a的静态库与其他特定于编译器的文件一起安装到你的编译器前缀中。 请注意,一些体系结构,如ARM,有多种类型的ABI和指令集:因此,这些目标需要多个版本的libgcc,具体取决于你使用的特定编译选项,它们都会进入编译器特定的子目录。

如何与libgcc链接

正文: Bare Bones

在将内核与编译器链接时,可以通过传递 -lgcclibgcc 链接。 除非通过了-nodefaultlibs选项(由-nostdlib隐含),否则不需要这样做。 例如:

i686-elf-gcc -T linker.ld -o myos.kernel -ffreestanding boot.o kernel.o -nostdlib -lgcc

请注意如何将 libgcc 安装对于编译器是已知的,但链接器不知道的特定于编译器的目录中。 因此,必须将编译器用作链接器,而不是直接调用ld,否则需要告诉链接器在哪里可以找到libgcc。 还要注意,你必须确保在链接时为机器提供编译选项(-mfoo-fbar等选项),否则可能会得到错误的libgcc。 如果你想知道 libgcc 的完整路径 (如果你坚持用 ld 而不是你的编译器链接),你可以这样做:

i686-elf-gcc $CFLAGS -print-libgcc-file-name

在使用-print libgcc file name选项时,当然也需要传递机器编译选项。 使你的构建脚本或Makefile位于libgcc,而不是在某个地方硬编码路径,否则其他人将更难构建你的操作系统。

常见问题

我什么时候需要链接到libgcc=

所有用GCC编译的代码都必须链接libgcc

我和libgcc链接,什么都没有改变?

编译器不需要发出对libgcc的调用,在某些平台上,libgcc是一个小型库,你也可以编写不需要libgcc的复杂程序。 编译器将只链接libgcc中需要的特定部分,因为它是一个静态库。 但是,编译器可以自由地改变主意,并突然发出对 libgcc 的调用,总会有一些突发情况 (或者你将编译器升级到较新的版本)。

我没有链接到libgcc,它还能工作吗

请参阅上面的问题。 编译器不在乎你是否实际与 libgcc 链接,它将发出相同的调用。 如果链接到libgcc,则永远不会出现与libgcc相关的链接错误,这很简单,并且使内核编译过程可靠。

可以这样想:你进入编译器源代码,随机注释掉几个随机选择的函数。 编译器仍然碰巧为你的内核工作,但它将始终为你可以编写的所有可能的内核源代码工作吗? libgcc库是编译器的一部分,不安全地禁用编译器的某些部分是愚蠢的。

我可以使用Linux现有libgcc吗?

你必须使用交叉编译器附带的正确的 libgcc。 你发现的任何其他libgcc都可能有不同的目标,使用不同的机器编译选项构建,依赖于标准库,是不同编译器版本的一部分(甚至你的发行版可能已经修补了其gcc)。 使用不同的libgcc可能会起作用,但可能不可靠。

我可以将所需的libgcc函数复制到操作系统中吗?

你真的不应该。 这些函数是特定于编译器版本的,当你升级编译器时,所需的函数集可能会发生变化。 你将和编译器开发人员玩起追逐游戏,如果你的其他贡献者使用不同的编译器版本,你可能会遇到更多麻烦。 还有一个问题是,你是否已经复制了足够多的libgcc,如果你没有全部复制,事情将无法可靠地运行。

我能否重新实现libgcc?

你真的不应该。 请看上面的问题。 这需要大量的工作,而且非常棘手,你可能会得到一个低效的64位除法函数,它并不能对所有的值都正常工作。 仅与 libgcc 链接并进行处理要简单得多。

libgcc是否有依赖关系

通常不会,这里用于教程的-elf目标肯定不会。 如果你有一个托管的用户空间,它可能会启用自身的其他部分,并具有依赖于在系统标头中找到的特定用户空间功能的特殊符号。 这些是你希望在内核标准库中实现的常见函数。 某些依赖项仅在使用触发它们的相关libgcc部件时才相关。

libgcc是什么许可证?

libgcc库在GNU GPL加上the GCC Runtime Library 例外下获得许可 (请参见gcc源代码树中的COPYING.RUNTIME)。 这大致意味着,只要你使用非专有版本的GCC,你就可以将libgcc链接到你的软件中,即使它通常会违反GNU GPL。 这并不奇怪,这样的授权代码与GCC创建的所有东西都有关联,尤其是用户空间程序,由于运行库例外,你可以合法地使用GCC编译和分发专有程序。

Libgcc太大了!

其实不是。 静态存档包含很多对象文件,每个文件都包含调试符号。 如果你尝试剥离它,你会发现归档变得相当小。 此外,它甚至还没有链接,很多信息只是文件格式和链接信息的开销。 最后,它是一个静态存档,这意味着只有相关的部分才能真正链接到二进制文件中。

如果我小心回避Libgcc呢?

在某些平台上,除特定情况外,编译器不会为大多数常见代码生成对 libgcc 的调用。 但我敢打赌,你还没有真正阅读过GCC的源代码,并找到了发出libgcc调用的确切规则。 更新编译器时,规则可能会更改。 事实上,明天可能是周二。(译者注:指美国历史上星期二突然爆破的股灾) 该库提供了有用的功能,并且很容易链接到它并享受这些功能。 如果规则发生变化,并且你无法使用新的编译器构建内核,但你修复了内核,那么你仍然无法构建操作系统的旧版本 (这对于调试或历史目的可能是有用的)。

我不想要它,因为我想要完全掌控===

你并不是在编写你正在使用的编译器。 考虑一下,让不重复发明的做法-从这里开始。

我真的不想要libgcc

上面的规则是:

所有使用gcc编译的代码都必须与 libgcc 链接。

但没关系,你可以改变gcc的定义,去掉这个规则。 修改GCC源代码,使其永远不向libgcc发出调用(可能是-ffreestanding),而是直接生成代码。 那你就不用和它链接了。

我在用clang

这是一篇关于GCC的文章,如果你正在使用另一个编译器,你需要做不同的事情。

我看到有人没有和libgcc链接!

向当地打击编译器犯罪警察报案。(译者注:这里是开玩笑,指这样的事情不太可能发生,或者即使读者以为发生了其实也是误解了吧。)

另见

文章