如何在编译时检查 GNU GAS 汇编代码上 Binutils 的版本?
How to check the version of Binutils on GNU GAS assembly code at compile time?
我有一些 GAS 汇编代码,我正在通过 GCC 直接编译它以使用预处理器功能,例如 #include
:
gcc main.S
我遇到了一个针对给定 Binutils 版本的报告 Binutils 错误,我想要两个版本的代码,在编译时决定:
- 有缺陷的 Binutils 版本的解决方法
- 否则为主要代码版本
对于 GCC 本身的版本,我可以使用 __GNUC__
和相关的宏,如下所述:How do I test at compile time the current version of GCC?
Binutils 版本有类似的东西吗?
我可以修改我的构建系统以自己检查 as --version
并传递 gcc -D
定义,但我想知道是否可以避免这种情况。
有一个叫做.gasversion.
的特殊符号(有一个前导点和一个尾随点)。你可以这样使用它:
.data
.if .gasversion. >= 22900
.ascii "binutils 2.29 or newer"
.endif
.if .gasversion. >= 22800
.ascii "binutils 2.28 or newer"
.endif
请注意,这不是预处理器功能(因为 GCC 不知道 GAS/BFD 版本并且不会将其传递给预处理器)。所以你必须使用像 .if
和 .macro
这样的 GAS 结构来实现你需要的东西。
通常,会使用一种替代方法,即在某些 configure
脚本中测试错误的实际存在,并且仅在必要时才激活解决方法。这意味着只有在绝对需要时才使用变通办法——版本号不反映本可以修复错误的分发向后移植。显然,这只有在解决方法成本高昂的情况下才有意义(因为它引入了额外的 运行 时间开销)。
How to check the version of Binutils on GNU GAS assembly code at compile time?
Crypto++ 也有类似的问题。他们需要知道 AS 和 LD 版本以确保 ISA 的指令部分,如 SSE4 (-msse4.1
)、AES (-maes
) 和 SHA (-msha
) 在构建期间可用(使用 Intel 作为一个例子)。
在 GNUmakefile Crypto++ 中 used to perform:
GCC_COMPILER := $(shell $(CXX) --version 2>/dev/null | $(GREP) -v -E '(llvm|clang)' | $(GREP) -i -c -E '(gcc|g\+\+)')
...
ifneq ($(GCC_COMPILER),0)
IS_GCC_29 := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E gcc-9[0-9][0-9])
GCC42_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[2-9]|[5-9]\.)")
GCC46_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[6-9]|[5-9]\.)")
endif
ifneq ($(HAVE_GAS),0)
GAS210_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.[1-9][0-9]|[3-9])")
GAS217_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[7-9]|2\.[2-9]|[3-9])")
GAS218_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[8-9]|2\.[2-9]|[3-9])")
GAS219_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.19|2\.[2-9]|[3-9])")
GAS224_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.2[4-9]|2\.[3-9]|[3-9])")
endif
后来 Crypto++ 会做类似的事情:
ifeq ($(HAVE_GAS)$(GAS224_OR_LATER),10)
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
endif
10
字符串基本上等同于下面的。这是执行布尔表达式的 GNU Makefile 方式:
if HAVE_GAS==true && GAS224_OR_LATER==false
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
fi
顺便说一句,GAS 版本检查与 Clang 和集成汇编程序一起中断。 Clang 不像 AS 那样响应 -Wa,-v
。 LLVM Bug 24200 因为它被归档:Fail to fetch version string of assembler when using integrated assembler.
Crypto++ 发现,这并不能很好地扩展。 10 或 20 年前(字面意思是最初使用时)还可以。然而,当 (1) 新平台使用古老的工具链时,如现代 BSD 固定到 GPL2 工具链 (2) 新编译器安装在旧平台上,如 Power6 机器上的 Clang 7.0,以及 (3) Clang 及其集成 assembler,不需要 AS 到 assemble 更高的 ISA。
ARM 平台也很麻烦,因为该项目无法可靠地 确定何时根据平台和编译器版本包含 <arm_neon.h>
和 <arm_acle.h>
。有时头文件可用于平台和编译器,有时则不可用(即使在具有同一编译器的不同版本的同一平台上)。 __ARM_ACLE__
之类的预处理器宏完全缺失(参见 ARM C Language Extensions (ACLE)). Android and iOS just does what the hell it wants breaking from what happens on armhf and friends or what is stated in the docs. And Microsoft found a new way to break it with their header <arm64_neon.h>
.
现在 Crypto++ 通过 GNU Makefile 执行测试编译,以查看程序是否可以编译、assembled 和链接。然而,它不像 Autotools 或 Cmake 那样脑残。 Crypto++ 寻找任何诊断并未能通过任何诊断的测试。这发现了 Autotools 和 Cmake 丢失的情况,例如 SunCC 发出 "illegal option: -xarch=sha"。 Autotools 和 Cmake 会报告成功,稍后构建会失败。 (显然 Autotools 和 Cmake 只检查编译器 return 代码,而不检查像 "illegal option" 这样的诊断消息)。
Crypto++ 测试 now look like:
SUN_COMPILER := $(shell $(CXX) -V 2>&1 | $(GREP) -i -c -E 'CC: (Sun|Studio)')
...
ifeq ($(SUN_COMPILER),1)
SSE2_FLAG = -xarch=sse2
else
SSE2_FLAG = -msse2
endif
...
TPROG = TestPrograms/test_x86_sse2.cxx
TOPT = $(SSE2_FLAG)
HAVE_OPT = $(shell $(CXX) $(TCXXFLAGS) $(ZOPT) $(TOPT) $(TPROG) -o $(TOUT) 2>&1 | tr ' ' '\n' | wc -l)
ifeq ($(strip $(HAVE_OPT)),0)
CHACHA_FLAG = $(SSE2_FLAG)
SUN_LDFLAGS += $(SSE2_FLAG)
else
SSE2_FLAG =
endif
和test_x86_sse2.cxx
,它是在-O0
处编译的,因此优化器不会删除代码:
$ cat TestPrograms/test_x86_sse2.cxx
#include <emmintrin.h>
int main(int argc, char* argv[])
{
__m128i x = _mm_setzero_si128();
x=_mm_add_epi64(x,x);
return 0;
}
我有一些 GAS 汇编代码,我正在通过 GCC 直接编译它以使用预处理器功能,例如 #include
:
gcc main.S
我遇到了一个针对给定 Binutils 版本的报告 Binutils 错误,我想要两个版本的代码,在编译时决定:
- 有缺陷的 Binutils 版本的解决方法
- 否则为主要代码版本
对于 GCC 本身的版本,我可以使用 __GNUC__
和相关的宏,如下所述:How do I test at compile time the current version of GCC?
Binutils 版本有类似的东西吗?
我可以修改我的构建系统以自己检查 as --version
并传递 gcc -D
定义,但我想知道是否可以避免这种情况。
有一个叫做.gasversion.
的特殊符号(有一个前导点和一个尾随点)。你可以这样使用它:
.data
.if .gasversion. >= 22900
.ascii "binutils 2.29 or newer"
.endif
.if .gasversion. >= 22800
.ascii "binutils 2.28 or newer"
.endif
请注意,这不是预处理器功能(因为 GCC 不知道 GAS/BFD 版本并且不会将其传递给预处理器)。所以你必须使用像 .if
和 .macro
这样的 GAS 结构来实现你需要的东西。
通常,会使用一种替代方法,即在某些 configure
脚本中测试错误的实际存在,并且仅在必要时才激活解决方法。这意味着只有在绝对需要时才使用变通办法——版本号不反映本可以修复错误的分发向后移植。显然,这只有在解决方法成本高昂的情况下才有意义(因为它引入了额外的 运行 时间开销)。
How to check the version of Binutils on GNU GAS assembly code at compile time?
Crypto++ 也有类似的问题。他们需要知道 AS 和 LD 版本以确保 ISA 的指令部分,如 SSE4 (-msse4.1
)、AES (-maes
) 和 SHA (-msha
) 在构建期间可用(使用 Intel 作为一个例子)。
在 GNUmakefile Crypto++ 中 used to perform:
GCC_COMPILER := $(shell $(CXX) --version 2>/dev/null | $(GREP) -v -E '(llvm|clang)' | $(GREP) -i -c -E '(gcc|g\+\+)')
...
ifneq ($(GCC_COMPILER),0)
IS_GCC_29 := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E gcc-9[0-9][0-9])
GCC42_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[2-9]|[5-9]\.)")
GCC46_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[6-9]|[5-9]\.)")
endif
ifneq ($(HAVE_GAS),0)
GAS210_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.[1-9][0-9]|[3-9])")
GAS217_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[7-9]|2\.[2-9]|[3-9])")
GAS218_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[8-9]|2\.[2-9]|[3-9])")
GAS219_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.19|2\.[2-9]|[3-9])")
GAS224_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.2[4-9]|2\.[3-9]|[3-9])")
endif
后来 Crypto++ 会做类似的事情:
ifeq ($(HAVE_GAS)$(GAS224_OR_LATER),10)
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
endif
10
字符串基本上等同于下面的。这是执行布尔表达式的 GNU Makefile 方式:
if HAVE_GAS==true && GAS224_OR_LATER==false
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
fi
顺便说一句,GAS 版本检查与 Clang 和集成汇编程序一起中断。 Clang 不像 AS 那样响应 -Wa,-v
。 LLVM Bug 24200 因为它被归档:Fail to fetch version string of assembler when using integrated assembler.
Crypto++ 发现,这并不能很好地扩展。 10 或 20 年前(字面意思是最初使用时)还可以。然而,当 (1) 新平台使用古老的工具链时,如现代 BSD 固定到 GPL2 工具链 (2) 新编译器安装在旧平台上,如 Power6 机器上的 Clang 7.0,以及 (3) Clang 及其集成 assembler,不需要 AS 到 assemble 更高的 ISA。
ARM 平台也很麻烦,因为该项目无法可靠地 确定何时根据平台和编译器版本包含 <arm_neon.h>
和 <arm_acle.h>
。有时头文件可用于平台和编译器,有时则不可用(即使在具有同一编译器的不同版本的同一平台上)。 __ARM_ACLE__
之类的预处理器宏完全缺失(参见 ARM C Language Extensions (ACLE)). Android and iOS just does what the hell it wants breaking from what happens on armhf and friends or what is stated in the docs. And Microsoft found a new way to break it with their header <arm64_neon.h>
.
现在 Crypto++ 通过 GNU Makefile 执行测试编译,以查看程序是否可以编译、assembled 和链接。然而,它不像 Autotools 或 Cmake 那样脑残。 Crypto++ 寻找任何诊断并未能通过任何诊断的测试。这发现了 Autotools 和 Cmake 丢失的情况,例如 SunCC 发出 "illegal option: -xarch=sha"。 Autotools 和 Cmake 会报告成功,稍后构建会失败。 (显然 Autotools 和 Cmake 只检查编译器 return 代码,而不检查像 "illegal option" 这样的诊断消息)。
Crypto++ 测试 now look like:
SUN_COMPILER := $(shell $(CXX) -V 2>&1 | $(GREP) -i -c -E 'CC: (Sun|Studio)')
...
ifeq ($(SUN_COMPILER),1)
SSE2_FLAG = -xarch=sse2
else
SSE2_FLAG = -msse2
endif
...
TPROG = TestPrograms/test_x86_sse2.cxx
TOPT = $(SSE2_FLAG)
HAVE_OPT = $(shell $(CXX) $(TCXXFLAGS) $(ZOPT) $(TOPT) $(TPROG) -o $(TOUT) 2>&1 | tr ' ' '\n' | wc -l)
ifeq ($(strip $(HAVE_OPT)),0)
CHACHA_FLAG = $(SSE2_FLAG)
SUN_LDFLAGS += $(SSE2_FLAG)
else
SSE2_FLAG =
endif
和test_x86_sse2.cxx
,它是在-O0
处编译的,因此优化器不会删除代码:
$ cat TestPrograms/test_x86_sse2.cxx
#include <emmintrin.h>
int main(int argc, char* argv[])
{
__m128i x = _mm_setzero_si128();
x=_mm_add_epi64(x,x);
return 0;
}