编译时如何在GNU GAS汇编代码上检查Binutils的版本?

时间:2018-06-24 08:35:45

标签: gcc gas

我有一些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定义,但是我想知道是否可以避免。

2 个答案:

答案 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;
}