summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Driver.cpp2
-rw-r--r--lld/ELF/InputFiles.cpp5
-rw-r--r--lld/ELF/Target.cpp171
-rw-r--r--lld/test/ELF/Inputs/arm-plt-reloc.s14
-rw-r--r--lld/test/ELF/Inputs/far-arm-abs.s13
-rw-r--r--lld/test/ELF/Inputs/relocation-copy-arm.s22
-rw-r--r--lld/test/ELF/arm-abs32-dyn.s32
-rw-r--r--lld/test/ELF/arm-attributes-remove.s44
-rw-r--r--lld/test/ELF/arm-branch-error.s19
-rw-r--r--lld/test/ELF/arm-branch.s59
-rw-r--r--lld/test/ELF/arm-copy.s81
-rw-r--r--lld/test/ELF/arm-data-prel.s63
-rw-r--r--lld/test/ELF/arm-data-relocs.s20
-rw-r--r--lld/test/ELF/arm-fpic-got.s63
-rw-r--r--lld/test/ELF/arm-gnu-ifunc-nosym.s27
-rw-r--r--lld/test/ELF/arm-gnu-ifunc.s131
-rw-r--r--lld/test/ELF/arm-got-relative.s53
-rw-r--r--lld/test/ELF/arm-gotoff.s74
-rw-r--r--lld/test/ELF/arm-mov-relocs.s53
-rw-r--r--lld/test/ELF/arm-plt-reloc.s90
20 files changed, 1036 insertions, 0 deletions
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 866656b1b1e..18893f91861 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -71,6 +71,8 @@ static std::pair<ELFKind, uint16_t> parseEmulation(StringRef S) {
return {ELF64LEKind, EM_X86_64};
if (S == "aarch64linux")
return {ELF64LEKind, EM_AARCH64};
+ if (S == "armelf_linux_eabi")
+ return {ELF32LEKind, EM_ARM};
if (S == "i386pe" || S == "i386pep" || S == "thumb2pe")
error("Windows targets are not supported on the ELF frontend: " + S);
else
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 48d04f894a3..19109bfa6d9 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -244,6 +244,11 @@ void elf::ObjectFile<ELFT>::initializeSections(
}
fatal("relocations pointing to SHF_MERGE are not supported");
}
+ case SHT_ARM_ATTRIBUTES:
+ // FIXME: ARM meta-data section. At present attributes are ignored,
+ // they can be used to reason about object compatibility.
+ Sections[I] = &InputSection<ELFT>::Discarded;
+ break;
default:
Sections[I] = createInputSection(Sec);
}
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 529abacae5f..562c9710517 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -170,6 +170,19 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
};
+class ARMTargetInfo final : public TargetInfo {
+public:
+ ARMTargetInfo();
+ RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
+ uint32_t getDynRel(uint32_t Type) const override;
+ uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override;
+ void writePltZero(uint8_t *Buf) const override;
+ void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
+ int32_t Index, unsigned RelOff) const override;
+ void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+};
+
template <class ELFT> class MipsTargetInfo final : public TargetInfo {
public:
MipsTargetInfo();
@@ -196,6 +209,8 @@ TargetInfo *createTarget() {
return new AArch64TargetInfo();
case EM_AMDGPU:
return new AMDGPUTargetInfo();
+ case EM_ARM:
+ return new ARMTargetInfo();
case EM_MIPS:
switch (Config->EKind) {
case ELF32LEKind:
@@ -1427,6 +1442,162 @@ RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
llvm_unreachable("not implemented");
}
+ARMTargetInfo::ARMTargetInfo() {
+ CopyRel = R_ARM_COPY;
+ RelativeRel = R_ARM_RELATIVE;
+ IRelativeRel = R_ARM_IRELATIVE;
+ GotRel = R_ARM_GLOB_DAT;
+ PltRel = R_ARM_JUMP_SLOT;
+ TlsGotRel = R_ARM_TLS_TPOFF32;
+ TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
+ TlsOffsetRel = R_ARM_TLS_DTPOFF32;
+ PltEntrySize = 16;
+ PltZeroSize = 20;
+}
+
+RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
+ switch (Type) {
+ default:
+ return R_ABS;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ return R_PLT_PC;
+ case R_ARM_GOTOFF32:
+ // (S + A) - GOT_ORG
+ return R_GOTREL;
+ case R_ARM_GOT_BREL:
+ // GOT(S) + A - GOT_ORG
+ return R_GOT_OFF;
+ case R_ARM_GOT_PREL:
+ // GOT(S) + - GOT_ORG
+ return R_GOT_PC;
+ case R_ARM_BASE_PREL:
+ // B(S) + A - P
+ // FIXME: currently B(S) assumed to be .got, this may not hold for all
+ // platforms.
+ return R_GOTONLY_PC;
+ case R_ARM_PREL31:
+ case R_ARM_REL32:
+ return R_PC;
+ }
+}
+
+uint32_t ARMTargetInfo::getDynRel(uint32_t Type) const {
+ if (Type == R_ARM_ABS32)
+ return Type;
+ StringRef S = getELFRelocationTypeName(EM_ARM, Type);
+ error("relocation " + S + " cannot be used when making a shared object; "
+ "recompile with -fPIC.");
+ // Keep it going with a dummy value so that we can find more reloc errors.
+ return R_ARM_ABS32;
+}
+
+void ARMTargetInfo::writeGotPlt(uint8_t *Buf, uint64_t Plt) const {
+ write32le(Buf, Out<ELF32LE>::Plt->getVA());
+}
+
+void ARMTargetInfo::writePltZero(uint8_t *Buf) const {
+ const uint8_t PltData[] = {
+ 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
+ 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
+ 0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
+ 0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8]
+ 0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8
+ };
+ memcpy(Buf, PltData, sizeof(PltData));
+ uint64_t GotPlt = Out<ELF32LE>::GotPlt->getVA();
+ uint64_t L1 = Out<ELF32LE>::Plt->getVA() + 8;
+ write32le(Buf + 16, GotPlt - L1 - 8);
+}
+
+void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) const {
+ // FIXME: Using simple code sequence with simple relocations.
+ // There is a more optimal sequence but it requires support for the group
+ // relocations. See ELF for the ARM Architecture Appendix A.3
+ const uint8_t PltData[] = {
+ 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
+ 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
+ 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8
+ };
+ memcpy(Buf, PltData, sizeof(PltData));
+ uint64_t L1 = PltEntryAddr + 4;
+ write32le(Buf + 12, GotEntryAddr - L1 - 8);
+}
+
+void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
+ uint64_t Val) const {
+ switch (Type) {
+ case R_ARM_NONE:
+ break;
+ case R_ARM_ABS32:
+ case R_ARM_BASE_PREL:
+ case R_ARM_GOTOFF32:
+ case R_ARM_GOT_BREL:
+ case R_ARM_GOT_PREL:
+ case R_ARM_REL32:
+ write32le(Loc, Val);
+ break;
+ case R_ARM_PREL31:
+ checkInt<31>(Val, Type);
+ write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
+ break;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ checkInt<26>(Val, Type);
+ write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
+ break;
+ case R_ARM_MOVW_ABS_NC:
+ write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) |
+ (Val & 0x0fff));
+ break;
+ case R_ARM_MOVT_ABS:
+ checkUInt<32>(Val, Type);
+ write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
+ (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
+ break;
+ default:
+ fatal("unrecognized reloc " + Twine(Type));
+ }
+}
+
+uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
+ uint32_t Type) const {
+ switch (Type) {
+ default:
+ return 0;
+ case R_ARM_ABS32:
+ case R_ARM_BASE_PREL:
+ case R_ARM_GOTOFF32:
+ case R_ARM_GOT_BREL:
+ case R_ARM_GOT_PREL:
+ case R_ARM_REL32:
+ return SignExtend64<32>(read32le(Buf));
+ break;
+ case R_ARM_PREL31:
+ return SignExtend64<31>(read32le(Buf));
+ break;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ return SignExtend64<26>((read32le(Buf) & 0x00ffffff) << 2);
+ case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVT_ABS: {
+ // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and
+ // MOVT is in the range -32768 <= A < 32768
+ uint64_t Val = read32le(Buf) & 0x000f0fff;
+ return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff));
+ }
+ }
+}
+
template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
GotPltHeaderEntriesNum = 2;
PageSize = 65536;
diff --git a/lld/test/ELF/Inputs/arm-plt-reloc.s b/lld/test/ELF/Inputs/arm-plt-reloc.s
new file mode 100644
index 00000000000..29f133e6eb8
--- /dev/null
+++ b/lld/test/ELF/Inputs/arm-plt-reloc.s
@@ -0,0 +1,14 @@
+.text
+ .align 2
+ .globl func1
+ .type func1,%function
+func1:
+ bx lr
+ .globl func2
+ .type func2,%function
+func2:
+ bx lr
+ .globl func3
+ .type func3,%function
+func3:
+ bx lr
diff --git a/lld/test/ELF/Inputs/far-arm-abs.s b/lld/test/ELF/Inputs/far-arm-abs.s
new file mode 100644
index 00000000000..68d6aabe00b
--- /dev/null
+++ b/lld/test/ELF/Inputs/far-arm-abs.s
@@ -0,0 +1,13 @@
+.global far
+.type far,%function
+far = 0x201001c
+
+.global too_far1
+.type too_far1,%function
+too_far1 = 0x2020008
+.global too_far2
+.type too_far2,%function
+too_far2 = 0x202000c
+.global too_far3
+.type too_far3,%function
+too_far3 = 0x2020010
diff --git a/lld/test/ELF/Inputs/relocation-copy-arm.s b/lld/test/ELF/Inputs/relocation-copy-arm.s
new file mode 100644
index 00000000000..ba5ab73043c
--- /dev/null
+++ b/lld/test/ELF/Inputs/relocation-copy-arm.s
@@ -0,0 +1,22 @@
+.bss
+
+.type x,%object
+.globl x
+.balign 16
+x:
+.long 0
+.size x, 4
+
+.type y,%object
+.globl y
+.balign 16
+y:
+.long 0
+.size y, 4
+
+.type z,%object
+.globl z
+.balign 4
+z:
+.long 0
+.size z, 4
diff --git a/lld/test/ELF/arm-abs32-dyn.s b/lld/test/ELF/arm-abs32-dyn.s
new file mode 100644
index 00000000000..68183fe6f19
--- /dev/null
+++ b/lld/test/ELF/arm-abs32-dyn.s
@@ -0,0 +1,32 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux %s -o %t.o
+
+// Creates a R_ARM_ABS32 relocation against foo and bar, bar has hidden
+// visibility so we expect a R_ARM_RELATIVE
+ .syntax unified
+ .globl foo
+foo:
+ .globl bar
+ .hidden bar
+bar:
+
+ .data
+ .word foo
+ .word bar
+
+// RUN: ld.lld -shared -o %t.so %t.o
+// RUN: llvm-readobj -symbols -dyn-relocations %t.so | FileCheck %s
+
+// CHECK: Dynamic Relocations {
+// CHECK-NEXT: 0x2004 R_ARM_RELATIVE
+// CHECK-NEXT: 0x2000 R_ARM_ABS32 foo 0x0
+// CHECK-NEXT: }
+
+// CHECK: Symbols [
+// CHECK: Symbol {
+// CHECK: Name: bar
+// CHECK-NEXT: Value: 0x1000
+
+// CHECK: Symbol {
+// CHECK: Name: foo
+// CHECK-NEXT: Value: 0x1000
diff --git a/lld/test/ELF/arm-attributes-remove.s b/lld/test/ELF/arm-attributes-remove.s
new file mode 100644
index 00000000000..04f3e829f40
--- /dev/null
+++ b/lld/test/ELF/arm-attributes-remove.s
@@ -0,0 +1,44 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s %t | FileCheck %s
+// RUN: ld.lld %t.o -shared -o %t2
+// RUN: llvm-readobj -s %t2 | FileCheck %s
+// RUN: ld.lld %t.o -r -o %t3
+// RUN: llvm-readobj -s %t3 | FileCheck %s
+
+// The .ARM.attributes section should be removed from executables and
+// shared objects.
+
+// At present we remove it from the -r object output as well which isn't ideal.
+// Unfortunately combining per-object attributes cannot be safely done by just
+// concatentation of input sections.
+
+// CHECK-NOT: Name: .ARM.attributes
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09" @ Tag_conformance
+ .cpu cortex-a8
+ .eabi_attribute 6, 10 @ Tag_CPU_arch
+ .eabi_attribute 7, 65 @ Tag_CPU_arch_profile
+ .eabi_attribute 8, 1 @ Tag_ARM_ISA_use
+ .eabi_attribute 9, 2 @ Tag_THUMB_ISA_use
+ .fpu neon
+ .eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
+ .eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
+ .eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
+ .eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
+ .eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
+ .eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
+ .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access
+ .eabi_attribute 24, 1 @ Tag_ABI_align_needed
+ .eabi_attribute 25, 1 @ Tag_ABI_align_preserved
+ .eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
+ .eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
+ .eabi_attribute 26, 2 @ Tag_ABI_enum_size
+ .eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
+ .eabi_attribute 68, 1 @ Tag_Virtualization_use
+ .globl _start
+ .p2align 2
+ .type _start,%function
+_start:
+ bx lr
diff --git a/lld/test/ELF/arm-branch-error.s b/lld/test/ELF/arm-branch-error.s
new file mode 100644
index 00000000000..f1a855d7373
--- /dev/null
+++ b/lld/test/ELF/arm-branch-error.s
@@ -0,0 +1,19 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
+// RUN: not ld.lld %t %tfar -o %t2 2>&1 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .section .text, "ax",%progbits
+ .globl _start
+ .balign 0x10000
+ .type _start,%function
+_start:
+ // address of too_far symbols are just out of range of ARM branch with
+ // 26-bit immediate field and an addend of -8
+ bl too_far1
+ b too_far2
+ beq too_far3
+
+// CHECK: R_ARM_CALL out of range
+// CHECK-NEXT: R_ARM_JUMP24 out of range
+// CHECK-NEXT: R_ARM_JUMP24 out of range
diff --git a/lld/test/ELF/arm-branch.s b/lld/test/ELF/arm-branch.s
new file mode 100644
index 00000000000..38266fabf85
--- /dev/null
+++ b/lld/test/ELF/arm-branch.s
@@ -0,0 +1,59 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
+// RUN: echo "SECTIONS { \
+// RUN: .callee1 : { *(.callee_low) } \
+// RUN: .caller : { *(.text) } \
+// RUN: .callee2 : { *(.callee_high) } } " > %t.script
+// RUN: ld.lld --script %t.script %t %tfar -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .section .callee_low, "ax",%progbits
+ .align 2
+ .type callee_low,%function
+callee_low:
+ bx lr
+
+ .section .text, "ax",%progbits
+ .globl _start
+ .balign 0x10000
+ .type _start,%function
+_start:
+ bl callee_low
+ b callee_low
+ beq callee_low
+ bl callee_high
+ b callee_high
+ bne callee_high
+ bl far
+ b far
+ bgt far
+ bx lr
+
+ .section .callee_high, "ax",%progbits
+ .align 2
+ .type callee_high,%function
+callee_high:
+ bx lr
+
+// CHECK: Disassembly of section .caller:
+// CHECK-NEXT: _start:
+// S(callee_low) = 0xb4 P = 0x10000 A = -8 = -0xff54 = -65364
+// CHECK-NEXT: 10000: 2b c0 ff eb bl #-65364 <callee_low>
+// S(callee_low) = 0xb4 P = 0x10004 A = -8 = -0xff58 = -65368
+// CHECK-NEXT: 10004: 2a c0 ff ea b #-65368 <callee_low>
+// S(callee_low) = 0xb4 P = 0x10008 A = -8 = -0xff5c -65372
+// CHECK-NEXT: 10008: 29 c0 ff 0a beq #-65372 <callee_low>
+// S(callee_high) = 0x10028 P = 0x1000c A = -8 = 0x14 = 20
+// CHECK-NEXT: 1000c: 05 00 00 eb bl #20 <callee_high>
+// S(callee_high) = 0x10028 P = 0x10010 A = -8 = 0x10 = 16
+// CHECK-NEXT: 10010: 04 00 00 ea b #16 <callee_high>
+// S(callee_high) = 0x10028 P = 0x10014 A = -8 = 0x0c =12
+// CHECK-NEXT: 10014: 03 00 00 1a bne #12 <callee_high>
+// S(far) = 0x201001c P = 0x10018 A = -8 = 0x1fffffc = 33554428
+// CHECK-NEXT: 10018: ff ff 7f eb bl #33554428
+// S(far) = 0x201001c P = 0x1001c A = -8 = 0x1fffff8 = 33554424
+// CHECK-NEXT: 1001c: fe ff 7f ea b #33554424
+// S(far) = 0x201001c P = 0x10020 A = -8 = 0x1fffff4 = 33554420
+// CHECK-NEXT: 10020: fd ff 7f ca bgt #33554420
+// CHECK-NEXT: 10024: 1e ff 2f e1 bx lr
diff --git a/lld/test/ELF/arm-copy.s b/lld/test/ELF/arm-copy.s
new file mode 100644
index 00000000000..e5ce1577bab
--- /dev/null
+++ b/lld/test/ELF/arm-copy.s
@@ -0,0 +1,81 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/relocation-copy-arm.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+// RUN: ld.lld %t.o %t2.so -o %t3
+// RUN: llvm-readobj -s -r --expand-relocs -symbols %t3 | FileCheck %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CODE %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi -section=.rodata %t3 | FileCheck -check-prefix=RODATA %s
+
+// Copy relocations R_ARM_COPY are required for y and z
+ .syntax unified
+ .text
+ .globl _start
+_start:
+ movw r2,:lower16: y
+ movt r2,:upper16: y
+ ldr r3,[pc,#4]
+ ldr r3,[r3,#0]
+ .rodata
+ .word z
+
+// CHECK: Name: .bss
+// CHECK-NEXT: Type: SHT_NOBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x13000
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment: 16
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section (5) .rel.dyn {
+// CHECK-NEXT: Relocation {
+// CHECK-NEXT: Offset: 0x13000
+// CHECK-NEXT: Type: R_ARM_COPY
+// CHECK-NEXT: Symbol: y
+// CHECK-NEXT: Addend: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Relocation {
+// CHECK-NEXT: Offset: 0x13004
+// CHECK-NEXT: Type: R_ARM_COPY
+// CHECK-NEXT: Symbol: z
+// CHECK-NEXT: Addend: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// CHECK: Symbols [
+// CHECK: Name: y
+// CHECK-NEXT: Value: 0x13000
+// CHECK-NEXT: Size: 4
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section: .bss
+// CHECK: Name: z
+// CHECK-NEXT: Value: 0x13004
+// CHECK-NEXT: Size: 4
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+
+// CODE: Disassembly of section .text:
+// CODE-NEXT: _start:
+// S(y) = 0x13000, A = 0
+// (S + A) & 0x0000ffff = 0x3000 = #12288
+// CODE-NEXT: 11000: 00 20 03 e3 movw r2, #12288
+// S(y) = 0x13000, A = 0
+// ((S + A) & 0xffff0000) >> 16 = 0x1
+// CODE-NEXT: 11004: 01 20 40 e3 movt r2, #1
+// CODE-NEXT: 11008: 04 30 9f e5 ldr r3, [pc, #4]
+// CODE-NEXT: 1100c: 00 30 93 e5 ldr r3, [r3]
+
+
+// RODATA: Contents of section .rodata:
+// S(z) = 0x13004
+// RODATA-NEXT: 10114 04300100
diff --git a/lld/test/ELF/arm-data-prel.s b/lld/test/ELF/arm-data-prel.s
new file mode 100644
index 00000000000..590d8118ae6
--- /dev/null
+++ b/lld/test/ELF/arm-data-prel.s
@@ -0,0 +1,63 @@
+// RUN: llvm-mc %s -triple=armv7-unknown-linux-gnueabi -filetype=obj -o %t.o
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .ARM.exidx : { *(.ARM.exidx) } \
+// RUN: .ARM.exidx.TEST1 : { *(.ARM.exidx.TEST1) } \
+// RUN: .TEST1 : { *(.TEST1) } } " > %t.script
+// RUN: ld.lld --script %t.script %t.o -o %t
+// RUN: llvm-readobj -s -sd %t | FileCheck --check-prefix=CHECK %s
+// REQUIRES: arm
+
+// The R_ARM_PREL31 relocation is used in by the .ARM.exidx exception tables
+// bit31 of the place denotes whether the field is an inline table entry
+// (bit31=1) or relocation (bit31=0)
+// The linker must preserve the value of bit31
+
+// This test case is adapted from llvm/test/MC/ARM/eh-compact-pr0.s
+// We use a linker script to place the .ARM.exidx sections in between
+// the code sections so that we can test positive and negative offsets
+ .syntax unified
+
+ .section .TEST1, "ax",%progbits
+ .globl _start
+ .align 2
+ .type _start,%function
+_start:
+ .fnstart
+ .save {r11, lr}
+ push {r11, lr}
+ .setfp r11, sp
+ mov r11, sp
+ pop {r11, lr}
+ mov pc, lr
+ .fnend
+
+ .section .text, "ax",%progbits
+// The generated .ARM.exidx section will refer to the personality
+// routine __aeabi_unwind_cpp_pr0. Provide a dummy implementation
+// to stop an undefined symbol error
+ .globl __aeabi_unwind_cpp_pr0
+ .align 2
+ .type __aeabi_unwind_cpp_pr0,%function
+__aeabi_unwind_cpp_pr0:
+ .fnstart
+ bx lr
+ .fnend
+
+// The expected value of the exception table is
+// Word0 0 in bit 31, -4 encoded in 31-bit signed offset
+// Word1 Inline table entry EHT Inline Personality Routine #0
+// CHECK: Name: .ARM.exidx
+// CHECK: SectionData (
+// CHECK: 0000: FCFFFF7F B0B0B080
+// CHECK: )
+
+// The expected value of the exception table is
+// Word0 0 in bit 31, +8 encoded in 31-bit signed offset
+// Word1 Inline table entry EHT Inline Personality Routine #0
+// set vsp = r11
+// pop r11, r14
+// CHECK: Name: .ARM.exidx.TEST1
+// CHECK: SectionData (
+// CHECK: 0000: 08000000 80849B80
+// CHECK: )
diff --git a/lld/test/ELF/arm-data-relocs.s b/lld/test/ELF/arm-data-relocs.s
new file mode 100644
index 00000000000..ed237850c4c
--- /dev/null
+++ b/lld/test/ELF/arm-data-relocs.s
@@ -0,0 +1,20 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/abs256.s -o %t256.o
+// RUN: ld.lld %t %t256.o -o %t2
+// RUN: llvm-objdump -d %t2 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .globl _start
+_start:
+ .section .R_ARM_ABS32POS, "ax",%progbits
+ .word foo + 0x24
+
+// S = 0x100, A = 0x24
+// S + A = 0x124
+// CHECK: Disassembly of section .R_ARM_ABS32POS:
+// CHECK: 11000: 24 01 00 00
+ .section .R_ARM_ABS32NEG, "ax",%progbits
+ .word foo - 0x24
+// S = 0x100, A = -0x24
+// CHECK: Disassembly of section .R_ARM_ABS32NEG:
+// CHECK: 11004: dc 00 00 00
diff --git a/lld/test/ELF/arm-fpic-got.s b/lld/test/ELF/arm-fpic-got.s
new file mode 100644
index 00000000000..4b6002d3c07
--- /dev/null
+++ b/lld/test/ELF/arm-fpic-got.s
@@ -0,0 +1,63 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s %t | FileCheck %s
+// RUN: llvm-readobj -s -symbols %t | FileCheck -check-prefix=SYMBOLS %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t | FileCheck -check-prefix=CODE %s
+
+// Test the R_ARM_GOT_PREL relocation
+ .syntax unified
+ .text
+ .globl _start
+ .align 2
+_start:
+ ldr r0, .LCPI0_0
+.LPC0_0:
+ ldr r0, [pc, r0]
+ ldr r0, [r0]
+ bx lr
+.LCPI0_0:
+.Ltmp0:
+ // Generate R_ARM_GOT_PREL
+ .long val(GOT_PREL)-((.LPC0_0+8)-.Ltmp0)
+
+ .data
+ .type val,%object
+ .globl val
+ .align 2
+val:
+ .long 10
+ .size val, 4
+
+// CHECK: Section {
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 4
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment: 4
+// CHECK-NEXT: EntrySize:
+
+// SYMBOLS: Name: val
+// SYMBOLS-NEXT: Value: 0x13000
+// SYMBOLS-NEXT: Size: 4
+// SYMBOLS-NEXT: Binding: Global
+// SYMBOLS-NEXT: Type: Object
+// SYMBOLS-NEXT: Other:
+// SYMBOLS-NEXT: Section: .data
+
+// CODE: Disassembly of section .text:
+// CODE-NEXT: _start:
+// CODE-NEXT: 11000: 08 00 9f e5 ldr r0, [pc, #8]
+// CODE-NEXT: 11004: 00 00 9f e7 ldr r0, [pc, r0]
+// CODE-NEXT: 11008: 00 00 90 e5 ldr r0, [r0]
+// CODE-NEXT: 1100c: 1e ff 2f e1 bx lr
+// CODE: $d.1:
+// 0x11004 + 0x0ff4 + 8 = 0x12000 = .got
+// CODE-NEXT: 11010: f4 0f 00 00
diff --git a/lld/test/ELF/arm-gnu-ifunc-nosym.s b/lld/test/ELF/arm-gnu-ifunc-nosym.s
new file mode 100644
index 00000000000..fa79aef7ced
--- /dev/null
+++ b/lld/test/ELF/arm-gnu-ifunc-nosym.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld -static %t.o -o %tout
+// RUN: llvm-readobj -symbols %tout | FileCheck %s
+// REQUIRES: arm
+
+// Check that no __rel_iplt_end/__rel_iplt_start
+// appear in symtab if there are no references to them.
+// CHECK: Symbols [
+// CHECK-NOT: __rel_iplt_end
+// CHECK-NOT: __rel_iplt_start
+// CHECK: ]
+ .syntax unified
+ .text
+ .type foo STT_GNU_IFUNC
+ .globl foo
+foo:
+ bx lr
+
+ .type bar STT_GNU_IFUNC
+ .globl bar
+bar:
+ bx lr
+
+ .globl _start
+_start:
+ bl foo
+ bl bar
diff --git a/lld/test/ELF/arm-gnu-ifunc.s b/lld/test/ELF/arm-gnu-ifunc.s
new file mode 100644
index 00000000000..c1e8a718353
--- /dev/null
+++ b/lld/test/ELF/arm-gnu-ifunc.s
@@ -0,0 +1,131 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld -static %t.o -o %tout
+// RUN: llvm-objdump -triple armv7a-none-linux-gnueabi -d %tout | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .text
+ .type foo STT_GNU_IFUNC
+ .globl foo
+foo:
+ bx lr
+
+ .type bar STT_GNU_IFUNC
+ .globl bar
+bar:
+ bx lr
+
+ .globl _start
+_start:
+ bl foo
+ bl bar
+ movw r0,:lower16:__rel_iplt_start
+ movt r0,:upper16:__rel_iplt_start
+ movw r0,:lower16:__rel_iplt_end
+ movt r0,:upper16:__rel_iplt_end
+
+// CHECK: Sections [
+// CHECK: Section {
+// CHECK: Index: 1
+// CHECK-NEXT: Name: .rel.plt
+// CHECK-NEXT: Type: SHT_REL
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: [[REL:.*]]
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 16
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment: 4
+// CHECK-NEXT: EntrySize: 8
+// CHECK-NEXT: }
+// CHECK: Relocations [
+// CHECK-NEXT: Section (1) .rel.plt {
+// CHECK-NEXT: 0x1200C R_ARM_IRELATIVE
+// CHECK-NEXT: 0x12010 R_ARM_IRELATIVE
+// CHECK-NEXT: }
+// CHECK-NEXT:]
+// CHECK: Symbols [
+// CHECK: Symbol {
+// CHECK: Name: __rel_iplt_end
+// CHECK-NEXT: Value: 0x100E4
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .rel.plt
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: __rel_iplt_start
+// CHECK-NEXT: Value: 0x100D4
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .rel.plt
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: _start (6)
+// CHECK-NEXT: Value: 0x11008
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section: .text
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: bar
+// CHECK-NEXT: Value: 0x11004
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: GNU_IFunc
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .text
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: foo
+// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: GNU_IFunc
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .text
+// CHECK-NEXT: }
+
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: foo:
+// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
+// DISASM: bar:
+// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
+// DISASM: _start:
+// DISASM-NEXT: 11008: 09 00 00 eb bl #36
+// DISASM-NEXT: 1100c: 0c 00 00 eb bl #48
+// DISASM-NEXT: 11010: d4 00 00 e3 movw r0, #212
+// DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1
+// r0 = 212 + 1 * 65536 = 100D4 = __rel_iplt_start
+// DISASM-NEXT: 11018: e4 00 00 e3 movw r0, #228
+// DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1
+// r1 = 228 + 1 * 65536 = 100E4 = __rel_iplt_end
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 11020: 04 e0 2d e5 str lr, [sp, #-4]!
+// DISASM-NEXT: 11024: 04 e0 9f e5 ldr lr, [pc, #4]
+// DISASM-NEXT: 11028: 0e e0 8f e0 add lr, pc, lr
+// DISASM-NEXT: 1102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// 0x0fd0 + 0x11028 + 0x8 = 0x12000
+// DISASM-NEXT: 11030: d0 0f 00 00
+// DISASM-NEXT: 11034: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11038: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 1103c: 00 f0 9c e5 ldr pc, [r12]
+// 0x0fcc + 0x11038 + 0x8 = 0x1200C
+// DISASM-NEXT: 11040: cc 0f 00 00
+// DISASM-NEXT: 11044: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11048: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 1104c: 00 f0 9c e5 ldr pc, [r12]
+// 0x0fc0 + 0x11048 + 0x8 = 0x12010
+// DISASM-NEXT: 11050: c0 0f 00 00
diff --git a/lld/test/ELF/arm-got-relative.s b/lld/test/ELF/arm-got-relative.s
new file mode 100644
index 00000000000..22ccb16a2a5
--- /dev/null
+++ b/lld/test/ELF/arm-got-relative.s
@@ -0,0 +1,53 @@
+// REQUIRES: arm
+// RUN: llvm-mc -position-independent -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -shared -o %t
+// RUN: llvm-readobj -s -symbols -dyn-relocations %t | FileCheck %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t | FileCheck -check-prefix=CODE %s
+ .syntax unified
+ .text
+ .globl _start
+ .align 2
+_start:
+ .type _start, %function
+ ldr r3, .LGOT
+ ldr r2, .LGOT+4
+.LPIC:
+ add r0, pc, r3
+ bx lr
+ .align 2
+.LGOT:
+ // gas implicitly uses (GOT_PREL) for _GLOBAL_OFFSET_TABLE_ in PIC
+ // llvm-mc needs the (GOT_PREL) suffix or it generates R_ARM_REL32
+ .word _GLOBAL_OFFSET_TABLE_(GOT_PREL) - (.LPIC+8)
+ .word function(GOT)
+
+ .globl function
+ .align 2
+function:
+ .type function, %function
+ bx lr
+
+// CHECK: Dynamic Relocations {
+// CHECK-NEXT: 0x204C R_ARM_GLOB_DAT function 0x0
+
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (16)
+// CHECK-NEXT: Value: 0x0
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: Absolute
+
+// CODE: Disassembly of section .text:
+// CODE-NEXT: _start:
+// CODE-NEXT: 1000: 08 30 9f e5 ldr r3, [pc, #8]
+// CODE-NEXT: 1004: 08 20 9f e5 ldr r2, [pc, #8]
+// CODE-NEXT: 1008: 03 00 8f e0 add r0, pc, r3
+// CODE-NEXT: 100c: 1e ff 2f e1 bx lr
+// CODE:$d.1:
+// (_GLOBAL_OFFSET_TABLE_ = 0x2048) - (0x1008 + 8) 0x1038
+// CODE-NEXT: 1010: 38 10 00 00
+// (Got(function) - GotBase = 0x4
+// CODE-NEXT: 1014: 04 00 00 00
diff --git a/lld/test/ELF/arm-gotoff.s b/lld/test/ELF/arm-gotoff.s
new file mode 100644
index 00000000000..5169f84e6a0
--- /dev/null
+++ b/lld/test/ELF/arm-gotoff.s
@@ -0,0 +1,74 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s -r -t %t | FileCheck %s
+// RUN: llvm-objdump -triple=armv7a-linux-gnueabi -d %t | FileCheck --check-prefix=DISASM %s
+// REQUIRES: arm
+
+// Test the R_ARM_GOTOFF32 relocation
+
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Offset: 0x2000
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment:
+
+// CHECK: Name: .bss
+// CHECK-NEXT: Type: SHT_NOBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 20
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment: 1
+
+// CHECK-NEXT: EntrySize: 0
+
+// CHECK: Symbol {
+// CHECK: Name: bar
+// CHECK-NEXT: Value: 0x12000
+// CHECK-NEXT: Size: 10
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: obj
+// CHECK-NEXT: Value: 0x1200A
+// CHECK-NEXT: Size: 10
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT :_start:
+// DISASM-NEXT 11000: 1e ff 2f e1 bx lr
+// Offset 0 from .got = bar
+// DISASM 11004: 00 00 00 00
+// Offset 10 from .got = obj
+// DISASM-NEXT 11008: 0a 00 00 00
+// Offset 15 from .got = obj +5
+// DISASM-NEXT 1100c: 0f 00 00 00
+ .syntax unified
+ .globl _start
+_start:
+ bx lr
+ .word bar(GOTOFF)
+ .word obj(GOTOFF)
+ .word obj(GOTOFF)+5
+ .type bar, %object
+ .comm bar, 10
+ .type obj, %object
+ .comm obj, 10
diff --git a/lld/test/ELF/arm-mov-relocs.s b/lld/test/ELF/arm-mov-relocs.s
new file mode 100644
index 00000000000..15a03789316
--- /dev/null
+++ b/lld/test/ELF/arm-mov-relocs.s
@@ -0,0 +1,53 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-unknown-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2
+// RUN: llvm-objdump -d %t2 -triple=armv7a-unknown-linux-gnueabi | FileCheck %s
+// REQUIRES: arm
+
+// Test the R_ARM_MOVW_ABS_NC and R_ARM_MOVT_ABS relocations
+ .syntax unified
+ .globl _start
+_start:
+ .section .R_ARM_MOVW_ABS_NC, "ax",%progbits
+ movw r0, :lower16:label
+ movw r1, :lower16:label1
+ movw r2, :lower16:label2 + 4
+ movw r3, :lower16:label3
+ movw r4, :lower16:label3 + 4
+// CHECK: Disassembly of section .R_ARM_MOVW_ABS_NC
+// CHECK: movw r0, #0
+// CHECK: movw r1, #4
+// CHECK: movw r2, #12
+// CHECK: movw r3, #65532
+// CHECK: movw r4, #0
+ .section .R_ARM_MOVT_ABS, "ax",%progbits
+ movt r0, :upper16:label
+ movt r1, :upper16:label1
+// FIXME: We shouldn't need to multiply by 65536.
+// arguably llvm-mc incorrectly assembles addends for
+// SHT_REL relocated movt instructions. When there is a relocation
+// the interpretation of the addend for SHT_REL is not shifted
+ movt r2, :upper16:label2 + (4 * 65536)
+ movt r3, :upper16:label3
+// FIXME: We shouldn't need to multiply by 65536 see comment above.
+ movt r4, :upper16:label3 + (4 * 65536)
+// CHECK: Disassembly of section .R_ARM_MOVT_ABS
+// CHECK: movt r0, #2
+// CHECK: movt r1, #2
+// CHECK: movt r2, #2
+// CHECK: movt r3, #2
+// CHECK: movt r4, #3
+
+ .section .destination, "aw",%progbits
+ .balign 65536
+label:
+ .word 0
+label1:
+ .word 1
+label2:
+ .word 2
+// Test label3 is immediately below 2^16 alignment boundary
+ .space 65536 - 16
+label3:
+ .word 3
+// label3 + 4 is on a 2^16 alignment boundary
+ .word 4
diff --git a/lld/test/ELF/arm-plt-reloc.s b/lld/test/ELF/arm-plt-reloc.s
new file mode 100644
index 00000000000..0616aa7966a
--- /dev/null
+++ b/lld/test/ELF/arm-plt-reloc.s
@@ -0,0 +1,90 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-plt-reloc.s -o %t1
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t2
+// RUN: ld.lld %t1 %t2 -o %t
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t | FileCheck %s
+// RUN: ld.lld -shared %t1 %t2 -o %t3
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t3 | FileCheck -check-prefix=DSO %s
+// RUN: llvm-readobj -s -r %t3 | FileCheck -check-prefix=DSOREL %s
+// REQUIRES: arm
+//
+// Test PLT entry generation
+ .syntax unified
+ .text
+ .align 2
+ .globl _start
+ .type _start,%function
+_start:
+ b func1
+ bl func2
+ beq func3
+
+// Executable, expect no PLT
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: func1:
+// CHECK-NEXT: 11000: 1e ff 2f e1 bx lr
+// CHECK: func2:
+// CHECK-NEXT: 11004: 1e ff 2f e1 bx lr
+// CHECK: func3:
+// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr
+// CHECK: _start:
+// CHECK-NEXT: 1100c: fb ff ff ea b #-20 <func1>
+// CHECK-NEXT: 11010: fb ff ff eb bl #-20 <func2>
+// CHECK-NEXT: 11014: fb ff ff 0a beq #-20 <func3>
+
+// Expect PLT entries as symbols can be preempted
+// DSO: Disassembly of section .text:
+// DSO-NEXT: func1:
+// DSO-NEXT: 1000: 1e ff 2f e1 bx lr
+// DSO: func2:
+// DSO-NEXT: 1004: 1e ff 2f e1 bx lr
+// DSO: func3:
+// DSO-NEXT: 1008: 1e ff 2f e1 bx lr
+// DSO: _start:
+// S(0x1034) - P(0x100c) + A(-8) = 0x20 = 32
+// DSO-NEXT: 100c: 08 00 00 ea b #32
+// S(0x1044) - P(0x1010) + A(-8) = 0x2c = 44
+// DSO-NEXT: 1010: 0b 00 00 eb bl #44
+// S(0x1054) - P(0x1014) + A(-8) = 0x38 = 56
+// DSO-NEXT: 1014: 0e 00 00 0a beq #56
+// DSO: Disassembly of section .plt:
+// DSO-NEXT:.plt:
+// DSO-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
+// DSO-NEXT: 1024: 04 e0 9f e5 ldr lr, [pc, #4]
+// DSO-NEXT: 1028: 0e e0 8f e0 add lr, pc, lr
+// DSO-NEXT: 102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// 0x1028 + 8 + 1fd0 = 0x3000
+// DSO-NEXT: 1030: d0 1f 00 00
+// DSO-NEXT: 1034: 04 c0 9f e5 ldr r12, [pc, #4]
+// DSO-NEXT: 1038: 0f c0 8c e0 add r12, r12, pc
+// DSO-NEXT: 103c: 00 f0 9c e5 ldr pc, [r12]
+// 0x1038 + 8 + 1fcc = 0x300c
+// DSO-NEXT: 1040: cc 1f 00 00
+// DSO-NEXT: 1044: 04 c0 9f e5 ldr r12, [pc, #4]
+// DSO-NEXT: 1048: 0f c0 8c e0 add r12, r12, pc
+// DSO-NEXT: 104c: 00 f0 9c e5 ldr pc, [r12]
+// 0x1048 + 8 + 1fc0 = 0x3010
+// DSO-NEXT: 1050: c0 1f 00 00
+// DSO-NEXT: 1054: 04 c0 9f e5 ldr r12, [pc, #4]
+// DSO-NEXT: 1058: 0f c0 8c e0 add r12, r12, pc
+// DSO-NEXT: 105c: 00 f0 9c e5 ldr pc, [r12]
+// 0x1058 + 8 + 1fb4 = 0x3014
+// DSO-NEXT: 1060: b4 1f 00 00
+
+// DSOREL: Name: .got.plt
+// DSOREL-NEXT: Type: SHT_PROGBITS
+// DSOREL-NEXT: Flags [
+// DSOREL-NEXT: SHF_ALLOC
+// DSOREL-NEXT: SHF_WRITE
+// DSOREL-NEXT: ]
+// DSOREL-NEXT: Address: 0x3000
+// DSOREL-NEXT: Offset:
+// DSOREL-NEXT: Size: 24
+// DSOREL-NEXT: Link:
+// DSOREL-NEXT: Info:
+// DSOREL-NEXT: AddressAlignment: 4
+// DSOREL-NEXT: EntrySize:
+// DSOREL: Relocations [
+// DSOREL-NEXT: Section (4) .rel.plt {
+// DSOREL-NEXT: 0x300C R_ARM_JUMP_SLOT func1 0x0
+// DSOREL-NEXT: 0x3010 R_ARM_JUMP_SLOT func2 0x0
+// DSOREL-NEXT: 0x3014 R_ARM_JUMP_SLOT func3 0x0
OpenPOWER on IntegriCloud