SerenityOS 如何为 GCC 定制交叉工具链:`Toolchain/Patches/gcc` 8 个补丁全解析
SerenityOS 如何为 GCC 定制交叉工具链Toolchain/Patches/gcc8 个补丁全解析【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenitySerenityOS 是一个从零开始打造的类 Unix 操作系统其内核、LibC、LibC 以及大部分用户态程序都由自家交叉编译工具链构建。为了让 GCC 认识*-*-serenity这个全新目标平台仓库在 Toolchain/Patches/gcc 下维护了 8 个累计数百行代码的补丁配套说明文档为 Toolchain/Patches/gcc/ReadMe.md。读完本文你将掌握这些补丁各自解决了什么问题、改动了 GCC 的哪些子系统以及它们如何协同让 GCC、libgcc、libstdc 在 LibC 尚未就绪的情况下完成自举。背景为什么一个全新的操作系统需要给 GCC 打补丁GCC 对目标平台的支持并不是一个开关而是散落在config.gcc、config.host、libgcc/config.host、libstdc-v3/configure.host等一系列配置文件和运行时源码中的大量分支。一个没有移植过的*-*-serenity三元组会被 GCC 判定为不支持的平台无法生成可执行文件。与此同时SerenityOS 的构建流程存在一个先有鸡还是先有蛋的问题工具链必须在 LibC 之前构建。GCC 的libgcc_s默认链接-lclibstdc 配置阶段默认探测目标系统的dlopen、clock_gettime等能力这些在目标库还不存在时都会失败。于是 Serenity 采用补丁 手工告知的策略在配置脚本里显式声明 SerenityOS 支持什么、不支持什么并砍掉所有依赖目标侧库的环节。这套补丁的消费方是 Toolchain/BuildGNU.sh构建脚本会按序 apply 这些 patch 后编译交叉 GCC而日常构建入口则是 Documentation/BuildInstructions.md 中介绍的Meta/serenity.sh run——首次执行时会自动下载依赖并构建交叉工具链之后每次构建直接复用。0001为 GCC 添加 SerenityOS 驱动driver这是最核心的一个补丁0001-Add-a-gcc-driver-for-SerenityOS.patch它让 GCC 正式认识*-*-serenity目标涉及以下改动gcc/config.gcc中的通用分支为所有 serenity 目标设置gasyes与gnu_ldyes声明使用 GNU 汇编器与 GNU 链接器default_use_cxa_atexityes默认使用__cxa_atexit处理 C 静态析构extra_options${extra_options} serenity.opt引入目标专属选项文件对aarch64*-* | riscv64-* | x86_64-*三个架构开启default_gnu_indirect_functionyes即默认启用 IFUNC间接函数让 libc 可以按 CPU 特性选择最佳实现。针对三个架构的tm_file组合复用现有 ELF 基础配置并叠加 serenity 专属头文件x86_64-*-serenity*i386/unix.h i386/att.h elfos.h glibc-stdint.h i386/i386elf.h i386/x86-64.h serenity.h i386/serenity.haarch64*-*-serenity*elfos.h glibc-stdint.h aarch64/aarch64-elf.h serenity.hriscv64-*-serenity*elfos.h glibc-stdint.h riscv/elf.h serenity.h。gcc/config/serenity.h是目标描述的核心定义了TARGET_SERENITY宏供其他补丁如 0005做条件编译LINK_SPEC链接器参数例如非静态链接时传入-dynamic-linker /usr/lib/Loader.so指向 SerenityOS 的动态链接器STARTFILE_SPEC/ENDFILE_SPEC链接顺序crt0.o在最前随后按shared|static-pie|!no-pie条件选择crtbeginS.o/crtbegin.o与crtendS.o/crtend.oLIB_SPEC -lc只链接 C 标准库MATH_LIBRARY为空——SerenityOS 的数学函数内置于 LibC不再额外链接-lmCC1_SPEC -fno-semantic-interposition默认关闭语义插桩允许编译器对函数调用做更多优化TARGET_LIBC_PROVIDES_SSP声明 LibC 自带栈保护stack smashing protector支持TARGET_OS_CPP_BUILTINS预定义__serenity__、__unix__宏并断言systemserenity、systemunix、systemposix。gcc/config/i386/serenity.h则修正了 SysV 语义下的整数类型宽度SIZE_TYPE64 位为long unsigned int32 位为unsigned intPTRDIFF_TYPE64 位为long int32 位为int。gcc/config/serenity.opt声明了posix、pthread、rdynamic三个仅驱动层可见Driver的选项它们本身不生成编译参数只是让-pthread、-rdynamic等命令行开关能够被 serenity 驱动正确透传。0002为 SerenityOS 目标跳过 fixincludesfixincludes是 GCC 构建时的一个修正器它扫描系统头文件自动修补那些依赖 GCC 不支持扩展、或在 C 模式下会报错的写法。补丁 0002-fixincludes-Skip-for-SerenityOS-targets.patch 在fixincludes/mkfixinc.sh的case $machine in分支中新增了*-serenity* |一行与其他已知无此问题的目标如i?86-*-cygwin*、*-mingw32*并列。文档原话解释了原因SerenityOS 的头文件没有这类问题因此这个 hack 对它毫无用处直接跳过还能避免在交叉编译环境下误伤自家头文件。这是典型的能砍就砍策略——工具链自举期间任何多余的探测与改写都可能因为目标库缺失而失败。0003让 libgcc 为 SerenityOS 构建libgcc 是 GCC 自带的 C 运行时库包含异常展开unwinding、软浮点、crt 启动对象等。补丁 0003-libgcc-Build-for-SerenityOS.patch 在libgcc/config.host中为riscv64-*-serenity*、x86_64-*-serenity*、aarch64-*-serenity*分别配置了extra_parts需要构建的 crt 对象包括crti.o、crtbegin.o、crtbeginS.o、crtend.o、crtendS.o、crtn.oaarch64 额外包含crtfastmath.otmake_file引入t-crtstuff-pic、t-libgcc-pic、t-slibgcc、t-eh-dw2-dip等片段开启 PIC 版本 crt 与 DW2 异常展开的 DIPdl-iterate-phdr路径。补丁还同步修改了gcc/configure把*-serenity*加入gcc_cv_target_dl_iterate_phdryes的名单与*-linux-musl*并列并在libgcc/unwind-dw2-fde-dip.c中为__serenity__目标启用USE_PT_GNU_EH_FRAME——这意味着 GCC 异常展开依赖 SerenityOS 动态链接器提供dl_iterate_phdr通过.eh_frame_hdr查找 FDE。0004禁止 libgcc_s 链接 LibC补丁 0004-libgcc-Do-not-link-libgcc_s-to-LibC.patch 只有一行实质改动在libgcc/config/t-slibgcc中删除SHLIB_LC -lc。原因非常直白工具链构建先于 LibC 存在如果共享版 libgcc 在生成时强制链接-lc链接器会因找不到目标系统的 C 库而失败。移除后libgcc_s 不再携带对 LibC 的硬性依赖运行时由最终可执行文件按需链接 LibC从而打破自举顺序上的死锁。0005i386 目标默认关闭 math errno补丁 0005-i386-Disable-math-errno-for-SerenityOS.patch 在gcc/common/config/i386/i386-common.cc的ix86_option_init_struct中利用#ifdef TARGET_SERENITY正是 0001 定义的那个宏把x_flag_errno_math置 0。其效果等同于默认开启-fno-math-errnoGCC 可以假设数学库函数不通过errno报告错误从而对sin、exp等调用做更激进的常量折叠与优化。文档指出这是因为SerenityOS 用异常而非errno处理数学错误——这既是性能优化也是平台语义对齐。0006让 libstdc 适配 SerenityOSlibstdc 的配置期会探测目标系统能力但构建工具链时 SerenityOS 库尚不存在无法运行测试程序。补丁 0006-libstdc-Support-SerenityOS.patch 的策略是手工告知 大部分走 Newlib 代码路径具体包括libstdc-v3/configure.host新增serenity*) os_include_diros/newlib直接复用 Newlib 的 OS 适配层头文件acinclude.m4/crossconfig.m4把serenity*并入freebsd*|netbsd*|dragonfly*|rtems*分支声明支持clock_monotonic、clock_realtime、nanosleep、sched_yield同时把serenity*加入openbsd*分支令 locale 采用newlib实现configure为serenity*声明lt_cv_dlopendlopen且lt_cv_dlopen_libs——dlopen由 SerenityOS 的 LibC/动态链接器直接提供无需链接-ldl并仿照avr*-*-*分支为*serenity*定义HAVE_ACOSF、HAVE_ASINF等一组数学函数宏表示这些浮点函数由目标 LibC 提供。0007libstdc 静态库改用 -fPIC 构建补丁 0007-libstdc-Build-static-library-with-fPIC.patch 修改了libstdc-v3/configure中enable_shared ! yes分支的两行glibcxx_lt_pic_flag-prefer-picglibcxx_compiler_pic_flag$lt_prog_compiler_pic_CXX原版构建系统在静态构建时强制 no-pic/pie而 SerenityOS 需要把libstdc.a静态链接进 LibC 与共享对象这就要求其中的代码必须是 PIC位置无关代码。文档注明该 hack 源自 GCC 上游 bugzilla 的 58638 号报告对 no-pic 强制行为的已知问题。有了这处改动libstdc 静态库可以被安全地链入任何共享库而不会引入重定位问题。0008RISC-V 目标通过动态链接器魔法函数填充特性位补丁 0008-RISC-V-Implement-__init_riscv_feature_bits-for-Seren.patch 针对 RISC-V 的 CPU 特性探测。GCC 的libgcc/config/riscv/feature_bits.c原本只在__linux__下通过riscv_hwprobe系统调用填充__riscv_feature_bits与__riscv_cpu_model补丁新增__serenity__分支以__attribute__((weak))声明外部函数__get_riscv_feature_bits(void*, void*)若该符号存在动态链接器已注入则调用它填充__riscv_feature_bits和__riscv_cpu_model否则将length置 0、厂商/架构/实现 ID 全部清零走不支持的保守路径。这个魔法函数并非凭空约定——SerenityOS 的动态链接器确实提供了它。在 Userland/Libraries/LibELF/DynamicLinker.cpp 中可以找到define_magic_function(__get_riscv_feature_bitssv, __get_riscv_feature_bits)的注册调用特性位的数据结构与位掩码定义则位于 Userland/Libraries/LibELF/Arch/riscv64/ExtensionBitmask.h 与 ExtensionBitmask.cpp。可见补丁与系统侧实现是一一对应的契约关系。补丁全景一张表看懂 8 个补丁的分工补丁子系统一句话作用0001gcc driver让 GCC 认识*-*-serenity定义链接/启动/宏/类型/IFUNC 行为0002fixincludes跳过系统头文件修正器避免误伤自家头文件0003libgcc为三架构配置 crt 对象与 DW2 异常展开支持0004libgcc移除SHLIB_LC -lc打破先有 LibC 还是先有工具链的死锁0005i386 后端默认-fno-math-errno配合 SerenityOS 的异常式数学错误处理0006libstdc手工声明 LibC 能力复用 Newlib 适配层完成交叉配置0007libstdc静态库以-fPIC构建便于静态链入 LibC/共享对象0008libgcc (RISC-V)通过动态链接器魔法函数填充 CPU 特性位小结从补丁看自举工具链的设计思路把这 8 个补丁放在一起可以清晰地看到 SerenityOS 交叉工具链自举的三条原则能跳则跳0002、0004所有依赖目标侧已有库的环节在自举阶段一律砍掉或显式放行能复用则复用0003、0006libgcc 的 crt/异常展开配置、libstdc 的 Newlib 适配层都尽量贴近成熟平台的既有代码路径只做最小增量系统侧契约闭环0001、0005、0008目标描述宏TARGET_SERENITY/__serenity__、数学错误语义、动态链接器魔法函数都需要 GCC 补丁与内核/用户态侧实现严格对应例如 Userland/Libraries/LibELF/DynamicLinker.cpp 中__get_riscv_feature_bits的注册即是 0008 的落地证据。如果你对构建流程的入口感兴趣可继续阅读 Toolchain/BuildGNU.sh补丁的实际应用脚本与 Documentation/BuildInstructions.md从零构建 SerenityOS 的完整步骤。理解这 8 个补丁也就理解了为一个新操作系统移植编译器这条路上的典型障碍与标准解法。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考