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