[COFF] When doing automatic dll imports, replace whole .refptr.<var> chunks with...
authorMartin Storsjo <martin@martin.st>
Fri, 31 Aug 2018 07:45:20 +0000 (07:45 +0000)
committerMartin Storsjo <martin@martin.st>
Fri, 31 Aug 2018 07:45:20 +0000 (07:45 +0000)
After fixing up the runtime pseudo relocation, the .refptr.<var>
will be a plain pointer with the same value as the IAT entry itself.
To save a little binary size and reduce the number of runtime pseudo
relocations, redirect references to the IAT entry (via the __imp_<var>
symbol) itself and discard the .refptr.<var> chunk (as long as the
same section chunk doesn't contain anything else than the single
pointer).

As there are now cases for both setting the Live variable to true
and false externally, remove the accessors and setters and just make
the variable public instead.

Differential Revision: https://reviews.llvm.org/D51456

llvm-svn: 341175

lld/COFF/Chunks.cpp
lld/COFF/Chunks.h
lld/COFF/ICF.cpp
lld/COFF/MarkLive.cpp
lld/COFF/PDB.cpp
lld/COFF/SymbolTable.cpp
lld/COFF/Symbols.cpp
lld/COFF/Writer.cpp
lld/test/COFF/autoimport-refptr.s [new file with mode: 0644]

