How to check the version of Binutils on GNU GAS assembly code at compile time?

I have some GAS assembly code, and I'm compiling it directly through GCC to use preprocessor features like #include:

gcc main.S

I came across a reported Binutils bug for a given Binutils version, and I would like to have two versions of the code, decided at compile time:

For the version of GCC itself, I can use __GNUC__ and related macros as explained at: How do I test at compile time the current version of GCC?

Is there something like that for the version of Binutils?

I could modify my build system to check the as --version myself and pass a gcc -D define, but I wonder if that can be avoided.

Upvotes: 2

Views: 1470

Answers (2)

jww
jww

Reputation: 102296

How to check the version of Binutils on GNU GAS assembly code at compile time?

Crypto++ had a a similar problem. They needed to know AS and LD versions to ensure instructions part of ISAs like SSE4 (-msse4.1), AES (-maes) and SHA (-msha) were available during a build (using Intel as an example).

In a 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

And later Crypto++ would do stuff like:

ifeq ($(HAVE_GAS)$(GAS224_OR_LATER),10)
  CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
endif

The 10 string is basically equivalent to the following. It is the GNU Makefile way of doing boolean expressions:

if HAVE_GAS==true && GAS224_OR_LATER==false
  CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
fi

By the way, the GAS version checking broke with Clang and the Integrated Assembler. Clang does not respond to -Wa,-v like AS does. LLVM Bug 24200 was filed because of it: Fail to fetch version string of assembler when using integrated assembler.


What Crypto++ found was, this did not scale well. It was OK 10 or 20 years ago (literally, when it was initially used). However it broke down when (1) new platforms use ancient toolchains, like modern BSD pinning to GPL2 toolchains (2) new compilers were installed on old platforms, like Clang 7.0 on a Power6 machine, and (3) Clang and its integrated assembler, which did not need AS to assemble higher ISAs.

ARM platforms was also very troublesome because the project could not reliably determine when to include <arm_neon.h> and <arm_acle.h> based on platforms and compiler versions. Sometimes the headers were available for a platform and compiler, sometimes they were not (even on the same platform with different versions of the same compiler). Preprocessor macros like __ARM_ACLE__ were completely missing (see 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>.

Now Crypto++ performs a test compile through the GNU Makefile to see if a program can be compiled, assembled and linked. However, it is not braindead like Autotools or Cmake. Crypto++ looks for any diagnostic and fails the test for any diagnostic. This caught cases Autotools and Cmake were missing, like SunCC emitting like "illegal option: -xarch=sha". Autotools and Cmake would report success and later the build would fail. (Apparently Autotools and Cmake only check compiler return codes, and not diagnostic messages like "illegal option").

The Crypto++ tests 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

And test_x86_sse2.cxx, which is compiled at -O0 so the optimzer does not remove the code:

$ 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;
}

Upvotes: 1

Florian Weimer
Florian Weimer

Reputation: 33719

There is a special symbol called .gasversion. (with a leading and trailing dot). You can use it this way:

        .data
.if .gasversion. >= 22900
        .ascii "binutils 2.29 or newer"
.endif
.if .gasversion. >= 22800
        .ascii "binutils 2.28 or newer"
.endif

Note that this is not a preprocessor feature (as GCC does not know the GAS/BFD version and does not pass it to the preprocessor). So you have to use GAS constructs like .if and .macro to implement what you need.

Often, an alternative approach is used where the actual presence of the bug is tested in some configure script and the workaround is activated only if necessary. This means that the workaround is only used when it is absolutely required—version numbers do not reflect distribution backports which could have fixed the bug. Obviously, this only makes sense if the workaround is costly (because it introduces additional run-time overhead).

Upvotes: 2

Related Questions