Files
compute-runtime/shared/test/unit_test/compiler_interface/linker_tests.cpp
Naklicki, Mateusz ff57bec1f0 fix: ignore subDeviceId token instead of skipping builtins resolve
Previous implementation skipped resolving the subDeviceId token, which
caused in returning the LinkedPartially status if token was present.
It could result in ZE_RESULT_ERROR_INVALID_MODULE_UNLINKED when kernel
was created and crashing an app.

Related-To: NEO-6875, HSD-14015133115
Signed-off-by: Naklicki, Mateusz <mateusz.naklicki@intel.com>
2023-09-05 16:26:44 +02:00

2396 lines
112 KiB
C++

/*
* Copyright (C) 2019-2023 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#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/helpers/string.h"
#include "shared/source/kernel/implicit_args.h"
#include "shared/source/kernel/kernel_descriptor.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/source/program/program_initialization.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/mocks/mock_device.h"
#include "shared/test/common/mocks/mock_elf.h"
#include "shared/test/common/mocks/mock_graphics_allocation.h"
#include "shared/test/common/mocks/mock_modules_zebin.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 <array>
#include <string>
TEST(SegmentTypeTests, givenSegmentTypeWhenAsStringIsCalledThenProperRepresentationIsReturned) {
EXPECT_STREQ("UNKOWN", 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 linkerInput;
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalConstantsBuffer);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfGlobalVariablesBuffer);
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.offset = 7U;
relocInfo.relocationSegment = NEO::SegmentType::GlobalConstants;
relocInfo.symbolName = "aaa";
relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::Address;
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());
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.setupSecionNames(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.setupSecionNames(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.setupSecionNames(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) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = Zebin::Elf::SectionNames::functions.str();
elf64.setupSecionNames(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());
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.setupSecionNames(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, GivenGlobalSymbolPointingToSectionDifferentThanInstructionsOrDataWhenDecodingElfThenItIsIgnored) {
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ""; // UNDEF section
sectionNames[1] = ".text.abc";
elf64.setupSecionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.addSymbol(0, 0, 8, 0, Elf::STT_OBJECT, 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, GivenSymbolPointingToInstructionSegmentAndInvalidInstructionSectionNameMappingWhenDecodingElfThenLinkerInputIsInvalid) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
elf64.setupSecionNames(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.setupSecionNames(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.setupSecionNames(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.setupSecionNames(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.setupSecionNames(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) {
NEO::LinkerInput linkerInput = {};
MockElf<NEO::Elf::EI_CLASS_64> elf64;
std::unordered_map<uint32_t, std::string> sectionNames;
sectionNames[0] = ".text.abc";
sectionNames[1] = Zebin::Elf::SectionNames::functions.str();
elf64.setupSecionNames(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());
}
HWTEST_F(LinkerTests, givenEmptyLinkerInputThenLinkerOutputIsEmpty) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::resolveBuiltins;
LinkerMock(const LinkerInput &data) : NEO::Linker(data) {
}
};
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);
linker.resolveBuiltins(pDevice, unresolvedExternals, instructionsSegments);
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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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, 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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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));
}
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 globalConstantsPatchableSegment{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegment{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
NEO::MockGraphicsAllocation exportedFunctions{&exportedFunctionsInit, sizeof(exportedFunctionsInit)};
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo, exportedFunctionsSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment.getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment.getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.getUnderlyingBufferSize();
exportedFunctionsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(exportedFunctions.getUnderlyingBuffer());
exportedFunctionsSegmentInfo.segmentSize = exportedFunctions.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, &globalConstantsPatchableSegment, {},
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 globalConstantsPatchableSegment{constantsSegmentData, sizeof(constantsSegmentData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegment{globalVariablesSegmentData, sizeof(globalVariablesSegmentData)};
globalConstantsPatchableSegment.gpuAddress = 0xA0000000;
globalVariablesPatchableSegment.gpuAddress = 0xB0000000;
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = static_cast<uintptr_t>(globalConstantsPatchableSegment.getGpuAddress());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = static_cast<uintptr_t>(globalVariablesPatchableSegment.getGpuAddress());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.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, &globalConstantsPatchableSegment, 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.getUnderlyingBuffer());
auto globalVariableSegmentAddr = reinterpret_cast<uint64_t *>(globalVariablesPatchableSegment.getUnderlyingBuffer());
auto var1Addr = globalVariablesPatchableSegment.getGpuAddress();
auto const1Addr = globalConstantsPatchableSegment.getGpuAddress();
auto bssConstAddrr = globalConstantsPatchableSegment.getGpuAddress() + sizeof(initGlobalConstantData);
auto bssVarAddr = globalVariablesPatchableSegment.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 globalConstantsPatchableSegment{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegment{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment.getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment.getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.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, &globalConstantsPatchableSegment, {},
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 globalConstantsPatchableSegment{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegment{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment.getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment.getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.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, &globalConstantsPatchableSegment, {},
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 globalConstantsPatchableSegment{initGlobalConstantData, sizeof(initGlobalConstantData)};
NEO::MockGraphicsAllocation globalVariablesPatchableSegment{initGlobalVariablesData, sizeof(initGlobalVariablesData)};
NEO::Linker::SegmentInfo globalConstantsSegmentInfo, globalVariablesSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment.getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.getUnderlyingBufferSize();
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment.getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.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, &globalConstantsPatchableSegment, {},
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 informations :\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.setupSecionNames(std::move(sectionNames));
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc0 = {};
reloc0.offset = 64;
reloc0.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
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::RELOCATION_X8664_TYPE::R_X8664_32);
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::RELOCATION_X8664_TYPE::R_X8664_64);
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::RELOCATION_X8664_TYPE::R_X8664_64);
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, givenImplicitArgRelocationAndStackCallsThenPatchRelocationWithSizeOfImplicitArgStructAndUpdateKernelDescriptor) {
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<const uint32_t *>(instructionSegment.data() + reloc.r_offset);
EXPECT_EQ(sizeof(ImplicitArgs), *addressToPatch);
EXPECT_EQ(initData, *(addressToPatch - 1));
EXPECT_EQ(initData, *(addressToPatch + 1));
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
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(sizeof(ImplicitArgs), *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(sizeof(ImplicitArgs), *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *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 globalConstantsPatchableSegment{&globalConstantSegmentData, sizeof(globalConstantSegmentData)};
NEO::Linker::SegmentInfo globalConstantsSegmentInfo;
globalConstantsSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalConstantsPatchableSegment.getUnderlyingBuffer());
globalConstantsSegmentInfo.segmentSize = globalConstantsPatchableSegment.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, 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 globalVariablesPatchableSegment{&globalVariableSegmentData, sizeof(globalVariableSegmentData)};
NEO::Linker::SegmentInfo globalVariablesSegmentInfo;
globalVariablesSegmentInfo.gpuAddress = reinterpret_cast<uintptr_t>(globalVariablesPatchableSegment.getUnderlyingBuffer());
globalVariablesSegmentInfo.segmentSize = globalVariablesPatchableSegment.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, {}, 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 = 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)));
EXPECT_EQ(kd.kernelAttributes.crossThreadDataSize, static_cast<uint32_t>(*perThreadPayloadOffsetPatchedValue));
}