[PECOFF] Support COMDAT associative sections.

COFF supports a feature similar to ELF's section groups. This
patch implements it.

In ELF, section groups are identified by their names, and they are
treated somewhat differently from regular symbols. In COFF, the
feature is realized in a more straightforward way. A section can
have an annotation saying "if Nth section is linked, link this
section too."

I added a new reference type, kindAssociate. If a target atom is
coalesced away, the referring atom is removed by Resolver, so that
they are treated as a group.

Differential Revision: http://reviews.llvm.org/D4028

llvm-svn: 211106
This commit is contained in:
Rui Ueyama 2014-06-17 16:19:33 +00:00
parent 8d7ab8c617
commit 61d7f97000
10 changed files with 188 additions and 13 deletions

View File

@ -91,6 +91,7 @@ public:
kindLayoutBefore = 3,
// kindGroupChild is treated as a bidirected edge too.
kindGroupChild = 4,
kindAssociate = 5,
};
// A value to be added to the value of a target

View File

@ -105,6 +105,7 @@ private:
std::vector<const Atom *> _atoms;
std::set<const Atom *> _deadStripRoots;
llvm::DenseSet<const Atom *> _liveAtoms;
llvm::DenseSet<const Atom *> _deadAtoms;
std::unique_ptr<MergedFile> _result;
llvm::DenseMap<const Atom *, llvm::DenseSet<const Atom *>> _reverseRef;
};

View File

@ -277,6 +277,16 @@ void Resolver::updateReferences() {
for (const Atom *atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
for (const Reference *ref : *defAtom) {
// A reference of type kindAssociate should't be updated.
// Instead, an atom having such reference will be removed
// if the target atom is coalesced away, so that they will
// go away as a group.
if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
ref->kindValue() == lld::Reference::kindAssociate) {
if (_symbolTable.isCoalescedAway(atom))
_deadAtoms.insert(ref->target());
continue;
}
const Atom *newTarget = _symbolTable.replacement(ref->target());
const_cast<Reference *>(ref)->setTarget(newTarget);
}
@ -399,7 +409,7 @@ bool Resolver::checkUndefines() {
void Resolver::removeCoalescedAwayAtoms() {
ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) {
return _symbolTable.isCoalescedAway(a);
return _symbolTable.isCoalescedAway(a) || _deadAtoms.count(a);
}),
_atoms.end());
}

View File

@ -158,7 +158,6 @@ public:
}
void setAlignment(Alignment val) { _alignment = val; }
SectionChoice sectionChoice() const override { return sectionCustomRequired; }
StringRef customSectionName() const override { return _sectionName; }
Scope scope() const override { return _scope; }
@ -167,6 +166,13 @@ public:
uint64_t ordinal() const override { return _ordinal; }
Alignment alignment() const override { return _alignment; }
void addAssociate(const DefinedAtom *other) {
auto *ref = new COFFReference(other, 0, lld::Reference::kindAssociate,
Reference::KindNamespace::all,
Reference::KindArch::all);
addReference(std::unique_ptr<COFFReference>(ref));
}
private:
StringRef _sectionName;
Scope _scope;

View File

