Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -110,6 +110,9 @@ std::vector BuildIdVector; llvm::MapVector RenamedSymbols; bool AllowMultipleDefinition; + bool ARMHasBlx = false; + bool ARMHasMovtMovw = false; + bool ARMJ1J2BranchEncoding = false; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -1083,6 +1083,28 @@ if (Config->EMachine == EM_MIPS) Config->MipsEFlags = calcMipsEFlags(); + if (Config->EMachine == EM_ARM) { + if (InX::ARMAttributes == nullptr) { + // No ARM attributes section present (assembler without .eabi_attribute + // directives, like lld tests), assume support for all features + Config->ARMHasBlx = true; + Config->ARMHasMovtMovw = true; + Config->ARMJ1J2BranchEncoding = true; + } + // FIXME: These warnings can be removed when lld only uses these features + // when the input objects have been compiled with an architecture that + // supports them. + if (Config->ARMHasBlx == false) + warn("lld uses blx instruction, no object with architecture supporting " + "feature detected."); + if (Config->ARMJ1J2BranchEncoding == false) + warn("lld uses extended branch encoding, no object with architecture " + "supporting feature detected."); + if (Config->ARMHasMovtMovw == false) + warn("lld may use movt/movw, no object with architecture supporting " + "feature detected."); + } + // This adds a .comment section containing a version string. We have to add it // before decompressAndMergeSections because the .comment section is a // mergeable section. Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -23,6 +23,8 @@ #include "llvm/LTO/LTO.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/ARMAttributeParser.h" +#include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -349,6 +351,47 @@ } } +// The ARM support in lld makes some use of instructions that are not available +// on all ARM architectures. Namely: +// - Use of BLX instruction for interworking between ARM and Thumb state. +// - Use of the extended Thumb branch encoding in relocation. +// - Use of the MOVT/MOVW instructions in Thumb Thunks. +// The ARM Attributes section contains information about the architecture chosen +// at compile time. We follow the convention that if at least one input object +// is compiled with an architecture that supports these features then lld is +// permitted to use them. +static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { + if (Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) { + auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + switch (Arch) { + case ARMBuildAttrs::Pre_v4: + case ARMBuildAttrs::v4: + case ARMBuildAttrs::v4T: + // Architectures prior to v5 do not support BLX instruction + break; + case ARMBuildAttrs::v5T: + case ARMBuildAttrs::v5TE: + case ARMBuildAttrs::v5TEJ: + case ARMBuildAttrs::v6: + case ARMBuildAttrs::v6KZ: + case ARMBuildAttrs::v6K: + Config->ARMHasBlx = true; + // Architectures used in pre-Cortex processors do not support + // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception + // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do. + break; + default: + // All other Architectures have BLX and extended branch encoding + Config->ARMHasBlx = true; + Config->ARMJ1J2BranchEncoding = true; + if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M) + // All Architectures used in Cortex processors with the exception + // of v6-M and v6S-M have the MOVT and MOVW instructions. + Config->ARMHasMovtMovw = true; + } + } +} + template InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &Sec) { uint32_t Idx = Sec.sh_info; @@ -381,16 +424,20 @@ StringRef Name = getSectionName(Sec); switch (Sec.sh_type) { - case SHT_ARM_ATTRIBUTES: - // FIXME: ARM meta-data section. Retain the first attribute section - // we see. The eglibc ARM dynamic loaders require the presence of an - // attribute section for dlopen to work. - // In a full implementation we would merge all attribute sections. + case SHT_ARM_ATTRIBUTES: { + ARMAttributeParser Attributes; + ArrayRef Contents = check(this->getObj().getSectionContents(&Sec)); + Attributes.Parse(Contents, true); + updateSupportedARMFeatures(Attributes); + // FIXME: Retain the first attribute section we see. The eglibc ARM + // dynamic loaders require the presence of an attribute section for dlopen + // to work. In a full implementation we would merge all attribute sections. if (InX::ARMAttributes == nullptr) { InX::ARMAttributes = make(this, &Sec, Name); return InX::ARMAttributes; } return &InputSection::Discarded; + } case SHT_RELA: case SHT_REL: { // Find the relocation target section and associate this Index: test/ELF/arm-bl-v6.s =================================================================== --- /dev/null +++ test/ELF/arm-bl-v6.s @@ -0,0 +1,50 @@ +// RUN: llvm-mc -filetype=obj -triple=arm-none-linux-gnueabi %s -o %t +// RUN: ld.lld %t -o %t2 2>&1 | FileCheck %s +// Requires: arm + +// On Arm v6 the range of a Thumb BL instruction is only 4 megabytes as the +// extended range encoding is not supported. The following example has a Thumb +// BL that is out of range on ARM v6 and requires a range extension thunk. +// As v6 does not support MOVT or MOVW instructions the Thunk must not +// use these instructions either. At present we don't support v6 so we give a +// warning for unsupported features. + +// CHECK: warning: lld uses extended branch encoding, no object with architecture supporting feature detected. +// CHECK-NEXT: warning: lld may use movt/movw, no object with architecture supporting feature detected. + + .text + .syntax unified + .cpu arm1176jzf-s + .eabi_attribute 6, 6 @ Tag_CPU_arch + .globl _start + .type _start,%function + .balign 0x1000 +_start: + bl thumbfunc + bx lr + + .thumb + .section .text.2, "ax", %progbits + .globl thumbfunc + .type thumbfunc,%function +thumbfunc: + bl farthumbfunc + +// 6 Megabytes, enough to make farthumbfunc out of range of caller on a v6 +// Arm, but not on a v7 Arm. + .section .text.3, "ax", %progbits + .space 0x200000 + + .section .text.4, "ax", %progbits + .space 0x200000 + + .section .text.5, "ax", %progbits + .space 0x200000 + + .thumb + .section .text.6, "ax", %progbits + .balign 0x1000 + .globl farthumbfunc + .type farthumbfunc,%function +farthumbfunc: + bx lr Index: test/ELF/arm-blx-v4t.s =================================================================== --- /dev/null +++ test/ELF/arm-blx-v4t.s @@ -0,0 +1,30 @@ +// RUN: llvm-mc -filetype=obj -triple=arm-none-linux-gnueabi %s -o %t +// RUN: ld.lld %t -o %t2 2>&1 | FileCheck %s +// REQUIRES: arm + +// On Arm v4t there is no blx instruction so all interworking must go via +// a thunk. At present we don't support v4t so we give a warning for unsupported +// features. + +// CHECK: warning: lld uses blx instruction, no object with architecture supporting feature detected. +// CHECK-NEXT: warning: lld uses extended branch encoding, no object with architecture supporting feature detected. +// CHECK-NEXT: warning: lld may use movt/movw, no object with architecture supporting feature detected. + + .text + .syntax unified + .cpu arm7tdmi + .eabi_attribute 6, 2 @ Tag_CPU_arch + .arm + .globl _start + .type _start,%function + .p2align 2 +_start: + bl thumbfunc + bx lr + + .thumb + .section .text.2, "ax", %progbits + .globl thumbfunc + .type thumbfunc,%function +thumbfunc: + bx lr