Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/eld/Diagnostics/DiagSymbolVersioning.inc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ DIAG(error_missing_version_node, DiagnosticEngine::Error,
DIAG(error_undefined_version, DiagnosticEngine::Error,
"version script assignment of '%0' to symbol '%1' failed: symbol not "
"defined")
DIAG(error_unknown_version_node_dependency, DiagnosticEngine::Error,
"%2: version node '%0' depends on unknown version node '%1'")
DIAG(trace_assign_output_version_ids, DiagnosticEngine::Trace,
"Assigning version IDs to output symbols")
DIAG(trace_adding_verneed_entry, DiagnosticEngine::Trace,
Expand Down
1 change: 1 addition & 0 deletions include/eld/Fragment/GNUVerDefFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class GNUVerDefFragment : public Fragment {
uint16_t VersionID = 0;
uint32_t VersionNameOffset = 0;
uint32_t VersionNameHash = 0;
std::vector<uint32_t> DependencyNameOffsets;
};

protected:
Expand Down
5 changes: 5 additions & 0 deletions include/eld/Script/VersionScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define ELD_SCRIPT_VERSIONSCRIPT_H

#include "eld/Script/ScriptSymbol.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace eld {
class StrToken;
Expand Down Expand Up @@ -166,6 +167,10 @@ class VersionScriptNode {

void setDependency(eld::StrToken *Dependency) { MDependency = Dependency; }

llvm::StringRef getDependency() const {
Comment thread
quic-areg marked this conversation as resolved.
return MDependency ? MDependency->name() : llvm::StringRef();
}
Comment thread
quic-seaswara marked this conversation as resolved.

VersionScriptBlock *getLocalBlock() const { return MLocal; }

VersionScriptBlock *getGlobalBlock() const { return MGlobal; }
Expand Down
47 changes: 33 additions & 14 deletions lib/Fragment/GNUVerDefFragment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/Object/ELF.h"
#include "llvm/Support/Path.h"
#include <cstdint>
#include <utility>

using namespace eld;

Expand Down Expand Up @@ -48,8 +49,10 @@ GNUVerDefFragment::computeVersionDefs(Module &M, DynStrFragment *DynStr,
baseVersion = std::string(llvm::sys::path::filename(outputFileName));
}
std::size_t baseVersionOffset = DynStr->addString(baseVersion);
VersionDefs.push_back(VerDefInfo{1, static_cast<uint32_t>(baseVersionOffset),
llvm::object::hashSysV(baseVersion)});
VersionDefs.push_back(VerDefInfo{1,
static_cast<uint32_t>(baseVersionOffset),
llvm::object::hashSysV(baseVersion),
{}});

uint16_t VerID = 2; // 0 and 1 are reserved
for (const auto *Node : VSNodes) {
Expand All @@ -58,16 +61,26 @@ GNUVerDefFragment::computeVersionDefs(Module &M, DynStrFragment *DynStr,
continue;
llvm::StringRef VerName = Node->getName();
std::size_t NameOffset = DynStr->addString(VerName.str());
VersionDefs.push_back(VerDefInfo{VerID, static_cast<uint32_t>(NameOffset),
llvm::object::hashSysV(VerName)});
std::vector<uint32_t> DependencyNameOffsets;
if (Node->hasDependency()) {
std::size_t DependencyOffset =
DynStr->addString(Node->getDependency().str());
DependencyNameOffsets.push_back(static_cast<uint32_t>(DependencyOffset));
}
VerDefInfo Def{VerID, static_cast<uint32_t>(NameOffset),
llvm::object::hashSysV(VerName),
std::move(DependencyNameOffsets)};
VersionDefs.push_back(std::move(Def));
++VerID;
}
return {};
}

size_t GNUVerDefFragment::size() const {
size_t VerDefSize = VersionDefs.size() * VerDefEntrySize;
size_t VerdAuxSize = VersionDefs.size() * VerdAuxEntrySize; // one aux/name
size_t VerdAuxSize = 0;
for (const auto &VD : VersionDefs)
VerdAuxSize += (1 + VD.DependencyNameOffsets.size()) * VerdAuxEntrySize;
return VerDefSize + VerdAuxSize;
}

Expand All @@ -85,26 +98,32 @@ eld::Expected<void> GNUVerDefFragment::emitImpl(uint8_t *Buf, Module &M) {
auto *VerDefBuf = reinterpret_cast<typename ELFT::Verdef *>(Buf);
auto *VerdAuxBuf = reinterpret_cast<typename ELFT::Verdaux *>(
VerDefBuf + VersionDefs.size());
for (const auto &VD : VersionDefs) {
for (size_t I = 0; I < VersionDefs.size(); ++I) {
const auto &VD = VersionDefs[I];
const size_t AuxCount = 1 + VD.DependencyNameOffsets.size();
uint16_t flags = (VD.VersionID == 1 ? llvm::ELF::VER_FLG_BASE : 0);
VerDefBuf->vd_version = 1;
VerDefBuf->vd_flags = flags;
VerDefBuf->vd_ndx = VD.VersionID;
VerDefBuf->vd_cnt = 1; // one aux: name
VerDefBuf->vd_cnt = AuxCount;
VerDefBuf->vd_hash = VD.VersionNameHash;
VerDefBuf->vd_aux = reinterpret_cast<const char *>(VerdAuxBuf) -
reinterpret_cast<const char *>(VerDefBuf);
VerDefBuf->vd_next = sizeof(typename ELFT::Verdef);
VerDefBuf->vd_next =
(I + 1 == VersionDefs.size()) ? 0 : sizeof(typename ELFT::Verdef);
++VerDefBuf;

VerdAuxBuf->vda_name = VD.VersionNameOffset;
VerdAuxBuf->vda_next = sizeof(typename ELFT::Verdaux);
VerdAuxBuf->vda_next = AuxCount == 1 ? 0 : sizeof(typename ELFT::Verdaux);
++VerdAuxBuf;
}
// Terminate aux and def chains
if (!VersionDefs.empty()) {
VerdAuxBuf[-1].vda_next = 0;
VerDefBuf[-1].vd_next = 0;

for (size_t J = 0; J < VD.DependencyNameOffsets.size(); ++J) {
VerdAuxBuf->vda_name = VD.DependencyNameOffsets[J];
VerdAuxBuf->vda_next = (J + 1 == VD.DependencyNameOffsets.size())
? 0
: sizeof(typename ELFT::Verdaux);
++VerdAuxBuf;
}
}
return {};
}
Expand Down
22 changes: 20 additions & 2 deletions lib/Object/ObjectLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#include "eld/Target/Relocator.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Bitcode/BitcodeWriter.h"
Expand Down Expand Up @@ -443,6 +444,13 @@ void ObjectLinker::createDefaultSymverNode() {

bool ObjectLinker::registerVersionScriptNodes(const VersionScript *VS,
llvm::StringRef DecoratedPath) {
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
llvm::StringSet<> SeenVersionNames;
for (const VersionScriptNode *N : ThisModule->getVersionScriptNodes())
if (N && !N->isAnonymous())
SeenVersionNames.insert(N->getName());
#endif

for (auto &VersionScriptNode : VS->getNodes()) {
if (!VersionScriptNode->isAnonymous()) {
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
Expand All @@ -453,10 +461,16 @@ bool ObjectLinker::registerVersionScriptNodes(const VersionScript *VS,
#endif
}
if (VersionScriptNode->hasDependency()) {
ThisConfig.raise(Diag::unsupported_dependent_node)
<< VersionScriptNode->getName() << DecoratedPath;
#ifndef ELD_ENABLE_SYMBOL_VERSIONING
continue;
#else
llvm::StringRef Dependency = VersionScriptNode->getDependency();
if (Dependency == VersionScriptNode->getName() ||
!SeenVersionNames.contains(Dependency)) {
ThisConfig.raise(Diag::error_unknown_version_node_dependency)
<< VersionScriptNode->getName() << Dependency << DecoratedPath;
return false;
}
#endif
}
// FIXME: Why did we reach here at all if the version script parsing
Expand All @@ -466,6 +480,10 @@ bool ObjectLinker::registerVersionScriptNodes(const VersionScript *VS,
return false;
}
ThisModule->addVersionScriptNode(VersionScriptNode);
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
if (!VersionScriptNode->isAnonymous())
SeenVersionNames.insert(VersionScriptNode->getName());
#endif
}
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions lib/Script/VersionScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ VersionScriptBlock *VersionScriptNode::switchToGlobal() {
void VersionScriptNode::dump(
llvm::raw_ostream &Ostream,
std::function<std::string(const Input *)> GetDecoratedPath) const {
if (!isAnonymous()) {
Ostream << "\nVersion node: " << getName();
if (hasDependency())
Ostream << "\nDependency: " << getDependency();
}
if (MGlobal)
MGlobal->dump(Ostream, GetDecoratedPath);
if (MLocal)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int foo() { return 0; }
int bar() { return 0; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
V2 {
global:
bar;
local:
*;
} V_UNKNOWN;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
V1 {
global:
foo;
local:
*;
};

V2 {
global:
bar;
local:
*;
} V1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
REQUIRES: symbol_versioning
#---VersionNodeDependency.test---------------- SymbolVersioning ---#
#BEGIN_COMMENT
# Verify that a version node dependency is emitted in .gnu.version_d without
# changing symbol version assignment.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -o %t.o %p/Inputs/1.c -c -fPIC
RUN: %link %linkopts -o %t.so %t.o -shared --version-script %p/Inputs/vs.t -Map %t.map 2>&1 | %filecheck %s --check-prefix=NO-WARN --allow-empty
RUN: %readelf --dyn-syms --version-info %t.so | %filecheck %s --check-prefix=VERINFO
RUN: %filecheck %s --check-prefix=MAP < %t.map
#END_TEST

NO-WARN-NOT: Dependent nodes are not supported

VERINFO-DAG: foo@@V1
VERINFO-DAG: bar@@V2
VERINFO: '.gnu.version_d' contains 3 entries:
VERINFO: Name: {{.*}}
VERINFO: Name: V1
VERINFO-NOT: Parent
VERINFO: Name: V2
VERINFO: Parent 1: V1
Comment thread
parth-07 marked this conversation as resolved.

MAP: Version Script Information
MAP: Version Script file
MAP: {{.*}}vs.t
MAP: Version node: V1
MAP-NOT: Dependency:
MAP: Global:
MAP: Version node: V2
MAP: Dependency: V1
MAP: Global:
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
REQUIRES: symbol_versioning
#---VersionNodeDependencyErrors.test------------ SymbolVersioning ---#
#BEGIN_COMMENT
# Verify that a dependency must refer to an earlier named version node.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -o %t.o %p/Inputs/1.c -c -fPIC
RUN: %not %link %linkopts -o %t.so %t.o -shared --version-script %p/Inputs/unknown.t 2>&1 | %filecheck %s
#END_TEST

CHECK: Error: {{.*}}unknown.t: version node 'V2' depends on unknown version node 'V_UNKNOWN'
Loading