如何检查二进制文件是否为 "runnable"

how to check if binary is "runnable"

在我的 CI-setup 中,我将我的 C 代码编译成许多不同的架构(x86_64-linux-gnu, i386-linux-gnuaarch64-linux-gnuarm-linux-gnueabihfx86_64-apple-darwini386-apple-darwini686-w64-mingw32x86_64-w64-mingw32、...)。 我可以简单地通过“启用它们”来添加新架构(至少对于 *-linux-gnu)。

相同的 CI-configuration 用于许多项目(不同的开发人员),并力求实际上是“零配置”(如:“删除此 CI-configuration在你的项目中忘记它,剩下的会为你处理”)。

一些目标是本机编译的,其他是交叉编译的。一些交叉编译的架构在构建机器上 运行 可用(例如我可以 运行 x86_64-apple-darwin 上的 i386-apple-darwin 二进制文件),其他的是不兼容的(例如我不能 运行 aarch64-linux-gnu binariesx86_64-linux-gnu builder).

目前一切正常。

但是,我还想在 CI 期间进行 运行 单元测试 - 但前提是单元测试实际上可以在构建机器上执行。 我对获得大量失败的测试完全不感兴趣,因为我正在交叉构建二进制文件。

使事情复杂化一点,我正在构建的不是独立的可执行文件,而是主机应用程序 dlopen()ed(或目标平台上的任何等效项)的插件。主机应用程序通常启动速度很慢,所以如果它无法使用插件,我想避免 运行ning 它。 构建插件也意味着我不能只尝试-运行它们。

我正在使用 GNU 工具链(makegcc),或者至少是兼容的东西(比如 ))。 在我第一次尝试检查我是否在交叉编译时,我将构建过程的目标架构(由 ${CC} -dumpmachine 返回)与 GNU make 的架构进行了比较( GNU make 将在使用 -v 标志调用时输出用于构建 make 本身的体系结构三元组。

像下面这样的东西出奇地好,至少对于 *-linux-gnu 目标:

if make --version | egrep ${TARGETARCH:-$(${CC:-cc} -dumpmachine) >/dev/null; then
  echo "native compilation"
else
  echo "cross compiling"
fi

但是,它在 Windows/MinGW 上根本不起作用(在进行本机构建时,gcc 目标 x86_64-w64-mingw32 make 是为 x86_64-pc-msys 构建的;更糟糕的是,当构建 32 位二进制文​​件时当然完全 运行nable)或 macOS(gccx86_64-apple-darwin18.0.0, makei386-apple-darwin11.3.0 (不要问我为什么)。 它正变得越来越成为一个问题,因为当我写这篇文章并做一些检查时,我注意到即使在 Linux 上我也会得到像 x86_64-pc-linux-gnux86_64-linux-gnu 这样的差异;我的 CI-builders 还没有出现这些差异,但我相信这只是时间问题)。

因此,我正在寻找更强大的解决方案来检测我的构建主机是否能够 运行 生成的二进制文件,如果不能则跳过单元测试。

据我了解你的要求(如果我错过了重点,我会删除这个答案),你可以分三步进行:

检测您的构建过程,以便它生成您正在使用的所有 (gcc 'dumpmachine', make 'built for') 对的准确列表。

只在列表中保留允许执行程序的对。

使用您收集的信息,根据 bash 确定您是否可以执行反映您的系统的二进制文件对:

#!/bin/bash
# Credits (some borrowed code):
# 
 
# bash 4 could use associative arrays, but darwin probably only has bash3 (and zsh)
# pairs of gcc -dumpmachine 
# ----- collected/formatted/filtered information begins -----
entries=(
  'x86_64-w64-mingw32        x86_64-pc-msys'
  'x86_64-pc-linux-gnu       x86_64-linux-gnu'
  'x86_64-apple-darwin18.0.0 i386-apple-darwin11.3.0'
)
# ----- collected/formatted/filtered information ends -----

is_executable()
{
  local gcc
  local make
  local found=0

  if [ $# -ne 2 ]
  then
    echo "is_executable() requires two parameters - terminating."
    exit 1
  fi

  for page in "${entries[@]}"
  do
      read -r -a arr  <<< "${page}"
      gcc="${arr[0]}"
      make="${arr[1]}"
      if [ "" == "${gcc}" ] && [ "" == "${make}" ]
      then
        found=1
        break;
      fi
  done

  return ${found}
}

# main
MAKE_BUILT_FOR=$( make --version | sed -n 's/Built for //p')
GCC_DUMPMACHINE=$(gcc -dumpmachine)

# pass
is_executable ${MAKE_BUILT_FOR} ${GCC_DUMPMACHINE}
echo $?

# pass
is_executable x86_64-w64-mingw32  x86_64-pc-msys
echo $?

# fail
is_executable arm-linux-gnueabihf x86_64-pc-msys
echo $?

作为额外的预防措施,您可能应该验证您正在使用的 gcc 'dumpmachine' 和 make 'built for' 是否在 gcc 列表中 , 让你正在使用,并记录一条错误消息 and/or 如果不是这种情况则退出。

也许包括一个额外的单元测试, 可以直接运行,只是一个“hello world”或 return EXIT_SUCCESS;,如果它失败,跳过所有其他插件该架构的测试?

有趣的事实:至少在 Linux 上,共享库(ELF 共享对象)可以有一个入口点并且可以执行。 (这就是 PIE 可执行文件的制作方式;过去是愚蠢的编译器/链接器技巧现在是默认的。)IDK 如果将 main 烘焙到您现有的插件测试之一中是否有用。