This page documents the most common causes of failure when building for the ARM EABI port.

char is unsigned by default

So

    char q;

    while ( (q = getchar()) != EOF ) {
        ....
    }

will loop forever.

Test for this kind of error by recompiling with -fsigned-char.

word accesses must be aligned to a multiple of their size

Accesses from non-aligned locations give garbled results

For example, loading a 32-bit word from a non-aligned pointer reads an aligned 32-bit word from the next lowest 32-bit-aligned location (ignoring the lower 2 bits of the pointer) and then rotates the result so that the byte indicated by the pointer ends up in the least significant byte.

This applies to 16-bit, 32-bit and 64-bit data.

You can test for this kind of error without recompiling, using the pseudo-file /proc/cpu/alignment. Catting it tells you whether misaligned user accesses are detected and fixed up or not (the default setting is usually no, 0).

Another mode is "fixup", in which the kernel will quietly perform correct unaligned access for user processes.

    echo 3 > /proc/cpu/alignment   # fixup and warn

enables this. However you cannot rely on this being enabled on users' machines.

Alternatively, you can find the unaligned access by saying

    echo 5 > /proc/cpu/alignment   # signal and warn

which will provoke "Bus error" on unaligned accesses, normally killing the process and allowing you to find the offending access with a debugger.

There is a detailed description of the behaviour on the CENS wiki.

To force alignment of data, use something like:

