Files
compute-runtime/shared/test/unit_test/compiler_interface/linker_tests.cpp
Mateusz Jablonski 15d0feeda8 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>
2025-09-29 15:34:41 +02:00

3000 lines
142 KiB
C++

/*
* Copyright (C) 2019-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/command_stream/command_stream_receiver.h"
#include "shared/source/compiler_interface/external_functions.h"
#include "shared/source/device_binary_format/zebin/zebin_elf.h"
#include "shared/source/helpers/ptr_math.h"
#include "shared/source/kernel/implicit_args_helper.h"
#include "shared/source/kernel/kernel_descriptor.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/test/common/compiler_interface/linker_mock.h"
#include "shared/test/common/fixtures/device_fixture.h"
#include "shared/test/common/helpers/debug_manager_state_restore.h"
#include "shared/test/common/helpers/default_hw_info.h"
#include "shared/test/common/helpers/gtest_helpers.h"
#include "shared/test/common/helpers/implicit_args_test_helper.h"
#include "shared/test/common/helpers/raii_gfx_core_helper.h"
#include "shared/test/common/mocks/mock_device.h"
#include "shared/test/common/mocks/mock_elf.h"
#include "shared/test/common/mocks/mock_execution_environment.h"
#include "shared/test/common/mocks/mock_graphics_allocation.h"
#include "shared/test/common/mocks/ult_device_factory.h"
#include "shared/test/common/test_macros/hw_test.h"
#include "RelocationInfo.h"
#include "gtest/gtest.h"
#include <string>
TEST(SegmentTypeTests, givenSegmentTypeWhenAsStringIsCalledThenProperRepresentationIsReturned) {
EXPECT_STREQ("UNKNOWN", NEO::asString(NEO::SegmentType::unknown));
EXPECT_STREQ("GLOBAL_CONSTANTS", NEO::asString(NEO::SegmentType::globalConstants));
EXPECT_STREQ("GLOBAL_VARIABLES", NEO::asString(NEO::SegmentType::globalVariables));
EXPECT_STREQ("INSTRUCTIONS", NEO::asString(NEO::SegmentType::instructions));
}
TEST(LinkerInputTraitsTests, whenPointerSizeNotSizeThenDefaultsToHostPointerSize) {
using PointerSize = NEO::LinkerInput::Traits::PointerSize;
auto expectedPointerSize = (sizeof(void *) == 4) ? PointerSize::Ptr32bit : PointerSize::Ptr64bit;
auto pointerSize = NEO::LinkerInput::Traits{}.pointerSize;
EXPECT_EQ(expectedPointerSize, pointerSize);
}
TEST(LinkerInputTests, givenGlobalsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants) {
NEO::LinkerInput linkerInput;
EXPECT_TRUE(linkerInput.isValid());
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_GLOBAL_VAR;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
auto decodeResult = linkerInput.decodeGlobalVariablesSymbolTable(entry, 2);
EXPECT_TRUE(decodeResult);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SegmentType::globalVariables, symbolA->second.segment);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SegmentType::globalConstants, symbolB->second.segment);
auto symbolC = linkerInput.getSymbols().find("C");
EXPECT_EQ(linkerInput.getSymbols().end(), symbolC);
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants) {
// Note : this is subject to change in IGC shotly.
// GLOBAL_VAR/CONST will be ultimately allowed only in globalVariables symbol table.
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_GLOBAL_VAR;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(entry, 2, 3);
EXPECT_TRUE(decodeResult);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SegmentType::globalVariables, symbolA->second.segment);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SegmentType::globalConstants, symbolB->second.segment);
auto symbolC = linkerInput.getSymbols().find("C");
EXPECT_EQ(linkerInput.getSymbols().end(), symbolC);
}
TEST(LinkerInputTests, givenGlobalsSymbolTableThenFunctionExportsAreNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry = {};
entry.s_name[0] = 'A';
entry.s_offset = 8;
entry.s_size = 16;
entry.s_type = vISA::GenSymType::S_FUNC;
auto decodeResult = linkerInput.decodeGlobalVariablesSymbolTable(&entry, 1);
EXPECT_FALSE(decodeResult);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenProperlyDecodesExportedFunctions) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_FUNC;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_FUNC;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_EQ(-1, linkerInput.getExportedFunctionsSegmentId());
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(entry, 2, 3);
EXPECT_TRUE(decodeResult);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_TRUE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SegmentType::instructions, symbolA->second.segment);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SegmentType::instructions, symbolB->second.segment);
EXPECT_EQ(3, linkerInput.getExportedFunctionsSegmentId());
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenUndefIsAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry = {};
entry.s_name[0] = 'A';
entry.s_offset = 8;
entry.s_size = 16;
entry.s_type = vISA::GenSymType::S_UNDEF;
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(&entry, 1, 3);
EXPECT_TRUE(decodeResult);
EXPECT_TRUE(linkerInput.isValid());
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenNoTypeIsNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry = {};
entry.s_name[0] = 'A';
entry.s_offset = 8;
entry.s_size = 16;
entry.s_type = vISA::GenSymType::S_NOTYPE;
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(&entry, 1, 3);
EXPECT_FALSE(decodeResult);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, givenRelocationTableThenRelocationEntriesAreProperlyParsed) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_SYM_ADDR;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 3);
EXPECT_TRUE(decodeResult);
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 1);
EXPECT_TRUE(decodeResult);
EXPECT_TRUE(linkerInput.isValid());
}
TEST(LinkerInputTests, givenRelocationTableThenNoneAsRelocationTypeIsNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_NONE;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 3);
EXPECT_FALSE(decodeResult);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, whenDataRelocationsAreAddedThenProperTraitsAreSet) {
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.offset = 7U;
relocInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocInfo.symbolName = "aaa";
relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
{
NEO::LinkerInput linkerInput;
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
linkerInput.addDataRelocationInfo(relocInfo);
ASSERT_EQ(1U, linkerInput.getDataRelocations().size());
EXPECT_EQ(relocInfo.offset, linkerInput.getDataRelocations()[0].offset);
EXPECT_EQ(relocInfo.relocationSegment, linkerInput.getDataRelocations()[0].relocationSegment);
EXPECT_EQ(relocInfo.symbolName, linkerInput.getDataRelocations()[0].symbolName);
EXPECT_EQ(relocInfo.type, linkerInput.getDataRelocations()[0].type);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
EXPECT_TRUE(linkerInput.isValid());
}
{
NEO::LinkerInput linkerInput;
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
relocInfo.relocationSegment = NEO::SegmentType::globalVariables;
linkerInput.addDataRelocationInfo(relocInfo);
ASSERT_EQ(1U, linkerInput.getDataRelocations().size());
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
EXPECT_TRUE(linkerInput.isValid());
}
}
TEST(LinkerInputTests, WhenGettingSegmentForSectionNameThenCorrectSegmentIsReturned) {
auto segmentConst = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataConst.str());
auto segmentGlobalConst = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataGlobalConst.str());
auto segmentGlobal = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataGlobal.str());
auto segmentConstString = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataConstString.str());
auto segmentInstructions = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::textPrefix.str());
auto segmentInstructions2 = NEO::LinkerInput::getSegmentForSection(".text.abc");
auto segmentGlobalZeroInit = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataGlobalZeroInit.str());
auto segmentGlobalConstZeroInit = NEO::LinkerInput::getSegmentForSection(NEO::Zebin::Elf::SectionNames::dataConstZeroInit.str());
EXPECT_EQ(NEO::SegmentType::globalConstants, segmentConst);
EXPECT_EQ(NEO::SegmentType::globalConstants, segmentGlobalConst);
EXPECT_EQ(NEO::SegmentType::globalVariables, segmentGlobal);
EXPECT_EQ(NEO::SegmentType::globalStrings, segmentConstString);
EXPECT_EQ(NEO::SegmentType::instructions, segmentInstructions);
EXPECT_EQ(NEO::SegmentType::instructions, segmentInstructions2);
EXPECT_EQ(NEO::SegmentType::globalVariablesZeroInit, segmentGlobalZeroInit);
EXPECT_EQ(NEO::SegmentType::globalConstantsZeroInit, segmentGlobalConstZeroInit);
}
TEST(LinkerInputTests, WhenGettingSegmentForUnknownSectionNameThenUnknownSegmentIsReturned) {
auto segment = NEO::LinkerInput::getSegmentForSection("Not_a_valid_section_name");
EXPECT_EQ(NEO::SegmentType::unknown, segment);
}
TEST(LinkerInputTests, WhenAddingElfTextRelocationForSegmentIndexThenInstructionSegmentForRelocationAndProperTraitsAreSet) {
NEO::LinkerInput linkerInput = {};
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.offset = 7u;
relocInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocInfo.symbolName = "test";
relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
relocInfo.relocationSegment = NEO::SegmentType::globalVariables;
linkerInput.addElfTextSegmentRelocation(relocInfo, 5);
ASSERT_EQ(0u, linkerInput.getDataRelocations().size());
ASSERT_EQ(6u, linkerInput.getRelocationsInInstructionSegments().size());
auto relocs = linkerInput.getRelocationsInInstructionSegments()[5];
ASSERT_EQ(1u, relocs.size());
EXPECT_EQ(NEO::SegmentType::instructions, relocs[0].relocationSegment);
EXPECT_EQ(std::string("test"), relocs[0].symbolName);
EXPECT_EQ(7u, relocs[0].offset);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
EXPECT_TRUE(linkerInput.isValid());
}
TEST(LinkerInputTests, WhenAddingTwoElfTextRelocationForSingleSegmentIndexThenBothRelocationsAreAddedForTheSameSegment) {
NEO::LinkerInput linkerInput = {};
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.offset = 7u;
relocInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocInfo.symbolName = "test";
relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
relocInfo.relocationSegment = NEO::SegmentType::globalVariables;
linkerInput.addElfTextSegmentRelocation(relocInfo, 0);
ASSERT_EQ(1u, linkerInput.getRelocationsInInstructionSegments().size());
auto &relocs = linkerInput.getRelocationsInInstructionSegments()[0];
EXPECT_EQ(1u, relocs.size());
relocInfo.offset = 24u;
linkerInput.addElfTextSegmentRelocation(relocInfo, 0);
EXPECT_EQ(2u, relocs.size());
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
}
TEST(LinkerInputTests, GivenVarDataSegmentThenIsVarDataSegmentReturnsTrue) {
EXPECT_TRUE(isVarDataSegment(SegmentType::globalVariables));
EXPECT_TRUE(isVarDataSegment(SegmentType::globalVariablesZeroInit));
}
TEST(LinkerInputTests, GivenConstDataSegmentThenIsConstDataSegmentReturnsTrue) {
EXPECT_TRUE(isConstDataSegment(SegmentType::globalConstants));
EXPECT_TRUE(isConstDataSegment(SegmentType::globalConstantsZeroInit));
}
TEST(LinkerInputTests, GivenTwoGlobalSymbolsOfTypeFunctionEachPointingToDifferentInstructionSectionWhenDecodingElfThenLinkerInputIsInvalid) {
NEO::LinkerInput linkerInput = {};
NEO::Elf::ElfFileHeader<NEO::Elf::EI_CLASS_64> header;
MockElf<NEO::Elf::EI_CLASS_64> elf64;
elf64.elfFileHeader = &header;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
sectionNames[2] = ".text.hello";
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x1234000, 8, 0, Elf::STT_FUNC, Elf::STB_GLOBAL);
elf64.addSymbol(8, 0x5000, 16, 2, Elf::STT_FUNC, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}, {"hello", 1}};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, GivenGlobalSymbolOfTypeObjectPointingToDataGlobalSectionWhenDecodingElfThenTraitExportsGlobalVariablesIsSet) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.global";
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x20, 8, 1, Elf::STT_OBJECT, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}};
NEO::LinkerInput linkerInput = {};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalVariables);
auto &symbol = linkerInput.getSymbols().at("0");
EXPECT_EQ(0x20U, symbol.offset);
EXPECT_EQ(8U, symbol.size);
EXPECT_EQ(SegmentType::globalVariables, symbol.segment);
EXPECT_TRUE(symbol.global);
}
TEST(LinkerInputTests, GivenGlobalSymbolOfTypeObjectPointingToDataConstSectionWhenDecodingElfThenTraitExportsGlobalVariablesIsSet) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 1, Elf::STT_OBJECT, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}};
NEO::LinkerInput linkerInput = {};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
auto &symbol = linkerInput.getSymbols().at("0");
EXPECT_EQ(0U, symbol.offset);
EXPECT_EQ(8U, symbol.size);
EXPECT_EQ(SegmentType::globalConstants, symbol.segment);
EXPECT_TRUE(symbol.global);
}
TEST(LinkerInputTests, GivenGlobalSymbolOfTypeFuncPointingToFunctionsSectionWhenDecodingElfThenTraitExportsFunctionsIsSetAndExportedFunctionsSegmentIdIsSet) {
for (auto functionsSectionName : {Zebin::Elf::SectionNames::functions, Zebin::Elf::SectionNames::text}) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = functionsSectionName.str();
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 32, 1, Elf::STT_FUNC, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0},
{Zebin::Elf::SectionNames::externalFunctions.str(), 1}};
NEO::LinkerInput linkerInput = {};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_TRUE(linkerInput.getTraits().exportsFunctions);
EXPECT_EQ(1, linkerInput.getExportedFunctionsSegmentId()) << functionsSectionName.str();
auto &symbol = linkerInput.getSymbols().at("0");
EXPECT_EQ(0U, symbol.offset);
EXPECT_EQ(32U, symbol.size);
EXPECT_EQ(SegmentType::instructions, symbol.segment);
EXPECT_EQ(1U, symbol.instructionSegmentId);
EXPECT_TRUE(symbol.global);
}
}
TEST(LinkerInputTests, GivenGlobalSymbolOfTypeDifferentThantObjectOrFuncWhenDecodingElfThenItIsIgnored) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_NOTYPE, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}};
NEO::LinkerInput linkerInput = {};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_EQ(0U, linkerInput.getSymbols().size());
}
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.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
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}};
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) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
elf64.setupSectionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_FUNC, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
nameToKernelId["wrong_name"] = 0;
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, GivenInstructionRelocationAndInvalidInstructionSectionNameMappingWhenDecodingElfThenLinkerInputIsInvalid) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 0, 0, "0");
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x1234000, 8, 1, Elf::STT_OBJECT, Elf::STB_GLOBAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"wrong_name", 0}};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, GivenRelocationWithSymbolIdOutOfBoundsOfSymbolTableWhenDecodingElfThenLinkerInputIsInvalid) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames = {{0, ".text.abc"}};
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 0, 2, "symbol");
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
nameToKernelId["abc"] = 0;
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_FALSE(linkerInput.isValid());
}
TEST(LinkerInputTests, GivenRelocationToSectionDifferentThanDataOrInstructionsWhenDecodingElfThenItIsIgnored) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames = {{0, ".text.abc"},
{1, "unknown.section"}};
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 0, Zebin::Elf::R_ZE_SYM_ADDR, 1, 2, "symbol");
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
nameToKernelId["abc"] = 0;
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
EXPECT_TRUE(linkerInput.getDataRelocations().empty());
EXPECT_TRUE(linkerInput.getRelocationsInInstructionSegments().empty());
}
TEST(LinkerInputTests, GivenGlobalDataRelocationWithLocalSymbolPointingToConstDataWhenDecodingElfThenRelocationInfoAndSymbolInfoAreCreated) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = ".data.const";
sectionNames[2] = ".data.global";
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 10, Zebin::Elf::R_ZE_SYM_ADDR, 2, 0, "0");
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x0, 8, 1, Elf::STT_OBJECT, Elf::STB_LOCAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0}};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
auto &symbol = linkerInput.getSymbols().at("0");
EXPECT_FALSE(symbol.global);
EXPECT_EQ(0U, symbol.offset);
EXPECT_EQ(8U, symbol.size);
EXPECT_EQ(SegmentType::globalConstants, symbol.segment);
auto &dataRelocations = linkerInput.getDataRelocations();
EXPECT_EQ(1U, dataRelocations.size());
EXPECT_EQ(64U, dataRelocations[0].offset);
EXPECT_EQ(10U, dataRelocations[0].addend);
EXPECT_EQ(SegmentType::globalVariables, dataRelocations[0].relocationSegment);
EXPECT_EQ("0", dataRelocations[0].symbolName);
EXPECT_EQ(LinkerInput::RelocationInfo::Type::address, dataRelocations[0].type);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
}
TEST(LinkerInputTests, GivenInstructionRelocationWithLocalSymbolPointingToFunctionsWhendDecodingElfThenRelocationsInfoAndSymbolInfoAreCreated) {
for (auto functionsSectionName : {Zebin::Elf::SectionNames::functions, Zebin::Elf::SectionNames::text}) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = functionsSectionName.str();
elf64.setupSectionNames(std::move(sectionNames));
elf64.addReloc(64, 10, Zebin::Elf::R_ZE_SYM_ADDR, 0, 0, "0");
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0x10, 0x40, 1, Elf::STT_FUNC, Elf::STB_LOCAL);
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId = {{"abc", 0},
{Zebin::Elf::SectionNames::externalFunctions.str(), 1}};
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
EXPECT_TRUE(linkerInput.isValid());
auto &symbol = linkerInput.getSymbols().at("0");
EXPECT_FALSE(symbol.global);
EXPECT_EQ(0x10U, symbol.offset);
EXPECT_EQ(0x40U, symbol.size);
EXPECT_EQ(SegmentType::instructions, symbol.segment);
EXPECT_EQ(1U, symbol.instructionSegmentId);
auto &instructionRelocsPerSeg = linkerInput.getRelocationsInInstructionSegments();
EXPECT_EQ(1U, instructionRelocsPerSeg.size());
auto &relocations = instructionRelocsPerSeg[0];
EXPECT_EQ(1U, relocations.size());
EXPECT_EQ(64U, relocations[0].offset);
EXPECT_EQ(10U, relocations[0].addend);
EXPECT_EQ(SegmentType::instructions, relocations[0].relocationSegment);
EXPECT_EQ("0", relocations[0].symbolName);
EXPECT_EQ(LinkerInput::RelocationInfo::Type::address, relocations[0].type);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
}
}
using LinkerTests = Test<DeviceFixture>;
TEST_F(LinkerTests, GivenSymbolToInstructionsAndNoCorrespondingInstructionSegmentWhenRelocatingSymbolsThenFail) {
WhiteBox<NEO::LinkerInput> linkerInput;
auto &symbol = linkerInput.symbols["func"];
symbol.segment = SegmentType::instructions;
symbol.instructionSegmentId = 4;
WhiteBox<NEO::Linker> linker(linkerInput);
Linker::PatchableSegments instSegments;
auto result = linker.relocateSymbols({}, {}, {}, {}, instSegments, 0U, 0U);
EXPECT_FALSE(result);
}
TEST_F(LinkerTests, GivenSymbolToInstructionBiggerThanCorrespondingInstructionSegmentWhenRelocatingSymbolsThenFail) {
WhiteBox<NEO::LinkerInput> linkerInput;
auto &symbol = linkerInput.symbols["func"];
symbol.segment = SegmentType::instructions;
symbol.instructionSegmentId = 0;
symbol.offset = 8;
symbol.size = 8;
WhiteBox<NEO::Linker> linker(linkerInput);
Linker::PatchableSegments instSegments(1);
instSegments[0].segmentSize = static_cast<size_t>(symbol.offset + symbol.size - 1);
auto result = linker.relocateSymbols({}, {}, {}, {}, instSegments, 0U, 0U);
EXPECT_FALSE(result);
}
TEST_F(LinkerTests, GivenValidSymbolToInstructionsWhenRelocatingSymbolsThenSymbolIsProperlyRelocated) {
WhiteBox<NEO::LinkerInput> linkerInput;
auto &symbol = linkerInput.symbols["func"];
symbol.segment = SegmentType::instructions;
symbol.instructionSegmentId = 0;
symbol.offset = 4;
symbol.size = 8;
WhiteBox<NEO::Linker> linker(linkerInput);
Linker::PatchableSegments instSegments(1);
instSegments[0].segmentSize = 32;
instSegments[0].gpuAddress = 0xaabbccdd;
auto result = linker.relocateSymbols({}, {}, {}, {}, instSegments, 0U, 0U);
EXPECT_TRUE(result);
EXPECT_EQ(1U, linker.relocatedSymbols.size());
EXPECT_EQ(linker.relocatedSymbols["func"].gpuAddress, instSegments[0].gpuAddress + symbol.offset);
}
TEST_F(LinkerTests, GivenRelocationToInstructionSegmentWithLocalUndefinedSymbolWhenPatchingInstructionsSegmentsThenItIsPatchedWithZeroes) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::LinkerInput::RelocationInfo rela;
rela.offset = 0U;
rela.addend = 128U;
rela.type = NEO::LinkerInput::RelocationInfo::Type::address;
rela.symbolName = "";
rela.relocationSegment = NEO::SegmentType::instructions;
linkerInput.textRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
uint64_t instructionSegmentData{std::numeric_limits<uint64_t>::max()};
NEO::Linker::PatchableSegment instructionSegmentToPatch;
instructionSegmentToPatch.hostPointer = reinterpret_cast<void *>(&instructionSegmentData);
instructionSegmentToPatch.segmentSize = sizeof(instructionSegmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
linker.patchInstructionsSegments({instructionSegmentToPatch}, unresolvedExternals, kernelDescriptors);
auto instructionSegmentPatchedData = reinterpret_cast<uint64_t *>(ptrOffset(instructionSegmentToPatch.hostPointer, static_cast<size_t>(rela.offset)));
EXPECT_EQ(0u, static_cast<uint64_t>(*instructionSegmentPatchedData));
EXPECT_EQ(0u, unresolvedExternals.size());
}
TEST(LinkerInputTests, GivenInvalidFunctionsSymbolsUsedInFunctionsRelocationsWhenParsingRelocationsForExtFuncUsageThenDoNotAddDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &extFuncSymbols = mockLinkerInput.extFuncSymbols;
extFuncSymbols.resize(1);
auto &funSym = extFuncSymbols[0];
funSym.first = "fun";
funSym.second.offset = 4U;
funSym.second.size = 4U;
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.symbolName = "fun";
relocInfo.offset = 0U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Zebin::Elf::SectionNames::externalFunctions.str());
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
relocInfo.offset = 0x10U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Zebin::Elf::SectionNames::externalFunctions.str());
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
}
TEST(LinkerInputTests, GivenFunctionSymbolsUsedInFunctionsRelocationsWhenParsingRelocationsForExtFuncUsageThenAddDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &extFuncSymbols = mockLinkerInput.extFuncSymbols;
extFuncSymbols.resize(2);
auto &funSym = extFuncSymbols[0];
funSym.first = "fun";
funSym.second.offset = 4U;
funSym.second.size = 4U;
auto &fun2Sym = extFuncSymbols[1];
fun2Sym.first = "fun2";
fun2Sym.second.offset = 8U;
fun2Sym.second.size = 4U;
NEO::LinkerInput::RelocationInfo relocInfo{};
relocInfo.symbolName = "fun3";
relocInfo.offset = 6U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Zebin::Elf::SectionNames::externalFunctions.str());
EXPECT_EQ(1u, mockLinkerInput.extFunDependencies.size());
EXPECT_EQ(relocInfo.symbolName, mockLinkerInput.extFunDependencies[0].usedFuncName);
EXPECT_EQ(funSym.first, mockLinkerInput.extFunDependencies[0].callerFuncName);
mockLinkerInput.extFunDependencies.clear();
relocInfo.offset = 8U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Zebin::Elf::SectionNames::externalFunctions.str());
EXPECT_EQ(1u, mockLinkerInput.extFunDependencies.size());
EXPECT_EQ(relocInfo.symbolName, mockLinkerInput.extFunDependencies[0].usedFuncName);
EXPECT_EQ(fun2Sym.first, mockLinkerInput.extFunDependencies[0].callerFuncName);
}
TEST(LinkerInputTests, GivenExternalFunctionsSymbolsUsedInKernelRelocationsWhenParsingRelocationsForExtFuncUsageForKernelThenAddKernelDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
NEO::LinkerInput::RelocationInfo relocInfo{};
relocInfo.symbolName = "fun";
std::string kernelName = "kernel";
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, kernelName);
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
EXPECT_EQ(1u, mockLinkerInput.kernelDependencies.size());
EXPECT_EQ(relocInfo.symbolName, mockLinkerInput.kernelDependencies[0].usedFuncName);
EXPECT_EQ(kernelName, mockLinkerInput.kernelDependencies[0].kernelName);
}
TEST(LinkerInputTests, GivenNonFunctionSymbolRelocationInKernelRelocationsWhenParsingRelocationsForExtFuncUsageForKernelThenDoNotAddKernelDependency) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &symbols = mockLinkerInput.symbols;
SymbolInfo symbol0 = {
.segment = SegmentType::globalStrings};
SymbolInfo symbol1 = {
.segment = SegmentType::globalVariables};
SymbolInfo symbol2 = {
.segment = SegmentType::instructions};
symbols.emplace(".str", symbol0);
symbols.emplace("globalVar", symbol1);
symbols.emplace("fun", symbol2);
for (auto nonFuncRelocationName : {
std::string_view(".str"),
std::string_view("globalVar"),
std::string_view("")}) {
NEO::LinkerInput::RelocationInfo relocInfo{};
relocInfo.symbolName = nonFuncRelocationName;
std::string kernelName = "kernel";
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, kernelName);
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
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);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
}
HWTEST_F(LinkerTests, givenInvalidLinkerInputThenLinkerFails) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
mockLinkerInput.valid = false;
NEO::Linker linker(mockLinkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT extFuncs;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, extFuncs);
EXPECT_EQ(NEO::LinkingStatus::error, linkResult);
}
HWTEST_F(LinkerTests, givenUnresolvedExternalSymbolsWhenResolveBuiltinsIsCalledThenSubDeviceIDSymbolsAreRemoved) {
struct LinkerMock : public NEO::Linker {
public:
using NEO::Linker::Linker;
using NEO::Linker::resolveBuiltins;
};
NEO::LinkerInput linkerInput;
LinkerMock linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
unresolvedExternals.push_back({{"__SubDeviceID", 0, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 156, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 140, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__SubDeviceID", 64, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
std::vector<char> instructionSegment;
instructionSegment.resize(128u);
NEO::Linker::PatchableSegments instructionsSegments;
instructionsSegments.push_back({instructionSegment.data(), 64u});
instructionsSegments.push_back({&instructionSegment[64], 64u});
DebugManagerStateRestore restorer;
debugManager.flags.CreateMultipleSubDevices.set(2);
debugManager.flags.EnableImplicitScaling.set(1);
NEO::Linker::KernelDescriptorsT kernelDescriptors;
linker.resolveBuiltins(pDevice, unresolvedExternals, instructionsSegments, kernelDescriptors);
EXPECT_EQ(2U, unresolvedExternals.size());
for (auto &symbol : unresolvedExternals) {
EXPECT_NE(NEO::Linker::subDeviceID, symbol.unresolvedRelocation.symbolName);
}
auto gpuAddressAs64bit = pDevice->getDefaultEngine().commandStreamReceiver->getWorkPartitionAllocationGpuAddress();
EXPECT_EQ(*reinterpret_cast<uint32_t *>(&instructionSegment[64]), static_cast<uint32_t>((gpuAddressAs64bit >> 32) & 0xffffffff));
EXPECT_EQ(*reinterpret_cast<uint32_t *>(instructionSegment.data()), static_cast<uint32_t>(gpuAddressAs64bit & 0xffffffff));
}
HWTEST_F(LinkerTests, givenUnresolvedExternalsWhenLinkThenSubDeviceIDSymbolsAreCorrectlyHandled) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::Linker::UnresolvedExternals unresolvedExternals;
unresolvedExternals.push_back({{"__SubDeviceID", 0, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 156, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 140, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__SubDeviceID", 64, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
std::vector<char> instructionSegment;
instructionSegment.resize(128u);
NEO::Linker::PatchableSegments instructionsSegments;
instructionsSegments.push_back({instructionSegment.data(), 0});
instructionsSegments.push_back({&instructionSegment[64], 64u});
DebugManagerStateRestore restorer;
debugManager.flags.CreateMultipleSubDevices.set(2);
debugManager.flags.EnableImplicitScaling.set(1);
linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, instructionsSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
EXPECT_EQ(2u, unresolvedExternals.size());
for (auto &symbol : unresolvedExternals) {
EXPECT_NE(NEO::Linker::subDeviceID, symbol.unresolvedRelocation.symbolName);
}
auto gpuAddressAs64bit = pDevice->getDefaultEngine().commandStreamReceiver->getWorkPartitionAllocationGpuAddress();
EXPECT_EQ(*reinterpret_cast<uint32_t *>(&instructionSegment[64]), static_cast<uint32_t>((gpuAddressAs64bit >> 32) & 0xffffffff));
EXPECT_EQ(*reinterpret_cast<uint32_t *>(instructionSegment.data()), static_cast<uint32_t>(gpuAddressAs64bit & 0xffffffff));
}
HWTEST_F(LinkerTests, givenUnresolvedExternalSymbolsWhenResolveBuiltinsIsCalledThenPerThreadOffSymbolsAreResolvedAndRemoved) {
struct LinkerMock : public NEO::Linker {
public:
using NEO::Linker::Linker;
using NEO::Linker::resolveBuiltins;
};
const uint64_t kernel1RelocOffset = 40;
const uint64_t kernel2RelocOffset = 0;
NEO::LinkerInput linkerInput;
LinkerMock linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
unresolvedExternals.push_back({{"__INTEL_PER_THREAD_OFF", 0, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions, ".text.kernel_func2"}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 156, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 140, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__INTEL_PER_THREAD_OFF", kernel1RelocOffset, static_cast<NEO::Linker::RelocationInfo::Type>(7u), NEO::SegmentType::instructions, ".text.kernel_func1"}, 0u, false});
std::vector<char> instructionSegment;
instructionSegment.resize(kernel1RelocOffset + 16);
NEO::Linker::PatchableSegments instructionsSegments;
instructionsSegments.push_back({instructionSegment.data(), 0u});
KernelDescriptor kernelDescriptor1;
kernelDescriptor1.kernelMetadata.kernelName = "kernel_func1";
kernelDescriptor1.kernelAttributes.crossThreadDataSize = 96;
kernelDescriptor1.kernelAttributes.inlineDataPayloadSize = 64;
KernelDescriptor kernelDescriptor2;
kernelDescriptor2.kernelMetadata.kernelName = "kernel_func2";
kernelDescriptor2.kernelAttributes.crossThreadDataSize = 192;
kernelDescriptor2.kernelAttributes.inlineDataPayloadSize = 64;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
kernelDescriptors.push_back(&kernelDescriptor1);
kernelDescriptors.push_back(&kernelDescriptor2);
linker.resolveBuiltins(pDevice, unresolvedExternals, instructionsSegments, kernelDescriptors);
EXPECT_EQ(2U, unresolvedExternals.size());
for (auto &symbol : unresolvedExternals) {
EXPECT_NE(NEO::Linker::perThreadOff, symbol.unresolvedRelocation.symbolName);
}
uint16_t gpuAddress1 = kernelDescriptor1.kernelAttributes.crossThreadDataSize -
kernelDescriptor1.kernelAttributes.inlineDataPayloadSize;
uint16_t gpuAddress2 = kernelDescriptor2.kernelAttributes.crossThreadDataSize -
kernelDescriptor2.kernelAttributes.inlineDataPayloadSize;
EXPECT_EQ(*reinterpret_cast<uint16_t *>(&instructionSegment[kernel1RelocOffset]), static_cast<uint16_t>(gpuAddress1));
EXPECT_EQ(*reinterpret_cast<uint32_t *>(&instructionSegment[kernel2RelocOffset]), static_cast<uint32_t>(gpuAddress2 & 0xffffffff));
}
HWTEST_F(LinkerTests, givenPerThreadOffSymbolInUnresolvedExternalSymbolsAndMissingKernelDescriptorForPerThreadOffSymbolWhenResolveBuiltinsThenPerThreadOffSymbolIsNotResolved) {
struct LinkerMock : public NEO::Linker {
public:
using NEO::Linker::Linker;
using NEO::Linker::resolveBuiltins;
};
const uint64_t kernelRelocOffset = 40;
NEO::LinkerInput linkerInput;
LinkerMock linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 156, NEO::Linker::RelocationInfo::Type::addressLow, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__MaxHWThreadIDPerSubDevice", 140, NEO::Linker::RelocationInfo::Type::addressHigh, NEO::SegmentType::instructions}, 0u, false});
unresolvedExternals.push_back({{"__INTEL_PER_THREAD_OFF", kernelRelocOffset, NEO::Linker::RelocationInfo::Type::address16, NEO::SegmentType::instructions, ".text.kernel_func"}, 0u, false});
std::vector<char> instructionSegment;
instructionSegment.resize(64);
NEO::Linker::PatchableSegments instructionsSegments;
instructionsSegments.push_back({instructionSegment.data(), 0u});
KernelDescriptor kernelDescriptor;
kernelDescriptor.kernelMetadata.kernelName = "kernel_name";
kernelDescriptor.kernelAttributes.crossThreadDataSize = 96;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
kernelDescriptors.push_back(&kernelDescriptor);
linker.resolveBuiltins(pDevice, unresolvedExternals, instructionsSegments, kernelDescriptors);
EXPECT_EQ(3U, unresolvedExternals.size());
bool isPerThreadOffUnresolved = false;
for (auto &symbol : unresolvedExternals) {
if (NEO::Linker::perThreadOff == symbol.unresolvedRelocation.symbolName) {
isPerThreadOffUnresolved = true;
}
}
EXPECT_TRUE(isPerThreadOffUnresolved);
}
HWTEST_F(LinkerTests, givenUnresolvedExternalSymbolsAndCrossThreadDataSmallerThanInlineDataWhenResolveBuiltinsIsCalledThenPerThreadOffSymbolIsResolvedAndRemoved) {
struct LinkerMock : public NEO::Linker {
public:
using NEO::Linker::Linker;
using NEO::Linker::resolveBuiltins;
};
const uint64_t kernelRelocOffset = 40u;
NEO::LinkerInput linkerInput{};
LinkerMock linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals{};
unresolvedExternals.push_back({{std::string(NEO::Linker::perThreadOff), kernelRelocOffset, NEO::Linker::RelocationInfo::Type::address16, NEO::SegmentType::instructions, ".text.kernel_func1"}, 0u, false});
std::vector<char> instructionSegment{};
instructionSegment.resize(kernelRelocOffset + 16);
NEO::Linker::PatchableSegments instructionsSegments{};
instructionsSegments.push_back({instructionSegment.data(), 0u});
KernelDescriptor kernelDescriptor1{};
kernelDescriptor1.kernelMetadata.kernelName = "kernel_func1";
kernelDescriptor1.kernelAttributes.crossThreadDataSize = 40u;
kernelDescriptor1.kernelAttributes.inlineDataPayloadSize = 64u;
NEO::Linker::KernelDescriptorsT kernelDescriptors{};
kernelDescriptors.push_back(&kernelDescriptor1);
linker.resolveBuiltins(pDevice, unresolvedExternals, instructionsSegments, kernelDescriptors);
EXPECT_EQ(0U, unresolvedExternals.size());
uint16_t gpuAddress = 0u;
EXPECT_EQ(*reinterpret_cast<uint16_t *>(&instructionSegment[kernelRelocOffset]), gpuAddress);
}
HWTEST_F(LinkerTests, givenUnresolvedExternalWhenPatchingInstructionsThenLinkPartially) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_SYM_ADDR;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 0);
EXPECT_TRUE(decodeResult);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedPartially, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
ASSERT_EQ(1U, unresolvedExternals.size());
EXPECT_EQ(0U, unresolvedExternals[0].instructionsSegmentId);
EXPECT_FALSE(unresolvedExternals[0].internalError);
EXPECT_EQ(entry.r_offset, unresolvedExternals[0].unresolvedRelocation.offset);
EXPECT_EQ(std::string(entry.r_symbol), std::string(unresolvedExternals[0].unresolvedRelocation.symbolName));
}
HWTEST_F(LinkerTests, givenValidSymbolsAndRelocationsThenInstructionSegmentsAreProperlyPatched) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 4;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
vISA::GenSymEntry symGlobalConstant = {};
symGlobalConstant.s_name[0] = 'B';
symGlobalConstant.s_offset = 20;
symGlobalConstant.s_size = 8;
symGlobalConstant.s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
decodeSymSuccess = decodeSymSuccess && linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalConstant, 1);
vISA::GenSymEntry symExportedFunc = {};
symExportedFunc.s_name[0] = 'C';
symExportedFunc.s_offset = 16;
symExportedFunc.s_size = 32;
symExportedFunc.s_type = vISA::GenSymType::S_FUNC;
decodeSymSuccess = decodeSymSuccess && linkerInput.decodeExportedFunctionsSymbolTable(&symExportedFunc, 1, 0);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 0;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocB = {};
relocB.r_symbol[0] = 'B';
relocB.r_offset = 8;
relocB.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocC = {};
relocC.r_symbol[0] = 'C';
relocC.r_offset = 16;
relocC.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocCPartHigh = {};
relocCPartHigh.r_symbol[0] = 'C';
relocCPartHigh.r_offset = 28;
relocCPartHigh.r_type = vISA::GenRelocType::R_SYM_ADDR_32_HI;
vISA::GenRelocEntry relocCPartLow = {};
relocCPartLow.r_symbol[0] = 'C';
relocCPartLow.r_offset = 36;
relocCPartLow.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry relocPerThreadPayloadOffset = {};
relocPerThreadPayloadOffset.r_symbol[0] = 'X';
relocPerThreadPayloadOffset.r_offset = 44;
relocPerThreadPayloadOffset.r_type = vISA::GenRelocType::R_PER_THREAD_PAYLOAD_OFFSET_32;
vISA::GenRelocEntry relocs[] = {relocA, relocB, relocC, relocCPartHigh, relocCPartLow, relocPerThreadPayloadOffset};
constexpr uint32_t numRelocations = sizeof(relocs) / sizeof(relocs[0]);
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 1);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 128;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> exportedFuncSegmentData(exportedFuncSegment.segmentSize, 0);
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(64, static_cast<char>(initData));
NEO::Linker::PatchableSegments patchableInstructionSegments(2);
patchableInstructionSegments[0].hostPointer = exportedFuncSegmentData.data();
patchableInstructionSegments[0].gpuAddress = exportedFuncSegment.gpuAddress;
patchableInstructionSegments[0].segmentSize = exportedFuncSegment.segmentSize;
patchableInstructionSegments[1].hostPointer = instructionSegment.data();
patchableInstructionSegments[1].gpuAddress = 0x0;
patchableInstructionSegments[1].segmentSize = instructionSegment.size();
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
kernelDescriptors.resize(2);
KernelDescriptor dummyKernelDescriptor;
KernelDescriptor kd;
kd.kernelAttributes.crossThreadDataSize = 0x20;
kernelDescriptors[0] = &dummyKernelDescriptor;
kernelDescriptors[1] = &kd;
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments, unresolvedExternals,
pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(3U, relocatedSymbols.size());
ASSERT_EQ(1U, relocatedSymbols.count(symGlobalVariable.s_name));
ASSERT_EQ(1U, relocatedSymbols.count(symGlobalConstant.s_name));
ASSERT_EQ(1U, relocatedSymbols.count(symExportedFunc.s_name));
EXPECT_EQ(relocatedSymbols[symGlobalVariable.s_name].gpuAddress, globalVarSegment.gpuAddress + symGlobalVariable.s_offset);
EXPECT_EQ(relocatedSymbols[symGlobalConstant.s_name].gpuAddress, globalConstSegment.gpuAddress + symGlobalConstant.s_offset);
EXPECT_EQ(relocatedSymbols[symExportedFunc.s_name].gpuAddress, exportedFuncSegment.gpuAddress + symExportedFunc.s_offset);
EXPECT_EQ(relocatedSymbols[symGlobalVariable.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocA.r_offset));
EXPECT_EQ(relocatedSymbols[symGlobalConstant.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocB.r_offset));
EXPECT_EQ(relocatedSymbols[symExportedFunc.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocC.r_offset));
auto funcGpuAddressAs64bit = static_cast<uint64_t>(relocatedSymbols[symExportedFunc.s_name].gpuAddress);
auto funcAddressLow = static_cast<uint32_t>(funcGpuAddressAs64bit & 0xffffffff);
auto funcAddressHigh = static_cast<uint32_t>((funcGpuAddressAs64bit >> 32) & 0xffffffff);
EXPECT_EQ(funcAddressLow, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartLow.r_offset));
EXPECT_EQ(initData, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartLow.r_offset - sizeof(uint32_t)));
EXPECT_EQ(initData, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartLow.r_offset + sizeof(uint32_t)));
EXPECT_EQ(funcAddressHigh, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartHigh.r_offset));
EXPECT_EQ(initData, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartHigh.r_offset - sizeof(uint32_t)));
EXPECT_EQ(initData, *reinterpret_cast<const uint32_t *>(instructionSegment.data() + relocCPartHigh.r_offset + sizeof(uint32_t)));
auto perThreadPayloadOffsetPatchedValue = *reinterpret_cast<uint32_t *>(instructionSegment.data() + relocPerThreadPayloadOffset.r_offset);
EXPECT_EQ(kd.kernelAttributes.crossThreadDataSize, perThreadPayloadOffsetPatchedValue);
}
HWTEST_F(LinkerTests, givenInvalidSymbolOffsetWhenPatchingInstructionsThenRelocationFails) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 64;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = symGlobalVariable.s_offset;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::error, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
globalVarSegment.segmentSize = symGlobalVariable.s_offset + symGlobalVariable.s_size;
linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments, unresolvedExternals,
pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
}
HWTEST_F(LinkerTests, givenInvalidRelocationOffsetThenPatchingOfInstructionsFails) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 64;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 32;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocA, 1, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = symGlobalVariable.s_offset + symGlobalVariable.s_size;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(relocA.r_offset + sizeof(uint64_t), 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = relocA.r_offset;
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedPartially, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(1U, relocatedSymbols.size());
ASSERT_EQ(1U, unresolvedExternals.size());
EXPECT_TRUE(unresolvedExternals[0].internalError);
patchableInstructionSegments[0].segmentSize = relocA.r_offset + sizeof(uint64_t);
linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
}
HWTEST_F(LinkerTests, givenUnknownSymbolTypeWhenPatchingInstructionsThenRelocationFails) {
WhiteBox<NEO::LinkerInput> linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 0;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 0;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocA, 1, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
ASSERT_EQ(1U, linkerInput.symbols.count("A"));
linkerInput.symbols["A"].segment = NEO::SegmentType::unknown;
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::error, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
ASSERT_EQ(0U, unresolvedExternals.size());
linkerInput.symbols["A"].segment = NEO::SegmentType::globalVariables;
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
}
HWTEST_F(LinkerTests, givenValidStringSymbolsAndRelocationsWhenPatchingThenItIsProperlyPatched) {
NEO::Linker::SegmentInfo stringSegment;
stringSegment.gpuAddress = 0x1234;
stringSegment.segmentSize = 0x10;
uint8_t instructionSegment[32] = {};
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment;
seg0.segmentSize = sizeof(instructionSegment);
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
WhiteBox<NEO::LinkerInput> linkerInput;
SymbolInfo strSymbol;
strSymbol.segment = SegmentType::globalStrings;
strSymbol.offset = 0U;
strSymbol.size = 8U;
linkerInput.symbols.insert({".str", strSymbol});
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0x8U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = ".str";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.textRelocations.push_back({relocation});
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
{}, {}, {}, stringSegment,
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_TRUE(linker.extractRelocatedSymbols().empty());
uintptr_t strAddr = static_cast<uintptr_t>(stringSegment.gpuAddress) + static_cast<uintptr_t>(strSymbol.offset);
uintptr_t patchAddr = reinterpret_cast<uintptr_t>(instructionSegment) + static_cast<uintptr_t>(relocation.offset);
EXPECT_EQ(static_cast<size_t>(strAddr), *reinterpret_cast<const size_t *>(patchAddr));
}
TEST_F(LinkerTests, givenValidStringSymbolsAndRelocationsWithWordMisalignedOffsetWhenPatchingThenItIsProperlyPatched) {
NEO::Linker::SegmentInfo stringSegment;
stringSegment.gpuAddress = 0x1234000000567800;
stringSegment.segmentSize = 0x20;
uint8_t instructionSegment[32] = {};
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment;
seg0.segmentSize = sizeof(instructionSegment);
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0, seg0, seg0, seg0};
WhiteBox<NEO::LinkerInput> linkerInput;
SymbolInfo strSymbol;
strSymbol.segment = SegmentType::globalStrings;
strSymbol.offset = 0U;
strSymbol.size = 8U;
linkerInput.symbols.insert({".str8", strSymbol});
strSymbol.offset = 8U;
strSymbol.size = 4U;
linkerInput.symbols.insert({".str4H", strSymbol});
strSymbol.offset = 12U;
strSymbol.size = 4U;
linkerInput.symbols.insert({".str4L", strSymbol});
strSymbol.offset = 16U;
strSymbol.size = 2U;
linkerInput.symbols.insert({".str2", strSymbol});
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 1U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = ".str8";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.textRelocations.push_back({relocation});
relocation.offset = 9U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = ".str4H";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::addressHigh;
linkerInput.textRelocations.push_back({relocation});
relocation.offset = 13U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = ".str4L";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::addressLow;
linkerInput.textRelocations.push_back({relocation});
relocation.offset = 17U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = ".str2";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address16;
linkerInput.textRelocations.push_back({relocation});
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
{}, {}, {}, stringSegment,
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_TRUE(linker.extractRelocatedSymbols().empty());
uint64_t str8Addr = static_cast<uint64_t>(stringSegment.gpuAddress);
uint32_t str4HAddr = static_cast<uint32_t>((stringSegment.gpuAddress + 8u) >> 32);
uint32_t str4LAddr = static_cast<uint32_t>((stringSegment.gpuAddress + 12u) & 0xffffffff);
uint16_t str2Addr = static_cast<uint16_t>((stringSegment.gpuAddress + 16u) & 0xffff);
EXPECT_NE(0u, str8Addr);
EXPECT_NE(0u, str4HAddr);
EXPECT_NE(0u, str4LAddr);
EXPECT_NE(0u, str2Addr);
auto patchAddrStr8 = ptrOffset(instructionSegment, 1u);
auto patchAddrStr4H = ptrOffset(instructionSegment, 9u);
auto patchAddrStr4L = ptrOffset(instructionSegment, 13u);
auto patchAddrStr2 = ptrOffset(instructionSegment, 17u);
EXPECT_EQ(0, memcmp(patchAddrStr8, &str8Addr, 8));
EXPECT_EQ(0, memcmp(patchAddrStr4H, &str4HAddr, 4));
EXPECT_EQ(0, memcmp(patchAddrStr4L, &str4LAddr, 4));
EXPECT_EQ(0, memcmp(patchAddrStr2, &str2Addr, 2));
}
HWTEST_F(LinkerTests, givenValidSymbolsAndRelocationsWhenPatchingDataSegmentsThenTheyAreProperlyPatched) {
uint64_t initGlobalConstantData[3];
initGlobalConstantData[0] = 0x10; // var1 address will be added here
initGlobalConstantData[1] = 0x1234; // <- const1
initGlobalConstantData[2] = 0x0; // fun1 address will be added here
uint64_t initGlobalVariablesData[3];
initGlobalVariablesData[0] = 0x20; // const1 address will be added here
initGlobalVariablesData[1] = 0x4321; // <- var1
initGlobalVariablesData[2] = 0x0; // fun2 address will be added here
uint64_t exportedFunctionsInit[2];
exportedFunctionsInit[0] = 0x12; // <- fun1
exportedFunctionsInit[1] = 0x34; // <- fun2
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
NEO::MockGraphicsAllocation exportedFunctionsMockGA{&exportedFunctionsInit, sizeof(exportedFunctionsInit)};
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo, exportedFunctionsSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
exportedFunctionsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(exportedFunctionsMockGA.getUnderlyingBuffer());
exportedFunctionsSegmentInfo.segmentSize = exportedFunctionsMockGA.getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
auto &fun1 = linkerInput.symbols["fun1"];
fun1.global = true;
fun1.segment = SegmentType::instructions;
fun1.offset = 0U;
fun1.size = 8U;
auto &fun2 = linkerInput.symbols["fun2"];
fun2.global = true;
fun2.segment = SegmentType::instructions;
fun2.offset = 8U;
fun2.size = 8U;
auto &var1 = linkerInput.symbols["var1"];
var1.global = true;
var1.segment = SegmentType::globalVariables;
var1.offset = 8U;
var1.size = 8U;
auto &const1 = linkerInput.symbols["const1"];
const1.global = true;
const1.segment = SegmentType::globalConstants;
const1.offset = 8U;
const1.size = 8U;
/*
Segments:
Const:
0x00 0x10
0x08 0x1234 <- const1
0x10 0x0
Var:
0x00 0x20
0x08 0x4321 <- var1
0x10 0x0
ExportFun:
0x00 0x12 <- fun1
0x08 0x34 <- fun2
After patching:
Const:
0x00 0x10 + &var1
0x08 0x1234
0x10 0x0 + &fun1
Var:
0x00 0x20 + &const1
0x08 0x4321
0x10 0x0 + &fun2
*/
// var1 -> Constant
// *(uint64_t*)(constant + 0) += *(uint64_t*)(var + 8)
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::globalConstants;
relocation.symbolName = "var1";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocation);
}
// const1 -> Var
// *(uint64_t*)(var + 0) += *(uint64_t*)(const + 8)
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::globalVariables;
relocation.symbolName = "const1";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocation);
}
// fun1 -> Const
// *(uint64_t*)(const + 0x10) += *(uint64_t*)(export_fun + 0)
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0x10U;
relocation.relocationSegment = NEO::SegmentType::globalConstants;
relocation.symbolName = "fun1";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocation);
}
if (LinkerInput::Traits::PointerSize::Ptr64bit == linkerInput.getTraits().pointerSize) {
// fun2_LO -> var
// *(uint32_t*)(var + 0x10) += *(uint32_t*)((export_fun + 8) & 0xffffffff)
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0x10U;
relocation.relocationSegment = NEO::SegmentType::globalVariables;
relocation.symbolName = "fun2";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::addressLow;
linkerInput.dataRelocations.push_back(relocation);
}
// fun2_HI -> var
// *(uint32_t*)(var + 0x14) += *(uint32_t*)(((export_fun + 8) >> 32) & 0xffffffff)
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0x14U;
relocation.relocationSegment = NEO::SegmentType::globalVariables;
relocation.symbolName = "fun2";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::addressHigh;
linkerInput.dataRelocations.push_back(relocation);
}
}
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, exportedFunctionsSegmentInfo, {},
globalVariablesPatchableSegment.get(), globalConstantsPatchableSegment.get(), {},
unresolvedExternals, pDevice, initGlobalConstantData, sizeof(initGlobalConstantData),
initGlobalVariablesData, sizeof(initGlobalVariablesData), kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
auto constantsAddr = globalConstantsSegmentInfo.gpuAddress;
auto varsAddr = globalVariablesSegmentInfo.gpuAddress;
auto exportFunAddr = exportedFunctionsSegmentInfo.gpuAddress;
auto const1Addr = constantsAddr + const1.offset;
auto var1Addr = varsAddr + var1.offset;
auto fun1Addr = exportFunAddr + fun1.offset;
auto fun2Addr = exportFunAddr + fun2.offset;
if (LinkerInput::Traits::PointerSize::Ptr64bit == linkerInput.getTraits().pointerSize) {
EXPECT_EQ(0x10U + var1Addr, *(uint64_t *)(constantsAddr));
EXPECT_EQ(0x1234U, *(uint64_t *)(constantsAddr + 0x8));
EXPECT_EQ(fun1Addr, *(uint64_t *)(constantsAddr + 0x10));
EXPECT_EQ(0x20U + const1Addr, *(uint64_t *)(varsAddr));
EXPECT_EQ(0x4321U, *(uint64_t *)(varsAddr + 0x8));
EXPECT_EQ(fun2Addr, *(uint64_t *)(varsAddr + 0x10));
} else if (LinkerInput::Traits::PointerSize::Ptr32bit == linkerInput.getTraits().pointerSize) {
EXPECT_EQ(0x10U + var1Addr, *(uint32_t *)(constantsAddr));
EXPECT_EQ(0x1234U, *(uint32_t *)(constantsAddr + 0x8));
EXPECT_EQ(fun1Addr, *(uint32_t *)(constantsAddr + 0x10));
EXPECT_EQ(0x20U + const1Addr, *(uint32_t *)(varsAddr));
}
}
HWTEST_F(LinkerTests, givenValidSymbolsAndRelocationsToBssDataSectionsWhenPatchingDataSegmentsThenTheyAreProperlyPatched) {
uint64_t initGlobalConstantData[] = {0x1234}; //<- const1 - initValue should be ignored
uint64_t initGlobalVariablesData[] = {0x4321}; // <- var1 - initValue should be ignored
uint64_t constantsSegmentData[2]{0}; // size 2 * uint64_t - contains also bss at the end
uint64_t globalVariablesSegmentData[2]{0}; // size 2 * uint64_t - contains also bss at the end
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{constantsSegmentData, sizeof(constantsSegmentData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{globalVariablesSegmentData, sizeof(globalVariablesSegmentData)};
globalConstantsPatchableSegmentMockGA.gpuAddress = 0xA0000000;
globalVariablesPatchableSegmentMockGA.gpuAddress = 0xB0000000;
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = static_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getGpuAddress());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = static_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getGpuAddress());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
auto setUpInstructionSeg = [](std::vector<uint8_t> &instrData, NEO::Linker::PatchableSegments &patchableInstrSeg) -> void {
uint64_t initData = 0x77777777;
instrData.resize(8, static_cast<uint8_t>(initData));
auto &emplaced = patchableInstrSeg.emplace_back();
emplaced.hostPointer = instrData.data();
emplaced.segmentSize = instrData.size();
};
NEO::Linker::PatchableSegments patchableInstructionSegments;
std::vector<uint8_t> instructionsData1, instructionsData2;
setUpInstructionSeg(instructionsData1, patchableInstructionSegments);
setUpInstructionSeg(instructionsData2, patchableInstructionSegments);
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
auto &var1 = linkerInput.symbols["var1"];
var1.segment = SegmentType::globalVariables;
var1.offset = 0U;
var1.size = 8U;
auto &bssVar = linkerInput.symbols["bssVar"];
bssVar.segment = SegmentType::globalVariablesZeroInit;
bssVar.offset = 0U;
bssVar.size = 8U;
auto &const1 = linkerInput.symbols["const1"];
const1.segment = SegmentType::globalConstants;
const1.offset = 0U;
const1.size = 8U;
auto &bssConst = linkerInput.symbols["bssConst"];
bssConst.segment = SegmentType::globalConstantsZeroInit;
bssConst.offset = 0U;
bssConst.size = 8U;
/*
Segments:
Const:
0x00 0x1000 <- const 1
0x08 0x0 <- bss
Var:
0x00 0x4000 <- var 1
0x08 0x0 <- bss
Instructions:
0x0 0x0 <- will be patched with bss.const
0x08 0x0 <- will be patched with bss.global
1. Patch bss data from const segment with var 1
Patch bss data from global variables segment with const 1
2. Patch const 2 with symbol pointing to bss in const (patched in step 1).
Patch var 2 with symbol pointing to bss in variables (patched in step 1).
*/
// Relocations for step 1.
// bssConst[0] = &var1
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::globalConstantsZeroInit;
relocation.symbolName = "var1";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocation);
}
// bssGlobal[0] = &const1
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::globalVariablesZeroInit;
relocation.symbolName = "const1";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocation);
}
// Relocation for step 2.
// instructions[0] = &bssConst
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = "bssConst";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.textRelocations.push_back({relocation});
}
// instructions[1] = &bssVar
{
NEO::LinkerInput::RelocationInfo relocation;
relocation.offset = 0U;
relocation.relocationSegment = NEO::SegmentType::instructions;
relocation.symbolName = "bssVar";
relocation.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.textRelocations.push_back({relocation});
}
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
globalVariablesPatchableSegment.get(), globalConstantsPatchableSegment.get(), patchableInstructionSegments,
unresolvedExternals, pDevice, initGlobalConstantData, sizeof(initGlobalConstantData),
initGlobalVariablesData, sizeof(initGlobalVariablesData), kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
auto globalConstantsSegmentAddr = reinterpret_cast<uint64_t *>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
auto globalVariableSegmentAddr = reinterpret_cast<uint64_t *>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
auto var1Addr = globalVariablesPatchableSegment->getGraphicsAllocation()->getGpuAddress();
auto const1Addr = globalConstantsPatchableSegment->getGraphicsAllocation()->getGpuAddress();
auto bssConstAddrr = globalConstantsPatchableSegment->getGraphicsAllocation()->getGpuAddress() + sizeof(initGlobalConstantData);
auto bssVarAddr = globalVariablesPatchableSegment->getGraphicsAllocation()->getGpuAddress() + sizeof(initGlobalVariablesData);
EXPECT_EQ(var1Addr, *(globalConstantsSegmentAddr + 1));
EXPECT_EQ(const1Addr, *(globalVariableSegmentAddr + 1));
EXPECT_EQ(bssConstAddrr, *(reinterpret_cast<uint64_t *>(instructionsData1.data())));
EXPECT_EQ(bssVarAddr, *(reinterpret_cast<uint64_t *>(instructionsData2.data())));
}
HWTEST_F(LinkerTests, givenInvalidSymbolWhenPatchingDataSegmentsThenRelocationIsUnresolved) {
uint64_t initGlobalConstantData[3] = {};
uint64_t initGlobalVariablesData[3] = {};
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = 0U;
relocationInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocationInfo.symbolName = "symbol";
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocationInfo);
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
globalVariablesPatchableSegment.get(), globalConstantsPatchableSegment.get(), {},
unresolvedExternals, pDevice, initGlobalConstantData, sizeof(initGlobalConstantData), initGlobalVariablesData,
sizeof(initGlobalVariablesData), kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
HWTEST_F(LinkerTests, givenInvalidRelocationOffsetWhenPatchingDataSegmentsThenRelocationIsUnresolved) {
uint64_t initGlobalConstantData[3] = {};
uint64_t initGlobalVariablesData[3] = {};
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
auto &symbol = linkerInput.symbols["symbol"];
symbol.segment = SegmentType::globalVariables;
symbol.offset = 0U;
symbol.size = 8U;
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = globalConstantsSegmentInfo.segmentSize + 1U;
relocationInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocationInfo.symbolName = "symbol";
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocationInfo);
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
globalVariablesPatchableSegment.get(), globalConstantsPatchableSegment.get(), {},
unresolvedExternals, pDevice, initGlobalConstantData, sizeof(initGlobalConstantData),
initGlobalVariablesData, sizeof(initGlobalVariablesData), kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
HWTEST_F(LinkerTests, givenInvalidRelocationSegmentWhenPatchingDataSegmentsThenRelocationIsUnresolved) {
WhiteBox<NEO::LinkerInput> linkerInput;
auto &symbol = linkerInput.symbols["symbol"];
symbol.segment = SegmentType::globalVariables;
symbol.offset = 0U;
symbol.size = 8U;
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = 0U;
relocationInfo.relocationSegment = NEO::SegmentType::unknown;
relocationInfo.symbolName = "symbol";
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocationInfo);
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::SegmentInfo globalSegment;
globalSegment.segmentSize = 8u;
auto linkResult = linker.link(globalSegment, {}, {}, {},
nullptr, nullptr, {},
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
HWTEST_F(LinkerTests, given32BitBinaryWithValidSymbolsAndRelocationsWhenPatchingDataSegmentsThenTreatAddressRelocationAsLowerHalfOfTheAddress) {
uint64_t initGlobalConstantData[3] = {};
uint64_t initGlobalVariablesData[3] = {};
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.setPointerSize(NEO::LinkerInput::Traits::PointerSize::Ptr32bit);
auto &fun1 = linkerInput.symbols["symbol"];
fun1.segment = SegmentType::globalVariables;
fun1.offset = 0U;
fun1.size = 8U;
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = 0U;
relocationInfo.relocationSegment = NEO::SegmentType::globalConstants;
relocationInfo.symbolName = "symbol";
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
linkerInput.dataRelocations.push_back(relocationInfo);
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
globalVariablesPatchableSegment.get(), globalConstantsPatchableSegment.get(), {},
unresolvedExternals, pDevice, initGlobalConstantData, sizeof(initGlobalConstantData), initGlobalVariablesData,
sizeof(initGlobalVariablesData), kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(globalVariablesSegmentInfo.gpuAddress & 0xffffffff, *(uint32_t *)(globalConstantsSegmentInfo.gpuAddress));
EXPECT_EQ(0U, *(uint32_t *)(globalConstantsSegmentInfo.gpuAddress + 4));
}
TEST(LinkerErrorMessageTests, whenListOfUnresolvedExternalsIsEmptyThenErrorTypeDefaultsToInternalError) {
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<std::string> segmentsNames{"kernel1", "kernel2"};
auto err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_EQ(std::string("Internal linker error"), err);
}
TEST(LinkerErrorMessageTests, givenListOfUnresolvedExternalsThenSymbolNameOrSymbolSegmentTypeGetsEmbededInErrorMessage) {
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<std::string> segmentsNames{"kernel1", "kernel2"};
NEO::Linker::UnresolvedExternal unresolvedExternal = {};
unresolvedExternal.instructionsSegmentId = 1;
unresolvedExternal.internalError = false;
unresolvedExternal.unresolvedRelocation.offset = 64;
unresolvedExternal.unresolvedRelocation.symbolName = "arrayABC";
unresolvedExternal.unresolvedRelocation.relocationSegment = NEO::SegmentType::instructions;
unresolvedExternals.push_back(unresolvedExternal);
auto err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_TRUE(hasSubstr(err, unresolvedExternal.unresolvedRelocation.symbolName));
EXPECT_TRUE(hasSubstr(err, segmentsNames[unresolvedExternal.instructionsSegmentId]));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
EXPECT_FALSE(hasSubstr(err, "internal error"));
unresolvedExternals[0].internalError = true;
err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_TRUE(hasSubstr(err, unresolvedExternal.unresolvedRelocation.symbolName));
EXPECT_TRUE(hasSubstr(err, segmentsNames[unresolvedExternal.instructionsSegmentId]));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
EXPECT_TRUE(hasSubstr(err, "internal linker error"));
err = NEO::constructLinkerErrorMessage(unresolvedExternals, {});
EXPECT_TRUE(hasSubstr(err, unresolvedExternal.unresolvedRelocation.symbolName));
EXPECT_FALSE(hasSubstr(err, segmentsNames[unresolvedExternal.instructionsSegmentId]));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
EXPECT_TRUE(hasSubstr(err, "internal linker error"));
unresolvedExternals[0].unresolvedRelocation.relocationSegment = NEO::SegmentType::globalConstants;
err = NEO::constructLinkerErrorMessage(unresolvedExternals, {});
EXPECT_TRUE(hasSubstr(err, NEO::asString(NEO::SegmentType::globalConstants)));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
unresolvedExternals[0].unresolvedRelocation.relocationSegment = NEO::SegmentType::globalVariables;
err = NEO::constructLinkerErrorMessage(unresolvedExternals, {});
EXPECT_TRUE(hasSubstr(err, NEO::asString(NEO::SegmentType::globalVariables)));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
unresolvedExternals[0].unresolvedRelocation.relocationSegment = NEO::SegmentType::unknown;
err = NEO::constructLinkerErrorMessage(unresolvedExternals, {});
EXPECT_TRUE(hasSubstr(err, NEO::asString(NEO::SegmentType::unknown)));
EXPECT_TRUE(hasSubstr(err, std::to_string(unresolvedExternal.unresolvedRelocation.offset)));
}
TEST(RelocationsDebugMessageTests, givenEmptyListOfRelocatedSymbolsThenReturnsEmptyString) {
auto message = NEO::constructRelocationsDebugMessage({});
EXPECT_EQ(0U, message.size()) << message;
}
TEST(RelocationsDebugMessageTests, givenListOfRelocatedSymbolsThenReturnProperDebugMessage) {
NEO::Linker::RelocatedSymbolsMap symbols;
auto &funcSymbol = symbols["foo"];
auto &constDataSymbol = symbols["constInt"];
auto &globalVarSymbol = symbols["intX"];
funcSymbol.symbol.segment = NEO::SegmentType::instructions;
funcSymbol.symbol.offset = 64U;
funcSymbol.symbol.size = 1024U;
funcSymbol.gpuAddress = 4096U;
constDataSymbol.symbol.segment = NEO::SegmentType::globalConstants;
constDataSymbol.symbol.offset = 32U;
constDataSymbol.symbol.size = 16U;
constDataSymbol.gpuAddress = 8U;
globalVarSymbol.symbol.segment = NEO::SegmentType::globalVariables;
globalVarSymbol.symbol.offset = 72U;
globalVarSymbol.symbol.size = 8U;
globalVarSymbol.gpuAddress = 256U;
auto message = NEO::constructRelocationsDebugMessage(symbols);
std::stringstream expected;
expected << "Relocations debug information :\n";
for (const auto &symbol : symbols) {
if (symbol.first == "foo") {
expected << " * \"foo\" [1024 bytes] INSTRUCTIONS_SEGMENT@64 -> 0x1000 GPUVA\n";
} else if (symbol.first == "constInt") {
expected << " * \"constInt\" [16 bytes] GLOBAL_CONSTANTS_SEGMENT@32 -> 0x8 GPUVA\n";
} else {
expected << " * \"intX\" [8 bytes] GLOBAL_VARIABLES_SEGMENT@72 -> 0x100 GPUVA\n";
}
}
EXPECT_STREQ(expected.str().c_str(), message.c_str());
}
TEST_F(LinkerTests, GivenDebugDataWhenApplyingDebugDataRelocationsThenRelocationsAreAppliedInMemory) {
NEO::Elf::ElfFileHeader<NEO::Elf::EI_CLASS_64> header;
header.shOff = header.ehSize;
header.shNum = 4;
header.shStrNdx = 0;
NEO::Elf::ElfSectionHeader<NEO::Elf::EI_CLASS_64> sectionHeader0;
sectionHeader0.size = 80;
sectionHeader0.offset = 80;
NEO::Elf::ElfSectionHeader<NEO::Elf::EI_CLASS_64> sectionHeader1;
sectionHeader1.size = 80;
sectionHeader1.offset = 160;
NEO::Elf::ElfSectionHeader<NEO::Elf::EI_CLASS_64> sectionHeader2;
sectionHeader2.size = 80;
sectionHeader2.offset = 240;
NEO::Elf::ElfSectionHeader<NEO::Elf::EI_CLASS_64> sectionHeader3;
sectionHeader3.size = 80;
sectionHeader3.offset = 320;
NEO::Elf::ElfSectionHeader<NEO::Elf::EI_CLASS_64> sectionHeader4;
sectionHeader4.size = 80;
sectionHeader4.offset = 400;
MockElf<NEO::Elf::EI_CLASS_64> elf64;
elf64.elfFileHeader = &header;
uint64_t dummyData[100];
uint8_t *storage = reinterpret_cast<uint8_t *>(dummyData);
elf64.sectionHeaders.push_back({&sectionHeader0, {&storage[80], 80}});
elf64.sectionHeaders.push_back({&sectionHeader1, {&storage[160], 80}});
elf64.sectionHeaders.push_back({&sectionHeader2, {&storage[240], 80}});
elf64.sectionHeaders.push_back({&sectionHeader3, {&storage[320], 80}});
elf64.sectionHeaders.push_back({&sectionHeader4, {&storage[400], 80}});
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text";
sectionNames[1] = ".data.global";
sectionNames[2] = ".debug_info";
sectionNames[3] = ".debug_abbrev";
sectionNames[4] = ".debug_line";
sectionNames[5] = ".data.const";
elf64.setupSectionNames(std::move(sectionNames));
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc0 = {};
reloc0.offset = 64;
reloc0.relocType = static_cast<uint32_t>(Elf::RelocationX8664Type::relocation64);
reloc0.symbolName = ".debug_abbrev";
reloc0.symbolSectionIndex = 3;
reloc0.symbolTableIndex = 0;
reloc0.targetSectionIndex = 2;
reloc0.addend = 0;
elf64.debugInfoRelocations.emplace_back(reloc0);
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc1 = {};
reloc1.offset = 32;
reloc1.relocType = static_cast<uint32_t>(Elf::RelocationX8664Type::relocation32);
reloc1.symbolName = ".debug_line";
reloc1.symbolSectionIndex = 4;
reloc1.symbolTableIndex = 0;
reloc1.targetSectionIndex = 2;
reloc1.addend = 4;
elf64.debugInfoRelocations.emplace_back(reloc1);
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc2 = {};
reloc2.offset = 32;
reloc2.relocType = static_cast<uint32_t>(Elf::RelocationX8664Type::relocation64);
reloc2.symbolName = ".text";
reloc2.symbolSectionIndex = 0;
reloc2.symbolTableIndex = 0;
reloc2.targetSectionIndex = 4;
reloc2.addend = 18;
elf64.debugInfoRelocations.emplace_back(reloc2);
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc3 = {};
reloc3.offset = 0;
reloc3.relocType = static_cast<uint32_t>(Elf::RelocationX8664Type::relocation64);
reloc3.symbolName = ".data";
reloc3.symbolSectionIndex = 1;
reloc3.symbolTableIndex = 0;
reloc3.targetSectionIndex = 4;
reloc3.addend = 55;
elf64.debugInfoRelocations.emplace_back(reloc3);
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc4 = {};
reloc4.offset = 8;
reloc4.relocType = static_cast<uint32_t>(0);
reloc4.symbolName = ".text";
reloc4.symbolSectionIndex = 0;
reloc4.symbolTableIndex = 0;
reloc4.targetSectionIndex = 4;
reloc4.addend = 77;
elf64.debugInfoRelocations.emplace_back(reloc4);
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc5 = {};
reloc5.offset = 16;
reloc5.relocType = static_cast<uint32_t>(1);
reloc5.symbolName = ".data.const";
reloc5.symbolSectionIndex = 5;
reloc5.symbolTableIndex = 0;
reloc5.targetSectionIndex = 4;
reloc5.addend = 20;
elf64.debugInfoRelocations.emplace_back(reloc5);
uint64_t *relocInDebugLine = reinterpret_cast<uint64_t *>(&storage[400]);
*relocInDebugLine = 0;
uint64_t *relocInDebugLine2 = reinterpret_cast<uint64_t *>(&storage[408]);
*relocInDebugLine2 = 0;
uint64_t *relocInDebugLine5 = reinterpret_cast<uint64_t *>(&storage[416]);
*relocInDebugLine5 = 0;
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
symbol.value = 0;
elf64.symbolTable.push_back(symbol);
NEO::Linker::SegmentInfo text = {static_cast<uintptr_t>(0x80001000), 0x10000};
NEO::Linker::SegmentInfo dataGlobal = {static_cast<uintptr_t>(0x123000), 0x10000};
NEO::Linker::SegmentInfo dataConst = {static_cast<uintptr_t>(0xabc000), 0x10000};
NEO::Linker::applyDebugDataRelocations(elf64, {storage, sizeof(dummyData)}, text, dataGlobal, dataConst);
auto reloc0Location = reinterpret_cast<const uint64_t *>(&elf64.sectionHeaders[reloc0.targetSectionIndex].data[static_cast<size_t>(reloc0.offset)]);
auto reloc1Location = reinterpret_cast<const uint32_t *>(&elf64.sectionHeaders[reloc1.targetSectionIndex].data[static_cast<size_t>(reloc1.offset)]);
auto reloc2Location = reinterpret_cast<const uint64_t *>(&elf64.sectionHeaders[reloc2.targetSectionIndex].data[static_cast<size_t>(reloc2.offset)]);
auto reloc3Location = reinterpret_cast<const uint64_t *>(&elf64.sectionHeaders[reloc3.targetSectionIndex].data[static_cast<size_t>(reloc3.offset)]);
auto reloc4Location = reinterpret_cast<const uint64_t *>(&elf64.sectionHeaders[reloc4.targetSectionIndex].data[static_cast<size_t>(reloc4.offset)]);
auto reloc5Location = reinterpret_cast<const uint64_t *>(&elf64.sectionHeaders[reloc5.targetSectionIndex].data[static_cast<size_t>(reloc5.offset)]);
uint64_t expectedValue0 = reloc0.addend;
uint64_t expectedValue1 = reloc1.addend;
auto expectedValue2 = uint64_t(0x80001000) + reloc2.addend;
uint64_t expectedValue3 = dataGlobal.gpuAddress + reloc3.addend;
uint64_t expectedValue5 = dataConst.gpuAddress + reloc5.addend;
EXPECT_EQ(expectedValue0, *reloc0Location);
EXPECT_EQ(expectedValue1, *reloc1Location);
EXPECT_EQ(expectedValue2, *reloc2Location);
EXPECT_EQ(expectedValue3, *reloc3Location);
EXPECT_EQ(0u, *reloc4Location);
EXPECT_EQ(expectedValue5, *reloc5Location);
}
TEST_F(LinkerTests, givenImplicitArgRelocationAndStackCallsOrRequiredImplicitArgsThenPatchRelocationWithSizeOfImplicitArgStructAndUpdateKernelDescriptor) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry relocs[] = {reloc};
constexpr uint32_t numRelocations = 1;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
UltDeviceFactory deviceFactory{1, 0};
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
auto addressToPatch = reinterpret_cast<uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(ImplicitArgsTestHelper::getImplicitArgsSize(deviceFactory.rootDevices[0]->getGfxCoreHelper().getImplicitArgsVersion()), *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = true;
kernelDescriptor.kernelAttributes.flags.useStackCalls = false;
*addressToPatch = 0;
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
HWTEST_F(LinkerTests, givenImplicitArgRelocationAndKernelDescriptorWithImplicitArgsV1WhenLinkingThenPatchRelocationWithSizeOfImplicitArgsV1) {
DebugManagerStateRestore restore;
struct MockGfxCoreHelper : NEO::GfxCoreHelperHw<FamilyType> {
uint32_t getImplicitArgsVersion() const override {
return 0;
}
};
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry reloc64 = {};
memcpy_s(reloc64.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc64.r_offset = 16;
reloc64.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocs[] = {reloc, reloc64};
constexpr uint32_t numRelocations = 2;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = true;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
kernelDescriptor.kernelMetadata.indirectAccessBuffer = 1;
HardwareInfo hwInfo = *defaultHwInfo;
MockExecutionEnvironment executionEnvironment(&hwInfo, false, 1);
executionEnvironment.incRefInternal();
UltDeviceFactory deviceFactory{1, 0, executionEnvironment};
auto rootDeviceIndex = deviceFactory.rootDevices[0]->getRootDeviceIndex();
RAIIGfxCoreHelperFactory<MockGfxCoreHelper> raii(*deviceFactory.rootDevices[0]->getExecutionEnvironment()->rootDeviceEnvironments[rootDeviceIndex]);
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto addressToPatch = reinterpret_cast<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(ImplicitArgsV1::getAlignedSize(), *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
auto addressToPatch64 = (instructionSegment.data() + reloc64.r_offset);
uint64_t patchedValue64 = 0;
memcpy_s(&patchedValue64, sizeof(patchedValue64), addressToPatch64, sizeof(patchedValue64));
EXPECT_EQ(ImplicitArgsV1::getAlignedSize(), patchedValue64);
}
HWTEST_F(LinkerTests, givenImplicitArgRelocationAndImplicitArgsV1WhenLinkingThenPatchRelocationWithSizeOfImplicitArgsV1) {
DebugManagerStateRestore restore;
struct MockGfxCoreHelper : NEO::GfxCoreHelperHw<FamilyType> {
uint32_t getImplicitArgsVersion() const override {
return 1;
}
};
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry reloc64 = {};
memcpy_s(reloc64.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc64.r_offset = 16;
reloc64.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocs[] = {reloc, reloc64};
constexpr uint32_t numRelocations = 2;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = true;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
HardwareInfo hwInfo = *defaultHwInfo;
MockExecutionEnvironment executionEnvironment(&hwInfo, false, 1);
executionEnvironment.incRefInternal();
UltDeviceFactory deviceFactory{1, 0, executionEnvironment};
auto rootDeviceIndex = deviceFactory.rootDevices[0]->getRootDeviceIndex();
RAIIGfxCoreHelperFactory<MockGfxCoreHelper> raii(*deviceFactory.rootDevices[0]->getExecutionEnvironment()->rootDeviceEnvironments[rootDeviceIndex]);
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto addressToPatch = reinterpret_cast<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(ImplicitArgsV1::getAlignedSize(), *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
auto addressToPatch64 = (instructionSegment.data() + reloc64.r_offset);
uint64_t patchedValue64 = 0;
memcpy_s(&patchedValue64, sizeof(patchedValue64), addressToPatch64, sizeof(patchedValue64));
EXPECT_EQ(ImplicitArgsV1::getAlignedSize(), patchedValue64);
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
HWTEST_F(LinkerTests, givenImplicitArgRelocationAndImplicitArgsWithUnknownVersionWhenLinkingThenUnrecoverableIfCalled) {
DebugManagerStateRestore restore;
struct MockGfxCoreHelper : NEO::GfxCoreHelperHw<FamilyType> {
uint32_t getImplicitArgsVersion() const override {
return 3; // unknown version
}
};
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry relocs[] = {reloc};
constexpr uint32_t numRelocations = 1;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = true;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
HardwareInfo hwInfo = *defaultHwInfo;
MockExecutionEnvironment executionEnvironment(&hwInfo, false, 1);
executionEnvironment.incRefInternal();
UltDeviceFactory deviceFactory{1, 0, executionEnvironment};
auto rootDeviceIndex = deviceFactory.rootDevices[0]->getRootDeviceIndex();
RAIIGfxCoreHelperFactory<MockGfxCoreHelper> raii(*deviceFactory.rootDevices[0]->getExecutionEnvironment()->rootDeviceEnvironments[rootDeviceIndex]);
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
EXPECT_THROW(linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions),
std::exception);
}
using LinkerDebuggingSupportedTests = ::testing::Test;
TEST_F(LinkerDebuggingSupportedTests, givenImplicitArgRelocationAndEnabledDebuggerThenPatchRelocationWithSizeOfImplicitArgStructAndUpdateKernelDescriptor) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry relocs[] = {reloc};
constexpr uint32_t numRelocations = 1;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = false;
NEO::HardwareInfo hwInfo = *NEO::defaultHwInfo;
auto executionEnvironment = MockDevice::prepareExecutionEnvironment(&hwInfo, 0u);
UltDeviceFactory deviceFactory{1, 0, *executionEnvironment};
auto device = deviceFactory.rootDevices[0];
executionEnvironment->rootDeviceEnvironments[0]->initDebuggerL0(device);
EXPECT_NE(nullptr, device->getDebugger());
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
auto addressToPatch = reinterpret_cast<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(ImplicitArgsTestHelper::getImplicitArgsSize(device->getGfxCoreHelper().getImplicitArgsVersion()), *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
TEST_F(LinkerDebuggingSupportedTests, givenNoImplicitArgRelocationAndEnabledDebuggerThenImplicitArgsAreNotRequired) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = false;
NEO::HardwareInfo hwInfo = *NEO::defaultHwInfo;
auto executionEnvironment = MockDevice::prepareExecutionEnvironment(&hwInfo, 0u);
UltDeviceFactory deviceFactory{1, 0, *executionEnvironment};
auto device = deviceFactory.rootDevices[0];
executionEnvironment->rootDeviceEnvironments[0]->initDebuggerL0(device);
EXPECT_NE(nullptr, device->getDebugger());
std::vector<char> instructionSegment;
char initData = 0x77;
instructionSegment.resize(32, initData);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
for (auto &data : instructionSegment) {
EXPECT_EQ(initData, data);
}
EXPECT_FALSE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
TEST_F(LinkerTests, givenImplicitArgRelocationWithoutStackCallsAndDisabledDebuggerThenDontPatchRelocationAndUpdateKernelDescriptor) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc.r_offset = 8;
reloc.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry relocs[] = {reloc};
constexpr uint32_t numRelocations = 1;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = false;
UltDeviceFactory deviceFactory{1, 0};
auto device = deviceFactory.rootDevices[0];
EXPECT_EQ(nullptr, device->getDebugger());
std::vector<char> instructionSegment;
uint32_t initData = 0x77777777;
instructionSegment.resize(32, static_cast<char>(initData));
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
auto addressToPatch = reinterpret_cast<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(initData, *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
EXPECT_FALSE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
TEST_F(LinkerTests, givenNoImplicitArgRelocationAndStackCallsThenImplicitArgsAreNotRequired) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
UltDeviceFactory deviceFactory{1, 0};
std::vector<char> instructionSegment;
char initData = 0x77;
instructionSegment.resize(32, initData);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
for (auto &data : instructionSegment) {
EXPECT_EQ(initData, data);
}
EXPECT_FALSE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
TEST_F(LinkerTests, givenMultipleImplicitArgsRelocationsWithinSingleKernelWhenLinkingThenPatchAllOfThem) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry reloc0 = {};
std::string relocationName{implicitArgsRelocationSymbolName};
memcpy_s(reloc0.r_symbol, 1024, relocationName.c_str(), relocationName.size());
reloc0.r_offset = 8;
reloc0.r_type = vISA::GenRelocType::R_SYM_ADDR_32;
vISA::GenRelocEntry reloc1 = reloc0;
reloc1.r_offset = 24;
vISA::GenRelocEntry relocs[] = {reloc0, reloc1};
constexpr uint32_t numRelocations = 2;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, numRelocations, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
kernelDescriptor.kernelAttributes.flags.useStackCalls = true;
UltDeviceFactory deviceFactory{1, 0};
std::vector<char> instructionSegment;
char initData = 0x77;
instructionSegment.resize(32, initData);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::linkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
for (const auto &reloc : relocs) {
auto addressToPatch = reinterpret_cast<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(ImplicitArgsTestHelper::getImplicitArgsSize(deviceFactory.rootDevices[0]->getGfxCoreHelper().getImplicitArgsVersion()), *addressToPatch);
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
}
HWTEST_F(LinkerTests, givenDependencyOnMissingExternalFunctionWhenLinkingThenFail) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.extFunDependencies.push_back({"fun0", "fun1"});
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions = {{"fun1", 0U, 128U, 8U}};
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(LinkingStatus::error, linkResult);
}
HWTEST_F(LinkerTests, givenDependencyOnMissingExternalFunctionAndNoExternalFunctionInfosWhenLinkingThenDoNotResolveDependenciesAndReturnSuccess) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.extFunDependencies.push_back({"fun0", "fun1"});
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::SharedPoolAllocation *patchableGlobalVarSeg = nullptr;
NEO::SharedPoolAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, pDevice, nullptr, 0, nullptr, 0, kernelDescriptors, externalFunctions);
EXPECT_EQ(LinkingStatus::linkedFully, linkResult);
}
TEST_F(LinkerTests, givenRelaWhenPatchingInstructionsSegmentThenAddendIsAdded) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::LinkerInput::RelocationInfo rela;
rela.offset = 0U;
rela.addend = 128U;
rela.type = NEO::LinkerInput::RelocationInfo::Type::address;
rela.symbolName = "symbol";
rela.relocationSegment = NEO::SegmentType::instructions;
linkerInput.textRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
constexpr uint64_t symValue = 64U;
linker.relocatedSymbols[rela.symbolName].gpuAddress = symValue;
uint64_t segmentData{0};
NEO::Linker::PatchableSegment segmentToPatch;
segmentToPatch.hostPointer = reinterpret_cast<void *>(&segmentData);
segmentToPatch.segmentSize = sizeof(segmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
linker.patchInstructionsSegments({segmentToPatch}, unresolvedExternals, kernelDescriptors);
EXPECT_EQ(static_cast<uint64_t>(rela.addend + symValue), segmentData);
}
HWTEST_F(LinkerTests, givenRelaWhenPatchingDataSegmentThenAddendIsAdded) {
uint64_t globalConstantSegmentData{0U};
NEO::MockGraphicsAllocation globalConstantsPatchableSegmentMockGA{&globalConstantSegmentData, sizeof(globalConstantSegmentData)};
auto globalConstantsPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalConstantsPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalConstantsSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfGlobalConstantsBuffer = true;
NEO::LinkerInput::RelocationInfo rela;
rela.offset = 0U;
rela.addend = 128U;
rela.type = NEO::LinkerInput::RelocationInfo::Type::address;
rela.symbolName = "symbol";
rela.relocationSegment = NEO::SegmentType::globalConstants;
linkerInput.dataRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
constexpr uint64_t symValue = 64U;
linker.relocatedSymbols[rela.symbolName].gpuAddress = symValue;
NEO::Linker::UnresolvedExternals unresolvedExternals;
linker.patchDataSegments({}, globalConstantsSegmentInfo, {}, globalConstantsPatchableSegment.get(), unresolvedExternals, pDevice, &globalConstantSegmentData, sizeof(globalConstantSegmentData), nullptr, 0);
EXPECT_EQ(static_cast<uint64_t>(rela.addend + symValue), globalConstantSegmentData);
}
HWTEST_F(LinkerTests, givenRelocationInfoWhenPatchingDataSegmentWithGlobalVariableSymbolThenAddendIsAdded) {
uint64_t globalVariableSegmentData{0U};
NEO::MockGraphicsAllocation globalVariablesPatchableSegmentMockGA{&globalVariableSegmentData, sizeof(globalVariableSegmentData)};
auto globalVariablesPatchableSegment = std::make_unique<NEO::SharedPoolAllocation>(&globalVariablesPatchableSegmentMockGA);
NEO::Linker::SegmentInfo globalVariablesSegmentInfo;
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment->getGraphicsAllocation()->getUnderlyingBufferSize();
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfGlobalVariablesBuffer = true;
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = 0U;
relocationInfo.addend = 128U;
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::address;
relocationInfo.symbolName = "symbol";
relocationInfo.relocationSegment = NEO::SegmentType::globalVariables;
linkerInput.dataRelocations.push_back({relocationInfo});
WhiteBox<NEO::Linker> linker(linkerInput);
constexpr uint64_t symValue = 64U;
linker.relocatedSymbols[relocationInfo.symbolName].gpuAddress = symValue;
NEO::Linker::UnresolvedExternals unresolvedExternals;
linker.patchDataSegments(globalVariablesSegmentInfo, {}, globalVariablesPatchableSegment.get(), {}, unresolvedExternals, pDevice, nullptr, 0, &globalVariableSegmentData, sizeof(globalVariableSegmentData));
EXPECT_EQ(static_cast<uint64_t>(relocationInfo.addend + symValue), globalVariableSegmentData);
}
TEST_F(LinkerTests, givenPerThreadPayloadOffsetRelocationWhenPatchingInstructionSegmentsThenPatchItWithCTDSize) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::LinkerInput::RelocationInfo rel;
rel.offset = 0x4;
rel.type = NEO::LinkerInput::RelocationInfo::Type::perThreadPayloadOffset;
rel.relocationSegment = NEO::SegmentType::instructions;
linkerInput.textRelocations.push_back({rel});
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kd;
kd.kernelAttributes.crossThreadDataSize = 0x40;
kd.kernelAttributes.inlineDataPayloadSize = 0x20;
kernelDescriptors.push_back(&kd);
WhiteBox<NEO::Linker> linker(linkerInput);
uint64_t segmentData{0};
NEO::Linker::PatchableSegment segmentToPatch;
segmentToPatch.hostPointer = reinterpret_cast<void *>(&segmentData);
segmentToPatch.segmentSize = sizeof(segmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
linker.patchInstructionsSegments({segmentToPatch}, unresolvedExternals, kernelDescriptors);
auto perThreadPayloadOffsetPatchedValue = reinterpret_cast<uint32_t *>(ptrOffset(segmentToPatch.hostPointer, static_cast<size_t>(rel.offset)));
uint32_t expectedPatchedValue = kd.kernelAttributes.crossThreadDataSize - kd.kernelAttributes.inlineDataPayloadSize;
EXPECT_EQ(expectedPatchedValue, static_cast<uint32_t>(*perThreadPayloadOffsetPatchedValue));
}
TEST_F(LinkerTests, givenPerThreadPayloadOffsetRelocationAndCrossThreadDataSmallerThanInlineDataWhenPatchingInstructionSegmentsThenPatchItWithOffsetZero) {
WhiteBox<NEO::LinkerInput> linkerInput{};
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::LinkerInput::RelocationInfo rel{};
rel.offset = 0x4;
rel.type = NEO::LinkerInput::RelocationInfo::Type::perThreadPayloadOffset;
rel.relocationSegment = NEO::SegmentType::instructions;
linkerInput.textRelocations.push_back({rel});
NEO::Linker::KernelDescriptorsT kernelDescriptors{};
KernelDescriptor kd{};
kd.kernelAttributes.crossThreadDataSize = 40u;
kd.kernelAttributes.inlineDataPayloadSize = 64u;
kernelDescriptors.push_back(&kd);
WhiteBox<NEO::Linker> linker(linkerInput);
uint64_t segmentData{0};
NEO::Linker::PatchableSegment segmentToPatch{};
segmentToPatch.hostPointer = reinterpret_cast<void *>(&segmentData);
segmentToPatch.segmentSize = sizeof(segmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals{};
linker.patchInstructionsSegments({segmentToPatch}, unresolvedExternals, kernelDescriptors);
auto perThreadPayloadOffsetPatchedValue = reinterpret_cast<uint32_t *>(ptrOffset(segmentToPatch.hostPointer, static_cast<size_t>(rel.offset)));
uint32_t expectedPatchedValue = 0u;
EXPECT_EQ(expectedPatchedValue, static_cast<uint32_t>(*perThreadPayloadOffsetPatchedValue));
}