[core] support .gnu.linkonce sections

.gnu.linkonce sections are similar to section groups.

They were supported before section groups existed and provided a way
to resolve COMDAT sections using a different design.

There are few implementations that use .gnu.linkonce sections
to store simple floating point constants which doesnot require complex section
group support but need a way to store only one copy of the floating point
constant in a binary.

.gnu.linkonce based symbol resolution achieves that.

Review : http://llvm-reviews.chandlerc.com/D3242

llvm-svn: 205280
This commit is contained in:
Shankar Easwaran 2014-04-01 03:49:55 +00:00
parent d09ba23faf
commit 9316c40a1b
11 changed files with 603 additions and 11 deletions

View File

@ -147,6 +147,7 @@ public:
typeRWNote, // Identifies readwrite note sections [ELF]
typeNoAlloc, // Identifies non allocatable sections [ELF]
typeGroupComdat, // Identifies a section group [ELF, COFF]
typeGnuLinkOnce, // Identifies a gnu.linkonce section [ELF]
};
// Permission bits for atoms and segments. The order of these values are
@ -330,6 +331,14 @@ public:
atomContentType == DefinedAtom::typeThreadZeroFill);
}
/// Utility function to check if the atom belongs to a group section
/// that represents section groups or .gnu.linkonce sections.
bool isGroupParent() const {
ContentType atomContentType = contentType();
return (atomContentType == DefinedAtom::typeGroupComdat ||
atomContentType == DefinedAtom::typeGnuLinkOnce);
}
protected:
// DefinedAtom is an abstract base class. Only subclasses can access
// constructor.

View File

@ -67,8 +67,8 @@ public:
private:
typedef std::function<void(StringRef, bool)> UndefCallback;
/// \brief Add section group if it does not exist previously.
void maybeAddSectionGroup(const DefinedAtom &atom);
/// \brief Add section group/.gnu.linkonce if it does not exist previously.
bool maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom);
/// \brief The main function that iterates over the files to resolve
bool resolveUndefines();

View File

@ -72,6 +72,7 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
return permRW_L;
case typeGroupComdat:
case typeGnuLinkOnce:
case typeUnknown:
case typeTempLTO:
return permUnknown;

View File

