两个文件都是 32 位,而只有其中一个能在你的板卡上运行。面向 Intel 的 ELF32 构建和面向 ARM 的 ELF32 构建在类字段上一致,却在所有要紧的地方不一致。一个提供 32 位选项的下载页面,在你读取实际下载文件的头之前,几乎什么也没告诉你。改个名字不会改变任何一条指令。

类与机器回答的是不同问题

类字段说明文件使用的是 32 位还是 64 位结构。机器字段指明文件是为哪套指令集编译的。只读第一个,正是 x86 构建带着看似完全合理的文件名落到 ARM 板卡上的原因。两个值来自同一条命令,所以漏掉一个才会如此容易。

同样的混淆也适用于 64 位标签:aarch64 与 x86-64 共享字长,却完全不共享指令。一个只列出单个 64 位下载的厂商页面描述的是它自己的构建,而不是你的机器。检查文件的头,而不是页面上的列标题。

readelf -h ./vendor-vpn
readelf -l ./vendor-vpn | head -20
file ./vendor-vpn

第一条命令打印头部,包含类和机器。第二条显示程序头,解释器请求就写在那里。当表格超出你需要时,第三条命令只给你一行。

添加执行权限或修改发行版检查并不能完成任何转换。在平台支持的地方,模拟是一项部署决定,带有自己的性能和依赖成本。

在运行安装程序之前先读头

在从某个可信产物中执行任何东西之前,先检查它。一个半途失败的安装程序可能留下被替换的库和什么都不能用的状态,这比你开始时的归档更糟。头部检查是只读的,就系统上什么都没改变这一点而言也是可逆的。

把机器字段与正在运行系统的包架构比较,而不是与板卡的市场名称比较。运行 32 位用户空间的 64 位 CPU 需要 32 位构建,这个细节决定哪个下载才是原生的。约束不是板卡的能力,而是已安装的用户空间。

dpkg --print-architecture
uname -m
getconf LONG_BIT

第一条命令报告系统为其安装软件包的架构,这正是必须匹配的值。另外两条描述内核和字长,在多架构安装中它们可能与用户空间不一致。

当这些答案不一致时,在下载另一个归档之前先确定厂商支持哪一个。猜测会造成一轮又一轮的安装,每次都在不同地方失败,而每次失败看起来都像新问题。

检查解释器,而不只是架构

动态可执行文件会在程序头中记录它所期望的加载器。在加载器路径不同的系统上,即使机器字段完全匹配,内核也会拒绝启动该文件。正是这种失败让人去找缺失的库。

错误信息会告诉你属于哪种情况,而这两种情况很容易混淆。格式或架构被拒绝,与文件或解释器缺失是两回事,而只有其中一种能靠安装另一个软件包解决。

readelf -l ./vendor-vpn | grep -A1 INTERP
ls -l /lib64/ld-linux-x86-64.so.2

第一条命令显示二进制文件请求的解释器路径。第二条检查该路径是否存在,从而把错误构建与缺失加载器区分开。

不要对不受信任的二进制文件运行依赖列表工具,因为有些实现会执行它正在检查的文件。让检查保持只读,并抵制为了让不受支持的构建启动而添加加载器符号链接的冲动。

带着事实去要一个构建

把操作系统版本、包架构、类和机器值以及第一条错误信息发给厂商。这样的组合能换来有用的答复,而一张泛泛失败的截图只能换来支持模板。

把二进制问题与配置问题分开。正确的构建一旦启动,配置文件、凭据和路由仍是它们自己的问题,再多的配置编辑也不会让架构错误的二进制文件跑起来。

相关文章