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
31 changes: 23 additions & 8 deletions source/link/linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
const DefUseManager& def_use_manager,
const DecorationManager& decoration_manager,
bool allow_partial_linkage,
bool allow_multiple_exports,
LinkageTable* linkings_to_do);

// Checks that for each pair of import and export, the import and export have
Expand Down Expand Up @@ -416,6 +417,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
const DefUseManager& def_use_manager,
const DecorationManager& decoration_manager,
bool allow_partial_linkage,
bool allow_multiple_exports,
LinkageTable* linkings_to_do) {
spv_position_t position = {};

Expand Down Expand Up @@ -504,6 +506,23 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
<< possible_export.second.name << "\".";
}

// Detect multiple strong definitions of the same symbol.
// The import-matching loop below only visits symbols that are imported, so it
// would silently accept multiple exports when nothing imports them. Check
// all exports here so that duplicates are always rejected.
//
// The function-variants (multitarget) flow intentionally keeps several
// exports of the same name (one per variant), so skip the check in that case.
if (!allow_multiple_exports) {
for (const auto& exp : exports) {
if (exp.second.size() > 1u)
return DiagnosticStream(position, consumer, "",
SPV_ERROR_INVALID_BINARY)
<< "Too many external references, " << exp.second.size()
<< ", were found for \"" << exp.first << "\".";
}
}

// Find the import/export pairs
for (const auto& import : imports) {
std::vector<LinkageSymbolInfo> possible_exports;
Expand All @@ -512,10 +531,6 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
if (possible_exports.empty() && !allow_partial_linkage)
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
<< "Unresolved external reference to \"" << import.name << "\".";
else if (possible_exports.size() > 1u)
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
<< "Too many external references, " << possible_exports.size()
<< ", were found for \"" << import.name << "\".";

if (!possible_exports.empty())
linkings_to_do->emplace_back(import, possible_exports.front());
Expand Down Expand Up @@ -910,10 +925,10 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,

// Phase 5: Find the import/export pairs
LinkageTable linkings_to_do;
res = GetImportExportPairs(consumer, linked_context,
*linked_context.get_def_use_mgr(),
*linked_context.get_decoration_mgr(),
options.GetAllowPartialLinkage(), &linkings_to_do);
res = GetImportExportPairs(
consumer, linked_context, *linked_context.get_def_use_mgr(),
*linked_context.get_decoration_mgr(), options.GetAllowPartialLinkage(),
make_multitarget, &linkings_to_do);
if (res != SPV_SUCCESS) return res;

// Phase 6: Ensure the import and export have the same types and decorations.
Expand Down
33 changes: 33 additions & 0 deletions test/link/matching_imports_to_exports_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,39 @@ OpDecorate %1 LinkageAttributes "foo" Export
}
}

TEST_F(MatchingImportsToExports, MultipleDefinitionsNoImport) {
// Two modules export the same symbol but nothing imports it.
// The linker must still reject this as a duplicate strong definition.
const std::string body1 = R"(
OpCapability Linkage
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
const std::string body2 = R"(
OpCapability Linkage
OpCapability Addresses
OpCapability Kernel
OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 -1
%1 = OpVariable %2 Uniform %3
)";

spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Too many external references, 2, were found "
"for \"foo\"."));
}

TEST_F(MatchingImportsToExports, SameNameDifferentTypes) {
const std::string body1 = R"(
OpCapability Linkage
Expand Down