index e565d73..1e0637a 100644 (file)
@@ -758,12 +758,12 @@ void MergeChunk::addSection(SectionChunk *C) {
 
 void MergeChunk::finalizeContents() {
   for (SectionChunk *C : Sections)
-    if (C->isLive())
+    if (C->Live)
       Builder.add(toStringRef(C->getContents()));
   Builder.finalize();
 
   for (SectionChunk *C : Sections) {
-    if (!C->isLive())
+    if (!C->Live)
       continue;
     size_t Off = Builder.getOffset(toStringRef(C->getContents()));
     C->setOutputSection(Out);
index d73449f..9d6b46e 100644 (file)
@@ -174,16 +174,6 @@ public:
 
   StringRef getDebugName() override;
 
-  // Returns true if the chunk was not dropped by GC.
-  bool isLive() { return Live; }
-
-  // Used by the garbage collector.
-  void markLive() {
-    assert(Config->DoGC && "should only mark things live from GC");
-    assert(!isLive() && "Cannot mark an already live section!");
-    Live = true;
-  }
-
   // True if this is a codeview debug info chunk. These will not be laid out in
   // the image. Instead they will end up in the PDB, if one is requested.
   bool isCodeView() const {
@@ -224,13 +214,13 @@ public:
 
   ArrayRef<coff_relocation> Relocs;
 
+  // Used by the garbage collector.
+  bool Live;
+
 private:
   StringRef SectionName;
   std::vector<SectionChunk *> AssocChildren;
 
-  // Used by the garbage collector.
-  bool Live;
-
   // Used for ICF (Identical COMDAT Folding)
   void replace(SectionChunk *Other);
   uint32_t Class[2] = {0, 0};
index 943f6ed..9acd6e0 100644 (file)
@@ -80,7 +80,7 @@ private:
 bool ICF::isEligible(SectionChunk *C) {
   // Non-comdat chunks, dead chunks, and writable chunks are not elegible.
   bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
-  if (!C->isCOMDAT() || !C->isLive() || Writable)
+  if (!C->isCOMDAT() || !C->Live || Writable)
     return false;
 
   // Code sections are eligible.
index 57ae450..18b1c9c 100644 (file)
@@ -32,13 +32,13 @@ void markLive(ArrayRef<Chunk *> Chunks) {
   // COMDAT section chunks are dead by default. Add non-COMDAT chunks.
   for (Chunk *C : Chunks)
     if (auto *SC = dyn_cast<SectionChunk>(C))
-      if (SC->isLive())
+      if (SC->Live)
         Worklist.push_back(SC);
 
   auto Enqueue = [&](SectionChunk *C) {
-    if (C->isLive())
+    if (C->Live)
       return;
-    C->markLive();
+    C->Live = true;
     Worklist.push_back(C);
   };
 
@@ -57,7 +57,7 @@ void markLive(ArrayRef<Chunk *> Chunks) {
 
   while (!Worklist.empty()) {
     SectionChunk *SC = Worklist.pop_back_val();
-    assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
+    assert(SC->Live && "We mark as live when pushing onto the worklist!");
 
     // Mark all symbols listed in the relocation table for this section.
     for (Symbol *B : SC->symbols())
index 766bf3f..624fa00 100644 (file)
@@ -821,7 +821,7 @@ void PDBLinker::addObjFile(ObjFile *File) {
   uint32_t Modi = File->ModuleDBI->getModuleIndex();
   for (Chunk *C : Chunks) {
     auto *SecChunk = dyn_cast<SectionChunk>(C);
-    if (!SecChunk || !SecChunk->isLive())
+    if (!SecChunk || !SecChunk->Live)
       continue;
     pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi);
     File->ModuleDBI->setFirstSectionContrib(SC);
@@ -851,7 +851,7 @@ void PDBLinker::addObjFile(ObjFile *File) {
   DebugChecksumsSubsectionRef Checksums;
   std::vector<ulittle32_t *> StringTableReferences;
   for (SectionChunk *DebugChunk : File->getDebugChunks()) {
-    if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
+    if (!DebugChunk->Live || DebugChunk->getSectionName() != ".debug$S")
       continue;
 
     ArrayRef<uint8_t> RelocatedDebugContents =
index f66fba2..1f04124 100644 (file)
@@ -169,6 +169,23 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
   // reference itself to point at the IAT entry.
   Sym->replaceKeepingName(Imp, sizeof(DefinedImportData));
   cast<DefinedImportData>(Sym)->IsRuntimePseudoReloc = true;
+
+  // There may exist symbols named .refptr.<name> which only consist
+  // of a single pointer to <name>. If it turns out <name> is
+  // automatically imported, we don't need to keep the .refptr.<name>
+  // pointer at all, but redirect all accesses to it to the IAT entry
+  // for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
+  DefinedRegular *Refptr =
+      dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
+  size_t PtrSize = Config->is64() ? 8 : 4;
+  if (Refptr && Refptr->getChunk()->getSize() == PtrSize) {
+    SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
+    if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
+        log("Replacing .refptr." + Name + " with " + Imp->getName());
+        Refptr->getChunk()->Live = false;
+        Refptr->replaceKeepingName(Imp, sizeof(DefinedImportData));
+    }
+  }
   return true;
 }
 
index fc0ade2..ccaf864 100644 (file)
@@ -54,7 +54,7 @@ InputFile *Symbol::getFile() {
 
 bool Symbol::isLive() const {
   if (auto *R = dyn_cast<DefinedRegular>(this))
-    return R->getChunk()->isLive();
+    return R->getChunk()->Live;
   if (auto *Imp = dyn_cast<DefinedImportData>(this))
     return Imp->File->Live;
   if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
index 5f87f0e..ff92901 100644 (file)
@@ -435,7 +435,7 @@ void Writer::createSections() {
   std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map;
   for (Chunk *C : Symtab->getChunks()) {
     auto *SC = dyn_cast<SectionChunk>(C);
-    if (SC && !SC->isLive()) {
+    if (SC && !SC->Live) {
       if (Config->Verbose)
         SC->printDiscardedMessage();
       continue;
@@ -1014,7 +1014,7 @@ static void markSymbolsWithRelocations(ObjFile *File,
     // We only care about live section chunks. Common chunks and other chunks
     // don't generally contain relocations.
     SectionChunk *SC = dyn_cast<SectionChunk>(C);
-    if (!SC || !SC->isLive())
+    if (!SC || !SC->Live)
       continue;
 
     for (const coff_relocation &Reloc : SC->Relocs) {
@@ -1097,7 +1097,7 @@ void Writer::markSymbolsForRVATable(ObjFile *File,
     // is associated with something like a vtable and the vtable is discarded.
     // In this case, the associated gfids section is discarded, and we don't
     // mark the virtual member functions as address-taken by the vtable.
-    if (!C->isLive())
+    if (!C->Live)
       continue;
 
     // Validate that the contents look like symbol table indices.
@@ -1153,7 +1153,7 @@ void Writer::createRuntimePseudoRelocs() {
 
   for (Chunk *C : Symtab->getChunks()) {
     auto *SC = dyn_cast<SectionChunk>(C);
-    if (!SC || !SC->isLive())
+    if (!SC || !SC->Live)
       continue;
     SC->getRuntimePseudoRelocs(Rels);
   }
diff --git a/lld/test/COFF/autoimport-refptr.s b/lld/test/COFF/autoimport-refptr.s
new file mode 100644 (file)
index 0000000..6ef7822
--- /dev/null
@@ -0,0 +1,65 @@
+# REQUIRES: x86
+
+# RUN: echo -e ".global variable\n.global DllMainCRTStartup\n.text\nDllMainCRTStartup:\nret\n.data\nvariable:\n.long 42" > %t-lib.s
+# RUN: llvm-mc -triple=x86_64-windows-gnu %t-lib.s -filetype=obj -o %t-lib.obj
+# RUN: lld-link -out:%t-lib.dll -dll -entry:DllMainCRTStartup %t-lib.obj -lldmingw -implib:%t-lib.lib
+
+# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.obj
+# RUN: lld-link -lldmingw -out:%t.exe -entry:main %t.obj %t-lib.lib -verbose
+
+# RUN: llvm-readobj -coff-imports %t.exe | FileCheck -check-prefix=IMPORTS %s
+# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s
+# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=CONTENTS %s
+
+# IMPORTS: Import {
+# IMPORTS-NEXT: Name: autoimport-refptr.s.tmp-lib.dll
+# IMPORTS-NEXT: ImportLookupTableRVA: 0x2030
+# IMPORTS-NEXT: ImportAddressTableRVA: 0x2040
+# IMPORTS-NEXT: Symbol: variable (0)
+# IMPORTS-NEXT: }
+
+# DISASM: Disassembly of section .text:
+# DISASM: .text:
+# Relative offset at 0x1002 pointing at the IAT at 0x2040
+# DISASM: 140001000:      48 8b 05 39 10 00 00    movq    4153(%rip), %rax
+# DISASM: 140001007:      8b 00   movl    (%rax), %eax
+# Relative offset at 0x100b pointing at the .refptr.localvar stub at
+# 0x2000
+# DISASM: 140001009:      48 8b 0d f0 0f 00 00    movq    4080(%rip), %rcx
+# DISASM: 140001010:      03 01   addl    (%rcx), %eax
+# DISASM: 140001012:      c3      retq
+
+# relocs: pointing at an empty list of runtime pseudo relocs.
+# localvar: 42
+# CONTENTS: Contents of section .data:
+# CONTENTS:  140003000 08200040 01000000 08200040 01000000
+# CONTENTS:  140003010 2a000000
+
+    .global main
+    .global localvar
+    .text
+main:
+    movq .refptr.variable(%rip), %rax
+    movl (%rax), %eax
+    movq .refptr.localvar(%rip), %rcx
+    addl (%rcx), %eax
+    ret
+
+    .data
+relocs:
+    .quad __RUNTIME_PSEUDO_RELOC_LIST__
+    .quad __RUNTIME_PSEUDO_RELOC_LIST_END__
+localvar:
+    .int 42
+
+# Normally the compiler wouldn't emit a stub for a variable that is
+# emitted in the same translation unit.
+    .section .rdata$.refptr.localvar,"dr",discard,.refptr.localvar
+    .global .refptr.localvar
+.refptr.localvar:
+    .quad localvar
+
+    .section .rdata$.refptr.variable,"dr",discard,.refptr.variable
+    .global .refptr.variable
+.refptr.variable:
+    .quad variable