Memo/fix-zig-tsan-linux-scc-header

2 分钟阅读

修复 Zig TSan 找不到 linux/scc.h

用幂等 mise task 临时修补 Zig bundled libtsan 与新版 Linux headers 的兼容问题。

新版 Linux headers 上启用 Zig ThreadSanitizer,构建在项目代码编译前就失败:

error: sub-compilation of libtsan failed
lib/libtsan/sanitizer_common/sanitizer_platform_limits_posix.cpp:160:10:
error: 'linux/scc.h' file not found
#include <linux/scc.h>
         ^~~~~~~~~~~~~

不是项目代码的并发问题。Zig 的 -fsanitize-thread 首次构建时会从安装目录里的源码编译一份 ThreadSanitizer runtime,以 Zig 0.16.0 为例在 <zig-lib-dir>/libtsan/sanitizer_common/sanitizer_platform_limits_posix.cpp。这份 vendored libtsan 里有三行死代码:

#include <linux/scc.h>

unsigned struct_scc_modem_sz = sizeof(struct scc_modem);
unsigned struct_scc_stat_sz = sizeof(struct scc_stat);

Linux 内核删掉了废弃的 SCC amateur-radio 接口,linux/scc.h 也从 UAPI headers 里消失,滚动发行版先撞上。LLVM compiler-rt 已通过 #194116 移除这三处引用,但 Zig 用的是自己的快照,要等发布包同步才算修好。

补丁

仓库里建 .mise/tasks/fix/zig-tsan

#!/usr/bin/env bash
#MISE description="Patch Zig libtsan for Linux headers without linux/scc.h"
set -euo pipefail

if [ "$(uname -s)" != Linux ]; then
    echo "Zig libtsan scc.h patch is only needed on Linux"
    exit 0
fi

zig_exe=$(readlink -f "$(command -v zig)")
zig_lib_dir=${ZIG_LIB_DIR:-"$(dirname "$zig_exe")/lib"}
source_file="$zig_lib_dir/libtsan/sanitizer_common/sanitizer_platform_limits_posix.cpp"

if [ ! -f "$source_file" ]; then
    echo "error: Zig libtsan source not found: $source_file" >&2
    exit 1
fi

include='#include <linux/scc.h>'
modem='  unsigned struct_scc_modem_sz = sizeof(struct scc_modem);'
stats='  unsigned struct_scc_stat_sz = sizeof(struct scc_stat);'

remaining=0
for line in "$include" "$modem" "$stats"; do
    count=$(grep -Fxc "$line" "$source_file" || true)
    if [ "$count" -gt 1 ]; then
        echo "error: unexpected duplicate in Zig libtsan: $line" >&2
        exit 1
    fi
    remaining=$((remaining + count))
done

if [ "$remaining" -eq 0 ]; then
    echo "Zig $(zig version) libtsan is already compatible with current Linux headers"
    exit 0
fi

if [ "$remaining" -ne 3 ]; then
    echo "error: Zig libtsan is only partially compatible; refusing to modify it" >&2
    exit 1
fi
if [ ! -w "$source_file" ]; then
    echo "error: Zig libtsan source is not writable: $source_file" >&2
    exit 1
fi

sed -i \
    -e "\\|^$include\$|d" \
    -e "\\|^$modem\$|d" \
    -e "\\|^$stats\$|d" \
    "$source_file"

for line in "$include" "$modem" "$stats"; do
    if grep -Fxq "$line" "$source_file"; then
        echo "error: failed to remove obsolete Zig libtsan reference: $line" >&2
        exit 1
    fi
done

echo "Patched Zig $(zig version) libtsan: $source_file"
chmod +x .mise/tasks/fix/zig-tsan
mise run fix:zig-tsan

任务靠 command -v zigreadlink 定位 mise 当前选中的 Zig,不硬编码版本目录。三行目标代码必须各出现一次才动手:都在就补,都不在就输出 already compatible 直接退出,部分存在或重复则拒绝改。所以可以重复执行,Zig 同步上游修复后也不用删。

验证

清掉独立的 TSan 构建缓存,让 runtime 用修补后的源码重新编译:

rm -rf .zig-cache/tsan
mise run test:tsan

没有封装测试任务就重跑原来的 zig build,保留 -fsanitize-thread 或对应 build option。重新安装或切换 Zig 版本后 mise 会建新的工具链目录,需要再跑一次补丁。

参考

相关阅读

MemoArchLinux 编译 python-kornia-rsMemo命令行工具与实用技巧Memo修复 xfs.h 的 void 指针算术错误

讨论

邮箱用于身份识别和回复通知,并由 Waline 存储。