COFF: Implement safe ICF on rodata using address-significance tables.

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

llvm-svn: 340555
This commit is contained in:
Peter Collingbourne 2018-08-23 17:44:42 +00:00
parent 075412d9cf
commit ab038025a5
7 changed files with 105 additions and 2 deletions

View File

@ -114,6 +114,10 @@ protected:
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.

View File

@ -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"
@ -741,6 +742,46 @@ static void parseOrderFile(StringRef Arg) {
}
}
static void markAddrsig(Symbol *S) {
if (auto *D = dyn_cast_or_null<Defined>(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<Symbol *> Syms = Obj->getSymbols();
if (Obj->AddrsigSec) {
ArrayRef<uint8_t> 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);
if (SymIndex >= Syms.size())
fatal(toString(Obj) + ": invalid symbol index in addrsig section");
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<const char *> 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.
@ -1452,8 +1493,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
markLive(Symtab->getChunks());
// Identify identical COMDAT sections to merge them.
if (Config->DoICF)
if (Config->DoICF) {
findKeepUniqueSections();
doICF(Symtab->getChunks());
}
// Write the result.
writeResult();

View File

@ -93,7 +93,11 @@ bool ICF::isEligible(SectionChunk *C) {
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.

View File

@ -161,6 +161,11 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
return nullptr;
}
if (Name == ".llvm_addrsig") {
AddrsigSec = Sec;
return nullptr;
}
// Object files may have DWARF debug info or MS CodeView debug info
// (or both).
//

View File

@ -145,6 +145,8 @@ public:
// if we are not producing a PDB.
llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr;
const coff_section *AddrsigSec = nullptr;
private:
void initializeChunks();
void initializeSymbols();

View File

@ -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

36
lld/test/COFF/icf-safe.s Normal file
View File

@ -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