Index: lld/COFF/Chunks.h =================================================================== --- lld/COFF/Chunks.h +++ lld/COFF/Chunks.h @@ -114,6 +114,10 @@ public: // The offset from beginning of the output section. The writer sets a value. uint64_t OutputSectionOff = 0; + + // Whether this section needs to be kept distinct from other sections during + // ICF. This is set by the driver using address-significance tables. + bool KeepUnique = false; }; // A chunk corresponding a section of an input file. Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -32,6 +32,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" @@ -731,6 +732,44 @@ } } +static void markAddrsig(Symbol *S) { + if (auto *D = dyn_cast_or_null(S)) + if (Chunk *C = D->getChunk()) + C->KeepUnique = true; +} + +static void findKeepUniqueSections() { + // Exported symbols could be address-significant in other executables or DSOs, + // so we conservatively mark them as address-significant. + for (Export &R : Config->Exports) + markAddrsig(R.Sym); + + // Visit the address-significance table in each object file and mark each + // referenced symbol as address-significant. + for (ObjFile *Obj : ObjFile::Instances) { + ArrayRef Syms = Obj->getSymbols(); + if (Obj->AddrsigSec) { + ArrayRef Contents; + Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents); + const uint8_t *Cur = Contents.begin(); + while (Cur != Contents.end()) { + unsigned Size; + const char *Err; + uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); + if (Err) + fatal(toString(Obj) + ": could not decode addrsig section: " + Err); + markAddrsig(Syms[SymIndex]); + Cur += Size; + } + } else { + // If an object file does not have an address-significance table, + // conservatively mark all of its symbols as address-significant. + for (Symbol *S : Syms) + markAddrsig(S); + } + } +} + void LinkerDriver::link(ArrayRef ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. @@ -1442,8 +1481,10 @@ markLive(Symtab->getChunks()); // Identify identical COMDAT sections to merge them. - if (Config->DoICF) + if (Config->DoICF) { + findKeepUniqueSections(); doICF(Symtab->getChunks()); + } // Write the result. writeResult(); Index: lld/COFF/ICF.cpp =================================================================== --- lld/COFF/ICF.cpp +++ lld/COFF/ICF.cpp @@ -93,7 +93,11 @@ return true; // So are vtables. - return C->Sym && C->Sym->getName().startswith("??_7"); + if (C->Sym && C->Sym->getName().startswith("??_7")) + return true; + + // Anything else not in an address-significance table is eligible. + return !C->KeepUnique; } // Split an equivalence class into smaller classes. Index: lld/COFF/InputFiles.h =================================================================== --- lld/COFF/InputFiles.h +++ lld/COFF/InputFiles.h @@ -145,6 +145,8 @@ // if we are not producing a PDB. llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr; + const coff_section *AddrsigSec = nullptr; + private: void initializeChunks(); void initializeSymbols(); Index: lld/COFF/InputFiles.cpp =================================================================== --- lld/COFF/InputFiles.cpp +++ lld/COFF/InputFiles.cpp @@ -161,6 +161,11 @@ return nullptr; } + if (Name == ".llvm_addrsig") { + AddrsigSec = Sec; + return nullptr; + } + // Object files may have DWARF debug info or MS CodeView debug info // (or both). // Index: lld/test/COFF/Inputs/icf-safe.s =================================================================== --- /dev/null +++ lld/test/COFF/Inputs/icf-safe.s @@ -0,0 +1,9 @@ +.section .rdata,"dr",one_only,non_addrsig1 +.globl non_addrsig1 +non_addrsig1: +.byte 3 + +.section .rdata,"dr",one_only,non_addrsig2 +.globl non_addrsig2 +non_addrsig2: +.byte 3 Index: lld/test/COFF/icf-safe.s =================================================================== --- /dev/null +++ lld/test/COFF/icf-safe.s @@ -0,0 +1,36 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-win32 %s -o %t1.obj +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-win32 %S/Inputs/icf-safe.s -o %t2.obj +# RUN: lld-link /dll /noentry /out:%t.dll /verbose /opt:noref,icf %t1.obj %t2.obj 2>&1 | FileCheck %s +# RUN: lld-link /dll /noentry /out:%t.dll /verbose /opt:noref,icf /export:g3 /export:g4 %t1.obj %t2.obj 2>&1 | FileCheck --check-prefix=EXPORT %s + +# CHECK-NOT: Selected +# CHECK: Selected g3 +# CHECK-NEXT: Removed g4 +# CHECK-NOT: Removed +# CHECK-NOT: Selected + +# EXPORT-NOT: Selected + +.section .rdata,"dr",one_only,g1 +.globl g1 +g1: +.byte 1 + +.section .rdata,"dr",one_only,g2 +.globl g2 +g2: +.byte 1 + +.section .rdata,"dr",one_only,g3 +.globl g3 +g3: +.byte 2 + +.section .rdata,"dr",one_only,g4 +.globl g4 +g4: +.byte 2 + +.addrsig +.addrsig_sym g1 +.addrsig_sym g2