news 2026/9/12 16:22:36

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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++/libc++abi 支持与 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.cppExecutorSharedMemoryMapperService.cpp)把预处理器条件从LLVM_ON_UNIX && !defined(__ANDROID__)扩展为同时排除__serenity__,使共享内存映射路径在 SerenityOS 上被编译掉。

3. 增大默认线程栈到 4MiB

SerenityOS 给每个线程默认分配 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-size=4194304") endif()

即通过-Wl,-z,stack-size=4194304将可执行文件默认栈大小提升到 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.cmakeset(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.patch

SerenityOS 需要libLLVM.solibclang.so这类共享库形态(LLVMConfig.cmake中设置了LLVM_BUILD_LLVM_DYLIB ONLLVM_LINK_LLVM_DYLIB ON,见 Toolchain/CMake/LLVMConfig.cmake)。该补丁从两个层面保证共享库能正确生成:

  1. --whole-archive全量归档:在 [llvm/tools/llvm-shlib/CMakeLists.txt] 中,将--whole-archive组合归档参与共享库构建的LIB_NAMES时,跳过SERENITYOS(原有排除列表为 Solaris ld、MINGW、CYGWIN),确保共享库成员来自全部所需静态库;
  2. 禁用符号版本脚本:在 [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.cmakeRUNTIMES_${target}_COMPILER_RT_BUILD_PROFILE ON的配置相互印证(Toolchain/CMake/LLVMConfig.cmake)。

补丁 0005:libc++ 对 SerenityOS 的适配

对应文件:0005-libcxx-Add-support-for-SerenityOS.patch

libc++ 是 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.cpp,nl_langinfo_l定义于 Userland/Libraries/LibC/langinfo.cpp,locale_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)接口。

补丁 0006:RISC-V 的 __init_riscv_feature_bits 实现

对应文件:0006-RISCV-Implement-__init_riscv_feature_bits-for-Sereni.patch

SerenityOS 是同时支持 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_bits"sv, __get_riscv_feature_bits)注册该符号,其实现位于 Userland/Libraries/LibELF/Arch/riscv64/ExtensionBitmask.cpp,内部通过archctl(ARCHCTL_RISCV64_GET_CPU_INFO, ...)向内核查询 CPU 扩展位掩码与 CPU 型号。整个链条为:编译器插桩 → compiler-rt 构造函数 → 动态链接器 magic 函数 →archctl系统调用 → 内核返回特性信息。

补丁 0007:libc++abi 定义 __cxa_thread_atexit

对应文件:0007-libcxxabi-Define-__cxa_thread_atexit-on-serenity.patch

__cxa_thread_atexit是 Itanium ABI 中用于thread_local变量析构的扩展接口(在 glibc 中实现,尚未成为 ABI 正式组成部分)。SerenityOS 的动态链接器已支持线程局部存储的析构回调机制,因此该补丁在 libc++abi 的两处平台条件中追加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 展示了完整流程:

  1. 依赖检查:要求 ninja、cmake、GNU patch 与可用的 C/C++ 编译器;若宿主机提供 LLD 则-fuse-ld=lld加速链接(Toolchain/BuildClang.sh);
  2. 下载与打补丁:按固定 commit(LLVM_COMMIT)下载 llvm-project 压缩包,校验 MD5 后解压并按序应用本文所述的 7 个补丁(Toolchain/BuildClang.sh);
  3. 链接 LibC 头文件:对x86_64aarch64riscv64三个架构分别调用Meta/CMake/link_libc_headers.cmake生成 sysroot;
  4. CMake 配置与编译:以Toolchain/CMake/LLVMConfig.cmake为缓存文件配置,其中LLVM_TARGETS_TO_BUILDX86;AArch64;RISCVLLVM_ENABLE_PROJECTSllvm;clang;lld;clang-tools-extraLLVM_ENABLE_RUNTIMEScompiler-rt;libunwind;libcxxabi;libcxx(Toolchain/CMake/LLVMConfig.cmake);
  5. 安装与符号链接ninja install/strip安装到Toolchain/Local/clang/,并为每个架构创建x86_64-serenity-clangaarch64-serenity-clangriscv64-serenity-clang等驱动别名,同时生成携带--sysroot*.cfg配置文件(Toolchain/BuildClang.sh)。

补丁 0001 与 0003 正对应这套流程中"在系统内构建 Ports"与"产出共享库"两个需求,其余补丁则分别对应 LibC 能力差异、profile 工具链与 RISC-V 平台特性——7 个补丁共同构成了 SerenityOS 移植 LLVM/Clang 的最小充分集。

小结

补丁修改对象核心要点
0001clang Driver(Serenity.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 表、禁用 catopen
0006compiler-rt + clang CodeGenRISC-V 特性检测对接动态链接器 magic 函数,放开 FMV 限制
0007libc++abi定义__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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:15:37

VSCode原型设计:CodeBuddy与Craft智能体实战指南

1. 项目概述&#xff1a;当原型设计遇上代码编辑器最近在技术社区看到一个有趣的讨论&#xff1a;产品经理用VSCode写代码&#xff1f;这其实是个美丽的误会。真实情况是&#xff0c;越来越多的产品人开始用CodeBuddy这类工具在代码编辑器里直接绘制高保真原型图。这种跨界玩法…

作者头像 李华
网站建设 2026/9/12 16:14:59

RPA多店铺评论自动回复实战:从人工3小时到效率提升300%

1. 5个店铺、每天400条评论&#xff0c;这个项目是被逼出来的1.1 最绝望的瞬间&#xff1a;评论像瀑布一样往下刷先说个真实场景。你每天早上打开电脑&#xff0c;第一件事不是看数据大屏&#xff0c;而是逐个店铺点进TikTok Shop Seller Center&#xff0c;把视频评论、商品评…

作者头像 李华