我有一些GAS汇编代码,我正在直接通过GCC对其进行编译,以使用#include
之类的预处理器功能:
gcc main.S
我遇到了报告的给定Binutils版本的Binutils错误,我想在编译时确定两个版本的代码:
对于GCC本身的版本,我可以使用__GNUC__
和相关的宏,如How do I test at compile time the current version of GCC?
Binutils版本是否有类似的东西?
我可以修改构建系统以自己检查as --version
并通过gcc -D
定义,但是我想知道是否可以避免。
答案 0 :(得分:2)
有一个称为.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
脚本中测试了错误的实际存在,并且仅在必要时才激活解决方法。这意味着该解决方法仅在绝对需要时才使用-版本号不反映可能已修复该错误的发行版反向移植。显然,这仅在解决方法成本很高时才有意义(因为这会引入额外的运行时开销)。
答案 1 :(得分:1)
如何在编译时检查GNU GAS汇编代码上Binutils的版本?
Crypto ++也有类似的问题。他们需要了解AS和LD版本,以确保在构建期间可以使用ISA的指令部分,如SSE4(-msse4.1
),AES(-maes
)和SHA(-msha
)(使用Intel作为一个例子)。
在GNUmakefile加密++ 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和Integrated Assembler破坏了。 Clang不像AS那样对-Wa,-v
做出响应。 LLVM错误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及其集成的汇编器时,它就崩溃了不需要AS来组装更高的ISA。
ARM平台也很麻烦,因为该项目无法可靠地根据平台和编译器版本确定何时包括<arm_neon.h>
和<arm_acle.h>
。有时,标头可用于平台和编译器,有时则不可(即使在具有相同编译器不同版本的同一平台上)。 __ARM_ACLE__
之类的预处理器宏完全丢失(请参阅ARM C Language Extensions (ACLE))。 Android和iOS完全可以打破armhf和朋友身上发生的事情,或者文档中所说的事情。微软找到了一种新方法来破坏其标头<arm64_neon.h>
。
现在,Crypto ++通过GNU Makefile执行测试编译,以查看程序是否可以编译,组装和链接。但是,它不像Autotools或Cmake那样死灵。 Crypto ++查找任何诊断,但未通过任何诊断的测试。这种捕获的案例缺少Autotools和Cmake,例如SunCC发出“非法选项:-xarch = sha” 。 Autotools和Cmake将报告成功,以后构建将失败。 (显然,Autotools和Cmake仅检查编译器的返回代码,而不检查诸如“非法选项”之类的诊断消息。)
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;
}