summaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
authorSimon Atanasyan <simon@atanasyan.com>2018-07-24 05:40:37 +0000
committerSimon Atanasyan <simon@atanasyan.com>2018-07-24 05:40:37 +0000
commitda0ecb6c7296cc1d74ab497e94584285035e43b6 (patch)
treeab6cf19dbc615de8ee1c548d7db8fc3cc5d916b3 /lld
parentdfb6f4bf093db568495b14f0c54696173d0d48c3 (diff)
[ELF][MIPS] Fix primary GOT sometimes overflowing by one or two words
If we fail to merge a secondary GOT with the primary GOT but so far only one merged GOT has been created (the primary one), the final element in MergedGots is the primary GOT. Thus we should not try to merge with this final element passing IsPrimary=false, since this will ignore the fact that the destination GOT does in fact need a header, and those extra two entries can be enough to allow the merge to incorrectly occur. Instead we should check for this case before attempting the second merge. Patch by James Clarke. Differential revision: https://reviews.llvm.org/D49422
Diffstat (limited to 'lld')
-rw-r--r--lld/ELF/SyntheticSections.cpp8
-rw-r--r--lld/test/ELF/Inputs/mips-64-got-load.s8
-rw-r--r--lld/test/ELF/mips-64-got-overflow.s80
3 files changed, 95 insertions, 1 deletions
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index cbcf6bf39a1..38859e1650b 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -872,7 +872,13 @@ template <class ELFT> void MipsGotSection::build() {
if (tryMergeGots(MergedGots.front(), SrcGot, true)) {
File->MipsGotIndex = 0;
} else {
- if (!tryMergeGots(MergedGots.back(), SrcGot, false)) {
+ // If this is the first time we failed to merge with the primary GOT,
+ // MergedGots.back() will also be the primary GOT. We must make sure not
+ // to try to merge again with IsPrimary=false, as otherwise, if the
+ // inputs are just right, we could allow the primary GOT to become 1 or 2
+ // words too big due to ignoring the header size.
+ if (MergedGots.size() == 1 ||
+ !tryMergeGots(MergedGots.back(), SrcGot, false)) {
MergedGots.emplace_back();
std::swap(MergedGots.back(), SrcGot);
}
diff --git a/lld/test/ELF/Inputs/mips-64-got-load.s b/lld/test/ELF/Inputs/mips-64-got-load.s
new file mode 100644
index 00000000000..dffc6fb335c
--- /dev/null
+++ b/lld/test/ELF/Inputs/mips-64-got-load.s
@@ -0,0 +1,8 @@
+ .text
+ .global foo1
+foo1:
+ ld $2, %got_disp(local1)($gp)
+
+ .bss
+local1:
+ .word 0
diff --git a/lld/test/ELF/mips-64-got-overflow.s b/lld/test/ELF/mips-64-got-overflow.s
new file mode 100644
index 00000000000..5de71b1b366
--- /dev/null
+++ b/lld/test/ELF/mips-64-got-overflow.s
@@ -0,0 +1,80 @@
+# REQUIRES: mips
+# Check the primary GOT cannot be made to overflow
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: %p/Inputs/mips-64-got-load.s -o %t1.so.o
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t2.so.o
+# RUN: ld.lld -shared -mips-got-size 32 %t1.so.o %t2.so.o -o %t-sgot.so
+# RUN: ld.lld -shared -mips-got-size 24 %t1.so.o %t2.so.o -o %t-mgot.so
+# RUN: llvm-readobj -r -dt -mips-plt-got %t-sgot.so | FileCheck -check-prefix=SGOT %s
+# RUN: llvm-readobj -r -dt -mips-plt-got %t-mgot.so | FileCheck -check-prefix=MGOT %s
+
+# SGOT: Primary GOT {
+# SGOT-NEXT: Canonical gp value: 0x27FF0
+# SGOT-NEXT: Reserved entries [
+# SGOT-NEXT: Entry {
+# SGOT-NEXT: Address:
+# SGOT-NEXT: Access: -32752
+# SGOT-NEXT: Initial: 0x0
+# SGOT-NEXT: Purpose: Lazy resolver
+# SGOT-NEXT: }
+# SGOT-NEXT: Entry {
+# SGOT-NEXT: Address:
+# SGOT-NEXT: Access: -32744
+# SGOT-NEXT: Initial: 0x80000000
+# SGOT-NEXT: Purpose: Module pointer (GNU extension)
+# SGOT-NEXT: }
+# SGOT-NEXT: ]
+# SGOT-NEXT: Local entries [
+# SGOT-NEXT: Entry {
+# SGOT-NEXT: Address:
+# SGOT-NEXT: Access: -32736
+# SGOT-NEXT: Initial: 0x20020
+# SGOT-NEXT: }
+# SGOT-NEXT: Entry {
+# SGOT-NEXT: Address:
+# SGOT-NEXT: Access: -32728
+# SGOT-NEXT: Initial: 0x20030
+# SGOT-NEXT: }
+# SGOT-NEXT: ]
+# SGOT-NEXT: Global entries [
+# SGOT-NEXT: ]
+# SGOT-NEXT: Number of TLS and multi-GOT entries: 0
+# SGOT-NEXT: }
+
+# MGOT: Primary GOT {
+# MGOT-NEXT: Canonical gp value: 0x27FF0
+# MGOT-NEXT: Reserved entries [
+# MGOT-NEXT: Entry {
+# MGOT-NEXT: Address:
+# MGOT-NEXT: Access: -32752
+# MGOT-NEXT: Initial: 0x0
+# MGOT-NEXT: Purpose: Lazy resolver
+# MGOT-NEXT: }
+# MGOT-NEXT: Entry {
+# MGOT-NEXT: Address:
+# MGOT-NEXT: Access: -32744
+# MGOT-NEXT: Initial: 0x80000000
+# MGOT-NEXT: Purpose: Module pointer (GNU extension)
+# MGOT-NEXT: }
+# MGOT-NEXT: ]
+# MGOT-NEXT: Local entries [
+# MGOT-NEXT: Entry {
+# MGOT-NEXT: Address:
+# MGOT-NEXT: Access: -32736
+# MGOT-NEXT: Initial: 0x20020
+# MGOT-NEXT: }
+# MGOT-NEXT: ]
+# MGOT-NEXT: Global entries [
+# MGOT-NEXT: ]
+# MGOT-NEXT: Number of TLS and multi-GOT entries: 1
+# MGOT-NEXT: }
+
+ .text
+ .global foo2
+foo2:
+ ld $2, %got_disp(local2)($gp)
+
+ .bss
+local2:
+ .word 0