From 79cfed55fc923b137a1a41702534b6830c4f5851 Mon Sep 17 00:00:00 2001 From: Shankar Easwaran Date: Mon, 31 Mar 2014 03:16:37 +0000 Subject: [PATCH] [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. .gnu.linkonce based symbol resolution achieves that. llvm-svn: 205163 --- lld/include/lld/Core/DefinedAtom.h | 1 + lld/include/lld/Core/Resolver.h | 4 +- lld/lib/Core/DefinedAtom.cpp | 1 + lld/lib/Core/Resolver.cpp | 28 +++- lld/lib/ReaderWriter/Native/WriterNative.cpp | 3 +- .../ReaderWriter/YAML/ReaderWriterYAML.cpp | 10 +- .../core/gnulinkonce-rearrange-resolve.objtxt | 115 +++++++++++++++ .../core/gnulinkonce-remaining-undef.objtxt | 113 +++++++++++++++ lld/test/core/gnulinkonce-resolve.objtxt | 133 ++++++++++++++++++ lld/test/core/gnulinkonce-simple.objtxt | 112 +++++++++++++++ .../sectiongroup-gnulinkonce-error.objtxt | 88 ++++++++++++ 11 files changed, 597 insertions(+), 11 deletions(-) create mode 100644 lld/test/core/gnulinkonce-rearrange-resolve.objtxt create mode 100644 lld/test/core/gnulinkonce-remaining-undef.objtxt create mode 100644 lld/test/core/gnulinkonce-resolve.objtxt create mode 100644 lld/test/core/gnulinkonce-simple.objtxt create mode 100644 lld/test/core/sectiongroup-gnulinkonce-error.objtxt diff --git a/lld/include/lld/Core/DefinedAtom.h b/lld/include/lld/Core/DefinedAtom.h index de325ada4b85..b911d2323b1d 100644 --- a/lld/include/lld/Core/DefinedAtom.h +++ b/lld/include/lld/Core/DefinedAtom.h @@ -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 diff --git a/lld/include/lld/Core/Resolver.h b/lld/include/lld/Core/Resolver.h index 006ed6e0e799..b6d8185b3cdb 100644 --- a/lld/include/lld/Core/Resolver.h +++ b/lld/include/lld/Core/Resolver.h @@ -67,8 +67,8 @@ public: private: typedef std::function 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(); diff --git a/lld/lib/Core/DefinedAtom.cpp b/lld/lib/Core/DefinedAtom.cpp index 8362aadb21cb..806bd6f45211 100644 --- a/lld/lib/Core/DefinedAtom.cpp +++ b/lld/lib/Core/DefinedAtom.cpp @@ -72,6 +72,7 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) { return permRW_L; case typeGroupComdat: + case typeGnuLinkOnce: case typeUnknown: case typeTempLTO: return permUnknown; diff --git a/lld/lib/Core/Resolver.cpp b/lld/lib/Core/Resolver.cpp index 9555fef22ba0..6ff898726b37 100644 --- a/lld/lib/Core/Resolver.cpp +++ b/lld/lib/Core/Resolver.cpp @@ -184,11 +184,22 @@ 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(_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 +211,7 @@ void Resolver::maybeAddSectionGroup(const DefinedAtom &atom) { _symbolTable.add(*target); } } + return true; } // called on each atom when a file is added @@ -229,8 +241,14 @@ void Resolver::doDefinedAtom(const DefinedAtom &atom) { // add to list of known atoms _atoms.push_back(&atom); - if (atom.contentType() == DefinedAtom::typeGroupComdat) - maybeAddSectionGroup(atom); + if ((atom.contentType() == DefinedAtom::typeGroupComdat) || + (atom.contentType() == DefinedAtom::typeGnuLinkOnce)) { + // 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); diff --git a/lld/lib/ReaderWriter/Native/WriterNative.cpp b/lld/lib/ReaderWriter/Native/WriterNative.cpp index 9735783b0a9c..c1173380faa3 100644 --- a/lld/lib/ReaderWriter/Native/WriterNative.cpp +++ b/lld/lib/ReaderWriter/Native/WriterNative.cpp @@ -41,7 +41,8 @@ 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->contentType() == DefinedAtom::typeGroupComdat || + defAtom->contentType() == DefinedAtom::typeGnuLinkOnce) { for (const Reference *r : *defAtom) { if (r->kindNamespace() != lld::Reference::KindNamespace::all) continue; diff --git a/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp index 49de7f551cf9..b2295f87d66a 100644 --- a/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ b/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -71,7 +71,9 @@ public: if (!atom->name().empty()) buildDuplicateNameMap(*atom); - if (atom->contentType() == DefinedAtom::typeGroupComdat) { + if (atom->contentType() == DefinedAtom::typeGroupComdat || + atom->contentType() == DefinedAtom::typeGnuLinkOnce) { + for (const lld::Reference *ref : *atom) { if (ref->kindNamespace() != lld::Reference::KindNamespace::all) continue; @@ -229,7 +231,8 @@ private: } if (const lld::DefinedAtom *da = dyn_cast(atom)) { - if (da->contentType() == DefinedAtom::typeGroupComdat) { + if (da->contentType() == DefinedAtom::typeGroupComdat || + da->contentType() == DefinedAtom::typeGnuLinkOnce) { if (_groupMap.count(name)) { _io.setError(Twine("duplicate group name: ") + name); } else { @@ -476,7 +479,8 @@ template <> struct ScalarEnumerationTraits { io.enumCase(value, "ro-note", DefinedAtom::typeRONote); io.enumCase(value, "rw-note", DefinedAtom::typeRWNote); io.enumCase(value, "no-alloc", DefinedAtom::typeNoAlloc); - io.enumCase(value, "group-comdat", DefinedAtom::typeGroupComdat); + io.enumCase(value, "group-comdat", DefinedAtom::typeGroupComdat); + io.enumCase(value, "gnu-linkonce", DefinedAtom::typeGnuLinkOnce); } }; diff --git a/lld/test/core/gnulinkonce-rearrange-resolve.objtxt b/lld/test/core/gnulinkonce-rearrange-resolve.objtxt new file mode 100644 index 000000000000..dbcc95591a9c --- /dev/null +++ b/lld/test/core/gnulinkonce-rearrange-resolve.objtxt @@ -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]] diff --git a/lld/test/core/gnulinkonce-remaining-undef.objtxt b/lld/test/core/gnulinkonce-remaining-undef.objtxt new file mode 100644 index 000000000000..ee5f681ac02e --- /dev/null +++ b/lld/test/core/gnulinkonce-remaining-undef.objtxt @@ -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 diff --git a/lld/test/core/gnulinkonce-resolve.objtxt b/lld/test/core/gnulinkonce-resolve.objtxt new file mode 100644 index 000000000000..ff099a5ba9d0 --- /dev/null +++ b/lld/test/core/gnulinkonce-resolve.objtxt @@ -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: ... diff --git a/lld/test/core/gnulinkonce-simple.objtxt b/lld/test/core/gnulinkonce-simple.objtxt new file mode 100644 index 000000000000..1a96b62669be --- /dev/null +++ b/lld/test/core/gnulinkonce-simple.objtxt @@ -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: ... diff --git a/lld/test/core/sectiongroup-gnulinkonce-error.objtxt b/lld/test/core/sectiongroup-gnulinkonce-error.objtxt new file mode 100644 index 000000000000..e54cdfe0571e --- /dev/null +++ b/lld/test/core/sectiongroup-gnulinkonce-error.objtxt @@ -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 +