diff --git a/source/link/linker.cpp b/source/link/linker.cpp index 002cc41f18..8c055475b1 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -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 @@ -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 = {}; @@ -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 possible_exports; @@ -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()); @@ -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. diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp index db94cb8362..0574f02e58 100644 --- a/test/link/matching_imports_to_exports_test.cpp +++ b/test/link/matching_imports_to_exports_test.cpp @@ -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