forked from OSchip/llvm-project
[COFF] Improve synthetic symbol handling
Summary: The main change is that we can have SECREL and SECTION relocations against ___safe_se_handler_table, which is important for handling the debug info in the MSVCRT. Previously we were using DefinedRelative for __safe_se_handler_table and __ImageBase, and after we implement CFGuard, we plan to extend it to handle __guard_fids_table, __guard_longjmp_table, and more. However, DefinedRelative is really only suitable for implementing __ImageBase, because it lacks a Chunk, which you need in order to figure out the output section index and output section offset when resolving SECREl and SECTION relocations. This change renames DefinedRelative to DefinedSynthetic and gives it a Chunk. One wart is that __ImageBase doesn't have a chunk. It points to the PE header, effectively. We could split DefinedRelative and DefinedSynthetic if we think that's cleaner and creates fewer special cases. I also added safeseh.s, which checks that we don't emit a safe seh table entries pointing to garbage collected handlers and that we don't emit a table at all when there are no handlers. Reviewers: ruiu Reviewed By: ruiu Subscribers: inglorion, pcc, llvm-commits, aprantl Differential Revision: https://reviews.llvm.org/D34577 llvm-svn: 306293
This commit is contained in:
parent
92c3781959
commit
502d4ce2e4
|
@ -1026,10 +1026,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
if (Config->ImageBase == uint64_t(-1))
|
||||
Config->ImageBase = getDefaultImageBase();
|
||||
|
||||
Symtab.addRelative(mangle("__ImageBase"), 0);
|
||||
Symtab.addSynthetic(mangle("__ImageBase"), nullptr);
|
||||
if (Config->Machine == I386) {
|
||||
Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0);
|
||||
Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0);
|
||||
Symtab.addAbsolute("___safe_se_handler_table", 0);
|
||||
Symtab.addAbsolute("___safe_se_handler_count", 0);
|
||||
}
|
||||
|
||||
// We do not support /guard:cf (control flow protection) yet.
|
||||
|
|
|
@ -219,13 +219,13 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
|
|||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
|
||||
Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedRelative>(S, N, VA);
|
||||
replaceBody<DefinedSynthetic>(S, N, C);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
// Creates an Undefined symbol for a given name.
|
||||
SymbolBody *addUndefined(StringRef Name);
|
||||
|
||||
Symbol *addRelative(StringRef N, uint64_t VA);
|
||||
Symbol *addSynthetic(StringRef N, Chunk *C);
|
||||
Symbol *addAbsolute(StringRef N, uint64_t VA);
|
||||
|
||||
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
|
||||
|
|
|
@ -50,13 +50,13 @@ public:
|
|||
DefinedImportThunkKind,
|
||||
DefinedImportDataKind,
|
||||
DefinedAbsoluteKind,
|
||||
DefinedRelativeKind,
|
||||
DefinedSyntheticKind,
|
||||
|
||||
UndefinedKind,
|
||||
LazyKind,
|
||||
|
||||
LastDefinedCOFFKind = DefinedCommonKind,
|
||||
LastDefinedKind = DefinedRelativeKind,
|
||||
LastDefinedKind = DefinedSyntheticKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
@ -112,11 +112,11 @@ public:
|
|||
|
||||
// Returns the RVA relative to the beginning of the output section.
|
||||
// Used to implement SECREL relocation type.
|
||||
uint64_t getSecrel();
|
||||
uint32_t getSecrel();
|
||||
|
||||
// Returns the output section index.
|
||||
// Used to implement SECTION relocation type.
|
||||
uint64_t getSectionIndex();
|
||||
uint16_t getSectionIndex();
|
||||
|
||||
// Returns true if this symbol points to an executable (e.g. .text) section.
|
||||
// Used to implement ARM relocations.
|
||||
|
@ -167,6 +167,7 @@ public:
|
|||
bool isCOMDAT() { return IsCOMDAT; }
|
||||
SectionChunk *getChunk() { return *Data; }
|
||||
uint32_t getValue() { return Sym->Value; }
|
||||
uint32_t getSecrel();
|
||||
|
||||
private:
|
||||
SectionChunk **Data;
|
||||
|
@ -221,24 +222,25 @@ private:
|
|||
uint64_t VA;
|
||||
};
|
||||
|
||||
// This is a kind of absolute symbol but relative to the image base.
|
||||
// Unlike absolute symbols, relocations referring this kind of symbols
|
||||
// are subject of the base relocation. This type is used rarely --
|
||||
// mainly for __ImageBase.
|
||||
class DefinedRelative : public Defined {
|
||||
// This symbol is used for linker-synthesized symbols like __ImageBase and
|
||||
// __safe_se_handler_table.
|
||||
class DefinedSynthetic : public Defined {
|
||||
public:
|
||||
explicit DefinedRelative(StringRef Name, uint64_t V = 0)
|
||||
: Defined(DefinedRelativeKind, Name), RVA(V) {}
|
||||
explicit DefinedSynthetic(StringRef Name, Chunk *C)
|
||||
: Defined(DefinedSyntheticKind, Name), C(C) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == DefinedRelativeKind;
|
||||
return S->kind() == DefinedSyntheticKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return RVA; }
|
||||
void setRVA(uint64_t V) { RVA = V; }
|
||||
// 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; }
|
||||
|
||||
private:
|
||||
uint64_t RVA;
|
||||
Chunk *C;
|
||||
};
|
||||
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
|
@ -355,8 +357,8 @@ inline uint64_t Defined::getRVA() {
|
|||
switch (kind()) {
|
||||
case DefinedAbsoluteKind:
|
||||
return cast<DefinedAbsolute>(this)->getRVA();
|
||||
case DefinedRelativeKind:
|
||||
return cast<DefinedRelative>(this)->getRVA();
|
||||
case DefinedSyntheticKind:
|
||||
return cast<DefinedSynthetic>(this)->getRVA();
|
||||
case DefinedImportDataKind:
|
||||
return cast<DefinedImportData>(this)->getRVA();
|
||||
case DefinedImportThunkKind:
|
||||
|
@ -391,7 +393,7 @@ struct Symbol {
|
|||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy,
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy,
|
||||
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
|
||||
Body;
|
||||
|
||||
|
|
|
@ -210,17 +210,36 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t Defined::getSecrel() {
|
||||
if (auto *D = dyn_cast<DefinedRegular>(this))
|
||||
return getRVA() - D->getChunk()->getOutputSection()->getRVA();
|
||||
uint32_t Defined::getSecrel() {
|
||||
assert(this);
|
||||
switch (kind()) {
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getSecrel();
|
||||
case DefinedSyntheticKind:
|
||||
return cast<DefinedSynthetic>(this)->getSecrel();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fatal("SECREL relocation points to a non-regular symbol: " + toString(*this));
|
||||
}
|
||||
|
||||
uint64_t Defined::getSectionIndex() {
|
||||
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<DefinedSynthetic>(this)) {
|
||||
if (!D->getChunk())
|
||||
return 0;
|
||||
return D->getChunk()->getOutputSection()->SectionIndex;
|
||||
}
|
||||
fatal("SECTION relocation points to a non-regular symbol: " +
|
||||
toString(*this));
|
||||
}
|
||||
|
@ -348,12 +367,19 @@ void Writer::createMiscChunks() {
|
|||
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
|
||||
if (!File->SEHCompat)
|
||||
return;
|
||||
for (SymbolBody *B : File->SEHandlers)
|
||||
Handlers.insert(cast<Defined>(B));
|
||||
for (SymbolBody *B : File->SEHandlers) {
|
||||
// Make sure the handler is still live. Assume all handlers are regular
|
||||
// symbols.
|
||||
auto *D = dyn_cast<DefinedRegular>(B);
|
||||
if (D && D->getChunk()->isLive())
|
||||
Handlers.insert(D);
|
||||
}
|
||||
}
|
||||
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
if (!Handlers.empty()) {
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
}
|
||||
}
|
||||
|
||||
// Create .idata section for the DLL-imported symbol table.
|
||||
|
@ -445,7 +471,7 @@ size_t Writer::addEntryToStringTable(StringRef Str) {
|
|||
|
||||
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
|
||||
// Relative symbols are unrepresentable in a COFF symbol table.
|
||||
if (isa<DefinedRelative>(Def))
|
||||
if (isa<DefinedSynthetic>(Def))
|
||||
return None;
|
||||
|
||||
if (auto *D = dyn_cast<DefinedRegular>(Def)) {
|
||||
|
@ -758,10 +784,13 @@ void Writer::openFile(StringRef Path) {
|
|||
void Writer::fixSafeSEHSymbols() {
|
||||
if (!SEHTable)
|
||||
return;
|
||||
if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body()))
|
||||
T->setRVA(SEHTable->getRVA());
|
||||
if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body()))
|
||||
C->setVA(SEHTable->getSize() / 4);
|
||||
// Replace the absolute table symbol with a synthetic symbol pointing to the
|
||||
// SEHTable chunk so that we can emit base relocations for it and resolve
|
||||
// section relative relocations.
|
||||
Symbol *T = Symtab->find("___safe_se_handler_table");
|
||||
Symbol *C = Symtab->find("___safe_se_handler_count");
|
||||
replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable);
|
||||
cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4);
|
||||
}
|
||||
|
||||
// Handles /section options to allow users to overwrite
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# RUN: yaml2obj %s -o %t.obj
|
||||
# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj
|
||||
# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
|
||||
|
||||
# There is an S_GDATA32 symbol record with .secrel32 and .secidx relocations in
|
||||
# it in this debug info. This is similar to the relocations in the loadcfg.obj
|
||||
# file in the MSVC CRT. We need to make sure that our relocation logic matches
|
||||
# MSVC's for these absolute, linker-provided symbols.
|
||||
|
||||
# CHECK: Mod 0000 |
|
||||
# CHECK-NEXT: - S_GDATA32 [size = 40] `___safe_se_handler_table`
|
||||
# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000
|
||||
# CHECK-NEXT: Mod 0001 | `* Linker *`:
|
||||
|
||||
--- !COFF
|
||||
header:
|
||||
Machine: IMAGE_FILE_MACHINE_I386
|
||||
Characteristics: [ ]
|
||||
sections:
|
||||
- Name: '.debug$S'
|
||||
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
|
||||
Alignment: 1
|
||||
Subsections:
|
||||
- !Symbols
|
||||
Records:
|
||||
- Kind: S_GDATA32
|
||||
DataSym:
|
||||
Type: 34
|
||||
DisplayName: ___safe_se_handler_table
|
||||
- !StringTable
|
||||
Strings:
|
||||
Relocations:
|
||||
- VirtualAddress: 20
|
||||
SymbolName: ___safe_se_handler_table
|
||||
Type: IMAGE_REL_I386_SECREL
|
||||
- VirtualAddress: 24
|
||||
SymbolName: ___safe_se_handler_table
|
||||
Type: IMAGE_REL_I386_SECTION
|
||||
- Name: '.text$mn'
|
||||
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
|
||||
Alignment: 16
|
||||
SectionData: 488D0500000000C3
|
||||
Relocations:
|
||||
- VirtualAddress: 3
|
||||
SymbolName: ___safe_se_handler_table
|
||||
Type: IMAGE_REL_I386_REL32
|
||||
symbols:
|
||||
- Name: '.debug$S'
|
||||
Value: 0
|
||||
SectionNumber: 1
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
SectionDefinition:
|
||||
Length: 372
|
||||
NumberOfRelocations: 6
|
||||
NumberOfLinenumbers: 0
|
||||
CheckSum: 0
|
||||
Number: 0
|
||||
- Name: '.text$mn'
|
||||
Value: 0
|
||||
SectionNumber: 2
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
SectionDefinition:
|
||||
Length: 8
|
||||
NumberOfRelocations: 1
|
||||
NumberOfLinenumbers: 0
|
||||
CheckSum: 1092178131
|
||||
Number: 0
|
||||
- Name: _main
|
||||
Value: 0
|
||||
SectionNumber: 2
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
|
||||
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
- Name: ___safe_se_handler_table
|
||||
Value: 0
|
||||
SectionNumber: 0
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
...
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# RUN: llvm-mc -triple i686-windows-msvc %s -filetype=obj -o %t.obj
|
||||
# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:noref -entry:main
|
||||
# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC
|
||||
# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:ref -entry:main
|
||||
# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC
|
||||
|
||||
# CHECK-NOGC: LoadConfig [
|
||||
# CHECK-NOGC: Size: 0x48
|
||||
# CHECK-NOGC: SEHandlerTable: 0x401048
|
||||
# CHECK-NOGC: SEHandlerCount: 1
|
||||
# CHECK-NOGC: ]
|
||||
# CHECK-NOGC: SEHTable [
|
||||
# CHECK-NOGC-NEXT: 0x402006
|
||||
# CHECK-NOGC-NEXT: ]
|
||||
|
||||
# CHECK-GC: LoadConfig [
|
||||
# CHECK-GC: Size: 0x48
|
||||
# CHECK-GC: SEHandlerTable: 0x0
|
||||
# CHECK-GC: SEHandlerCount: 0
|
||||
# CHECK-GC: ]
|
||||
# CHECK-GC-NOT: SEHTable
|
||||
|
||||
|
||||
.def @feat.00;
|
||||
.scl 3;
|
||||
.type 0;
|
||||
.endef
|
||||
.globl @feat.00
|
||||
@feat.00 = 1
|
||||
|
||||
.def _main;
|
||||
.scl 2;
|
||||
.type 32;
|
||||
.endef
|
||||
.section .text,"xr",one_only,_main
|
||||
.globl _main
|
||||
_main:
|
||||
movl $42, %eax
|
||||
ret
|
||||
|
||||
# This handler can be GCd, which will make the safeseh table empty, so it should
|
||||
# appear null.
|
||||
.def _my_handler;
|
||||
.scl 3;
|
||||
.type 32;
|
||||
.endef
|
||||
.section .text,"xr",one_only,_my_handler
|
||||
_my_handler:
|
||||
ret
|
||||
|
||||
.safeseh _my_handler
|
||||
|
||||
|
||||
.section .rdata,"dr"
|
||||
.globl __load_config_used
|
||||
__load_config_used:
|
||||
.long 72
|
||||
.fill 60, 1, 0
|
||||
.long ___safe_se_handler_table
|
||||
.long ___safe_se_handler_count
|
Loading…
Reference in New Issue