下载页写着 32 位,文件落到 Raspberry Pi 上,安装程序却用一条并不指出问题任何有用部分的提示失败了。两个完全不同的可执行文件在 ELF 意义上都算 32 位,而其中只有一个能在这块板子上运行。class 字段是一件事,machine 字段是另一件事,文件名可靠地两者都不告诉你。在更换库或尝试另一个镜像之前,先读头部。

class 说字长,machine 说指令集

Linux ELF 参考手册在文件头中定义了 class 字段和 machine 字段,两者回答的是不同问题。class 说明文件使用 32 位还是 64 位形式;machine 说明它是为哪套指令集构建的。32 位的 x86 构建和 32 位的 ARM 构建在第一项上一致,在第二项上不同。于是,一个承诺正确字长的标签仍然可能送来处理器无法解码的代码。第三个值得读取的值是所请求的解释器,当二进制文件声明了解释器时,程序头会写明它。

readelf -h -- ./vendor-vpn
readelf -l -- ./vendor-vpn
readelf -d -- ./vendor-vpn | grep NEEDED

第一条命令打印头部,第二条在文件有程序头时打印程序头,第三条列出加载器将需要的共享对象。把 class、machine 和解释器一起记下来,因为这三个值就是这一层的全部兼容性问题。三者中任何一个都不会出现在下载按钮上,所以文件必须被检查,而不是被描述。

它向内核请求哪个解释器

声明了解释器的可执行文件会向内核请求一个特定的加载器路径。当那个加载器缺失时,即便你点名的文件就在那里,启动也会失败。人们正是在这里断定下载损坏,而真正的答案是发行版并不提供的加载器。

把加载器与你实际运行的系统对上:操作系统版本、用户空间架构,以及提供加载器的软件包。64 位内核只有在用户空间及其加载器都安装时才能运行 32 位用户空间,所以处理器能力并不是决定因素。一次简短检查就能在一行内写出解释器,比开支持工单更省时间。shell 脚本的失败方式又不同,因为它的解释器写在第一行而不是 ELF 头里,而读那一行不花什么代价。

file --brief ./vendor-vpn

把下载与软件包架构对上

发行版的软件包架构用它们自己的词汇编码了同样的区分,而供应商的支持声明指的正是那套词汇。把你包管理器报告的值与供应商标注的值作比较,而不是拿营销标签去比文件名。容易出问题的组合是 amd64 对 arm64,以及 armhf 对 armel,因为每一对在下载列表里看起来都可以互换。以 x64 或 ia32 结尾的文件名描述的是构建目标,而不是承载它的机器。

Label on the downloadELF classe_machineInterpreter the kernel wantsHow it fails on the wrong host
amd64ELF64x86-64/lib64/ld-linux-x86-64.so.2Immediate exec failure on an ARM board
armhfELF32ARM/lib/ld-linux-armhf.so.3Loads on a soft-float system, then fails on a float ABI call
i386ELF32Intel 80386/lib/ld-linux.so.2Exec format error where no 32-bit loader package exists

每一行都是一种情况:字长看起来对,代码却仍然出错;或者它能启动,却在下一层失败。把解释器那一列当作硬性要求而不是提示,因为内核会在你的任何代码执行之前解析那条路径。模拟是一项部署决定,会带来自己的性能与支持后果,而一块宣传更宽字长的处理器从不暗示它。

dpkg --print-architecture

ENOEXEC 与 ENOENT 是两种不同的失败

启动阶段会报告两种错误之一,它们指向不同的问题。读取错误值就能在运行其他任何东西之前对失败分类。

ENOEXEC 表示格式没有被识别:架构不对、class 不对,或者根本不是可执行文件。ELF 文件上的 ENOENT 通常表示解释器缺失,即便你请求的文件存在。同一个 ENOENT 也可能表示脚本的解释器缺失,所以脚本的第一行很重要。无论哪种情况,消息都点出了层级;把每个“未找到”都当成配置缺失,会把调查带向错误的方向。在工单里引用错误值,因为它比周围的句子更经得起转述。

二进制文件起不来时不该做什么

把来路不明的二进制文件硬塞进 shell 包装里并不能让它运行。不要对你不信任的文件随手跑依赖检查器,因为某些情况下运行它会执行二进制文件里的代码。不要添加加载器符号链接、替换宿主库,或从第三方镜像抓旧归档来绕过提示。这些做法都扩大了改动面,却没有触及头部已经点出的不匹配。

联系供应商时,发值而不是讲故事:操作系统版本、软件包架构、class 与 machine 字符串、所请求的解释器,以及第一行错误。这组信息比一张安装失败截图更快解决支持问题。旧产物和记忆中的论坛输出不是当前的支持矩阵;而一旦原生二进制文件加载成功,配置、认证与路由仍然是另一项工作。

相关文章