没有标准库的准系统 C++?

Barebones C++ without standard library?

GCC 和 Clang 等编译器允许在没有 C++ 标准库的情况下编译 C++ 程序,例如使用 -nostdlib 命令行标志。好像这样的经常不行link你,例如:

void f() noexcept { throw 42; }
int main() { f(); }

通常由于 __cxa_allocate_exceptiontypeinfo for int__cxa_throw__gxx_personality_v0__clang_call_terminate、[= 等未定义符号而无法 link 18=]、std::terminate()

即使是简单的

int main() {}

无法 link

ld: warning: cannot find entry symbol _start; defaulting to 0000000000400120

并在执行时被 OS 杀死。使用 -c 编译器仍然运行 linker,它公然失败:

ld: error in mytest(.eh_frame); no .eh_frame_hdr table will be created.

在不使用和 link 标准库的情况下编写和编译 C++ 应用程序或库是否是一个现实的目标?如何在 Linux 上使用 GCC 或 Clang 编译我的代码?如果没有标准库,哪些核心语言功能将无法使用?

您基本上会在 osdev.org 找到您所有问题的答案,但无论如何我都会给出一个简短的总结。

当你给 GCC -nostdlib 时,你说的是 "no startup or library files"。这包括:

  • crti.ocrtbegin.ocrtend.ocrtn.o。通常内核开发人员只关心实现 crti.ocrtend.o 并让 GCC 通过将 -print-file-name= 传递给链接器来提供 crtbegin.ocrtend.o。通常这些只是分别由 .init.fini 组成的存根,为 GCC 分别推送 crtbegin.ocrtend.o 的内容留出空间。这些文件是调用全局 constructors/destructors 所必需的。
  • 您无法避免链接 libgcc (the "low-level runtime library" (-lgcc) because even if you pass -nostdlib GCC will emit calls to its functions whenever you use it, leading to inexplicable linking errors for seemingly no reason. This is the case even when you're implementing/porting a C library
  • 您不需要 "need" libstdc++ 不,但通常内核开发人员需要它。 Porting a C library 那么从头开始实现 C++ 标准库是一项极其困难的任务。

由于您只想摆脱 "standard library",但保留 libc(在 Linux 系统上),您实际上只是在使用 C 库对 C++ 进行编程。当然,这没有错,你做你,但最终我不明白这一点,除非你打算开发内核。

必读:

OSDev's C++ page - If you really care about RTTI/exception support, it's more annoying to implement than it sounds。通常人们只是通过 -fno-rtti-fno-exceptions 然后担心它是否在线。

"Standard" 是用词不当。在这种情况下,它并不意味着 "the library (set of functions, classes etc) as defined by the C++ standard",而是 "the usual set of libraries and objects (compiled files in a certain format) gcc links with by default"。其中一些是大多数甚至所有程序运行所必需的。

如果您使用此标志,则您有责任提供任何缺失的功能。有几种方法可以这样做:

  1. 从默认集合中挑选出您的程序真正需要的库和对象。 (没有什么意义,因为结果很可能与默认的 link 标志完全相同)。
  2. 提供您自己的缺失功能实现。
  3. 通过编译器标志明确禁用您的程序未使用的语言功能。我知道有两个这样的特性:异常和 RTTI。这是必需的,因为编译器需要生成与异常相关的代码和 RTTI 信息,即使这些功能未明确使用在此模块