ARM/thumb interworking confuses unwinder

Jonathan Larmour jifl@eCosCentric.com
Thu Jul 24 01:54:00 GMT 2008


With current binutils, building for arm-eabi with -mcpu=arm9 -mthumb
-mthumb-interwork -Wl,-static, calling from thumb to ARM has regressed
since 2.18. Here is the asm generated in the stub to switch to ARM mode:

0x200406d4 <___Unwind_RaiseException_from_thumb+0>:     push    {r6, lr}
0x200406d6 <___Unwind_RaiseException_from_thumb+2>:     ldr     r6, [pc,
#12]   (0x200406e4 <___Unwind_RaiseException_from_thumb+16>)
0x200406d8 <___Unwind_RaiseException_from_thumb+4>:     mov     lr, pc
0x200406da <___Unwind_RaiseException_from_thumb+6>:     bx      r6
0x200406dc <___Unwind_RaiseException_from_thumb+8>:     pop     {r6, lr}
0x200406e0 <___Unwind_RaiseException_from_thumb+12>:    bx      lr

(___Unwind_RaiseException is at 0x20052aa0)

This may work, but causes problems in GCC when lr is used as a starting
point for the unwinder - there's no unwind information so the unwinder
gives up, calls abort() etc.

binutils 2.18 generates this, which works because lr does not get frobbed
and is obviously more efficient anyway as both here and with CVS, the
destination is within the 22-bit thumb branch limit):
0x20058568 <___Unwind_RaiseException_from_thumb+0>:     bx      pc
0x2005856a <___Unwind_RaiseException_from_thumb+2>:     nop
0x2005856c <___Unwind_RaiseException_change_to_arm>:    b       0x20052918
<___Unwind_RaiseException>

The problem originates with this change, checked in on 2008-05-15 or so:
http://sourceware.org/ml/binutils/2008-04/msg00423.html

So the first issue is: why is this generating a long call stub in the first
place? I believe the answer is that in 2.18, elf32_thumb_to_arm_stub()
generated the stub, called from elf32_arm_final_link_relocate(). In CVS,
the choice of stub is decided in arm_type_of_stub(), called from
elf32_arm_size_stubs() which is called in ld's emultempl/armelf.em in
"gld${EMULATION_NAME}_finish". It does not try and deal with a simple stub
in that code, but then as a result identifies a long call stub instead -
perhaps it was thinking that other code would fix it up? To cut a long
story short, the overall effect is that arm_type_of_stub() is called from
lang_process(), whereas elf32_thumb_to_arm_stub() is called from ldwrite(),
which is called subsequently, and thus is too late. I believe.

The second issue is that the long call stub can never work with C++
exceptions in GCC, at least for ARM EABI, and possibly the standard dwarf
unwinder as well. So if a call _was_ too far away for a simple stub, then
it would go wrong.

Jifl
-- 
eCosCentric Limited      http://www.eCosCentric.com/     The eCos experts
Barnwell House, Barnwell Drive, Cambridge, UK.       Tel: +44 1223 245571
Registered in England and Wales: Reg No 4422071.
------["Si fractum non sit, noli id reficere"]------       Opinions==mine



More information about the Binutils mailing list