uint8_t srtp_plaintext[38] __attribute__((aligned(4))) = {
    0x80, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad,
    0xca, 0xfe, 0xba, 0xbe, 0xab, 0xab, 0xab, 0xab,
    0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
    0xab, 0xab, 0xab, 0xab, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

$ is reserved as symbol name on arm EABI

C Code such as

   int $version = 1;

Will barf on arm:

swfdec_movie.c: In function 'swfdec_movie_set_version':
swfdec_movie.c:1529: error: stray '$' in program
swfdec_movie.c:1529: error: 'version' undeclared (first use in this function)
swfdec_movie.c:1529: error: (Each undeclared identifier is reported only once
swfdec_movie.c:1529: error: for each function it appears in.)
swfdec_movie.c:1529: error: stray '$' in program
swfdec_movie.c:1529: error: expected ')' before 'version'

Fixing is really trivial, just use some other allowed character than "$" as identifier.

Alternatively, use  -fdollars-in-identifiers  to explicitly allow compiling with dollar symbols. Notice that this is not necessarily portable outside gcc, so you should add a configure check for the compiler flag.

ISO C defines identifiers as a-zA-Z0-9_, and supporting other characters is implementation specific. In other words, using them is not portable. GCC in general allows $, but Arm EABI reserves $ for internal ABI symbols.

"All symbol names containing a dollar character ($) are reserved to the ARM EABI."

http://refspecs.freestandards.org/elf/ARMELF.pdf http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf (See Page 51)

Floating point format has changed

On old ARM it used to be word-swapped 64-bit IEEE754, now it's little-endian IEEE754 same as everything else. Old hacky bits of

    #ifdef __arm__
        wordswap a double read from somewhere
    #endif

need protecting

    #if defined(__arm__) && !defined(__ARM_EABI__)
        wordswap a double
    #endif

FPA instructions have gone; softfloat has come

Any complaints about lacking assembly instructions:

    ldfeqs f0,[sp,#20]
    ldfeqd f0,[sp,#20]

are ARM FPA assembly that needs recoding. There's a full list of them in the GCC sources in gcc/config/arm/fpa.md

If the loader complains about missing symbols like __aeabi_f2ulz, these are the stubs in libgcc that do FP conversion, division etc. Be suspicious of custom linker scripts or calls to ld instead of gcc.

Alignment of structures has changed

And how? Well,

    struct { char c; } foo;

used to have a sizeof 4 on ARM but now has sizeof 1, the same as the x86.

Alignment of structure members has changed

As an effect of the 64-bit alignment of 64-bit data, the structure:

    struct { double d1; int i; double d2 };

has a 4-byte hole in it between i and d2 on armel, whereas it didn't on ARM.

Alignment of 64-bit data has changed

Double and long long data needs to be 64-bit-aligned, both in memory and in register pairs when passing them as syscall parameters.

I was going to say "also as function arguments" but just did a survey of

    main(char c, double d, char e)
    {
        printf("%d, %d\n", (char *)&c - (char *)&d,
                           (char *)&d - (char *)&e);

and got:

c-d

d-e

ARM gcc-3.4

11

1

ARM gcc-4.X

8

4

EABI gcc-4.3

15

-32

x86 gcc-3.4

-13

14

x86 gcc-4.3

12

4

so I guess it's the same headache as usual, just different, for varargs code.

clisp and ffcall have the same problem of this kind.

Exception unwinding in C++

 catch {} try {}  clauses in C++ do not always unwind properly in EABI, though the Debian GCC-4.3 seems to get a known problem testcase right.

Affected packages in the ArmEabiProblems are: gobj.

Compiler bug when comparing single-precision floats

A bug in GCC emits the wrong code when comparing two single-precision floats when using ARM EABI softfloat. One workaround is to reduce the optimization level, typically by adding -fno-finite-math-only if -ffast-math is already specified, or -fno-schedule-insns -fno-schedule-insns2 if it is not.

This was discovered in libvorbisenc but also affects LAME (in debian-multimedia).

qreal (qMin, qMax, Qt)

qreal is a "internal" datatype used by Qt. It is a typedef to double on some archs and a typedef to float on other archs (notably, armel/armhf). Some times, there is build failures related to this, especially involving the qMin and qMax functions.

if function foo() returns qreal, then the following is expected to fail:

    qMax(foo(),1.5);

as 1.5 is interpreted as a double, foo() returns a float, and qMax is defined for qMax(double,double) and qMax(float,float)

The suggested fix is here:

    qMax(foo(),static_cast<qreal>(1.5));

'clz' instruction not defined

Debian armel is targetted on the armv4t instruction set, while the Count Leading Zeros instruction was introduced in armv5.

An example of fixing an assembler fragment, from GMP is:

 PROLOGUE(mpn_mod_1_1p_cps)
         stmfd   sp!, {r4, r5, r6, r14}
         mov     r5, r0
+#ifdef __ARM_ARCH_4T__    ; or something
+        ; Untested
+        dnl     r1 = argument
+        dnl     r4 = count result
+        dnl     r6 = scratch in which to destroy argument
+        dnl If argument==0, return 32
+        mov     r4, #32
+        movs    r6, r1
+        beq     out
+        dnl otherwise count the top clear bits
+        mov     r4, #0
+        b       in
+found_clear_bit:
+        add     r4, #1
+in:
+        lsl     r6, #31
+        bcc     found_clear_bit
+out:
+#else
         clz     r4, r1
+#fi
         mov     r0, r1, asl r4
         rsb     r6, r0, #0
         bl      mpn_invert_limb

Historic Notes

Oldabi ARM has a number of peculiarities which one needs to be aware of when writing portable code. Essentially you need to be aware of alignment and packing rules, type sizes and the floating point format. Here is some info on floating point formats and packing which packagers will find useful. Very few programs deal directly with floats - normally leaving it to glibc, but those that do need to be aware that the arm format is 'mixed-endian', i.e. Big-endian word order but native byte order within words (which is little-endian on debian 'arm'). This is IEEE-754 compliant, but unique to arm. An overview of the FP status (in 1999, but it gives the right general idea still) is given here: http://netwinder.osuosl.org/users/s/scottb/public_html/notes/FP-Notes.html

This document is very old (written by Brian Bray when the original arm port for the netwinder was done) but still provides a good explanation of the packing issues you should be aware of: http://www.aleph1.co.uk/node/294. Essentially packing is 'natural', but limited to 4-byte maximum alignment. - i.e. 8, 16, 32 bit entities are aligned on 8,16,32 bit boundaries. larger entities are still 32-bit aligned. Pointers are always 32-bit aligned, so using a pointer into a packed structure is likely to produce 'unexpected' results.