From 9aa82f76acb7f6ece2c93a385db6b653622311a0 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Wed, 24 May 2017 22:30:06 +0000 Subject: [PATCH] Garbage collect dllimported symbols. This is a different implementation than r303225 (which was reverted in r303270, re-submitted in r303304 and then re-reverted in r303527). In the previous patch, I tried to add Live bit to each dllimported symbol. It turned out that it didn't work with "oldnames.lib" which contains a lot of weak aliases to dllimported symbols. The way we handle weak aliases is to check if undefined symbols can be resolved using weak aliases, and if so, memcpy the Defined symbols to weak Undefined symbols, so that any references to weak aliases automatically see defined symbols instead of undefined ones. This memcpy happens before MarkLive kicks in. That means we may have multiple copies of dllimported symbols. So turning on one instance's Live bit is not enough. This patch moves the Live bit to dllimport file. Since multiple copies of dllsymbols still point to the same file, we can use it as the central repository to keep track of liveness. Differential Revision: https://reviews.llvm.org/D33520 llvm-svn: 303814 --- lld/COFF/InputFiles.h | 13 ++++++- lld/COFF/MarkLive.cpp | 17 ++++++--- lld/COFF/Symbols.cpp | 2 +- lld/COFF/Symbols.h | 3 +- lld/COFF/Writer.cpp | 17 +++++++++ lld/test/COFF/Inputs/import.yaml | 9 ++++- lld/test/COFF/Inputs/oldname.yaml | 26 ++++++++++++++ lld/test/COFF/dllimport-gc.test | 58 +++++++++++++++++++++++++++++++ 8 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 lld/test/COFF/Inputs/oldname.yaml create mode 100644 lld/test/COFF/dllimport-gc.test diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 9e32b3b9f9d6..9449f24ac241 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -10,6 +10,7 @@ #ifndef LLD_COFF_INPUT_FILES_H #define LLD_COFF_INPUT_FILES_H +#include "Config.h" #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" @@ -161,7 +162,9 @@ private: // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {} + explicit ImportFile(MemoryBufferRef M) + : InputFile(ImportKind, M), Live(!Config->DoGC) {} + static bool classof(const InputFile *F) { return F->kind() == ImportKind; } DefinedImportData *ImpSym = nullptr; @@ -176,6 +179,14 @@ public: StringRef ExternalName; const coff_import_header *Hdr; Chunk *Location = nullptr; + + // We want to eliminate dllimported symbols if no one actually refers them. + // This "Live" bit is used to keep track of which import library members + // are actually in use. + // + // If the Live bit is turned off by MarkLive, Writer will ignore dllimported + // symbols provided by this import library member. + bool Live; }; // Used for LTO. diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp index 0156d238b672..25e5cc350673 100644 --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -37,19 +37,26 @@ void markLive(const std::vector &Chunks) { Worklist.push_back(C); }; + auto AddSym = [&](SymbolBody *B) { + if (auto *Sym = dyn_cast(B)) + Enqueue(Sym->getChunk()); + else if (auto *Sym = dyn_cast(B)) + Sym->File->Live = true; + else if (auto *Sym = dyn_cast(B)) + Sym->WrappedSym->File->Live = true; + }; + // Add GC root chunks. for (SymbolBody *B : Config->GCRoot) - if (auto *D = dyn_cast(B)) - Enqueue(D->getChunk()); + AddSym(B); while (!Worklist.empty()) { SectionChunk *SC = Worklist.pop_back_val(); assert(SC->isLive() && "We mark as live when pushing onto the worklist!"); // Mark all symbols listed in the relocation table for this section. - for (SymbolBody *S : SC->symbols()) - if (auto *D = dyn_cast(S)) - Enqueue(D->getChunk()); + for (SymbolBody *B : SC->symbols()) + AddSym(B); // Mark associative sections if any. for (SectionChunk *C : SC->children()) diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp index 5fd17a02ffc3..5c185a511dd7 100644 --- a/lld/COFF/Symbols.cpp +++ b/lld/COFF/Symbols.cpp @@ -72,7 +72,7 @@ static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine) - : Defined(DefinedImportThunkKind, Name), + : Defined(DefinedImportThunkKind, Name), WrappedSym(S), Data(makeImportThunk(S, Machine)) {} Defined *Undefined::getWeakAlias() { diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index 1b83f73ff20c..801fc87f91d9 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -300,7 +300,6 @@ public: void setLocation(Chunk *AddressTable) { File->Location = AddressTable; } uint16_t getOrdinal() { return File->Hdr->OrdinalHint; } -private: ImportFile *File; }; @@ -320,6 +319,8 @@ public: uint64_t getRVA() { return Data->getRVA(); } Chunk *getChunk() { return Data; } + DefinedImportData *WrappedSym; + private: Chunk *Data; }; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index cf3ad7ef045c..aba33546154b 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -365,6 +365,9 @@ void Writer::createImportTables() { // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) for (ImportFile *File : Symtab->ImportFiles) { + if (!File->Live) + continue; + std::string DLL = StringRef(File->DLLName).lower(); if (Config->DLLOrder.count(DLL) == 0) Config->DLLOrder[DLL] = Config->DLLOrder.size(); @@ -372,19 +375,25 @@ void Writer::createImportTables() { OutputSection *Text = createSection(".text"); for (ImportFile *File : Symtab->ImportFiles) { + if (!File->Live) + continue; + if (DefinedImportThunk *Thunk = File->ThunkSym) Text->addChunk(Thunk->getChunk()); + if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { DelayIdata.add(File->ImpSym); } else { Idata.add(File->ImpSym); } } + if (!Idata.empty()) { OutputSection *Sec = createSection(".idata"); for (Chunk *C : Idata.getChunks()) Sec->addChunk(C); } + if (!DelayIdata.empty()) { Defined *Helper = cast(Config->DelayLoadHelper); DelayIdata.create(Helper); @@ -437,6 +446,14 @@ Optional Writer::createSymbol(Defined *Def) { if (!D->getChunk()->isLive()) return None; + if (auto *Sym = dyn_cast(Def)) + if (!Sym->File->Live) + return None; + + if (auto *Sym = dyn_cast(Def)) + if (!Sym->WrappedSym->File->Live) + return None; + coff_symbol16 Sym; StringRef Name = Def->getName(); if (Name.size() > COFF::NameSize) { diff --git a/lld/test/COFF/Inputs/import.yaml b/lld/test/COFF/Inputs/import.yaml index 493400143d6c..b7ae026de29b 100644 --- a/lld/test/COFF/Inputs/import.yaml +++ b/lld/test/COFF/Inputs/import.yaml @@ -7,6 +7,13 @@ sections: Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] Alignment: 4 SectionData: 0000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: exportfn1 + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 4 + SymbolName: exportfn2 + Type: IMAGE_REL_AMD64_ADDR32NB symbols: - Name: .text Value: 0 @@ -16,7 +23,7 @@ symbols: StorageClass: IMAGE_SYM_CLASS_STATIC SectionDefinition: Length: 8 - NumberOfRelocations: 0 + NumberOfRelocations: 2 NumberOfLinenumbers: 0 CheckSum: 0 Number: 0 diff --git a/lld/test/COFF/Inputs/oldname.yaml b/lld/test/COFF/Inputs/oldname.yaml new file mode 100644 index 000000000000..42ee5b2269bd --- /dev/null +++ b/lld/test/COFF/Inputs/oldname.yaml @@ -0,0 +1,26 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_UNKNOWN + Characteristics: [ ] +sections: + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 1 + SectionData: '' +symbols: + - Name: exportfn1 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: exportfn1_alias + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL + WeakExternal: + TagIndex: 0 + Characteristics: IMAGE_WEAK_EXTERN_SEARCH_ALIAS +... diff --git a/lld/test/COFF/dllimport-gc.test b/lld/test/COFF/dllimport-gc.test new file mode 100644 index 000000000000..54ae773e793f --- /dev/null +++ b/lld/test/COFF/dllimport-gc.test @@ -0,0 +1,58 @@ +# REQUIRES: winres + +# RUN: yaml2obj < %p/Inputs/export.yaml > %t-lib.obj +# RUN: lld-link /out:%t.dll /dll %t-lib.obj /implib:%t.lib /export:exportfn1 + +# RUN: yaml2obj < %p/Inputs/oldname.yaml > %t-oldname.obj + +# RUN: yaml2obj < %s > %t.obj + +# RUN: lld-link /out:%t1.exe /entry:main %t.obj %t-oldname.obj %t.lib +# RUN: llvm-readobj -coff-imports %t1.exe | FileCheck -check-prefix=REF %s +# REF-NOT: Symbol: exportfn1 + +# RUN: lld-link /out:%t2.exe /entry:main %t.obj %t-oldname.obj %t.lib /opt:noref +# RUN: llvm-readobj -coff-imports %t2.exe | FileCheck -check-prefix=NOREF %s +# NOREF: Symbol: exportfn1 + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 0000000000000000 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: exportfn1 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: exportfn1_alias + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +...