Skip to content
Open
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
14 changes: 14 additions & 0 deletions include/eld/Config/LinkerConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class LinkerConfig {
std::optional<bool> EnableWholeArchiveWarnings;
std::optional<bool> EnableCommandLineWarnings;
std::optional<bool> EnableOSABIWarnings;
std::optional<bool> EnableVersionScriptWarnings;
};

struct MappingFileInfo {
Expand Down Expand Up @@ -348,6 +349,14 @@ class LinkerConfig {
return (hasOSABIWarnings() && *WarnOpt.EnableOSABIWarnings);
}

bool hasVersionScriptWarnings() const {
return WarnOpt.EnableVersionScriptWarnings.has_value();
}

bool showVersionScriptWarnings() const {
return (hasVersionScriptWarnings() && *WarnOpt.EnableVersionScriptWarnings);
}

void setShowAllWarnings() {
WarnOpt.EnableAllWarnings = true;
WarnOpt.EnableLinkerScriptWarnings = true;
Expand All @@ -358,6 +367,7 @@ class LinkerConfig {
WarnOpt.EnableBadDotAssignmentWarnings = true;
WarnOpt.EnableWholeArchiveWarnings = true;
WarnOpt.EnableOSABIWarnings = true;
WarnOpt.EnableVersionScriptWarnings = true;
}

void setShowLinkerScriptWarning(bool Option) {
Expand Down Expand Up @@ -396,6 +406,10 @@ class LinkerConfig {
WarnOpt.EnableOSABIWarnings = Option;
}

void setShowVersionScriptWarning(bool Option) {
WarnOpt.EnableVersionScriptWarnings = Option;
}

bool setWarningOption(llvm::StringRef WarnOpt);

/// Returns true if UseOldStyleTrampolineNames contains any value.
Expand Down
2 changes: 2 additions & 0 deletions include/eld/Diagnostics/DiagDynamicLink.inc
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ DIAG(error_discarded_dynamic_section_required, DiagnosticEngine::Error,
"symbol '%0' referenced in file '%1' depends upon discarded %2 section")
DIAG(trace_set_soname, DiagnosticEngine::Trace,
"Setting '%0' as the soname")
DIAG(warn_version_script_reassign, DiagnosticEngine::Warning,
"%0: attempt to reassign symbol '%1' of version '%2' to version '%3'")
1 change: 0 additions & 1 deletion include/eld/Diagnostics/DiagSymbolVersioning.inc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ DIAG(trace_adding_verneed_entry, DiagnosticEngine::Trace,
"Adding verneed entry: '%0' -> '%1'")
DIAG(trace_reading_symbol_versioning_section, DiagnosticEngine::Trace,
"Reading %0[%1]")

DIAG(trace_symbol_to_output_version_id, DiagnosticEngine::Trace,
"Symbol '%0' -> version id %1")
2 changes: 2 additions & 0 deletions include/eld/Driver/GnuLinkerOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,8 @@ def W : Joined<["-"], "W">,
"is enabled for any archive file\n"
"\t\t\t -W[no-]osabi : display a warning when linking objects "
"with different values for OS/ABI\n"
"\t\t\t -W[no-]version-script : display warnings detected while "
"processing version scripts\n"
>,
Group<grp_diagopts>;

Expand Down
6 changes: 6 additions & 0 deletions include/eld/Object/ObjectLinker.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,12 @@ class ObjectLinker {
bool emitArchiveMemberReport(llvm::StringRef Filename) const;

private:
/// Assigns version nodes to symbols with GNU ld semantics:
/// - Pass 1: Exact matches (forward order, first wins, warns on reassign)
/// - Pass 2: Non-* wildcards (reverse order, last wins)
/// - Pass 3: * wildcard (reverse order, last wins, lowest priority)
void assignVersionNodesToSymbols();

std::unique_ptr<llvm::lto::LTO> ltoInit(llvm::lto::Config Conf,
bool CompileToAssembly);

Expand Down
7 changes: 6 additions & 1 deletion include/eld/Script/VersionScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@ class GlobalVersionScriptBlock : public VersionScriptBlock {
const override;
};

class VersionScript;

class VersionScriptNode {
public:
VersionScriptNode();
VersionScriptNode(const VersionScript &);

VersionScriptBlock *switchToGlobal();

Expand Down Expand Up @@ -182,6 +184,8 @@ class VersionScriptNode {
void dump(llvm::raw_ostream &Ostream,
std::function<std::string(const Input *)> GetDecoratedPath) const;

const VersionScript &getVersionScript() const { return VS; }

private:
VersionScriptBlock *MLocal = nullptr;
VersionScriptBlock *MGlobal = nullptr;
Expand All @@ -190,6 +194,7 @@ class VersionScriptNode {
eld::StrToken *Name = nullptr;
eld::StrToken *MDependency = nullptr;
bool MHasErrorDuringParsing = false;
const VersionScript &VS;
};

class VersionScript {
Expand Down
2 changes: 0 additions & 2 deletions include/eld/Target/GNULDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,12 @@ class GNULDBackend {

void addSymbolScope(ResolveInfo *R, VersionSymbol *V) { SymbolScopes[R] = V; }

#ifdef ELD_ENABLE_SYMBOL_VERSIONING
VersionSymbol *getSymbolScope(const ResolveInfo *R) const {
auto it = SymbolScopes.find(R);
if (it != SymbolScopes.end())
return it->second;
return nullptr;
}
#endif

std::vector<Relocation *> &getInternalRelocs() { return m_InternalRelocs; }

Expand Down
9 changes: 9 additions & 0 deletions lib/Config/LinkerConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,15 @@ bool LinkerConfig::setWarningOption(llvm::StringRef WarnOption) {
if (WarnOpt == "no-osabi") {
setShowOSABIWarning(false);
}
// -Wversion-script and -Wno-version-script
if (WarnOpt == "version-script") {
setShowVersionScriptWarning(true);
return true;
}
if (WarnOpt == "no-version-script") {
setShowVersionScriptWarning(false);
return true;
}
return false;
}

Expand Down
154 changes: 105 additions & 49 deletions lib/Object/ObjectLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,65 +407,121 @@ bool ObjectLinker::parseVersionScript() {
ThisModule->addVersionScriptNode(VersionScriptNode);
}
}
auto &SymbolScopes = getTargetBackend().symbolScopes();
assignVersionNodesToSymbols();
return true;
}

void ObjectLinker::assignVersionNodesToSymbols() {
auto &NP = ThisModule->getNamePool();
auto &VersionNodes = ThisModule->getVersionScriptNodes();

if (VersionNodes.empty())
return;

#ifdef ELD_ENABLE_SYMBOL_VERSIONING
DemangledNamesMap DemangledNames;
#endif
for (auto &G : NP.getGlobals()) {
ResolveInfo *R = G.getValue();
for (auto &VersionScriptNode : ThisModule->getVersionScriptNodes()) {
if (VersionScriptNode->getGlobalBlock()) {
for (auto *Sym : VersionScriptNode->getGlobalBlock()->getSymbols()) {
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
bool isMatched = Sym->matched(*R, NP, DemangledNames);
#else
bool isMatched = Sym->getSymbolPattern()->matched(*R);
DemangledNamesMap demangledNames;
#endif
if (isMatched) {
getTargetBackend().addSymbolScope(R, Sym);

auto canAssignVersionNode = [](const ResolveInfo &R) {
return (R.isDefine() || R.isCommon()) && !R.isDyn();
};

auto getVersionName = [](const VersionSymbol *VS) -> llvm::StringRef {
auto *block = VS->getBlock();
auto *node = block->getNode();
if (node->isAnonymous()) {
if (block->isGlobal())
return "VER_NDX_GLOBAL";
else
return "VER_NDX_LOCAL";
}
return (block->isLocal() ? "VER_NDX_LOCAL" : node->getName());
};

auto tryAssign = [&](ResolveInfo *R, VersionSymbol *VS, bool warnOnReassign) {
VersionSymbol *existing = getTargetBackend().getSymbolScope(R);
InputFile *verSymInputFile =
VS->getBlock()->getNode()->getVersionScript().getInputFile();
if (existing != nullptr) {
if (warnOnReassign && ThisConfig.showVersionScriptWarnings()) {
ThisConfig.raise(Diag::warn_version_script_reassign)
<< verSymInputFile->getInput()->decoratedPath() << R->name()
<< getVersionName(existing) << getVersionName(VS);
}
return false;
}

getTargetBackend().addSymbolScope(R, VS);

#ifdef ELD_ENABLE_SYMBOL_VERSIONING
if (ThisConfig.getPrinter()->traceSymbolVersioning())
ThisConfig.raise(Diag::trace_version_script_matched_scope)
<< "global" << R->name();
break;
if (ThisConfig.getPrinter()->traceSymbolVersioning()) {
ThisConfig.raise(Diag::trace_version_script_matched_scope)
<< (VS->isGlobal() ? "global" : "local") << R->name();
}
#endif
} // end Symbol Match
} // end Symbols
} // end Global
if (SymbolScopes.find(R) != SymbolScopes.end()) {
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
break;
#else
return true;
};

using PatternFilter = std::function<bool(const WildcardPattern &)>;

auto processBlock = [&](VersionScriptBlock *block, PatternFilter filter,
bool warnOnReassign) {
if (!block)
return;

for (auto *sym : block->getSymbols()) {
auto *pattern = sym->getSymbolPattern();
if (!filter(*pattern))
continue;
#endif
}
if (VersionScriptNode->getLocalBlock()) {
for (auto *Sym : VersionScriptNode->getLocalBlock()->getSymbols()) {

for (auto &G : NP.getGlobals()) {
ResolveInfo *R = G.getValue();
if (!canAssignVersionNode(*R))
continue;
if (!warnOnReassign && getTargetBackend().getSymbolScope(R) != nullptr)
continue;

#ifdef ELD_ENABLE_SYMBOL_VERSIONING
bool isMatched = Sym->matched(*R, NP, DemangledNames);
if (sym->matched(*R, NP, demangledNames))
#else
bool isMatched = Sym->getSymbolPattern()->matched(*R);
if (pattern->matched(*R))
#endif
if (isMatched) {
getTargetBackend().addSymbolScope(R, Sym);
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
if (ThisConfig.getPrinter()->traceSymbolVersioning())
ThisConfig.raise(Diag::trace_version_script_matched_scope)
<< "local" << R->name();
break;
#endif
} // end Symbol Match
} // end Symbols
} // end Local
#ifdef ELD_ENABLE_SYMBOL_VERSIONING
if (SymbolScopes.find(R) != SymbolScopes.end()) {
break;
{
tryAssign(R, sym, warnOnReassign);
}
}
#endif
} // end all Nodes
} // end Globals
return true;
}
};

auto processNodeFirstWins = [&](const VersionScriptNode *node,
PatternFilter filter, bool warnOnReassign) {
processBlock(node->getGlobalBlock(), filter, warnOnReassign);
processBlock(node->getLocalBlock(), filter, warnOnReassign);
};

auto processNodeLastWins = [&](const VersionScriptNode *node,
PatternFilter filter, bool warnOnReassign) {
processBlock(node->getLocalBlock(), filter, warnOnReassign);
processBlock(node->getGlobalBlock(), filter, warnOnReassign);
};

auto isExact = [](const WildcardPattern &P) { return !P.hasGlob(); };
auto isNonStarWildcard = [](const WildcardPattern &P) {
return P.hasGlob() && !P.isMatchAll();
};
auto isMatchAll = [](const WildcardPattern &P) { return P.isMatchAll(); };

for (const auto *N : VersionNodes) {
processNodeFirstWins(N, isExact, true);
}

for (auto It = VersionNodes.rbegin(); It != VersionNodes.rend(); ++It) {
processNodeLastWins(*It, isNonStarWildcard, false);
}

for (auto It = VersionNodes.rbegin(); It != VersionNodes.rend(); ++It) {
processNodeLastWins(*It, isMatchAll, false);
}
}

std::string ObjectLinker::getLTOTempPrefix() const {
Expand Down
4 changes: 2 additions & 2 deletions lib/Script/VersionScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace eld;
VersionScript::VersionScript(InputFile *Inp) : MInputFile(Inp) {}

VersionScriptNode *VersionScript::createVersionScriptNode() {
MCurrentNode = eld::make<VersionScriptNode>();
MCurrentNode = eld::make<VersionScriptNode>(*this);
VersionScriptNodes.push_back(MCurrentNode);
return MCurrentNode;
}
Expand Down Expand Up @@ -49,7 +49,7 @@ void VersionScript::dump(
}

// -----------------------VersionScriptNode -------------------------------
VersionScriptNode::VersionScriptNode() {}
VersionScriptNode::VersionScriptNode(const VersionScript &pVS) : VS(pVS) {}

VersionScriptBlock *VersionScriptNode::switchToGlobal() {
if (MLocal && !MGlobal) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ RUN: %clang %clangopts -o %t1.3.o %p/Inputs/3.c -c -fPIC
RUN: %link %linkopts -o %t1.lib1.so %t1.3.o -shared --version-script %p/Inputs/vs.2.t \
RUN: --trace=symbol-versioning 2>&1 | %filecheck %s --check-prefix=TRACE1
#END_TEST
TRACE1: Trace: Version script node matched global symbol 'baz'
TRACE1: Trace: Version script node matched global symbol 'foo@V1'
TRACE1: Trace: Version script node matched global symbol 'bar@@V1'
TRACE1-DAG: Trace: Version script node matched global symbol 'baz'
TRACE1-DAG: Trace: Version script node matched global symbol 'foo@V1'
TRACE1-DAG: Trace: Version script node matched global symbol 'bar@@V1'
TRACE1: Trace: Creating symbol versioning section: .gnu.version
TRACE1: Trace: Creating symbol versioning section: .gnu.version_d
TRACE1: Trace: Assigning version IDs to output symbols
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
global:
foo;
local:
foo;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
global:
foo*;
local:
foo;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
global:
*;
local:
foo*;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int foo() { return 0; }
int foo1() { return 1; }
int foo2() { return 2; }
int bar() { return 3; }
int bar1() { return 4; }
int baz() { return 5; }
int qux() { return 6; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
global:
foo*;
local:
foo*;
};
Loading
Loading