如何检查二进制文件是否为 "runnable"
how to check if binary is "runnable"
在我的 CI-setup 中,我将我的 C 代码编译成许多不同的架构(x86_64-linux-gnu, i386-linux-gnu
、aarch64-linux-gnu
、arm-linux-gnueabihf
、x86_64-apple-darwin
、i386-apple-darwin
、i686-w64-mingw32
、x86_64-w64-mingw32
、...)。
我可以简单地通过“启用它们”来添加新架构(至少对于 *-linux-gnu)。
相同的 CI-configuration 用于许多项目(不同的开发人员),并力求实际上是“零配置”(如:“删除此 CI-configuration在你的项目中忘记它,剩下的会为你处理”)。
一些目标是本机编译的,其他是交叉编译的。一些交叉编译的架构在构建机器上 运行 可用(例如我可以 运行 x86_64-apple-darwin
上的 i386-apple-darwin
二进制文件),其他的是不兼容的(例如我不能 运行 aarch64-linux-gnu binaries
上 x86_64-linux-gnu builder
).
目前一切正常。
但是,我还想在 CI 期间进行 运行 单元测试 - 但前提是单元测试实际上可以在构建机器上执行。
我对获得大量失败的测试完全不感兴趣,因为我正在交叉构建二进制文件。
使事情复杂化一点,我正在构建的不是独立的可执行文件,而是主机应用程序 dlopen()ed(或目标平台上的任何等效项)的插件。主机应用程序通常启动速度很慢,所以如果它无法使用插件,我想避免 运行ning 它。
构建插件也意味着我不能只尝试-运行它们。
我正在使用 GNU 工具链(make、gcc),或者至少是兼容的东西(比如 铛))。
在我第一次尝试检查我是否在交叉编译时,我将构建过程的目标架构(由 ${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(gcc 说 x86_64-apple-darwin18.0.0
, make 说 i386-apple-darwin11.3.0
(不要问我为什么)。
它正变得越来越成为一个问题,因为当我写这篇文章并做一些检查时,我注意到即使在 Linux 上我也会得到像 x86_64-pc-linux-gnu
和 x86_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
烘焙到您现有的插件测试之一中是否有用。
在我的 CI-setup 中,我将我的 C 代码编译成许多不同的架构(x86_64-linux-gnu, i386-linux-gnu
、aarch64-linux-gnu
、arm-linux-gnueabihf
、x86_64-apple-darwin
、i386-apple-darwin
、i686-w64-mingw32
、x86_64-w64-mingw32
、...)。
我可以简单地通过“启用它们”来添加新架构(至少对于 *-linux-gnu)。
相同的 CI-configuration 用于许多项目(不同的开发人员),并力求实际上是“零配置”(如:“删除此 CI-configuration在你的项目中忘记它,剩下的会为你处理”)。
一些目标是本机编译的,其他是交叉编译的。一些交叉编译的架构在构建机器上 运行 可用(例如我可以 运行 x86_64-apple-darwin
上的 i386-apple-darwin
二进制文件),其他的是不兼容的(例如我不能 运行 aarch64-linux-gnu binaries
上 x86_64-linux-gnu builder
).
目前一切正常。
但是,我还想在 CI 期间进行 运行 单元测试 - 但前提是单元测试实际上可以在构建机器上执行。 我对获得大量失败的测试完全不感兴趣,因为我正在交叉构建二进制文件。
使事情复杂化一点,我正在构建的不是独立的可执行文件,而是主机应用程序 dlopen()ed(或目标平台上的任何等效项)的插件。主机应用程序通常启动速度很慢,所以如果它无法使用插件,我想避免 运行ning 它。 构建插件也意味着我不能只尝试-运行它们。
我正在使用 GNU 工具链(make、gcc),或者至少是兼容的东西(比如 铛))。
在我第一次尝试检查我是否在交叉编译时,我将构建过程的目标架构(由 ${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(gcc 说 x86_64-apple-darwin18.0.0
, make 说 i386-apple-darwin11.3.0
(不要问我为什么)。
它正变得越来越成为一个问题,因为当我写这篇文章并做一些检查时,我注意到即使在 Linux 上我也会得到像 x86_64-pc-linux-gnu
和 x86_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
烘焙到您现有的插件测试之一中是否有用。