summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-08-11 16:29:54 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-08-11 16:29:54 +0000
commit786d82c29598fe10a2b4888d1d1f426063c8bf7c (patch)
treec7de06a59e2f15463d2730ffd373409c8a321b5a
parent84c4061b34e048f47e5eb4fbabc1558495e8157c (diff)
downloadsrc-test2-786d82c29598fe10a2b4888d1d1f426063c8bf7c.tar.gz
src-test2-786d82c29598fe10a2b4888d1d1f426063c8bf7c.zip
Notes
-rw-r--r--COFF/InputFiles.cpp54
-rw-r--r--COFF/InputFiles.h17
-rw-r--r--ELF/LinkerScript.cpp22
-rw-r--r--ELF/Writer.cpp12
-rw-r--r--test/COFF/Inputs/associative-comdat-mingw-2.s34
-rw-r--r--test/COFF/associative-comdat-mingw.s73
-rw-r--r--test/ELF/linkerscript/Inputs/at6.s11
-rw-r--r--test/ELF/linkerscript/Inputs/at7.s7
-rw-r--r--test/ELF/linkerscript/Inputs/at8.s8
-rw-r--r--test/ELF/linkerscript/at6.test30
-rw-r--r--test/ELF/linkerscript/at7.test28
-rw-r--r--test/ELF/linkerscript/at8.test31
12 files changed, 307 insertions, 20 deletions
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 2b3e65fae04b..289cdb1f6cd0 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -205,7 +205,13 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
void ObjFile::readAssociativeDefinition(
COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
- SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())];
+ readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj()));
+}
+
+void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym,
+ const coff_aux_section_definition *Def,
+ uint32_t ParentSection) {
+ SectionChunk *Parent = SparseChunks[ParentSection];
// If the parent is pending, it probably means that its section definition
// appears after us in the symbol table. Leave the associated section as
@@ -225,6 +231,35 @@ void ObjFile::readAssociativeDefinition(
}
}
+void ObjFile::recordPrevailingSymbolForMingw(
+ COFFSymbolRef Sym, DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ // For comdat symbols in executable sections, where this is the copy
+ // of the section chunk we actually include instead of discarding it,
+ // add the symbol to a map to allow using it for implicitly
+ // associating .[px]data$<func> sections to it.
+ int32_t SectionNumber = Sym.getSectionNumber();
+ SectionChunk *SC = SparseChunks[SectionNumber];
+ if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
+ StringRef Name;
+ COFFObj->getSymbolName(Sym, Name);
+ PrevailingSectionMap[Name] = SectionNumber;
+ }
+}
+
+void ObjFile::maybeAssociateSEHForMingw(
+ COFFSymbolRef Sym, const coff_aux_section_definition *Def,
+ const DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ StringRef Name;
+ COFFObj->getSymbolName(Sym, Name);
+ if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) {
+ // For MinGW, treat .[px]data$<func> as implicitly associative to
+ // the symbol <func>.
+ auto ParentSym = PrevailingSectionMap.find(Name);
+ if (ParentSym != PrevailingSectionMap.end())
+ readAssociativeDefinition(Sym, Def, ParentSym->second);
+ }
+}
+
Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
if (Sym.isExternal()) {
@@ -248,19 +283,24 @@ void ObjFile::initializeSymbols() {
std::vector<uint32_t> PendingIndexes;
PendingIndexes.reserve(NumSymbols);
+ DenseMap<StringRef, uint32_t> PrevailingSectionMap;
std::vector<const coff_aux_section_definition *> ComdatDefs(
COFFObj->getNumberOfSections() + 1);
for (uint32_t I = 0; I < NumSymbols; ++I) {
COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
+ bool PrevailingComdat;
if (COFFSym.isUndefined()) {
Symbols[I] = createUndefined(COFFSym);
} else if (COFFSym.isWeakExternal()) {
Symbols[I] = createUndefined(COFFSym);
uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
WeakAliases.emplace_back(Symbols[I], TagIndex);
- } else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) {
+ } else if (Optional<Symbol *> OptSym =
+ createDefined(COFFSym, ComdatDefs, PrevailingComdat)) {
Symbols[I] = *OptSym;
+ if (Config->MinGW && PrevailingComdat)
+ recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap);
} else {
// createDefined() returns None if a symbol belongs to a section that
// was pending at the point when the symbol was read. This can happen in
@@ -278,9 +318,12 @@ void ObjFile::initializeSymbols() {
for (uint32_t I : PendingIndexes) {
COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
- if (auto *Def = Sym.getSectionDefinition())
+ if (auto *Def = Sym.getSectionDefinition()) {
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(Sym, Def);
+ else if (Config->MinGW)
+ maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap);
+ }
if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
@@ -306,7 +349,9 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
Optional<Symbol *> ObjFile::createDefined(
COFFSymbolRef Sym,
- std::vector<const coff_aux_section_definition *> &ComdatDefs) {
+ std::vector<const coff_aux_section_definition *> &ComdatDefs,
+ bool &Prevailing) {
+ Prevailing = false;
auto GetName = [&]() {
StringRef S;
COFFObj->getSymbolName(Sym, S);
@@ -352,7 +397,6 @@ Optional<Symbol *> ObjFile::createDefined(
if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
ComdatDefs[SectionNumber] = nullptr;
Symbol *Leader;
- bool Prevailing;
if (Sym.isExternal()) {
std::tie(Leader, Prevailing) =
Symtab->addComdat(this, GetName(), Sym.getGeneric());
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 4ee4b363886f..2bfb9e4b002a 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -13,6 +13,7 @@
#include "Config.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
@@ -157,10 +158,24 @@ private:
COFFSymbolRef COFFSym,
const llvm::object::coff_aux_section_definition *Def);
+ void readAssociativeDefinition(
+ COFFSymbolRef COFFSym,
+ const llvm::object::coff_aux_section_definition *Def,
+ uint32_t ParentSection);
+
+ void recordPrevailingSymbolForMingw(
+ COFFSymbolRef COFFSym,
+ llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+
+ void maybeAssociateSEHForMingw(
+ COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def,
+ const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+
llvm::Optional<Symbol *>
createDefined(COFFSymbolRef Sym,
std::vector<const llvm::object::coff_aux_section_definition *>
- &ComdatDefs);
+ &ComdatDefs,
+ bool &PrevailingComdat);
Symbol *createRegular(COFFSymbolRef Sym);
Symbol *createUndefined(COFFSymbolRef Sym);
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index abdd899da487..d94970e4847e 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -116,7 +116,8 @@ void LinkerScript::expandMemoryRegions(uint64_t Size) {
if (Ctx->MemRegion)
expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
Ctx->OutSec->Name);
- if (Ctx->LMARegion)
+ // Only expand the LMARegion if it is different from MemRegion.
+ if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion)
expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
Ctx->OutSec->Name);
}
@@ -750,6 +751,13 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
return nullptr;
}
+static OutputSection *findFirstSection(PhdrEntry *Load) {
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->PtLoad == Load)
+ return Sec;
+ return nullptr;
+}
+
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
void LinkerScript::assignOffsets(OutputSection *Sec) {
@@ -775,8 +783,11 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
+ // This, however, should only be done by the first "non-header" section
+ // in the segment.
if (PhdrEntry *L = Ctx->OutSec->PtLoad)
- L->LMAOffset = Ctx->LMAOffset;
+ if (Sec == findFirstSection(L))
+ L->LMAOffset = Ctx->LMAOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
@@ -953,13 +964,6 @@ void LinkerScript::adjustSectionsAfterSorting() {
}
}
-static OutputSection *findFirstSection(PhdrEntry *Load) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->PtLoad == Load)
- return Sec;
- return nullptr;
-}
-
static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
// If there is no SECTIONS or if the linkerscript is explicit about program
// headers, do our best to allocate them.
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 73a97380b54b..88a2d5c71266 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -1815,12 +1815,14 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
// Segments are contiguous memory regions that has the same attributes
// (e.g. executable or writable). There is one phdr for each segment.
// Therefore, we need to create a new phdr when the next section has
- // different flags or is loaded at a discontiguous address using AT linker
- // script command. At the same time, we don't want to create a separate
- // load segment for the headers, even if the first output section has
- // an AT attribute.
+ // different flags or is loaded at a discontiguous address or memory
+ // region using AT or AT> linker script command, respectively. At the same
+ // time, we don't want to create a separate load segment for the headers,
+ // even if the first output section has an AT or AT> attribute.
uint64_t NewFlags = computeFlags(Sec->getPhdrFlags());
- if ((Sec->LMAExpr && Load->LastSec != Out::ProgramHeaders) ||
+ if (((Sec->LMAExpr ||
+ (Sec->LMARegion && (Sec->LMARegion != Load->FirstSec->LMARegion))) &&
+ Load->LastSec != Out::ProgramHeaders) ||
Sec->MemRegion != Load->FirstSec->MemRegion || Flags != NewFlags) {
Load = AddHdr(PT_LOAD, NewFlags);
diff --git a/test/COFF/Inputs/associative-comdat-mingw-2.s b/test/COFF/Inputs/associative-comdat-mingw-2.s
new file mode 100644
index 000000000000..edb6a82da84b
--- /dev/null
+++ b/test/COFF/Inputs/associative-comdat-mingw-2.s
@@ -0,0 +1,34 @@
+ .section .xdata$foo,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 42
+
+ .section .xdata$bar,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 43
+
+ .section .xdata$baz,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 44
+
+ .def foo;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text$foo,"xr",discard,foo
+ .globl foo
+ .p2align 4
+foo:
+ ret
+
+ .def bar;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text$bar,"xr",discard,bar
+ .globl bar
+ .p2align 4
+bar:
+ ret
diff --git a/test/COFF/associative-comdat-mingw.s b/test/COFF/associative-comdat-mingw.s
new file mode 100644
index 000000000000..09cba9c4c6f7
--- /dev/null
+++ b/test/COFF/associative-comdat-mingw.s
@@ -0,0 +1,73 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t1.obj
+# RUN: llvm-mc -triple=x86_64-windows-gnu %S/Inputs/associative-comdat-mingw-2.s -filetype=obj -o %t2.obj
+
+# RUN: lld-link -lldmingw -entry:main %t1.obj %t2.obj -out:%t.gc.exe -verbose
+# RUN: llvm-readobj -sections %t.gc.exe | FileCheck %s
+
+# CHECK: Sections [
+# CHECK: Section {
+# CHECK: Number: 2
+# CHECK-LABEL: Name: .rdata (2E 72 64 61 74 61 00 00)
+# This is the critical check to show that only *one* definition of
+# .xdata$foo was retained. This *must* be 4.
+# Make sure that no other .xdata sections get included, which would
+# increase the size here.
+# CHECK-NEXT: VirtualSize: 0x4
+
+ .text
+ .def main;
+ .scl 2;
+ .type 32;
+ .endef
+ .globl main
+ .p2align 4, 0x90
+main:
+ call foo
+ retq
+
+# Defines .text$foo (which has a leader symbol and is referenced like
+# normally), and .xdata$foo (which lacks a leader symbol, which normally
+# would be declared associative to the symbol foo).
+# .xdata$foo should be implicitly treated as associative to foo and brought
+# in, while .xdata$bar, implicitly associative to bar, not included, and
+# .xdata$baz not included since there's no symbol baz.
+
+# GNU binutils ld doesn't do this at all, but always includes all .xdata/.pdata
+# comdat sections, even if --gc-sections is used.
+
+ .section .xdata$foo,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 42
+
+ .section .xdata$bar,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 43
+
+ .section .xdata$baz,"dr"
+ .linkonce discard
+ .p2align 3
+ .long 44
+
+ .def foo;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text$foo,"xr",discard,foo
+ .globl foo
+ .p2align 4
+foo:
+ ret
+
+ .def bar;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text$bar,"xr",discard,bar
+ .globl bar
+ .p2align 4
+bar:
+ ret
diff --git a/test/ELF/linkerscript/Inputs/at6.s b/test/ELF/linkerscript/Inputs/at6.s
new file mode 100644
index 000000000000..2d22d4d342dd
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/at6.s
@@ -0,0 +1,11 @@
+.global _start
+.text
+_start:
+nop
+
+.section .sec1,"aw",@progbits
+.long 1
+
+.section .sec2,"aw",@progbits
+.long 2
+
diff --git a/test/ELF/linkerscript/Inputs/at7.s b/test/ELF/linkerscript/Inputs/at7.s
new file mode 100644
index 000000000000..29d29635963b
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/at7.s
@@ -0,0 +1,7 @@
+.global _start
+.text
+_start:
+nop
+
+.section .sec, "aw"
+.word 4
diff --git a/test/ELF/linkerscript/Inputs/at8.s b/test/ELF/linkerscript/Inputs/at8.s
new file mode 100644
index 000000000000..e15e4cd3b77f
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/at8.s
@@ -0,0 +1,8 @@
+.section .sec1,"aw",@progbits
+.quad 1
+
+.section .sec2,"aw",@progbits
+.quad 2
+
+.section .sec3,"aw",@progbits
+.quad 3
diff --git a/test/ELF/linkerscript/at6.test b/test/ELF/linkerscript/at6.test
new file mode 100644
index 000000000000..498c0ef14f34
--- /dev/null
+++ b/test/ELF/linkerscript/at6.test
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at6.s -o %t.o
+# RUN: ld.lld %t.o --script %s -o %t
+# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s
+
+MEMORY {
+ FLASH : ORIGIN = 0x08000000, LENGTH = 0x100
+ RAM : ORIGIN = 0x20000000, LENGTH = 0x200
+}
+
+SECTIONS {
+ .text : { *(.text) } > FLASH
+ .sec1 : { *(.sec1) } > RAM
+ .sec2 : { *(.sec2) } > RAM AT > FLASH
+}
+
+# Make sure we create a separate PT_LOAD entry for .sec2. Previously,
+# it was added to the PT_LOAD entry of .sec1
+
+# CHECK: Name Type Address Off
+# CHECK: .text PROGBITS 0000000008000000 001000
+# CHECK: .sec1 PROGBITS 0000000020000000 002000
+# CHECK: .sec2 PROGBITS 0000000020000004 002004
+
+# CHECK: Program Headers:
+# CHECK: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: LOAD 0x001000 0x0000000008000000 0x0000000008000000
+# CHECK-NEXT: LOAD 0x002000 0x0000000020000000 0x0000000020000000
+# CHECK-NEXT: LOAD 0x002004 0x0000000020000004 0x0000000008000001
+# CHECK-NOT: LOAD
diff --git a/test/ELF/linkerscript/at7.test b/test/ELF/linkerscript/at7.test
new file mode 100644
index 000000000000..1f67df29fdcd
--- /dev/null
+++ b/test/ELF/linkerscript/at7.test
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at7.s -o %t.o
+# RUN: ld.lld %t.o --script %s -o %t
+# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s
+
+MEMORY {
+ RAM : ORIGIN = 0x20000000, LENGTH = 0x200
+}
+
+SECTIONS {
+ .text : { *(.text) } > RAM AT> RAM
+ .sec : { *(.sec) } > RAM
+}
+
+# Make sure the memory for the .text section is only reserved once.
+# Previously, the location counter for both MemRegion and LMARegion
+# was increased unconditionally.
+
+
+# CHECK: Name Type Address Off
+# CHECK: .text PROGBITS 0000000020000000 001000
+# CHECK: .sec PROGBITS 0000000020000001 001001
+
+# CHECK: Program Headers:
+# CHECK: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: LOAD 0x001000 0x0000000020000000 0x0000000020000000
+# CHECK-NEXT: LOAD 0x001001 0x0000000020000001 0x0000000020000001
+# CHECK-NOT: LOAD
diff --git a/test/ELF/linkerscript/at8.test b/test/ELF/linkerscript/at8.test
new file mode 100644
index 000000000000..48c0d4581422
--- /dev/null
+++ b/test/ELF/linkerscript/at8.test
@@ -0,0 +1,31 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at8.s -o %t.o
+# RUN: ld.lld %t.o --script %s -o %t
+# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s
+
+MEMORY {
+ FLASH : ORIGIN = 0x08000000, LENGTH = 0x100
+ RAM : ORIGIN = 0x20000000, LENGTH = 0x200
+}
+
+SECTIONS {
+ .text : { *(.text) } > FLASH
+ .sec1 : { *(.sec1) } > RAM AT > FLASH
+ .sec2 : { *(.sec2) } > RAM
+ .sec3 : { *(.sec3) } > RAM AT > FLASH
+}
+
+# Make sure we do not issue a load-address overlap error
+# Previously, .sec3 would overwrite the LMAOffset in the
+# PT_LOAD header.
+
+# CHECK: Name Type Address Off
+# CHECK: .text PROGBITS 0000000008000000 001000
+# CHECK: .sec1 PROGBITS 0000000020000000 001000
+# CHECK: .sec2 PROGBITS 0000000020000008 001008
+# CHECK: .sec3 PROGBITS 0000000020000010 001010
+
+# CHECK: Program Headers:
+# CHECK: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: LOAD 0x001000 0x0000000020000000 0x0000000008000000
+# CHECK-NOT: LOAD