VPN 安装程序说 32 位:不如直接检查 ELF 头
字长并不能标识指令集。按流程读取 class、machine 与解释器,再去索取与主机匹配的构建。
下载页写着 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 download | ELF class | e_machine | Interpreter the kernel wants | How it fails on the wrong host |
|---|---|---|---|---|
| amd64 | ELF64 | x86-64 | /lib64/ld-linux-x86-64.so.2 | Immediate exec failure on an ARM board |
| armhf | ELF32 | ARM | /lib/ld-linux-armhf.so.3 | Loads on a soft-float system, then fails on a float ABI call |
| i386 | ELF32 | Intel 80386 | /lib/ld-linux.so.2 | Exec format error where no 32-bit loader package exists |
每一行都是一种情况:字长看起来对,代码却仍然出错;或者它能启动,却在下一层失败。把解释器那一列当作硬性要求而不是提示,因为内核会在你的任何代码执行之前解析那条路径。模拟是一项部署决定,会带来自己的性能与支持后果,而一块宣传更宽字长的处理器从不暗示它。
dpkg --print-architecture
ENOEXEC 与 ENOENT 是两种不同的失败
启动阶段会报告两种错误之一,它们指向不同的问题。读取错误值就能在运行其他任何东西之前对失败分类。
ENOEXEC 表示格式没有被识别:架构不对、class 不对,或者根本不是可执行文件。ELF 文件上的 ENOENT 通常表示解释器缺失,即便你请求的文件存在。同一个 ENOENT 也可能表示脚本的解释器缺失,所以脚本的第一行很重要。无论哪种情况,消息都点出了层级;把每个“未找到”都当成配置缺失,会把调查带向错误的方向。在工单里引用错误值,因为它比周围的句子更经得起转述。
二进制文件起不来时不该做什么
把来路不明的二进制文件硬塞进 shell 包装里并不能让它运行。不要对你不信任的文件随手跑依赖检查器,因为某些情况下运行它会执行二进制文件里的代码。不要添加加载器符号链接、替换宿主库,或从第三方镜像抓旧归档来绕过提示。这些做法都扩大了改动面,却没有触及头部已经点出的不匹配。
联系供应商时,发值而不是讲故事:操作系统版本、软件包架构、class 与 machine 字符串、所请求的解释器,以及第一行错误。这组信息比一张安装失败截图更快解决支持问题。旧产物和记忆中的论坛输出不是当前的支持矩阵;而一旦原生二进制文件加载成功,配置、认证与路由仍然是另一项工作。