From 33b71ec9c6719d3b6429f121dcf1f6f7c7f5abef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
Date: Thu, 29 Apr 2021 14:06:24 +0300
Subject: [PATCH] [LLD] [COFF] Fix automatic export of symbols from LTO objects

Differential Revision: https://reviews.llvm.org/D101569
---
 COFF/Chunks.cpp             | 14 ++++++++++----
 COFF/Driver.cpp             | 13 ++++++++-----
 COFF/InputFiles.cpp         | 24 ++++++++++++++++++++++--
 test/COFF/export-all-lto.ll | 23 +++++++++++++++++++++++
 4 files changed, 63 insertions(+), 11 deletions(-)
 create mode 100644 test/COFF/export-all-lto.ll

diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 35fe61f18fa8..f344df042317 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -32,12 +32,15 @@ namespace coff {
 SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
     : Chunk(SectionKind), file(f), header(h), repl(this) {
   // Initialize relocs.
-  setRelocs(file->getCOFFObj()->getRelocations(header));
+  if (file)
+    setRelocs(file->getCOFFObj()->getRelocations(header));
 
   // Initialize sectionName.
   StringRef sectionName;
-  if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header))
-    sectionName = *e;
+  if (file) {
+    if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header))
+      sectionName = *e;
+  }
   sectionNameData = sectionName.data();
   sectionNameSize = sectionName.size();
 
@@ -49,7 +52,10 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
   // enabled, treat non-comdat sections as roots. Generally optimized object
   // files will be built with -ffunction-sections or /Gy, so most things worth
   // stripping will be in a comdat.
-  live = !config->doGC || !isCOMDAT();
+  if (config)
+    live = !config->doGC || !isCOMDAT();
+  else
+    live = true;
 }
 
 // SectionChunk is one of the most frequently allocated classes, so it is
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 029148c4722b..03db71cc9082 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -1220,6 +1220,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
     if (Chunk *c = def->getChunk())
       if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
         e.data = true;
+    s->isUsedInRegularObj = true;
     config->exports.push_back(e);
   });
 }
@@ -2117,6 +2118,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (errorCount())
     return;
 
+  config->hadExplicitExports = !config->exports.empty();
+  if (config->mingw) {
+    // In MinGW, all symbols are automatically exported if no symbols
+    // are chosen to be exported.
+    maybeExportMinGWSymbols(args);
+  }
+
   // Do LTO by compiling bitcode input files to a set of native COFF files then
   // link those files (unless -thinlto-index-only was given, in which case we
   // resolve symbols and write indices, but don't generate native code or link).
@@ -2141,12 +2149,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (errorCount())
     return;
 
-  config->hadExplicitExports = !config->exports.empty();
   if (config->mingw) {
-    // In MinGW, all symbols are automatically exported if no symbols
-    // are chosen to be exported.
-    maybeExportMinGWSymbols(args);
-
     // Make sure the crtend.o object is the last object file. This object
     // file can contain terminating section chunks that need to be placed
     // last. GNU ld processes files and static libraries explicitly in the
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index c7b75badbfe2..9900903bfe7c 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -1042,6 +1042,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
 
 BitcodeFile::~BitcodeFile() = default;
 
+namespace {
+// Convenience class for initializing a coff_section with specific flags.
+class FakeSection {
+public:
+  FakeSection(int c) { section.Characteristics = c; }
+
+  coff_section section;
+};
+
+FakeSection ltoTextSection(IMAGE_SCN_MEM_EXECUTE);
+FakeSection ltoDataSection(IMAGE_SCN_CNT_INITIALIZED_DATA);
+SectionChunk ltoTextSectionChunk(nullptr, &ltoTextSection.section);
+SectionChunk ltoDataSectionChunk(nullptr, &ltoDataSection.section);
+} // namespace
+
 void BitcodeFile::parse() {
   std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
   for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
@@ -1052,6 +1067,11 @@ void BitcodeFile::parse() {
     StringRef symName = saver.save(objSym.getName());
     int comdatIndex = objSym.getComdatIndex();
     Symbol *sym;
+    SectionChunk *fakeSC = nullptr;
+    if (objSym.isExecutable())
+      fakeSC = &ltoTextSectionChunk;
+    else
+      fakeSC = &ltoDataSectionChunk;
     if (objSym.isUndefined()) {
       sym = symtab->addUndefined(symName, this, false);
     } else if (objSym.isCommon()) {
@@ -1066,11 +1086,11 @@ void BitcodeFile::parse() {
       if (symName == obj->getComdatTable()[comdatIndex])
         sym = comdat[comdatIndex].first;
       else if (comdat[comdatIndex].second)
-        sym = symtab->addRegular(this, symName);
+        sym = symtab->addRegular(this, symName, nullptr, fakeSC);
       else
         sym = symtab->addUndefined(symName, this, false);
     } else {
-      sym = symtab->addRegular(this, symName);
+      sym = symtab->addRegular(this, symName, nullptr, fakeSC);
     }
     symbols.push_back(sym);
     if (objSym.isUsed())
diff --git a/test/COFF/export-all-lto.ll b/test/COFF/export-all-lto.ll
new file mode 100644
index 000000000000..9d68809a77e0
--- /dev/null
+++ b/test/COFF/export-all-lto.ll
@@ -0,0 +1,23 @@
+; REQUIRES: x86
+
+; RUN: llvm-as %s -o %t.bc
+
+; RUN: lld-link -lldmingw -dll -out:%t.dll %t.bc -noentry -output-def:%t.def
+; RUN: llvm-readobj --coff-exports %t.dll | grep Name: | FileCheck %s
+; RUN: cat %t.def | FileCheck --check-prefix=IMPLIB %s
+
+; CHECK: Name: MyExtData
+; CHECK: Name: MyLibFunc
+
+; IMPLIB: MyExtData @1 DATA
+; IMPLIB: MyLibFunc @2{{$}}
+
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-w64-windows-gnu"
+
+@MyExtData = dso_local global i32 42, align 4
+
+define dso_local void @MyLibFunc() {
+entry:
+  ret void
+}
-- 
2.31.1.windows.1

