fix: improve propagating external functions info to kernel

When relocation points to symbol that is not defined within module mark
it as optional. When symbol is available at dynamic linking time then
info from the function is retrieved but when the symbol is not available
then ignore the dependency.

Any unresolved symbol needed for module linking is already handled
in a separate place.

Related-To: NEO-16243, NEO-16263, NEO-16262, NEO-16268
Signed-off-by: Mateusz Jablonski <mateusz.jablonski@intel.com>
This commit is contained in:
Mateusz Jablonski
2025-09-26 12:21:34 +00:00
committed by Compute-Runtime-Automation
parent a440a3e8ea
commit 15d0feeda8
8 changed files with 127 additions and 29 deletions

View File

@@ -97,10 +97,16 @@ struct ExternalFunctionsTests : public ::testing::Test {
nameToKernelDescriptor[kernelName] = kd.get();
}
void addFuncDependency(const std::string &calleeName, const std::string &callerName) {
funcDependenciesStorage.push_back({calleeName, callerName});
funcDependenciesStorage.push_back({calleeName, callerName, false});
}
void addKernelDependency(const std::string &calleeName, const std::string &kernelCallerName) {
kernelDependenciesStorage.push_back({calleeName, kernelCallerName});
kernelDependenciesStorage.push_back({calleeName, kernelCallerName, false});
}
void addOptionalFuncDependency(const std::string &calleeName, const std::string &callerName) {
funcDependenciesStorage.push_back({calleeName, callerName, true});
}
void addOptionalKernelDependency(const std::string &calleeName, const std::string &kernelCallerName) {
kernelDependenciesStorage.push_back({calleeName, kernelCallerName, true});
}
void clear() {
@@ -157,6 +163,27 @@ TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInLookupMapWhenResolvingExtFun
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingOptionalExtFuncInLookupMapWhenResolvingExtFuncDependenciesThenReturnSuccess) {
addOptionalFuncDependency("fun1", "fun0");
set();
auto error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(RESOLVE_SUCCESS, error);
clear();
addOptionalFuncDependency("fun1", "fun0");
addExternalFunction("fun1", {});
set();
error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(RESOLVE_SUCCESS, error);
clear();
addOptionalFuncDependency("fun1", "fun0");
addExternalFunction("fun0", {});
set();
error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(RESOLVE_SUCCESS, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInLookupMapWhenResolvingKernelDependenciesThenReturnError) {
addKernel("kernel");
addKernelDependency("fun0", "kernel");
@@ -164,6 +191,13 @@ TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInLookupMapWhenResolvingKernel
auto error = resolveKernelDependencies(extFuncInfo, funcNameToId, kernelDependencies, nameToKernelDescriptor);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingOptionalExtFuncInLookupMapWhenResolvingKernelDependenciesThenReturnSuccess) {
addKernel("kernel");
addOptionalKernelDependency("fun0", "kernel");
set();
auto error = resolveKernelDependencies(extFuncInfo, funcNameToId, kernelDependencies, nameToKernelDescriptor);
EXPECT_EQ(RESOLVE_SUCCESS, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingKernelInLookupMapWhenResolvingKernelDependenciesThenReturnError) {
addExternalFunction("fun0", {});
@@ -294,3 +328,28 @@ TEST_F(ExternalFunctionsTests, GivenValidFunctionAndKernelDependenciesWhenResolv
EXPECT_FALSE(nameToKernelDescriptor["kernel1"]->kernelAttributes.flags.hasIndirectCalls);
EXPECT_TRUE(nameToKernelDescriptor["kernel2"]->kernelAttributes.flags.hasIndirectCalls);
}
TEST_F(ExternalFunctionsTests, GivenValidFunctionAndKernelOptionalDependenciesWhenResolvingDependenciesThenSetAppropriateHasIndirectfCallsAndReturnSuccess) {
addKernel("kernel0");
addKernel("kernel1");
addKernel("kernel2");
addExternalFunction("fun0", {.hasIndirectCalls = false});
addExternalFunction("fun1", {.hasIndirectCalls = true});
addExternalFunction("fun2", {.hasIndirectCalls = false});
addOptionalFuncDependency("fun1", "fun0");
addOptionalKernelDependency("fun0", "kernel0");
addOptionalKernelDependency("fun2", "kernel1");
addOptionalKernelDependency("fun2", "kernel2");
set();
nameToKernelDescriptor["kernel2"]->kernelAttributes.flags.hasIndirectCalls = true;
auto error = resolveExternalDependencies(extFuncInfo, kernelDependencies, functionDependencies, nameToKernelDescriptor);
EXPECT_EQ(RESOLVE_SUCCESS, error);
EXPECT_TRUE(extFuncInfo[funcNameToId["fun0"]]->hasIndirectCalls);
EXPECT_TRUE(extFuncInfo[funcNameToId["fun1"]]->hasIndirectCalls);
EXPECT_FALSE(extFuncInfo[funcNameToId["fun2"]]->hasIndirectCalls);
EXPECT_TRUE(nameToKernelDescriptor["kernel0"]->kernelAttributes.flags.hasIndirectCalls);
EXPECT_FALSE(nameToKernelDescriptor["kernel1"]->kernelAttributes.flags.hasIndirectCalls);
EXPECT_TRUE(nameToKernelDescriptor["kernel2"]->kernelAttributes.flags.hasIndirectCalls);
}

View File

@@ -388,7 +388,7 @@ TEST(LinkerInputTests, GivenTwoGlobalSymbolsOfTypeFunctionEachPointingToDifferen
sectionNames[1] = ".data.const";
sectionNames[2] = ".text.hello";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x1234000, 8, 0, Elf::STT_FUNC, Elf::STB_GLOBAL);
@@ -405,7 +405,7 @@ TEST(LinkerInputTests, GivenGlobalSymbolOfTypeObjectPointingToDataGlobalSectionW
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.global";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x20, 8, 1, Elf::STT_OBJECT, Elf::STB_GLOBAL);
@@ -428,7 +428,7 @@ TEST(LinkerInputTests, GivenGlobalSymbolOfTypeObjectPointingToDataConstSectionWh
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 1, Elf::STT_OBJECT, Elf::STB_GLOBAL);
@@ -452,7 +452,7 @@ TEST(LinkerInputTests, GivenGlobalSymbolOfTypeFuncPointingToFunctionsSectionWhen
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = functionsSectionName.str();
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 32, 1, Elf::STT_FUNC, Elf::STB_GLOBAL);
@@ -479,7 +479,7 @@ TEST(LinkerInputTests, GivenGlobalSymbolOfTypeDifferentThantObjectOrFuncWhenDeco
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_NOTYPE, Elf::STB_GLOBAL);
@@ -490,21 +490,24 @@ TEST(LinkerInputTests, GivenGlobalSymbolOfTypeDifferentThantObjectOrFuncWhenDeco
EXPECT_EQ(0U, linkerInput.getSymbols().size());
}
TEST(LinkerInputTests, GivenGlobalSymbolPointingToSectionDifferentThanInstructionsOrDataWhenDecodingElfThenItIsIgnored) {
TEST(LinkerInputTests, GivenGlobalSymbolPointingToSectionDifferentThanInstructionsOrDataWhenDecodingElfThenItIsIgnoredAndAddedToExternalSymbols) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ""; // UNDEF section
sectionNames[1] = ".text.abc";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_OBJECT, Elf::STB_GLOBAL);
constexpr uint32_t externalSymbol = 3;
elf64.addSymbol(externalSymbol, 0, 8, 0, Elf::STT_OBJECT, Elf::STB_GLOBAL);
WhiteBox<NEO::LinkerInput> linkerInput;
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}};
NEO::LinkerInput linkerInput = {};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_EQ(1U, linkerInput.externalSymbols.size());
EXPECT_EQ(std::to_string(externalSymbol), linkerInput.externalSymbols[0]);
}
TEST(LInkerInputTests, GivenSymbolPointingToInstructionSegmentAndInvalidInstructionSectionNameMappingWhenDecodingElfThenLinkerInputIsInvalid) {
@@ -513,7 +516,7 @@ TEST(LInkerInputTests, GivenSymbolPointingToInstructionSegmentAndInvalidInstruct
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_FUNC, Elf::STB_GLOBAL);
@@ -532,7 +535,7 @@ TEST(LinkerInputTests, GivenInstructionRelocationAndInvalidInstructionSectionNam
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 0, 0, "0");
elf64.overrideSymbolName = true;
@@ -548,7 +551,7 @@ TEST(LinkerInputTests, GivenRelocationWithSymbolIdOutOfBoundsOfSymbolTableWhenDe
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames = {{0, ".text.abc"}};
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 0, 2, "symbol");
@@ -564,7 +567,7 @@ TEST(LinkerInputTests, GivenRelocationToSectionDifferentThanDataOrInstructionsWh
std::unordered_map<uint32_t, std::string> sectionNames = {{0, ".text.abc"},
{1, "unknown.section"}};
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 1, 2, "symbol");
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
@@ -584,7 +587,7 @@ TEST(LinkerInputTests, GivenGlobalDataRelocationWithLocalSymbolPointingToConstDa
sectionNames[1] = ".data.const";
sectionNames[2] = ".data.global";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 10, Zebin::Elf::R_ZE_SYM_ADDR, 2, 0, "0");
elf64.overrideSymbolName = true;
@@ -617,7 +620,7 @@ TEST(LinkerInputTests, GivenInstructionRelocationWithLocalSymbolPointingToFuncti
sectionNames[0] = ".text.abc";
sectionNames[1] = functionsSectionName.str();
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 10, Zebin::Elf::R_ZE_SYM_ADDR, 0, 0, "0");
elf64.overrideSymbolName = true;
@@ -791,7 +794,7 @@ TEST(LinkerInputTests, GivenExternalFunctionsSymbolsUsedInKernelRelocationsWhenP
EXPECT_EQ(kernelName, mockLinkerInput.kernelDependencies[0].kernelName);
}
TEST(LinkerInputTests, GivenNonFunctionRelocationInKernelRelocationsWhenParsingRelocationsForExtFuncUsageForKernelThenDoNotAddKernelDependency) {
TEST(LinkerInputTests, GivenNonFunctionSymbolRelocationInKernelRelocationsWhenParsingRelocationsForExtFuncUsageForKernelThenDoNotAddKernelDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &symbols = mockLinkerInput.symbols;
@@ -806,11 +809,8 @@ TEST(LinkerInputTests, GivenNonFunctionRelocationInKernelRelocationsWhenParsingR
symbols.emplace("fun", symbol2);
for (auto nonFuncRelocationName : {
implicitArgsRelocationSymbolName,
std::string_view(".str"),
std::string_view("globalVar"),
Linker::perThreadOff,
Linker::subDeviceID,
std::string_view("")}) {
NEO::LinkerInput::RelocationInfo relocInfo{};
@@ -822,6 +822,35 @@ TEST(LinkerInputTests, GivenNonFunctionRelocationInKernelRelocationsWhenParsingR
EXPECT_EQ(0u, mockLinkerInput.kernelDependencies.size());
}
}
TEST(LinkerInputTests, GivenExternalSymbolRelocationInKernelRelocationsWhenParsingRelocationsForExtFuncUsageForKernelThenAddOptionalKernelDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &externalSymbols = mockLinkerInput.externalSymbols;
externalSymbols.push_back(std::string(implicitArgsRelocationSymbolName));
externalSymbols.push_back(std::string(Linker::perThreadOff));
externalSymbols.push_back(std::string(Linker::subDeviceID));
for (auto relocationName : {
implicitArgsRelocationSymbolName,
Linker::perThreadOff,
Linker::subDeviceID}) {
NEO::LinkerInput::RelocationInfo relocInfo{};
relocInfo.symbolName = relocationName;
std::string kernelName = "kernel";
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, kernelName);
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
}
EXPECT_EQ(3u, mockLinkerInput.kernelDependencies.size());
for (auto &kernelDependency : mockLinkerInput.kernelDependencies) {
EXPECT_TRUE(kernelDependency.optional);
}
}
HWTEST_F(LinkerTests, givenEmptyLinkerInputThenLinkerOutputIsEmpty) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
@@ -2140,7 +2169,7 @@ TEST_F(LinkerTests, GivenDebugDataWhenApplyingDebugDataRelocationsThenRelocation
sectionNames[4] = ".debug_line";
sectionNames[5] = ".data.const";
elf64.setupSecionNames(std::move(sectionNames));
elf64.setupSectionNames(std::move(sectionNames));
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc0 = {};
reloc0.offset = 64;