Index: include/llvm/MC/MCSymbolELF.h =================================================================== --- include/llvm/MC/MCSymbolELF.h +++ include/llvm/MC/MCSymbolELF.h @@ -44,6 +44,8 @@ void setIsSignature() const; bool isSignature() const; + bool isFunction() const; + static bool classof(const MCSymbol *S) { return S->isELF(); } private: Index: lib/MC/MCSymbolELF.cpp =================================================================== --- lib/MC/MCSymbolELF.cpp +++ lib/MC/MCSymbolELF.cpp @@ -194,6 +194,10 @@ return getFlags() & (0x1 << ELF_IsSignature_Shift); } +bool MCSymbolELF::isFunction() const { + return getType() == ELF::STT_FUNC || getType() == ELF::STT_GNU_IFUNC; +} + void MCSymbolELF::setIsBindingSet() const { uint32_t OtherFlags = getFlags() & ~(0x1 << ELF_BindingSet_Shift); setFlags(OtherFlags | (1 << ELF_BindingSet_Shift)); Index: lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp =================================================================== --- lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +++ lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp @@ -729,6 +729,11 @@ // linker can handle it. GNU AS produces an error in this case. if (Sym->isExternal() || Value >= 0x400004) IsResolved = false; + // When an ARM function is called from a Thumb function, produce a + // relocation so the linker will use the correct branch instruction. + if (Sym->isELF() && dyn_cast(Sym)->isFunction() && + !Asm.isThumbFunc(Sym)) + IsResolved = false; } // We must always generate a relocation for BL/BLX instructions if we have // a symbol to reference, as the linker relies on knowing the destination Index: lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp =================================================================== --- lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -1126,8 +1126,7 @@ return; Streamer.getAssembler().registerSymbol(*Symbol); - unsigned Type = cast(Symbol)->getType(); - if (Type == ELF::STT_FUNC || Type == ELF::STT_GNU_IFUNC) + if (cast(Symbol)->isFunction()) Streamer.EmitThumbFunc(Symbol); } Index: test/MC/ARM/big-endian-thumb-fixup.s =================================================================== --- test/MC/ARM/big-endian-thumb-fixup.s +++ test/MC/ARM/big-endian-thumb-fixup.s @@ -4,6 +4,7 @@ .text .align 2 .code 16 + .thumb_func @ARM::fixup_arm_thumb_bl .section s_thumb_bl,"ax",%progbits Index: test/MC/ARM/mixed-arm-thumb-bl-fixup.ll =================================================================== --- /dev/null +++ test/MC/ARM/mixed-arm-thumb-bl-fixup.ll @@ -0,0 +1,77 @@ +; RUN: llc -O0 < %s -mtriple armv7-linux-gnueabi -o - \ +; RUN: | llvm-mc -triple armv7-linux-gnueabi -filetype=obj -o - \ +; RUN: | llvm-readobj -r | FileCheck --check-prefix LINUX %s + +; RUN: llc -O0 < %s -mtriple armv7-linux-android -o - \ +; RUN: | llvm-mc -triple armv7-linux-android -filetype=obj -o - \ +; RUN: | llvm-readobj -r | FileCheck --check-prefix LINUX %s + + +; RUN: llc -O0 < %s -mtriple armv7-apple-ios -o - \ +; RUN: | llvm-mc -triple armv7-apple-ios -filetype=obj -o - \ +; RUN: | llvm-readobj -r | FileCheck --check-prefix IOS %s + + +define void @thumb_caller() #0 { + call void @internal_arm_fn() + call void @global_arm_fn() + call void @internal_thumb_fn() + call void @global_thumb_fn() + ret void +} + +define void @arm_caller() #1 { + call void @internal_arm_fn() + call void @global_arm_fn() + call void @internal_thumb_fn() + call void @global_thumb_fn() + ret void +} + +define internal void @internal_thumb_fn() #0 { + ret void +} + +define void @global_thumb_fn() #0 { +entry: + br label %end +end: + br label %end + ret void +} + +define internal void @internal_arm_fn() #1 { + ret void +} + +define void @global_arm_fn() #1 { +entry: + br label %end +end: + br label %end + ret void +} + +attributes #0 = { "target-features"="+thumb-mode" } +attributes #1 = { "target-features"="-thumb-mode" } + +; LINUX: Section (3) .rel.text { +; LINUX-NEXT: 0x2 R_ARM_THM_CALL internal_arm_fn 0x0 +; LINUX-NEXT: 0x6 R_ARM_THM_CALL global_arm_fn 0x0 +; LINUX-NEXT: 0xE R_ARM_THM_CALL global_thumb_fn 0x0 +; LINUX-NEXT: 0x1C R_ARM_CALL internal_arm_fn 0x0 +; LINUX-NEXT: 0x20 R_ARM_CALL global_arm_fn 0x0 +; LINUX-NEXT: 0x24 R_ARM_CALL internal_thumb_fn 0x0 +; LINUX-NEXT: 0x28 R_ARM_CALL global_thumb_fn 0x0 +; LINUX-NEXT: } + +; IOS: Section __text { +; IOS-NEXT: 0x2C 1 2 0 ARM_RELOC_BR24 0 __text +; IOS-NEXT: 0x28 1 2 0 ARM_RELOC_BR24 0 __text +; IOS-NEXT: 0x24 1 2 0 ARM_RELOC_BR24 0 __text +; IOS-NEXT: 0x20 1 2 0 ARM_RELOC_BR24 0 __text +; IOS-NEXT: 0x10 1 2 0 ARM_THUMB_RELOC_BR22 0 __text +; IOS-NEXT: 0xC 1 2 0 ARM_THUMB_RELOC_BR22 0 __text +; IOS-NEXT: 0x8 1 2 0 ARM_THUMB_RELOC_BR22 0 __text +; IOS-NEXT: 0x4 1 2 0 ARM_THUMB_RELOC_BR22 0 __text +; IOS-NEXT: }