[COFF] Allow debug info to relocate against discarded symbols
Summary:
In order to do this without switching on the symbol kind multiple times,
I created Defined::getChunkAndOffset and use that instead of
SymbolBody::getRVA in the inner relocation loop.
Now we get the symbol's chunk before switching over relocation types, so
we can test if it has been discarded outside the inner relocation type
switch. This also simplifies application of section relative
relocations. Previously we would switch on symbol kind to compute the
RVA, then the relocation type, and then the symbol kind again to get the
output section so we could subtract that from the symbol RVA. Now we
*always* have an OutputSection, so applying SECREL and SECTION
relocations isn't as much of a special case.
I'm still not quite happy with the cleanliness of this code. I'm not
sure what offsets and bases we should be using during the relocation
processing loop: VA, RVA, or OutputSectionOffset.
Reviewers: ruiu, pcc
Reviewed By: ruiu
Subscribers: majnemer, inglorion, llvm-commits, aprantl
Differential Revision: https://reviews.llvm.org/D34650
llvm-svn: 306566
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 56124ac..9b642dc 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -11,6 +11,7 @@
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
+#include "Writer.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
@@ -52,18 +53,27 @@
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
-static void applySecRel(const SectionChunk *Sec, uint8_t *Off, Defined *Sym) {
- // Don't apply section relative relocations to absolute symbols in codeview
- // debug info sections. MSVC does not treat such relocations as fatal errors,
- // and they can be found in the standard library for linker-provided symbols
- // like __guard_fids_table and __safe_se_handler_table.
- if (!(isa<DefinedAbsolute>(Sym) && Sec->isCodeView()))
- add32(Off, Sym->getSecrel());
+static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
+ OutputSection *OS, uint64_t S) {
+ if (!OS) {
+ if (Sec->isCodeView())
+ return;
+ fatal("SECREL relocation cannot be applied to absolute symbols");
+ }
+ uint64_t SecRel = S - OS->getRVA();
+ assert(SecRel < INT32_MAX && "overflow in SECREL relocation");
+ add32(Off, SecRel);
}
-void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+static void applySecIdx(uint8_t *Off, OutputSection *OS) {
+ // If we have no output section, this must be an absolute symbol. Use the
+ // sentinel absolute symbol section index.
+ uint16_t SecIdx = OS ? OS->SectionIndex : DefinedAbsolute::OutputSectionIndex;
+ add16(Off, SecIdx);
+}
+
+void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
switch (Type) {
case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break;
@@ -74,23 +84,22 @@
case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
- case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break;
- case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, Sym); break;
+ case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
}
-void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
switch (Type) {
case IMAGE_REL_I386_ABSOLUTE: break;
case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
- case IMAGE_REL_I386_SECREL: applySecRel(this, Off, Sym); break;
+ case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
@@ -137,20 +146,21 @@
write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
-void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
// Pointer to thumb code must have the LSB set.
- if (Sym->isExecutable())
- S |= 1;
+ uint64_t SX = S;
+ if (OS && (OS->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
+ SX |= 1;
switch (Type) {
- case IMAGE_REL_ARM_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break;
- case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break;
- case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
- case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, Sym); break;
+ case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break;
+ case IMAGE_REL_ARM_ADDR32NB: add32(Off, SX); break;
+ case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, SX + Config->ImageBase); break;
+ case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
@@ -166,18 +176,39 @@
// Apply relocations.
for (const coff_relocation &Rel : Relocs) {
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
+
+ // Get the output section of the symbol for this relocation. The output
+ // section is needed to compute SECREL and SECTION relocations used in debug
+ // info.
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
Defined *Sym = cast<Defined>(Body);
+ Chunk *C = Sym->getChunk();
+ OutputSection *OS = C ? C->getOutputSection() : nullptr;
+
+ // Only absolute and __ImageBase symbols lack an output section. For any
+ // other symbol, this indicates that the chunk was discarded. Normally
+ // relocations against discarded sections are an error. However, debug info
+ // sections are not GC roots and can end up with these kinds of relocations.
+ // Skip these relocations.
+ if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) {
+ if (isCodeView())
+ continue;
+ fatal("relocation against symbol in discarded section: " +
+ Sym->getName());
+ }
+ uint64_t S = Sym->getRVA();
+
+ // Compute the RVA of the relocation for relative relocations.
uint64_t P = RVA + Rel.VirtualAddress;
switch (Config->Machine) {
case AMD64:
- applyRelX64(Off, Rel.Type, Sym, P);
+ applyRelX64(Off, Rel.Type, OS, S, P);
break;
case I386:
- applyRelX86(Off, Rel.Type, Sym, P);
+ applyRelX86(Off, Rel.Type, OS, S, P);
break;
case ARMNT:
- applyRelARM(Off, Rel.Type, Sym, P);
+ applyRelARM(Off, Rel.Type, OS, S, P);
break;
default:
llvm_unreachable("unknown machine type");
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 54fffc5..6e1bf94 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -145,9 +145,12 @@
StringRef getSectionName() const override { return SectionName; }
void getBaserels(std::vector<Baserel> *Res) override;
bool isCOMDAT() const;
- void applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
- void applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
- void applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
+ void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
+ void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
+ void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp
index 25e5cc3..a2756e5 100644
--- a/lld/COFF/MarkLive.cpp
+++ b/lld/COFF/MarkLive.cpp
@@ -52,6 +52,13 @@
while (!Worklist.empty()) {
SectionChunk *SC = Worklist.pop_back_val();
+
+ // If this section was discarded, there are relocations referring to
+ // discarded sections. Ignore these sections to avoid crashing. They will be
+ // diagnosed during relocation processing.
+ if (SC->isDiscarded())
+ continue;
+
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index 8c1390c..a12ae1c 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -110,17 +110,9 @@
// writer sets and uses RVAs.
uint64_t getRVA();
- // Returns the RVA relative to the beginning of the output section.
- // Used to implement SECREL relocation type.
- uint32_t getSecrel();
-
- // Returns the output section index.
- // Used to implement SECTION relocation type.
- uint16_t getSectionIndex();
-
- // Returns true if this symbol points to an executable (e.g. .text) section.
- // Used to implement ARM relocations.
- bool isExecutable();
+ // Returns the chunk containing this symbol. Absolute symbols and __ImageBase
+ // do not have chunks, so this may return null.
+ Chunk *getChunk();
};
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
@@ -167,7 +159,6 @@
bool isCOMDAT() { return IsCOMDAT; }
SectionChunk *getChunk() { return *Data; }
uint32_t getValue() { return Sym->Value; }
- uint32_t getSecrel();
private:
SectionChunk **Data;
@@ -187,8 +178,7 @@
}
uint64_t getRVA() { return Data->getRVA(); }
- uint32_t getSecrel() { return Data->OutputSectionOff; }
- uint16_t getSectionIndex();
+ Chunk *getChunk() { return Data; }
private:
friend SymbolTable;
@@ -219,6 +209,7 @@
// against absolute symbols resolve to this 16 bit number, and it is the
// largest valid section index plus one. This is written by the Writer.
static uint16_t OutputSectionIndex;
+ uint16_t getSecIdx() { return OutputSectionIndex; }
private:
uint64_t VA;
@@ -237,9 +228,8 @@
// A null chunk indicates that this is __ImageBase. Otherwise, this is some
// other synthesized chunk, like SEHTableChunk.
- uint32_t getRVA() const { return C ? C->getRVA() : 0; }
- uint32_t getSecrel() const { return C ? C->OutputSectionOff : 0; }
- Chunk *getChunk() const { return C; }
+ uint32_t getRVA() { return C ? C->getRVA() : 0; }
+ Chunk *getChunk() { return C; }
private:
Chunk *C;
@@ -304,9 +294,11 @@
}
uint64_t getRVA() { return File->Location->getRVA(); }
+ Chunk *getChunk() { return File->Location; }
+ void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
+
StringRef getDLLName() { return File->DLLName; }
StringRef getExternalName() { return File->ExternalName; }
- void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
uint16_t getOrdinal() { return File->Hdr->OrdinalHint; }
ImportFile *File;
@@ -378,6 +370,29 @@
llvm_unreachable("unknown symbol kind");
}
+inline Chunk *Defined::getChunk() {
+ switch (kind()) {
+ case DefinedRegularKind:
+ return cast<DefinedRegular>(this)->getChunk();
+ case DefinedAbsoluteKind:
+ return nullptr;
+ case DefinedSyntheticKind:
+ return cast<DefinedSynthetic>(this)->getChunk();
+ case DefinedImportDataKind:
+ return cast<DefinedImportData>(this)->getChunk();
+ case DefinedImportThunkKind:
+ return cast<DefinedImportThunk>(this)->getChunk();
+ case DefinedLocalImportKind:
+ return cast<DefinedLocalImport>(this)->getChunk();
+ case DefinedCommonKind:
+ return cast<DefinedCommon>(this)->getChunk();
+ case LazyKind:
+ case UndefinedKind:
+ llvm_unreachable("Cannot get the chunk of an undefined symbol.");
+ }
+ llvm_unreachable("unknown symbol kind");
+}
+
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
// always one Symbol for each symbol name. The resolver updates the SymbolBody
// stored in the Body field of this object as it resolves symbols. Symbol also
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index d32577b..964e242 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -210,55 +210,6 @@
}
}
-uint32_t Defined::getSecrel() {
- assert(this);
- switch (kind()) {
- case DefinedRegularKind:
- return cast<DefinedRegular>(this)->getSecrel();
- case DefinedCommonKind:
- return cast<DefinedCommon>(this)->getSecrel();
- case DefinedSyntheticKind:
- return cast<DefinedSynthetic>(this)->getSecrel();
- default:
- break;
- }
- fatal("SECREL relocation points to a non-regular symbol: " + toString(*this));
-}
-
-uint32_t DefinedRegular::getSecrel() {
- assert(getChunk()->isLive() && "relocation against discarded section");
- uint64_t Diff = getRVA() - getChunk()->getOutputSection()->getRVA();
- assert(Diff < UINT32_MAX && "section offset too large");
- return (uint32_t)Diff;
-}
-
-uint16_t Defined::getSectionIndex() {
- if (auto *D = dyn_cast<DefinedRegular>(this))
- return D->getChunk()->getOutputSection()->SectionIndex;
- if (isa<DefinedAbsolute>(this))
- return DefinedAbsolute::OutputSectionIndex;
- if (auto *D = dyn_cast<DefinedCommon>(this))
- return D->getSectionIndex();
- if (auto *D = dyn_cast<DefinedSynthetic>(this)) {
- if (!D->getChunk())
- return 0;
- return D->getChunk()->getOutputSection()->SectionIndex;
- }
- fatal("SECTION relocation points to a non-regular symbol: " +
- toString(*this));
-}
-
-uint16_t DefinedCommon::getSectionIndex() {
- return Data->getOutputSection()->SectionIndex;
-}
-
-bool Defined::isExecutable() {
- const auto X = IMAGE_SCN_MEM_EXECUTE;
- if (auto *D = dyn_cast<DefinedRegular>(this))
- return D->getChunk()->getOutputSection()->getPermissions() & X;
- return isa<DefinedImportThunk>(this);
-}
-
} // namespace coff
} // namespace lld