From 57eb0469847bf535276f2a6364213b1fb1c2480c Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Tue, 28 Nov 2017 13:51:48 +0000 Subject: [PATCH] [ELF] Read ARM BuildAttributes section to determine supported features. lld assumes some ARM features that are not available in all Arm processors. In particular: - The blx instruction present for interworking. - The movt/movw instructions are used in Thunks. - The J1=1 J2=1 encoding of branch immediates to improve Thumb wide branch range are assumed to be present. This patch reads the ARM Attributes section to check for the architecture the object file was compiled with. If none of the objects have an architecture that supports either of these features a warning will be given. This is most likely to affect armv6 as used in the first Raspberry Pi. Differential Revision: https://reviews.llvm.org/D36823 llvm-svn: 319169 --- lld/ELF/Config.h | 3 +++ lld/ELF/Driver.cpp | 15 ++++++++++++ lld/ELF/InputFiles.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++---- lld/test/ELF/arm-bl-v6.s | 51 ++++++++++++++++++++++++++++++++++++++++ lld/test/ELF/arm-blx-v4t.s | 30 ++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 lld/test/ELF/arm-bl-v6.s create mode 100644 lld/test/ELF/arm-blx-v4t.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index d4b8510..d335617 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -105,6 +105,9 @@ struct Configuration { std::vector BuildIdVector; bool AllowMultipleDefinition; bool AndroidPackDynRelocs = false; + bool ARMHasBlx = false; + bool ARMHasMovtMovw = false; + bool ARMJ1J2BranchEncoding = false; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index e8d3639..146b01e 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1118,6 +1118,21 @@ template void LinkerDriver::link(opt::InputArgList &Args) { Config->EFlags = Target->calcEFlags(); + if (Config->EMachine == EM_ARM) { + // 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. diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 64b38f3..7d06fd7 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/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" @@ -394,6 +396,48 @@ void ObjFile::initializeSections( } } +// 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)) + return; + 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; + break; + } +} + template InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &Sec) { uint32_t Idx = Sec.sh_info; @@ -426,16 +470,20 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { 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, /*isLittle*/Config->EKind == ELF32LEKind); + 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 diff --git a/lld/test/ELF/arm-bl-v6.s b/lld/test/ELF/arm-bl-v6.s new file mode 100644 index 0000000..e077109 --- /dev/null +++ b/lld/test/ELF/arm-bl-v6.s @@ -0,0 +1,51 @@ +// 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. +// ARM v6 supports blx so we shouldn't see the blx not supported warning. +// CHECK-NOT: warning: lld uses blx instruction, 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 diff --git a/lld/test/ELF/arm-blx-v4t.s b/lld/test/ELF/arm-blx-v4t.s new file mode 100644 index 0000000..858b93f --- /dev/null +++ b/lld/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 -- 2.7.4