SerenityOS 的 LLVM/Clang 工具链补丁全解析:从系统搜索路径到 RISC-V 特性检测的 7 个移植要点

SerenityOS 的 LLVM/Clang 工具链补丁全解析:从系统搜索路径到 RISC-V 特性检测的 7 个移植要点 SerenityOS 的 LLVM/Clang 工具链补丁全解析从系统搜索路径到 RISC-V 特性检测的 7 个移植要点【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本指南以 Toolchain/Patches/llvm/ReadMe.md 为核心逐一拆解 SerenityOS 为将 LLVM/Clang 移植到自家内核与用户态环境而维护的 7 个补丁涵盖默认搜索路径、平台适配、共享库构建、profile 插桩、libc/libcabi 支持与 RISC-V CPU 特性检测。读完本文你将理解 SerenityOS 如何借助补丁体系让 Clang 既能在宿主机上交叉编译 SerenityOS 内核与应用又能在 SerenityOS 系统内部原生构建 Ports并能对照源码定位每一处改动背后的真实原因。背景为什么 SerenityOS 需要一套 LLVM 补丁SerenityOS 是一个从零开始构建的类 Unix 操作系统其工具链同时承担两类任务在宿主机上交叉编译 SerenityOS 自身以及在系统内通过 Ports 体系为 SerenityOS 构建第三方软件。由于 SerenityOS 的 LibC、动态链接器与 POSIX 子集与主流 Linux/BSD 存在差异LLVM/Clang 上游代码中许多默认按 Linux 处理的平台判断需要被显式地告知这里是 SerenityOS。这些差异被集中整理为 7 个补丁文件存放于 Toolchain/Patches/llvm/构建脚本 Toolchain/BuildClang.sh 会在编译前按序应用它们for patch in $DIR/Patches/llvm/*.patch; do patch -p1 $patch /dev/null done从脚本可以看到默认使用patch -p1逐个应用若以--dev模式构建则改用git am --keep-non-patch以保留提交元数据Toolchain/BuildClang.sh。脚本还会对已解压的 llvm-project 目录记录.patch.applied的 MD5 指纹只要补丁集有变化就会重新解压、重新打补丁避免增量状态下补丁状态错乱。补丁 0001为 Clang 增加 /usr/local 默认搜索路径对应文件0001-clang-Add-usr-local-to-the-default-search-path.patch该补丁修改clang/lib/Driver/ToolChains/Serenity.cpp向链接器命令行追加-L/usr/local/lib并在 Clang 系统头文件搜索路径中加入${SysRoot}/usr/local/include。其动机在补丁说明中写得很明确这些路径是构建 Ports 时用 Clang 找到已安装依赖所必需的。在 SerenityOS 的 Ports 体系中第三方软件被安装到/usr/local包括/usr/local/include与/usr/local/lib与系统自带的/usr分区隔离。如果 Clang 默认只搜索/usr/include与/usr/lib那么 Ports 编译时便无法找到先前通过 Ports 安装的库头文件与链接库。补丁通过两处精确改动解决链接阶段CmdArgs.push_back(-L/usr/local/lib);追加在--pop-state之后确保链接器在搜索系统库之前能看到/usr/local/lib编译阶段addSystemInclude(DriverArgs, CC1Args, concat(D.SysRoot, /usr/local/include));被插入到addSystemInclude(..., concat(D.SysRoot, /usr/include))之前且遵守-nostdlibinc的跳过语义。从源码结构看这两处改动都位于 Clang Driver 中专用于 SerenityOS 的Serenity.cpp工具链实现中说明 SerenityOS 在 Clang 中已有独立的三元组triple支持如x86_64-serenity补丁只需补齐该工具链类的默认路径。补丁 0002为在 SerenityOS 上构建 LLVM 添加平台适配对应文件0002-llvm-Add-support-for-building-LLVM-on-SerenityOS.patch这是让 LLVM 自身能在 SerenityOS 环境里编译并运行的核心补丁共修改 6 个文件覆盖四种平台差异1. wait4 缺失的桩实现SerenityOS 不支持查询子进程的资源使用信息因此没有wait4。补丁在 [llvm/lib/Support/Unix/Program.inc] 中仿照 AIX 的做法为 SerenityOS 声明并实现了一个llvm::sys::wait4内部直接转调::waitpid(pid, status, options)忽略rusage参数#ifdef __serenity__ pid_t (llvm::sys::wait4)(pid_t pid, int *status, int options, struct rusage*) { return ::waitpid(pid, status, options); } #endif这样 LLVM 的sys::Wait逻辑无需改动即可在 SerenityOS 上编译。2. Orc 禁用 POSIX 共享内存SerenityOS 尚未支持 POSIX shm因此补丁在两处MemoryMapper.cpp与ExecutorSharedMemoryMapperService.cpp把预处理器条件从LLVM_ON_UNIX !defined(__ANDROID__)扩展为同时排除__serenity__使共享内存映射路径在 SerenityOS 上被编译掉。3. 增大默认线程栈到 4MiBSerenityOS 给每个线程默认分配 1MiB 栈这对 LLVM 的部分应用如大型编译任务偏小。补丁在HandleLLVMOptions.cmake中为SERENITYOS分支增加链接器参数elseif(SERENITYOS) # SerenityOS sets a very low default stack size value, so increase it to 4MB manually. set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size4194304) endif()即通过-Wl,-z,stack-size4194304将可执行文件默认栈大小提升到 4MB。4. 字节序与文件系统挂载判断在 [llvm/include/llvm/ADT/bit.h] 的平台列表中追加defined(__serenity__)使bit.h使用endian.h获取字节序信息在 [llvm/lib/Support/Unix/Path.inc] 中将__serenity__加入使用f_flag而非f_flags的平台宏列表并在is_local_impl中让 SerenityOS 直接返回false——因为 SerenityOS 尚不支持远程文件系统挂载无需查询挂载类型。值得注意的是本补丁2022 年提交与LLVMConfig.cmake中set(RUNTIMES_${target}_CMAKE_SYSTEM_NAME SerenityOS)配合说明 SerenityOS 已通过 CMake 的SERENITYOS变量识别自身平台Toolchain/CMake/LLVMConfig.cmake。补丁 0003构建共享 libLLVM 与 libClang对应文件0003-tools-Support-building-shared-libLLVM-and-libClang-f.patchSerenityOS 需要libLLVM.so与libclang.so这类共享库形态LLVMConfig.cmake中设置了LLVM_BUILD_LLVM_DYLIB ON与LLVM_LINK_LLVM_DYLIB ON见 Toolchain/CMake/LLVMConfig.cmake。该补丁从两个层面保证共享库能正确生成--whole-archive全量归档在 [llvm/tools/llvm-shlib/CMakeLists.txt] 中将--whole-archive组合归档参与共享库构建的LIB_NAMES时跳过SERENITYOS原有排除列表为 Solaris ld、MINGW、CYGWIN确保共享库成员来自全部所需静态库禁用符号版本脚本在 [HandleLLVMOptions.cmake] 中将SERENITYOS加入LLVM_HAVE_LINK_VERSION_SCRIPT 0的平台列表与 APPLE、CYGWIN、AIX 并列。原因是 SerenityOS 的加载器不支持符号版本化生成携带 ELF 版本节.gnu.version*的库只会浪费空间。补丁 0004启用 profile 插桩InstrProfiling对应文件0004-compiler-rt-Enable-profile-instrumentation-for-Seren.patch该补丁让 SerenityOS 获得与 Linux 等平台相近的-fprofile-instr-generate插桩能力改动分三层CMake 开关在 [compiler-rt/cmake/config-ix.cmake] 的平台白名单中加入SerenityOS使COMPILER_RT_HAS_PROFILE为真从而构建libclang_rt.profile运行库运行库平台文件在 [compiler-rt/lib/profile/InstrProfilingPlatformLinux.c] 的预处理器条件中加入defined(__serenity__)复用 Linux 版插桩实现同时在 [InstrProfilingPlatformOther.c] 中反向排除__serenity__避免两套实现冲突驱动侧链接在 [clang/lib/Driver/ToolChains/Serenity.cpp] 的链接任务中当ShouldLinkCompilerRuntime为真时调用TC.addProfileRTLibs(Args, CmdArgs)把libclang_rt.profile.a自动加入链接。补丁还向 Clang 的 driver 测试 [clang/test/Driver/instrprof-ld.c] 添加了针对x86_64-pc-serenity的 FileCheck 用例验证静态链接与-shared两种场景下都会链接到libclang_rt.profile.a——这是移植代码同时配套回归测试的典型做法。该能力与LLVMConfig.cmake中RUNTIMES_${target}_COMPILER_RT_BUILD_PROFILE ON的配置相互印证Toolchain/CMake/LLVMConfig.cmake。补丁 0005libc 对 SerenityOS 的适配对应文件0005-libcxx-Add-support-for-SerenityOS.patchlibc 是 SerenityOS 选择的 C 标准库实现LLVM_ENABLE_RUNTIMES中包含libcxx见 Toolchain/CMake/LLVMConfig.cmake。该补丁向 libc 声明 SerenityOS 的 LibC 具备哪些能力核心是新增 273 行的 [libcxx/include/__locale_dir/support/serenity.h]并做四处联动1. 全新的 locale 支持层serenity.h是 libc locale 基础 API 的 SerenityOS 实现定义了__locale_guard通过uselocale保存/恢复线程 locale、__newlocale/__freelocale/__setlocale、字符转换__toupper/__tolower、宽字符分类__iswctype等、多字节转换__mbrtowc/__wcsnrtombs等与格式化__snprintf/__asprintf等一系列内联封装。这些函数大多通过__locale_guard临时切换 locale 后调用对应的 C 函数。这套 API 之所以可行是因为 SerenityOS 的 LibC 已实现相应的 locale 基础设施newlocale/uselocale/freelocale定义于 Userland/Libraries/LibC/locale.cppnl_langinfo_l定义于 Userland/Libraries/LibC/langinfo.cpplocale_t类型见 Userland/Libraries/LibC/locale.h。补丁同时把serenity.h注册进 [libcxx/include/CMakeLists.txt] 的安装文件列表并在 [locale_base_api.h] 的平台分支中通过#elif defined(__serenity__)引入。2. 使用 pthread 实现多线程在 [libcxx/include/__config] 中__serenity__被加入启用_LIBCPP_HAS_THREAD_API_PTHREAD的平台列表声明 SerenityOS 的多线程能力由 pthread 库提供。这与LLVMConfig.cmake中显式关闭LIBCXX_HAS_PTHREAD_LIB/LIBCXXABI_HAS_PTHREAD_LIB的硬编码自动检测结果策略并不矛盾——后者针对的是交叉编译时对尚未构建完成的 sysroot 的探测保护Toolchain/CMake/LLVMConfig.cmake。3. 使用 libc 内建字符类型表__serenity__同时被加入_LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE平台列表即 libc 使用自己内建的字符类型表而非 LibC 提供的表。补丁说明给出原因要让 locale.cpp 的其余部分正确使用 LibC 的字符类型表需要大量额外移植工作内建表是更务实的取舍。4. 禁用 catopen[libcxx/include/__locale_dir/messages.h] 中__serenity__被加入不定义_LIBCPP_HAS_CATOPEN的排除列表——SerenityOS 的 LibC 不提供消息目录message catalog接口。补丁 0006RISC-V 的 __init_riscv_feature_bits 实现对应文件0006-RISCV-Implement-__init_riscv_feature_bits-for-Sereni.patchSerenityOS 是同时支持 RISC-V 架构的操作系统LLVMConfig.cmake的目标列表包含riscv64-serenity构建目标含riscv64见 Toolchain/CMake/LLVMConfig.cmake。该补丁让 compiler-rt 的 RISC-V CPU 模型初始化在 SerenityOS 上可用在 [compiler-rt/lib/builtins/cpu_model/riscv.c] 中为__serenity__提供initRISCVFeature实现弱符号声明__get_riscv_feature_bits若存在则调用它填充__riscv_feature_bits与__riscv_cpu_model否则将长度置 0、vendor/arch/impl ID 置 0通过最高优先级构造函数__init_riscv_feature_bits_ctor调用__init_riscv_feature_bits(0)并借助FeaturesBitCached保证只初始化一次同时放宽 Clang 对 RISC-V 函数多版本FMV的 OS 限制诊断消息改为 only supported on Linux and SerenityOS[clang/lib/CodeGen/CodeGenFunction.cpp] 中的EmitRISCVMultiVersionResolver接受llvm::Triple::OSType::Serenity。这里提到的动态链接器提供的 magic 函数确实存在于仓库源码中SerenityOS 的动态链接器在 Userland/Libraries/LibELF/DynamicLinker.cpp 通过define_magic_function(__get_riscv_feature_bitssv, __get_riscv_feature_bits)注册该符号其实现位于 Userland/Libraries/LibELF/Arch/riscv64/ExtensionBitmask.cpp内部通过archctl(ARCHCTL_RISCV64_GET_CPU_INFO, ...)向内核查询 CPU 扩展位掩码与 CPU 型号。整个链条为编译器插桩 → compiler-rt 构造函数 → 动态链接器 magic 函数 →archctl系统调用 → 内核返回特性信息。补丁 0007libcabi 定义 __cxa_thread_atexit对应文件0007-libcxxabi-Define-__cxa_thread_atexit-on-serenity.patch__cxa_thread_atexit是 Itanium ABI 中用于thread_local变量析构的扩展接口在 glibc 中实现尚未成为 ABI 正式组成部分。SerenityOS 的动态链接器已支持线程局部存储的析构回调机制因此该补丁在 libcabi 的两处平台条件中追加defined(__serenity__)[libcxxabi/include/cxxabi.h]声明extern C int __cxa_thread_atexit(...)的宏条件从__linux__ || __Fuchsia__扩展为包含__serenity__[libcxxabi/src/cxa_thread_atexit.cpp]实际定义的编译条件同步扩展使 SerenityOS 链接到该实现。这保证了 SerenityOS 上thread_local对象在线程退出时能正确执行析构是 C 运行时完整性的关键一环。从代码注释可以推断该机制与 DynamicLinker.cpp 中注册的__create_new_tls_region、__free_tls_region、__call_fini_functions等 magic 函数共同构成完整的 TLS 生命周期管理。补丁之外完整的工具链构建链路理解 7 个补丁后再回看它们如何嵌入整体构建会更有全局感。Toolchain/BuildClang.sh 展示了完整流程依赖检查要求 ninja、cmake、GNU patch 与可用的 C/C 编译器若宿主机提供 LLD 则-fuse-ldlld加速链接Toolchain/BuildClang.sh下载与打补丁按固定 commitLLVM_COMMIT下载 llvm-project 压缩包校验 MD5 后解压并按序应用本文所述的 7 个补丁Toolchain/BuildClang.sh链接 LibC 头文件对x86_64、aarch64、riscv64三个架构分别调用Meta/CMake/link_libc_headers.cmake生成 sysrootCMake 配置与编译以Toolchain/CMake/LLVMConfig.cmake为缓存文件配置其中LLVM_TARGETS_TO_BUILD为X86;AArch64;RISCVLLVM_ENABLE_PROJECTS为llvm;clang;lld;clang-tools-extraLLVM_ENABLE_RUNTIMES为compiler-rt;libunwind;libcxxabi;libcxxToolchain/CMake/LLVMConfig.cmake安装与符号链接ninja install/strip安装到Toolchain/Local/clang/并为每个架构创建x86_64-serenity-clang、aarch64-serenity-clang、riscv64-serenity-clang等驱动别名同时生成携带--sysroot的*.cfg配置文件Toolchain/BuildClang.sh。补丁 0001 与 0003 正对应这套流程中在系统内构建 Ports与产出共享库两个需求其余补丁则分别对应 LibC 能力差异、profile 工具链与 RISC-V 平台特性——7 个补丁共同构成了 SerenityOS 移植 LLVM/Clang 的最小充分集。小结补丁修改对象核心要点0001clang DriverSerenity.cpp增加/usr/local/include、/usr/local/lib默认路径支撑 Ports 依赖查找0002LLVM Support/Orc/CMakewait4 桩、禁用 shm、默认栈 4MiB、字节序与文件系统判断0003llvm-shlib / CMake--whole-archive构建共享库禁用符号版本脚本0004compiler-rt clang Driver启用 InstrProfiling profile 插桩含 driver 回归测试0005libc新增support/serenity.hlocale 层pthread、内建 rune 表、禁用 catopen0006compiler-rt clang CodeGenRISC-V 特性检测对接动态链接器 magic 函数放开 FMV 限制0007libcabi定义__cxa_thread_atexit完善 thread_local 析构如需深入可对照阅读补丁源码、构建脚本 Toolchain/BuildClang.sh 与 CMake 配置 Toolchain/CMake/LLVMConfig.cmake并可在 Userland/Libraries/LibELF/DynamicLinker.cpp 与 Userland/Libraries/LibC/locale.cpp 中验证补丁所依赖的系统侧接口。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考