Targeting ARM Cortex-a9 from x86_64 with clang

Hi, here's the canonical helloworld.c

#include<stdio.h>

int main()
{
    printf("Hello World");
    return 0;
}

In accordance with the cross-compilation LLVM documentation [1], I am
trying to target the ARM on the Zedboard [2]. It is an ARM Cortex-A9.
The machine I am compiling on is an x86_64 Fedora Linux machine, using
clang 3.3. I am failing to generate an executable, instead seeing an
error "unrecognized option '-mfpu=neon'".

$ clang -v -target armv7a-linux-eabi -mcpu=cortex-a9 -mfloat-abi=soft
-mfpu=neon helloworld.c
clang version 3.3 (tags/RELEASE_33/rc3)
Target: armv7a--linux-eabi
Thread model: posix
"/usr/bin/clang" -cc1 -triple armv7--linux-eabi -S -disable-free
-disable-llvm-verifier -main-file-name helloworld.c -mrelocation-model
static -mdisable-fp-elim -fmath-errno -mconstructor-aliases
-fuse-init-array -target-abi aapcs -target-cpu cortex-a9 -msoft-float
-mfloat-abi soft -target-feature +soft-float -target-feature
+soft-float-abi -target-feature +neon -target-feature -neon
-target-linker-version 2.23.52.0.1 -v -resource-dir
/usr/bin/../lib/clang/3.3 -internal-isystem /usr/local/include
-internal-isystem /usr/bin/../lib/clang/3.3/include
-internal-externc-isystem /usr/include -internal-externc-isystem
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include
-fno-dwarf-directory-asm -fdebug-compilation-dir /tmp -ferror-limit 19
-fmessage-length 157 -mstackrealign -fno-signed-char
-fobjc-runtime=gcc -fobjc-default-synthesize-properties
-fdiagnostics-show-option -fcolor-diagnostics -backend-option
-vectorize-loops -o /tmp/helloworld-SVQDb9.s -x c helloworld.c
clang -cc1 version 3.3 based upon LLVM 3.3 default target
x86_64-redhat-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/bin/../lib/clang/3.3/include
/usr/include
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include
End of search list.
"/usr/bin/as" -mfpu=neon -mfloat-abi=soft -mcpu=cortex-a9 -mfpu=neon
-o /tmp/helloworld-DFsRi6.o /tmp/helloworld-SVQDb9.s
/usr/bin/as: unrecognized option '-mfpu=neon'
clang: error: assembler command failed with exit code 1 (use -v to see
invocation)

Any ideas? Thanks!

[1] - http://clang.llvm.org/docs/CrossCompilation.html
[2] - Zedboard - eLinux.org

$ clang -v -target armv7a-linux-eabi -mcpu=cortex-a9 -mfloat-abi=soft
-mfpu=neon helloworld.c

Hi Rod,