@ -184,11 +184,24 @@ void Resolver::doUndefinedAtom(const UndefinedAtom &atom) {
}
/// \brief Add the section group and the group-child reference members.
void Resolver::maybeAddSectionGroup(const DefinedAtom &atom) {
bool Resolver::maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom) {
// First time adding a group ?
bool isFirstTime = _symbolTable.addGroup(atom);
if (!isFirstTime)
return;
if (!isFirstTime) {
// If duplicate symbols are allowed, select the first group.
if (_context.getAllowDuplicates())
return true;
const DefinedAtom *prevGroup =
llvm::dyn_cast<DefinedAtom>(_symbolTable.findGroup(atom.name()));
assert(prevGroup &&
"Internal Error: The group atom could only be a defined atom");
// The atoms should be of the same content type, reject invalid group
// resolution behaviors.
if (atom.contentType() != prevGroup->contentType())
return false;
return true;
}
for (const Reference *r : atom) {
if ((r->kindNamespace() == lld::Reference::KindNamespace::all) &&
@ -200,6 +213,7 @@ void Resolver::maybeAddSectionGroup(const DefinedAtom &atom) {
_symbolTable.add(*target);
}
}
return true;
}
// called on each atom when a file is added
@ -229,10 +243,16 @@ void Resolver::doDefinedAtom(const DefinedAtom &atom) {
// add to list of known atoms
_atoms.push_back(&atom);
if (atom.contentType() == DefinedAtom::typeGroupComdat)
maybeAddSectionGroup(atom);
else
if (atom.isGroupParent()) {
// Raise error if there exists a similar gnu linkonce section.
if (!maybeAddSectionGroupOrGnuLinkOnce(atom)) {
llvm::errs() << "SymbolTable: error while merging " << atom.name()
<< "\n";
llvm::report_fatal_error("duplicate symbol error");
}
} else {
_symbolTable.add(atom);
}
if (_context.deadStrip()) {
// add to set of dead-strip-roots, all symbols that

View File

@ -41,7 +41,7 @@ public:
// We are trying to process all atoms, but the defined() iterator does not
// return group children. So, when a group parent is found, we need to
// handle each child atom.
if (defAtom->contentType() == DefinedAtom::typeGroupComdat) {
if (defAtom->isGroupParent()) {
for (const Reference *r : *defAtom) {
if (r->kindNamespace() != lld::Reference::KindNamespace::all)
continue;

View File

@ -71,7 +71,7 @@ public:
if (!atom->name().empty())
buildDuplicateNameMap(*atom);
if (atom->contentType() == DefinedAtom::typeGroupComdat) {
if (atom->isGroupParent()) {
for (const lld::Reference *ref : *atom) {
if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
continue;
@ -229,7 +229,7 @@ private:
}
if (const lld::DefinedAtom *da = dyn_cast<DefinedAtom>(atom)) {
if (da->contentType() == DefinedAtom::typeGroupComdat) {
if (da->isGroupParent()) {
if (_groupMap.count(name)) {
_io.setError(Twine("duplicate group name: ") + name);
} else {
@ -477,6 +477,7 @@ template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
io.enumCase(value, "rw-note", DefinedAtom::typeRWNote);
io.enumCase(value, "no-alloc", DefinedAtom::typeNoAlloc);
io.enumCase(value, "group-comdat", DefinedAtom::typeGroupComdat);
io.enumCase(value, "gnu-linkonce", DefinedAtom::typeGnuLinkOnce);
}
};

View File

@ -0,0 +1,115 @@
# RUN: lld -core %s | FileCheck %s
#
# Test that gnu linkonce sections are parsed and the first section selected for symbol
# resolution
#
---
defined-atoms:
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
---
defined-atoms:
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
...
# CHECK: defined-atoms:
# CHECK: - name: g1
# CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]]
# CHECK: type: gnu-linkonce
# CHECK: references:
# CHECK: - kind: group-child
# CHECK: target: f1
# CHECK: - kind: group-child
# CHECK: target: f2
# CHECK: - kind: group-child
# CHECK: target: [[CHILD:[a-zA-Z\.0-9_]+]]
# CHECK: - kind: group-child
# CHECK: target: d1
# CHECK: - name: f1
# CHECK: references:
# CHECK: - kind: group-parent
# CHECK: target: [[PARENT]]
# CHECK: - name: f2
# CHECK: references:
# CHECK: - kind: group-parent
# CHECK: target: [[PARENT]]
# CHECK: - name: g1
# CHECK: ref-name: [[CHILD]]
# CHECK: references:
# CHECK: - kind: group-parent
# CHECK: target: [[PARENT]]
# CHECK: - name: d1
# CHECK: references:
# CHECK: - kind: group-parent
# CHECK: target: [[PARENT]]

View File

@ -0,0 +1,113 @@
# RUN: lld -core %s | FileCheck %s
#
# Test that gnu linkonce sections are parsed and the first section selected for
# symbol resolution. The second file which has the same gnu linkonce section has
# a unresolved undefined symbol. lets make sure that the symbol is kept around
# in the final link and remains undefined.
#
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
---
defined-atoms:
- name: anotherfunction
scope: global
type: data
references:
- kind: layout-before
target: f3
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f3
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: f3
- kind: group-child
target: g1
- kind: group-child
target: d1
undefined-atoms:
- name: f3
can-be-null: never
...
#CHECK: - name: anotherfunction
#CHECK: scope: global
#CHECK: type: data
#CHECK: references:
#CHECK: - kind: layout-before
#CHECK: offset: 0
#CHECK: target: f3
#CHECK: undefined-atoms:
#CHECK: - name: f3

View File

@ -0,0 +1,133 @@
# RUN: lld -core %s | FileCheck %s
#
# Test that gnu linkonce sections are parsed and the first section selected for symbol
# resolution
#
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
...
#CHECK: defined-atoms:
#CHECK: - name: g1
#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]]
#CHECK: scope: global
#CHECK: type: gnu-linkonce
#CHECK: references:
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDONE:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDTWO:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDTHREE:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDFOUR:[a-zA-Z\.0-9_]+]]
#CHECK: - name: f1
#CHECK: ref-name: [[GCHILDONE]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: f2
#CHECK: ref-name: [[GCHILDTWO]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: g1
#CHECK: ref-name: [[GCHILDTHREE]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: d1
#CHECK: ref-name: [[GCHILDFOUR]]
#CHECK: scope: global
#CHECK: type: data
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: ...

View File

@ -0,0 +1,112 @@
# RUN: lld -core %s | FileCheck %s
#
# Test that gnu linkonce sections are parsed properly when there is a reference to a
# atom from outside the gnu linkonce section.
#
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: d1
- kind: group-child
target: g1
- name: anotherfunction
scope: global
type: data
references:
- kind: layout-before
target: f1
undefined-atoms:
- name: f1
can-be-null: never
...
#CHECK: defined-atoms:
#CHECK: - name: g1
#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]]
#CHECK: scope: global
#CHECK: type: gnu-linkonce
#CHECK: references:
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDONE:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDTWO:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDTHREE:[a-zA-Z\.0-9_]+]]
#CHECK: - kind: group-child
#CHECK: offset: 0
#CHECK: target: [[GCHILDFOUR:[a-zA-Z\.0-9_]+]]
#CHECK: - name: f1
#CHECK: ref-name: [[GCHILDONE]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: f2
#CHECK: ref-name: [[GCHILDTWO]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: d1
#CHECK: ref-name: [[GCHILDTHREE]]
#CHECK: scope: global
#CHECK: type: data
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: g1
#CHECK: ref-name: [[GCHILDFOUR]]
#CHECK: scope: global
#CHECK: references:
#CHECK: - kind: group-parent
#CHECK: offset: 0
#CHECK: target: [[PARENT]]
#CHECK: - name: anotherfunction
#CHECK: scope: global
#CHECK: type: data
#CHECK: references:
#CHECK: - kind: layout-before
#CHECK: offset: 0
#CHECK: target: [[GCHILDONE]]
#CHECK: ...

View File

@ -0,0 +1,88 @@
# RUN: not lld -core %s 2>&1 | FileCheck %s
#
# Test that section groups/gnu linkonce sections are parsed and a merge error
# is displayed at the time of symbol resolution.
#
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: group-comdat
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
---
defined-atoms:
- name: f1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: f2
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: code
references:
- kind: group-parent
target: g1
- name: d1
scope: global
type: data
references:
- kind: group-parent
target: g1
- name: g1
scope: global
type: gnu-linkonce
references:
- kind: group-child
target: f1
- kind: group-child
target: f2
- kind: group-child
target: g1
- kind: group-child
target: d1
...
#CHECK: SymbolTable: error while merging g1
#CHECK: LLVM ERROR: duplicate symbol error