目标文件损坏 file truncated 速查:中断写入、NFS 一致性与删重建
更新时间:2026-10-24。FAQ 速查页,面向链接阶段被
file truncated、file not recognized拦下的同学。这类报错的本质是:链接器(ld)打开某个.o或.a时,发现它根本不是完整的 ELF 文件——要么写到一半被掐断,要么被另一个进程改过,要么干脆是另一种格式。病因收敛在六根轴上:编译被 Ctrl+C 或 OOM 杀掉留下半截目标文件、make -j下两个规则同时写同一个输出、NFS 挂载目录的缓存一致性延迟导致读到旧内容、磁盘写满把文件截成零头、交叉编译混进宿主格式的.o、以及 ccache 缓存了坏条目。顺一遍基本必中。如果是磁盘满导致的写入失败,可配合看 make 磁盘写满速查。
本文要回答的问题
file not recognized: file truncated是哪个文件坏了,怎么定位?- 为什么并行编译比串行更容易出 truncated?
- 代码在 NFS 共享盘上,明明重编了为什么还报同样的错?
make clean后就好了,能不清全量吗?- 交叉编译时报 file format not recognized,是文件坏了吗?
一、报错原文与典型现象
报错几乎总是出现在链接阶段(路径以 /proj/demo 占位):
# 典型形态一:文件被截断
/usr/bin/ld: /proj/demo/build/net.o:
file not recognized: file truncated
collect2: error: ld returned 1 exit status# 典型形态二:格式不认(混进别的架构的 .o)
/usr/bin/ld: /proj/demo/build/util.o:
file not recognized: file format not recognized# 典型形态三:静态库里混入坏成员
/usr/bin/ld: /proj/demo/build/libcore.a(parser.o):
relocations in generic ELF (EM: 62)读现象先做两件事:一是从报错行抄下具体文件名——ld 会明确告诉你是哪个 .o 或 .a 出问题;二是用 file 命令看这个文件,file build/net.o 输出「data」或「empty」就是坏了,输出「ELF 64-bit LSB relocatable, x86-64」而你的目标是 ARM 就是格式混了。truncated 与 format not recognized 的处置分叉就在这一步。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| file truncated,串行编译也偶发 | 上次编译被 Ctrl+C / OOM 杀掉,留了半截 .o | file 看该文件是否非 ELF | 删掉该 .o 重编;排查内存与中断原因 |
| file truncated,只在 make -j 下出现 | 两条规则写同一个输出,并行互踩 | 串行 make 能过、并行复现 | 修 Makefile:一个输出只属于一条规则 |
| 重编后仍报同一个文件坏 | NFS 缓存一致性延迟,读到旧句柄 | ls -l 看 mtime 是否新鲜;换本地盘试 | 本地盘构建;或 sync 后重试、检查挂载参数 |
| 多个 .o 同时 truncated | 磁盘写满,编译器写到一半失败 | df -h 看构建分区 | 清理磁盘,删受损 .o 重编 |
| file format not recognized | 交叉编译目录里混进宿主编译的 .o | file 看 EM 字段是否目标架构 | 分架构分目录构建,清干净重编 |
| 清缓存前必现、清后消失 | ccache 缓存了损坏/错架构的编译结果 | ccache -s 后 CCACHE_DISABLE=1 make | 清 ccache,升级版本,交叉场景设对缓存键 |
三、排查决策图
下面这张图是标准排查路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓分水岭:file 命令的输出直接决定走左支还是右支——不是 ELF(data/empty/大小明显偏小)就是写中断或互踩,走删重建加查中断源;是 ELF 但机器类型(EM 字段)对不上就是格式混入,走分架构清理。两支最后都汇到「同一命令重跑链接通过」这个验证点。
四、逐条排查展开
中断残留是最常见的 truncated 来源。 编译器写 .o 不是原子的:先创建文件、再陆续写节区,写到一半进程被杀(Ctrl+C、OOM Killer、SSH 断连导致 SIGHUP),磁盘上就留下一个头部残缺或节表不全的文件。下次 make 看到 .o 的 mtime 比 .c 新,认为它是最新的,直接跳过编译进链接——ld 打开一读,格式不对,报 truncated。验证:file build/net.o 输出不是 ELF 就实锤。处置:删掉这个 .o 重跑 make 即可;顺手查一下上次编译为什么中断,dmesg 看有没有 OOM 记录。
并行互踩是 make -j 专属剧本。 Makefile 里两条规则生成同一个文件(典型:.o 规则与生成代码规则都写 gen.c、或多个目标共用中间文件),串行时后写的覆盖先写的,结果碰巧可用;并行时两个编译进程同时打开同一个文件写,内容交织成一堆垃圾,ld 读出来就是 truncated 或 format not recognized。判据很简单:make -j8 稳定复现、make -j1 永远不过。根治是改 Makefile:一个输出文件只由一条规则负责,共享中间产物用独立的 stamp 文件做接力,不要图省事复用文件名。
NFS 上「重编了还报同一个文件坏」是缓存一致性在作怪。 NFS 客户端默认对文件属性和内容做缓存,编译机在目录里删了坏 .o 重写了新的,链接进程(尤其在另一台挂载同一份代码的机器上)可能仍读到缓存里的旧版本甚至过期文件句柄(stale file handle)。验证:ls -l --full-time 看 mtime 是否真的更新了;把构建目录挪到本地盘重跑,如果报错消失,就是 NFS 一致性问题。处置:构建目录尽量放本地盘,NFS 只放源码;必须在 NFS 上构建时,检查挂载参数(actimeo、noac),必要时构建前 sync。
磁盘写满会把一批 .o 同时截断。 特征是一次报错里躺着好几个坏文件,且 df -h 显示构建分区 100%。编译器对写失败的处理各版本不一,有的报错退出、有的留下半截文件退出码仍是 0(极少见但存在),make 于是认为编译成功。处置:清磁盘 → 把 mtime 异常新但体积异常小的 .o 全删掉(find build -name '*.o' -size -1k 是个粗筛)→ 重编。详细盘面排查见 make 磁盘写满速查。
format not recognized 多半是架构混编,不是文件坏。 交叉编译 ARM 目标时,file util.o 显示「x86-64」——说明这个 .o 是之前用宿主 gcc 编出来的,切换工具链时没清干净。也可能把 .c 文件或文本文件误当 .o 传给 ld(比如变量拼错把源文件列表拼进了链接行)。处置:不同架构的构建目录严格分开(如 build/host/ 与 build/arm/),切换工具链必清目录;检查链接命令行里有没有混进非目标文件。
静态库里的坏成员会把问题藏得更深。 报错形态是 libcore.a(parser.o): relocations in generic ELF 或库级 truncated——坏的不是某个散落的 .o,而是已经被 ar 打进 .a 的某个成员。此时删对应的 .o 还不够,.a 里那份副本还是坏的:必须先删 .o、再删整个 .a(或 ar d 剔除坏成员),让归档步骤整体重做。懒人一刀切:find build -name '*.a' -delete 加重编,归档通常很快,代价可接受。
ccache 中毒是偶发 truncanted 的隐藏来源。 缓存条目损坏(磁盘抖动、并发写旧版本 bug)或缓存键没区分架构时,ccache 会把坏结果直接回填。判据:CCACHE_DISABLE=1 make 一切正常。处置:ccache -C 清缓存,交叉编译场景确认 CCACHE_BASEDIR、编译器哈希包含目标三元组,老版本升级。
五、规范做法与预防
- 构建目录分架构:
build/<arch>/一级分开,换工具链先清对应目录,杜绝格式混编。 - 一个输出一条规则:Makefile 里任何文件只能出现在一条规则的 target 里;共享中间产物用 stamp 文件接力。
- 中断后清残留:CI 脚本里编译失败后自动删本次新产生的
.o(按 mtime 筛),避免半截文件毒化下一次构建。 - 本地盘构建:源码可在 NFS,构建产物目录(
build/、CMakeFiles)一律本地盘,避开一致性问题还提速。 - 链接前快速体检:CI 里对产物目录跑一遍
find build -name '*.o' -exec file {} + | grep -v ELF,有输出即失败前置。 - 磁盘水位告警:构建分区超过 85% 就告警,别等写满截断一批
.o才发现。
这一篇在知识体系里的位置
本篇是 Make/CMake 排错速查簇的「文件完整性」入口篇:并行写同一输出的 Makefile 病根深挖看 make 偶发不确定报错速查;磁盘写满的全套处置看 make 磁盘写满速查;报错发生在链接但文件本身没坏、只是符号对不上时看 make undefined symbol 速查。「file 定性 → 删受损重建 → 查中断/互踩/一致性根源」这条主线对一切二进制文件损坏类报错通用。
一句话总结
file truncated 与 file not recognized 先从报错行抄下文件名,用 file 命令一锤定性:不是 ELF 就是写中断、并行互踩、磁盘满或 NFS 读到旧内容——删掉受损 .o 重编,再回头查中断源和 Makefile 里「一个输出多条规则」的暗坑;是 ELF 但架构不符就是工具链混编,分目录清干净重编。NFS 上构建把产物目录挪本地盘,ccache 嫌疑用 CCACHE_DISABLE=1 排除。中断即清残留、输出与规则一一对应、链接前 file 体检入库,这类报错基本绝迹。