0

In my setup using gcc-arm-none-eabi 4.8 and binutils 2.26 I get pretty undefined behavior when compiling the object files separately with -mthumb but leaving that flag out in the final linking step using ld without getting any warning from the linker. Why is that the case?

The undefined behavior is probably (according to the very helpful FOSS developers I have asked first) due to the default multilib architecture chosen by the linker due to the lack of the flag. However, why doesn't the linker warn about this issue? Can't it easily detect the ISA of the linked functions from the object and library files to determine that something fishy is going on?

Abhinay Reddy Keesara
  • 9,763
  • 2
  • 18
  • 28
stefanct
  • 2,503
  • 1
  • 28
  • 32

2 Answers2

2

Ideally yes it should know from the objects being linked. Disassemble and examine the interaction between functions of different objects.

I was recently trying to demonstrate how good the gnu tools/linker was at adding trampolines. When it consistently failed miserably. It would put the trampoline in for you for one direction but not the other (thumb to/from arm). I think this was an assembly to/from C issue and I didnt use a plethera of directives like the compiler uses, so that was probably it.

Fairly simple to setup some test cases, make an object out of functions built for arm, make another out of functions built for thumb, have them call each other. Put enough structure around it to get the tools to link without error and then disassemble and examine.

armstart.s

.global _start
_start:
    bl notmain
    b hang
hang:   b .

notmain.c

extern unsigned int one ( unsigned int );
int notmain ( void )
{
    one(1);
    return(0);
}

one.c

extern unsigned int two ( unsigned int x );
unsigned int one ( unsigned int x )
{
    return(two(x+5));
}

two.c

extern unsigned int one ( unsigned int x );
unsigned int two ( unsigned int x )
{
    return(one(x+7));
}

Makefile

ARMGNU = arm-none-eabi
#ARMGNU = arm-linux-gnueabi

AOPS = --warn --fatal-warnings
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding

all : notmain.bin


clean:
    rm -f *.bin
    rm -f *.o
    rm -f *.elf
    rm -f *.list
    rm -f *.bc
    rm -f *.opt.s
    rm -f *.norm.s
    rm -f *.hex

armstart.o : armstart.s
    $(ARMGNU)-as $(AOPS) armstart.s -o armstart.o

notmain.o : notmain.c
    $(ARMGNU)-gcc $(COPS) -c notmain.c -o notmain.o

two.o : two.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c two.c -o two.o

one.o : one.c
    $(ARMGNU)-gcc $(COPS) -c one.c -o one.o

notmain.bin : memmap armstart.o notmain.o one.o two.o
    $(ARMGNU)-ld -o notmain.elf -T memmap armstart.o notmain.o one.o two.o
    $(ARMGNU)-objdump -D notmain.elf > notmain.list
    $(ARMGNU)-objcopy notmain.elf notmain.hex -O ihex
    $(ARMGNU)-objcopy notmain.elf notmain.bin -O binary

some of the disassembly:

20000024 <one>:
20000024:   e92d4010    push    {r4, lr}
20000028:   e2800005    add r0, r0, #5
2000002c:   eb000007    bl  20000050 <__two_from_arm>
20000030:   e8bd4010    pop {r4, lr}
20000034:   e12fff1e    bx  lr

20000038 <two>:
20000038:   b510        push    {r4, lr}
2000003a:   3007        adds    r0, #7
2000003c:   f000 f804   bl  20000048 <__one_from_thumb>
20000040:   bc10        pop {r4}
20000042:   bc02        pop {r1}
20000044:   4708        bx  r1
20000046:   46c0        nop         ; (mov r8, r8)

20000048 <__one_from_thumb>:
20000048:   4778        bx  pc
2000004a:   46c0        nop         ; (mov r8, r8)
2000004c:   eafffff4    b   20000024 <one>

20000050 <__two_from_arm>:
20000050:   e59fc000    ldr ip, [pc]    ; 20000058 <__two_from_arm+0x8>
20000054:   e12fff1c    bx  ip
20000058:   20000039    andcs   r0, r0, r9, lsr r0
2000005c:   00000000    andeq   r0, r0, r0

And the toolchain in this case

arm-none-eabi-gcc (GCC) 6.1.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

(binutils 2.26.20160125)

Worked quite nicely.

Likewise with gcc 5.4.0 and binutils 2.26.1.

Looking at the differences between readelf for the two objects we see for example:

< 00000004  00000a1d R_ARM_JUMP24      00000000   two
---
> 00000004  00000a0a R_ARM_THM_CALL    00000000   one
old_timer
  • 69,149
  • 8
  • 89
  • 168
  • Note the elf type, arm32 littlearm or whatever it is for these. If your two compiled objects dont match there, the linker will error out. Not sure if just hard vs soft float is enough to trigger that or you have to work harder at it. – old_timer Jul 20 '16 at 02:52
0

compiling the object files separately with -mthumb but leaving that flag out in the final linking step using ld without getting any warning from the linker. Why is that the case?

The resulting executable file should work fine on most ARM platforms except for the microcontroller profiles (Cortex-M). There is even a good reason to mix thumb code with ARM libraries: Code size.

Turbo J
  • 7,563
  • 1
  • 23
  • 43
  • So the digest of your answer is: it's a common use case (i.e. nothing fishy); and thus the linker should certainly not warn just because I am instructing it to do the wrong thing for my CPU which it cannot infer. :) – stefanct Jul 20 '16 at 09:07