You need cross-binutils installed on your box. If you use Debian, there are
packages (gcc-4.7-arm-linux-gnueabi and friends). Other distros may have
similar packages, but you can always download the Linaro toolchain (
http://releases.linaro.org/latest/components/toolchain).

Supposing you already have it, and it's in the PATH, Clang only recognizes
it automatically if your triple is identical to the name of your cross
compiler (See "Toolchain Options" in the referred doc). That means, you
either need to have an "armv7a-linux-eabi-gcc" on the path, or you have to
change your triple to something like "arm-linux-gnueabi", because that's
what your cross-GCC will probably be called. The -mcpu will take care of
choosing v7A.

Otherwise, you'll have to set --sysroot or --gcc-name as well as the
triple, but that's not recommended.

"/usr/bin/as" -mfpu=neon -mfloat-abi=soft -mcpu=cortex-a9 -mfpu=neon

As you can see, it chose the platform assembler, which is x86_64-only, not
a cross-assembler. That's the hint that Clang didn't find your
cross-binutils.

I know, Clang could have better error detection and stop when it's clearly
the wrong architecture, but hey, Clang can deal with all archs on the same
binary, there's no reason your system assembler (whatever it is) can't,
too. One day, LLVM binutils will be... :wink:

cheers,
--renato

$ clang -v -target armv7a-linux-eabi -mcpu=cortex-a9 -mfloat-abi=soft
-mfpu=neon helloworld.c

Hi Rod,

You need cross-binutils installed on your box. If you use Debian, there
are packages (gcc-4.7-arm-linux-gnueabi and friends). Other distros may
have similar packages, but you can always download the Linaro toolchain (
http://releases.linaro.org/latest/components/toolchain).

Supposing you already have it, and it's in the PATH, Clang only recognizes
it automatically if your triple is identical to the name of your cross
compiler (See "Toolchain Options" in the referred doc). That means, you
either need to have an "armv7a-linux-eabi-gcc" on the path, or you have to
change your triple to something like "arm-linux-gnueabi", because that's
what your cross-GCC will probably be called. The -mcpu will take care of
choosing v7A.

Otherwise, you'll have to set --sysroot or --gcc-name as well as the
triple, but that's not recommended.

"/usr/bin/as" -mfpu=neon -mfloat-abi=soft -mcpu=cortex-a9 -mfpu=neon

As you can see, it chose the platform assembler, which is x86_64-only, not
a cross-assembler. That's the hint that Clang didn't find your
cross-binutils.

Out of curiosity, can't clang do the assembly itself in this case?

I know, Clang could have better error detection and stop when it's clearly
the wrong architecture, but hey, Clang can deal with all archs on the same
binary, there's no reason your system assembler (whatever it is) can't,
too. One day, LLVM binutils will be... :wink:

Well, just remember that the GNU binutils that those tools are derived from
support far more architectures (3x, 5x?) than LLVM does (just supporting
linux requires like 30 architectures). When we get to that point, it will
probably not make sense for a distro to ship a binary targeting all the
architectures that we support, for the same reason it doesn't make sense
for them to install binaries for every target supported by the GNU
toolchain. (but at least in LLVM's case, it would be a conscious tradeoff,
while IIRC the GNU toolchain simply can't be built in a way that targets
all of them from a single binary).

-- Sean Silva

As you can see, it chose the platform assembler, which is x86_64-only, not

a cross-assembler. That's the hint that Clang didn't find your
cross-binutils.

Out of curiosity, can't clang do the assembly itself in this case?

It can, but you need to use -integrated-as, because that's not the default
yet. (some missing features).

(but at least in LLVM's case, it would be a conscious tradeoff, while IIRC

the GNU toolchain simply can't be built in a way that targets all of them
from a single binary).

Exactly!

I only build the ARM back-end on my bots because I don't really care for
the rest, but I know I can, if I really want to.

cheers,
--renato

Out of curiosity, can't clang do the assembly itself in this case?

Integrated-assembler is still disabled by default on ARM ELF. It
really should be enabled now, in my opinion. Of course, you'd still
need the linker so dependencies wouldn't change noticeably.

Cheers.

Tim.

Out of curiosity, can't clang do the assembly itself in this case?

It can, but you need to use -integrated-as, because that's not the default
yet. (some missing features).

Do you remember what those features are Renato? MC has been around
years, we really should start sorting them now.

Cheers.

Tim

Here's where we left off:
http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-February/059099.html

In 3.4, I think the integrated assembler is in excellent shape and
should be enabled by default for ARM ELF. I've been using it
exclusively for at least 6 months now.

-Greg

Back then you mentioned a Chromium build, are you still running that
routinely with -integrated-as? That would give a reasonable
reassurance about general-usage, if not weird features.

Cheers.

Tim.

No. I remember there were issues, but I never catalogued them.

I agree we should move as soon as possible, and there's nothing holding me
of doing it.

I think post-3.4 we can cope with the consequences if they come, since it
seems nobody remembers what they were in the first place. :wink:

--renato

I know that FreeBSD limits the set of targets by default mostly for
compile reasons, the size difference is not that big. The trade off is
*much* better with LLVM.

Joerg

I think post-3.4 we can cope with the consequences if they come, since it
seems nobody remembers what they were in the first place. :wink:

Looks like there might be a handful of .eabi_attribute variantswe
can't handle (http://llvm.org/bugs/show_bug.cgi?id=15172) -- ones
involving a string?

Cheers.

Tim.

Back then you mentioned a Chromium build, are you still running that
routinely with -integrated-as?

Yes. It works perfectly for a C/C++ code. There have been a few
hiccups with inline assembly (missing aliases, switching ARM modes)
and we sometimes disable it explicitly for those. It's very possible
that we can use -integrated-as for most of those now too, but I
haven't tried. It's nice to still have that -no-integrated-as escape
hatch.

-Greg

post-3.4 release.

cheers,
-renato

We only do it, I believe, for the bootstrap build. When you build FreeBSD, you first build the toolchain that you'll build the system with (from the source tree that you're going to build, for the host arch), then you build the tree with that toolchain (for whatever your target arch is). Given how much of the total build time LLVM / Clang accounts for, building it twice caused a lot of irritating. We quite aggressively stripped down the bootstrap build, so that it only supports the specified target (unless requested otherwise), doesn't include the Clang rewriter, the static analyser, and a few other things. Removing the JIT would probably also make sense.

David

$ clang -v -target armv7a-linux-eabi -mcpu=cortex-a9 -mfloat-abi=soft
-mfpu=neon helloworld.c

Hi Rod,

I'm honoured. (But Rob is also OK) :slight_smile:

You need cross-binutils installed on your box. If you use Debian, there are
packages (gcc-4.7-arm-linux-gnueabi and friends). Other distros may have
similar packages, but you can always download the Linaro toolchain
(http://releases.linaro.org/latest/components/toolchain).

Supposing you already have it, and it's in the PATH, Clang only recognizes
it automatically if your triple is identical to the name of your cross
compiler (See "Toolchain Options" in the referred doc). That means, you
either need to have an "armv7a-linux-eabi-gcc" on the path, or you have to
change your triple to something like "arm-linux-gnueabi", because that's
what your cross-GCC will probably be called. The -mcpu will take care of
choosing v7A.

So on my Fedora box, I've installed packages gcc-arm-linux-gnu.x86_64
and cross-binutils-common.noarch . Moreover, the first of these two
packages provide `/usr/bin/arm-linux-gnu-gcc`
$ repoquery -lq gcc-arm-linux-gnu.x86_64
Repository google-chrome is listed more than once in the configuration
/usr/bin/arm-linux-gnu-cpp
/usr/bin/arm-linux-gnu-gcc
/usr/bin/arm-linux-gnu-gcov
/usr/lib/gcc/arm-linux-gnueabi
/usr/lib/gcc/arm-linux-gnueabi/4.8.1
/usr/lib/gcc/arm-linux-gnueabi/4.8.1/crtbegin.o
...

The -target that appears to be recognised by clang is `arm-none-eabi`.
However, I'm now falling in to another trap.
$ clang -v -target arm-none-eabi -mcpu=cortex-a9 -mfloat-abi=soft helloworld.c
...
arm-none-eabi-gcc: fatal error: selected multilib '.' not installed

I've put the -v output to gist: https://gist.github.com/robstewart57/7676030

Hi Rod,

I'm honoured. (But Rob is also OK) :slight_smile:

Haha! That was my best typo ever! :wink:

So on my Fedora box, I've installed packages gcc-arm-linux-gnu.x86_64
and cross-binutils-common.noarch.

I'm not sure Clang understands "arm-linux-gnu" as a triple. The driver
is not the most sane code in Clang, so it might have holes in it that
won't make sense.

It is worth opening a new thread on cfe-dev@ discussing the possible
outcome of using arm-linux-gnu or arm-linux-androideabi as your triple
and get your toolchain to be detected correctly on Fedora.

The -target that appears to be recognised by clang is `arm-none-eabi`.

That triple is normally used when targeting bare-metal ARM targets,
which I don't think is your case.

I can see from your output, all seem fine until...

arm-none-eabi-gcc: fatal error: selected multilib '.' not installed

Normally, the problem with this is that you have libraries compiled
with hard-float and is trying to compile code with soft-float. But you
should focus on getting Clang to recognize your original triple first,
I think.

cheers,
--renato

Your link says that clang is using gcc to compile rather than compiling directly. You might want to look at the ELLCC project, which is designed for just the sort of cross compilation you are trying to do. http://ellcc.org

-Rich

Hi Rich,

No, his output only has gcc to assemble and link:

"/usr/bin/arm-none-eabi-gcc" -v -mcpu=cortex-a9 -mfloat-abi=soft -c -o
/tmp/helloworld-2n4ZGp.o -x assembler /tmp/helloworld-Iarp5R.s
"/usr/bin/arm-none-eabi-gcc" -v -mcpu=cortex-a9 -mfloat-abi=soft -o
a.out /tmp/helloworld-2n4ZGp.o

Clang normally calls GAS in case "-integrated-as" is not specified for ARM.

Btw, Rod, you might try the integrated assembler, too.

cheers,
--renato

And I did it again! Argh! Sorry!

--renato

...

I know that FreeBSD limits the set of targets by default mostly for
compile reasons, the size difference is not that big. The trade off is
*much* better with LLVM.

We only do it, I believe, for the bootstrap build. When you build FreeBSD, you first build the toolchain that you'll build the system with (from the source tree that you're going to build, for the host arch), then you build the tree with that toolchain (for whatever your target arch is). Given how much of the total build time LLVM / Clang accounts for, building it twice caused a lot of irritating. We quite aggressively stripped down the bootstrap build, so that it only supports the specified target (unless requested otherwise), doesn't include the Clang rewriter, the static analyser, and a few other things. Removing the JIT would probably also make sense.

Not entirely, yet. We do strip out the static analyzer, arcmigrate, and the rewriter during the bootstrap build, and only enable them for the second (target system) build. Stripping out unused target arches during the bootstrap would be nice to have, but the work has not been done yet. :slight_smile:

Stripping out the JIT might indeed also be interesting, though I guess stripping out unused arches will save more compile time?

-Dimitry