[lld] r323856 - [ELF] Make overlapping output sections an error
Alexander Richardson via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 31 01:22:44 PST 2018
Author: arichardson
Date: Wed Jan 31 01:22:44 2018
New Revision: 323856
URL: http://llvm.org/viewvc/llvm-project?rev=323856&view=rev
Log:
[ELF] Make overlapping output sections an error
Summary:
While trying to make a linker script behave the same way with lld as it did
with bfd, I discovered that lld currently doesn't diagnose overlapping
output sections. I was getting very strange runtime failures which I
tracked down to overlapping sections in the resulting binary. When linking
with ld.bfd overlapping output sections are an error unless
--noinhibit-exec is passed and I believe lld should behave the same way
here to avoid surprising crashes at runtime.
The patch also uncovered an errors in the tests: arm-thumb-interwork-thunk
was creating a binary where .got.plt was placed at an address overlapping
with .got.
Reviewers: ruiu, grimar, rafael
Reviewed By: ruiu
Differential Revision: https://reviews.llvm.org/D41046
Added:
lld/trunk/test/ELF/linkerscript/overlapping-sections.s
Modified:
lld/trunk/ELF/Config.h
lld/trunk/ELF/Relocations.cpp
lld/trunk/ELF/Writer.cpp
lld/trunk/test/ELF/arm-thumb-interwork-thunk.s
Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=323856&r1=323855&r2=323856&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Wed Jan 31 01:22:44 2018
@@ -10,6 +10,7 @@
#ifndef LLD_ELF_CONFIG_H
#define LLD_ELF_CONFIG_H
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@@ -240,6 +241,12 @@ struct Configuration {
// The only instance of Configuration struct.
extern Configuration *Config;
+static inline void errorOrWarn(const Twine &Msg) {
+ if (!Config->NoinhibitExec)
+ error(Msg);
+ else
+ warn(Msg);
+}
} // namespace elf
} // namespace lld
Modified: lld/trunk/ELF/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.cpp?rev=323856&r1=323855&r2=323856&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.cpp (original)
+++ lld/trunk/ELF/Relocations.cpp Wed Jan 31 01:22:44 2018
@@ -534,13 +534,6 @@ template <class ELFT> static void addCop
InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, &SS, 0});
}
-static void errorOrWarn(const Twine &Msg) {
- if (!Config->NoinhibitExec)
- error(Msg);
- else
- warn(Msg);
-}
-
// MIPS has an odd notion of "paired" relocations to calculate addends.
// For example, if a relocation is of R_MIPS_HI16, there must be a
// R_MIPS_LO16 relocation after that, and an addend is calculated using
Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=323856&r1=323855&r2=323856&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Wed Jan 31 01:22:44 2018
@@ -62,6 +62,7 @@ private:
void assignFileOffsets();
void assignFileOffsetsBinary();
void setPhdrs();
+ void checkNoOverlappingSections();
void fixSectionAlignments();
void openFile();
void writeTrapInstr();
@@ -454,6 +455,8 @@ template <class ELFT> void Writer<ELFT>:
Sec->Addr = 0;
}
+ checkNoOverlappingSections();
+
// It does not make sense try to open the file if we have error already.
if (errorCount())
return;
@@ -1903,6 +1906,103 @@ template <class ELFT> void Writer<ELFT>:
}
}
+static std::string rangeToString(uint64_t Addr, uint64_t Len) {
+ if (Len == 0)
+ return "<emtpy range at 0x" + utohexstr(Addr) + ">";
+ return "[0x" + utohexstr(Addr) + " -> 0x" +
+ utohexstr(Addr + Len - 1) + "]";
+}
+
+// Check whether sections overlap for a specific address range (file offsets,
+// load and virtual adresses).
+//
+// This is a helper function called by Writer::checkNoOverlappingSections().
+template <typename Getter, typename Predicate>
+static void checkForSectionOverlap(ArrayRef<OutputSection *> AllSections,
+ StringRef Kind, Getter GetStart,
+ Predicate ShouldSkip) {
+ std::vector<OutputSection *> Sections;
+ // By removing all zero-size sections we can simplify the check for overlap to
+ // just checking whether the section range contains the other section's start
+ // address. Additionally, it also slightly speeds up the checking since we
+ // don't bother checking for overlap with sections that can never overlap.
+ for (OutputSection *Sec : AllSections)
+ if (Sec->Size > 0 && !ShouldSkip(Sec))
+ Sections.push_back(Sec);
+
+ // Instead of comparing every OutputSection with every other output section
+ // we sort the sections by address (file offset or load/virtual address). This
+ // way we find all overlapping sections but only need one comparision with the
+ // next section in the common non-overlapping case. The only time we end up
+ // doing more than one iteration of the following nested loop is if there are
+ // overlapping sections.
+ std::sort(Sections.begin(), Sections.end(),
+ [=](const OutputSection *A, const OutputSection *B) {
+ return GetStart(A) < GetStart(B);
+ });
+ for (size_t i = 0; i < Sections.size(); ++i) {
+ OutputSection *Sec = Sections[i];
+ uint64_t Start = GetStart(Sec);
+ for (auto *Other : ArrayRef<OutputSection *>(Sections).slice(i + 1)) {
+ // Since the sections are storted by start address we only need to check
+ // whether the other sections starts before the end of Sec. If this is
+ // not the case we can break out of this loop since all following sections
+ // will also start after the end of Sec.
+ if (Start + Sec->Size <= GetStart(Other))
+ break;
+ errorOrWarn("section " + Sec->Name + " " + Kind +
+ " range overlaps with " + Other->Name + "\n>>> " + Sec->Name +
+ " range is " + rangeToString(Start, Sec->Size) + "\n>>> " +
+ Other->Name + " range is " +
+ rangeToString(GetStart(Other), Other->Size));
+ }
+ }
+}
+
+// Check for overlapping sections
+//
+// In this function we check that none of the output sections have overlapping
+// file offsets. For SHF_ALLOC sections we also check that the load address
+// ranges and the virtual address ranges don't overlap
+template <class ELFT> void Writer<ELFT>::checkNoOverlappingSections() {
+ // First check for overlapping file offsets. In this case we need to skip
+ // Any section marked as SHT_NOBITS. These sections don't actually occupy
+ // space in the file so Sec->Offset + Sec->Size can overlap with others.
+ // If --oformat binary is specified only add SHF_ALLOC sections are added to
+ // the output file so we skip any non-allocated sections in that case.
+ checkForSectionOverlap(
+ OutputSections, "file", [](const OutputSection *Sec) { return Sec->Offset; },
+ [](const OutputSection *Sec) {
+ return Sec->Type == SHT_NOBITS ||
+ (Config->OFormatBinary && (Sec->Flags & SHF_ALLOC) == 0);
+ });
+
+ // When linking with -r there is no need to check for overlapping virtual/load
+ // addresses since those addresses will only be assigned when the final
+ // executable/shared object is created.
+ if (Config->Relocatable)
+ return;
+
+ // Checking for overlapping virtual and load addresses only needs to take
+ // into account SHF_ALLOC sections since since others will not be loaded.
+ // Furthermore, we also need to skip SHF_TLS sections since these will be
+ // mapped to other addresses at runtime and can therefore have overlapping
+ // ranges in the file.
+ auto SkipNonAllocSections = [](const OutputSection *Sec) {
+ return (Sec->Flags & SHF_ALLOC) == 0 || (Sec->Flags & SHF_TLS);
+ };
+ checkForSectionOverlap(OutputSections, "virtual address",
+ [](const OutputSection *Sec) { return Sec->Addr; },
+ SkipNonAllocSections);
+
+ // Finally, check that the load addresses don't overlap. This will usually be
+ // the same as the virtual addresses but can be different when using a linker
+ // script with AT().
+ checkForSectionOverlap(OutputSections, "load address",
+ [](const OutputSection *Sec) { return Sec->getLMA(); },
+ SkipNonAllocSections);
+}
+
// The entry point address is chosen in the following ways.
//
// 1. the '-e' entry command-line option;
Modified: lld/trunk/test/ELF/arm-thumb-interwork-thunk.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-thumb-interwork-thunk.s?rev=323856&r1=323855&r2=323856&view=diff
==============================================================================
--- lld/trunk/test/ELF/arm-thumb-interwork-thunk.s (original)
+++ lld/trunk/test/ELF/arm-thumb-interwork-thunk.s Wed Jan 31 01:22:44 2018
@@ -8,7 +8,7 @@
// RUN: .thumb_caller : { *(.thumb_caller) } \
// RUN: .R_ARM_JUMP24_callee_2 : { *(.R_ARM_JUMP24_callee_high) } \
// RUN: .R_ARM_THM_JUMP_callee_2 : { *(.R_ARM_THM_JUMP_callee_high) } \
-// RUN: .got.plt 0x1894 : { } } " > %t.script
+// RUN: .got.plt 0x18b4 : { } } " > %t.script
// RUN: ld.lld --script %t.script %t -o %t2 2>&1
// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-THUMB -check-prefix=CHECK-ABS-THUMB %s
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-ARM -check-prefix=CHECK-ABS-ARM %s
@@ -368,11 +368,11 @@ _start:
// CHECK-PI-ARM-PLT-NEXT: 183c: 00 f0 9c e5 ldr pc, [r12]
// CHECK-PI-ARM-PLT-NEXT: 1840: 7c 00 00 00
-// CHECK-DSO-REL: 0x18A0 R_ARM_JUMP_SLOT arm_caller
-// CHECK-DSO-REL-NEXT: 0x18A4 R_ARM_JUMP_SLOT thumb_caller
-// CHECK-DSO-REL-NEXT: 0x18A8 R_ARM_JUMP_SLOT thumb_callee1
-// CHECK-DSO-REL-NEXT: 0x18AC R_ARM_JUMP_SLOT thumb_callee2
-// CHECK-DSO-REL-NEXT: 0x18B0 R_ARM_JUMP_SLOT thumb_callee3
-// CHECK-DSO-REL-NEXT: 0x18B4 R_ARM_JUMP_SLOT arm_callee1
-// CHECK-DSO-REL-NEXT: 0x18B8 R_ARM_JUMP_SLOT arm_callee2
-// CHECK-DSO-REL-NEXT: 0x18BC R_ARM_JUMP_SLOT arm_callee3
+// CHECK-DSO-REL: 0x18C0 R_ARM_JUMP_SLOT arm_caller
+// CHECK-DSO-REL-NEXT: 0x18C4 R_ARM_JUMP_SLOT thumb_caller
+// CHECK-DSO-REL-NEXT: 0x18C8 R_ARM_JUMP_SLOT thumb_callee1
+// CHECK-DSO-REL-NEXT: 0x18CC R_ARM_JUMP_SLOT thumb_callee2
+// CHECK-DSO-REL-NEXT: 0x18D0 R_ARM_JUMP_SLOT thumb_callee3
+// CHECK-DSO-REL-NEXT: 0x18D4 R_ARM_JUMP_SLOT arm_callee1
+// CHECK-DSO-REL-NEXT: 0x18D8 R_ARM_JUMP_SLOT arm_callee2
+// CHECK-DSO-REL-NEXT: 0x18DC R_ARM_JUMP_SLOT arm_callee3
Added: lld/trunk/test/ELF/linkerscript/overlapping-sections.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/overlapping-sections.s?rev=323856&view=auto
==============================================================================
--- lld/trunk/test/ELF/linkerscript/overlapping-sections.s (added)
+++ lld/trunk/test/ELF/linkerscript/overlapping-sections.s Wed Jan 31 01:22:44 2018
@@ -0,0 +1,169 @@
+# TODO: maybe this should be converted to an x86 test to get more buildbot coverage
+# REQUIRES: mips
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { \
+# RUN: .sec1 0x8000 : { sec1_start = .; *(.first_sec) sec1_end = .;} \
+# RUN: .sec2 0x8800 : { sec2_start = .; *(.second_sec) sec2_end = .;} \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-readobj -sections -program-headers %t.so | FileCheck %s -check-prefix GOOD
+
+# GOOD: Name: .sec1
+# GOOD-NEXT: Type: SHT_PROGBITS (0x1)
+# GOOD-NEXT: Flags [ (0x3)
+# GOOD-NEXT: SHF_ALLOC (0x2)
+# GOOD-NEXT: SHF_WRITE (0x1)
+# GOOD-NEXT: ]
+# GOOD-NEXT: Address: 0x8000
+# GOOD-NEXT: Offset: 0x18000
+# GOOD-NEXT: Size: 256
+
+# GOOD: Name: .sec2
+# GOOD-NEXT: Type: SHT_PROGBITS (0x1)
+# GOOD-NEXT: Flags [ (0x3)
+# GOOD-NEXT: SHF_ALLOC (0x2)
+# GOOD-NEXT: SHF_WRITE (0x1)
+# GOOD-NEXT: ]
+# GOOD-NEXT: Address: 0x8800
+# GOOD-NEXT: Offset: 0x18800
+# GOOD-NEXT: Size: 256
+
+# GOOD: ProgramHeaders [
+# GOOD-NEXT: ProgramHeader {
+# GOOD-NEXT: Type: PT_LOAD (0x1)
+# GOOD-NEXT: Offset: 0x10000
+# GOOD-NEXT: VirtualAddress: 0x0
+# GOOD-NEXT: PhysicalAddress: 0x0
+# GOOD-NEXT: FileSize: 481
+# GOOD-NEXT: MemSize: 481
+# GOOD-NEXT: Flags [ (0x5)
+# GOOD-NEXT: PF_R (0x4)
+# GOOD-NEXT: PF_X (0x1)
+# GOOD-NEXT: ]
+# GOOD-NEXT: Alignment: 65536
+# GOOD-NEXT: }
+# GOOD-NEXT: ProgramHeader {
+# GOOD-NEXT: Type: PT_LOAD (0x1)
+# GOOD-NEXT: Offset: 0x18000
+# GOOD-NEXT: VirtualAddress: 0x8000
+# GOOD-NEXT: PhysicalAddress: 0x8000
+# GOOD-NEXT: FileSize: 2320
+# GOOD-NEXT: MemSize: 2320
+# GOOD-NEXT: Flags [ (0x6)
+# GOOD-NEXT: PF_R (0x4)
+# GOOD-NEXT: PF_W (0x2)
+# GOOD-NEXT: ]
+# GOOD-NEXT: Alignment: 65536
+# GOOD-NEXT: }
+
+# RUN: echo "SECTIONS { \
+# RUN: .sec1 0x8000 : AT(0x8000) { sec1_start = .; *(.first_sec) sec1_end = .;} \
+# RUN: .sec2 0x8800 : AT(0x8080) { sec2_start = .; *(.second_sec) sec2_end = .;} \
+# RUN: }" > %t-lma.script
+# RUN: not ld.lld -o %t.so --script %t-lma.script %t.o -shared 2>&1 | FileCheck %s -check-prefix LMA-OVERLAP-ERR
+# LMA-OVERLAP-ERR: error: section .sec1 load address range overlaps with .sec2
+# LMA-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
+# LMA-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8080 -> 0x817F]
+
+# check that we create the expected binary with --noinhibit-exec:
+# RUN: ld.lld -o %t.so --script %t-lma.script %t.o -shared --noinhibit-exec
+
+# Verify that the .sec2 was indeed placed in a PT_LOAD where the PhysAddr
+# overlaps with where .sec1 is loaded:
+# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-LMA
+# BAD-LMA-LABEL: Section Headers:
+# BAD-LMA: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
+# BAD-LMA: .sec2 PROGBITS 0000000000008800 018800 000100 00 WA 0 0 1
+# BAD-LMA-LABEL: Program Headers:
+# BAD-LMA-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+# BAD-LMA-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
+# BAD-LMA-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x10000
+# BAD-LMA-NEXT: LOAD 0x018800 0x0000000000008800 0x0000000000008080 0x000110 0x000110 RW 0x10000
+# BAD-LMA-LABEL: Section to Segment mapping:
+# BAD-LMA: 01 .sec1
+# BAD-LMA: 02 .sec2 .data .got
+
+
+# Now try a script where the virtual memory addresses overlap but ensure that the
+# load addresses don't:
+# RUN: echo "SECTIONS { \
+# RUN: .sec1 0x8000 : AT(0x8000) { sec1_start = .; *(.first_sec) sec1_end = .;} \
+# RUN: .sec2 0x8020 : AT(0x8800) { sec2_start = .; *(.second_sec) sec2_end = .;} \
+# RUN: }" > %t-vaddr.script
+# RUN: not ld.lld -o %t.so --script %t-vaddr.script %t.o -shared 2>&1 | FileCheck %s -check-prefix VADDR-OVERLAP-ERR
+# VADDR-OVERLAP-ERR: error: section .sec1 virtual address range overlaps with .sec2
+# VADDR-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
+# VADDR-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8020 -> 0x811F]
+
+# Check that the expected binary was created with --noinhibit-exec:
+# RUN: ld.lld -o %t.so --script %t-vaddr.script %t.o -shared --noinhibit-exec
+# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-VADDR
+# BAD-VADDR-LABEL: Section Headers:
+# BAD-VADDR: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
+# BAD-VADDR: .sec2 PROGBITS 0000000000008020 028020 000100 00 WA 0 0 1
+# BAD-VADDR-LABEL: Program Headers:
+# BAD-VADDR-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+# BAD-VADDR-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
+# BAD-VADDR-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x10000
+# BAD-VADDR-NEXT: LOAD 0x028020 0x0000000000008020 0x0000000000008800 0x000110 0x000110 RW 0x10000
+# BAD-VADDR-LABEL: Section to Segment mapping:
+# BAD-VADDR: 01 .sec1
+# BAD-VADDR: 02 .sec2 .data .got
+
+# Finally check the case where both LMA and vaddr overlap
+
+# RUN: echo "SECTIONS { \
+# RUN: .sec1 0x8000 : { sec1_start = .; *(.first_sec) sec1_end = .;} \
+# RUN: .sec2 0x8040 : { sec2_start = .; *(.second_sec) sec2_end = .;} \
+# RUN: }" > %t-both-overlap.script
+
+# RUN: not ld.lld -o %t.so --script %t-both-overlap.script %t.o -shared 2>&1 | FileCheck %s -check-prefix BOTH-OVERLAP-ERR
+
+# BOTH-OVERLAP-ERR: error: section .sec1 file range overlaps with .sec2
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x18000 -> 0x180FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x18040 -> 0x1813F]
+# BOTH-OVERLAP-ERR: error: section .sec1 virtual address range overlaps with .sec2
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040 -> 0x813F]
+# BOTH-OVERLAP-ERR: error: section .sec1 load address range overlaps with .sec2
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040 -> 0x813F]
+
+# RUN: ld.lld -o %t.so --script %t-both-overlap.script %t.o -shared --noinhibit-exec
+# Note: I case everything overlaps we create a binary with overlapping file
+# offsets. ld.bfd seems to place .sec1 to file offset 18000 and .sec2
+# at 18100 so that only virtual addr and LMA overlap
+# However, in order to create such a broken binary the user has to ignore a
+# fatal error by passing --noinhibit-exec, so this behaviour is fine.
+
+# RUN: llvm-objdump -s %t.so | FileCheck %s -check-prefix BROKEN-OUTPUT-FILE
+# BROKEN-OUTPUT-FILE-LABEL: Contents of section .sec1:
+# BROKEN-OUTPUT-FILE-NEXT: 8000 01010101 01010101 01010101 01010101 ................
+# BROKEN-OUTPUT-FILE-NEXT: 8010 01010101 01010101 01010101 01010101 ................
+# BROKEN-OUTPUT-FILE-NEXT: 8020 01010101 01010101 01010101 01010101 ................
+# BROKEN-OUTPUT-FILE-NEXT: 8030 01010101 01010101 01010101 01010101 ................
+# Starting here the contents of .sec2 overwrites .sec1:
+# BROKEN-OUTPUT-FILE-NEXT: 8040 02020202 02020202 02020202 02020202 ................
+
+# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-BOTH
+# BAD-BOTH-LABEL: Section Headers:
+# BAD-BOTH: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
+# BAD-BOTH: .sec2 PROGBITS 0000000000008040 018040 000100 00 WA 0 0 1
+# BAD-BOTH-LABEL: Program Headers:
+# BAD-BOTH-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+# BAD-BOTH-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
+# BAD-BOTH-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000150 0x000150 RW 0x10000
+# BAD-BOTH-LABEL: Section to Segment mapping:
+# BAD-BOTH: 01 .sec1 .sec2 .data .got
+
+
+.section .first_sec,"aw", at progbits
+.rept 0x100
+.byte 1
+.endr
+
+.section .second_sec,"aw", at progbits
+.rept 0x100
+.byte 2
+.endr
More information about the llvm-commits
mailing list