@ -174,6 +174,9 @@ private:
// A map to get whether the section allows its contents to be merged or not.
std::map<const coff_section *, DefinedAtom::Merge> _merge;
// COMDAT associative sections
std::map<const coff_section *, std::set<const coff_section *>> _association;
// A sorted map to find an atom from a section and an offset within
// the section.
std::map<const coff_section *,
@ -544,6 +547,7 @@ std::error_code FileCOFF::cacheSectionAttributes() {
// section. It feels to me that it's unnecessarily complicated, but this is
// how COFF works.
for (auto i : _auxSymbol) {
// Read a section from the file
const coff_symbol *sym = i.first;
if (sym->SectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE ||
sym->SectionNumber == llvm::COFF::IMAGE_SYM_UNDEFINED)
@ -552,19 +556,22 @@ std::error_code FileCOFF::cacheSectionAttributes() {
const coff_section *sec;
if (std::error_code ec = _obj->getSection(sym->SectionNumber, sec))
return ec;
if (_merge.count(sec))
continue;
if (!(sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT))
continue;
_comdatSections.insert(sec);
if (sym->NumberOfAuxSymbols == 0)
return llvm::object::object_error::parse_failed;
const coff_aux_section_definition *aux =
reinterpret_cast<const coff_aux_section_definition *>(i.second);
_merge[sec] = getMerge(aux);
if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT) {
// Read aux symbol data.
_comdatSections.insert(sec);
_merge[sec] = getMerge(aux);
}
// Handle associative sections.
if (aux->Selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
const coff_section *parent;
if (std::error_code ec = _obj->getSection(aux->Number, parent))
return ec;
_association[parent].insert(sec);
}
}
// The sections that does not have auxiliary symbol are regular sections, in
@ -699,6 +706,29 @@ std::error_code FileCOFF::AtomizeDefinedSymbols(
definedAtoms.push_back(atom);
}
}
// A COMDAT section with SELECT_ASSOCIATIVE attribute refer to other
// section. If the referred section is linked to a binary, the
// referring section needs to be linked too. A typical use case of
// this attribute is a static initializer; a parent is a comdat BSS
// section, and a child is a static initializer code for the data.
//
// We add referring section contents to the referred section's
// associate list, so that Resolver takes care of them.
for (auto i : _association) {
const coff_section *parent = i.first;
const std::set<const coff_section *> &childSections = i.second;
assert(_sectionAtoms[parent].size() > 0);
COFFDefinedFileAtom *p = _sectionAtoms[parent][0];
for (const coff_section *sec : childSections) {
if (_sectionAtoms.count(sec)) {
assert(_sectionAtoms[sec].size() > 0);
p->addAssociate(_sectionAtoms[sec][0]);
}
}
}
return std::error_code();
}

View File

@ -51,6 +51,7 @@ static const Registry::KindStrings kindStrings[] = {
{Reference::kindLayoutAfter, "layout-after"},
{Reference::kindLayoutBefore, "layout-before"},
{Reference::kindGroupChild, "group-child"},
{Reference::kindAssociate, "associate"},
LLD_KIND_STRING_END};
Registry::Registry() {

View File

@ -0,0 +1,30 @@
# RUN: lld -core %s | FileCheck %s
---
defined-atoms:
- name: f1
merge: as-weak
scope: global
references:
- kind: associate
target: f2
- name: f2
---
defined-atoms:
- name: f1
merge: as-weak
scope: global
references:
- kind: associate
target: f2
- name: f2
...
# CHECK: defined-atoms:
# CHECK: - name: f1
# CHECK: scope: global
# CHECK: references:
# CHECK: - kind: associate
# CHECK: target: f2
# CHECK: - name: f2
# CHECK-NOT: - name: f2

View File

@ -0,0 +1,53 @@
---
header:
Machine: IMAGE_FILE_MACHINE_I386
Characteristics: []
sections:
- Name: .data
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: 00000000
- Name: '.CRT$XCU'
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: 77777777
symbols:
- Name: .data
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 4
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
Selection: IMAGE_COMDAT_SELECT_ANY
- Name: _var
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: '.CRT$XCU'
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 4
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
- Name: _init
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

View File

@ -0,0 +1,33 @@
---
header:
Machine: IMAGE_FILE_MACHINE_I386
Characteristics: []
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: 0000000000000000
Relocations:
- VirtualAddress: 4
SymbolName: _var
Type: IMAGE_REL_I386_DIR32
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: _main
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: _var
Value: 0
SectionNumber: 0
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

View File

@ -0,0 +1,10 @@
# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t1.obj
# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t2.obj
# RUN: yaml2obj %p/Inputs/associative3.obj.yaml > %t3.obj
#
# RUN: lld -flavor link /machine:x86 /subsystem:console /entry:main \
# RUN: /out:%t.exe -- %t1.obj %t2.obj %t3.obj
# RUN: obj2yaml %t.exe | FileCheck %s
CHECK: - Name: .CRT
CHECK: SectionData: '77777777'