2487 lines
110 KiB
C++
2487 lines
110 KiB
C++
/*
|
|
* Copyright (C) 2019-2022 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*/
|
|
|
|
#include "shared/source/device_binary_format/elf/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/device_binary_format/zebin_tests.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/ult_device_factory.h"
|
|
|
|
#include "RelocationInfo.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#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::Elf::SectionsNamesZebin::dataConst.str());
|
|
auto segmentGlobalConst = NEO::LinkerInput::getSegmentForSection(NEO::Elf::SectionsNamesZebin::dataGlobalConst.str());
|
|
auto segmentGlobal = NEO::LinkerInput::getSegmentForSection(NEO::Elf::SectionsNamesZebin::dataGlobal.str());
|
|
auto segmentConstString = NEO::LinkerInput::getSegmentForSection(NEO::Elf::SectionsNamesZebin::dataConstString.str());
|
|
auto segmentInstructions = NEO::LinkerInput::getSegmentForSection(NEO::Elf::SectionsNamesZebin::textPrefix.str());
|
|
auto segmentInstructions2 = NEO::LinkerInput::getSegmentForSection(".text.abc");
|
|
|
|
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);
|
|
}
|
|
|
|
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, WhenDecodingElfRelocationsWithAddendThenAddendIsSet) {
|
|
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";
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.addend = 128;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
nameToKernelId["abc"] = 0;
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto &relocsPerInstSegment = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(1u, relocsPerInstSegment.size());
|
|
auto &relocsKernelABC = relocsPerInstSegment[0];
|
|
ASSERT_EQ(1u, relocsKernelABC.size());
|
|
|
|
EXPECT_EQ(reloc.offset, relocsKernelABC[0].offset);
|
|
EXPECT_EQ(reloc.addend, relocsKernelABC[0].addend);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocsKernelABC[0].type);
|
|
EXPECT_EQ(NEO::SegmentType::Instructions, relocsKernelABC[0].relocationSegment);
|
|
EXPECT_STREQ(reloc.symbolName.c_str(), relocsKernelABC[0].symbolName.c_str());
|
|
}
|
|
|
|
TEST(LinkerInputTests, WhenDecodingElfTextRelocationsThenCorrectRelocationsAreAdded) {
|
|
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));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
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_32);
|
|
reloc2.symbolName = "symbol2";
|
|
reloc2.symbolSectionIndex = 1;
|
|
reloc2.symbolTableIndex = 0;
|
|
reloc2.targetSectionIndex = 2;
|
|
elf64.relocations.emplace_back(reloc2);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(2u, relocations.size());
|
|
|
|
auto &segment0Relocs = relocations[0];
|
|
ASSERT_EQ(1u, segment0Relocs.size());
|
|
|
|
EXPECT_EQ(64u, segment0Relocs[0].offset);
|
|
EXPECT_EQ(NEO::SegmentType::Instructions, segment0Relocs[0].relocationSegment);
|
|
EXPECT_EQ("symbol1", segment0Relocs[0].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, segment0Relocs[0].type);
|
|
|
|
auto &segment1Relocs = relocations[1];
|
|
ASSERT_EQ(1u, segment1Relocs.size());
|
|
|
|
EXPECT_EQ(32u, segment1Relocs[0].offset);
|
|
EXPECT_EQ(NEO::SegmentType::Instructions, segment1Relocs[0].relocationSegment);
|
|
EXPECT_EQ("symbol2", segment1Relocs[0].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::AddressLow, segment1Relocs[0].type);
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenNoKernelNameToIdWhenDecodingElfTextRelocationsThenNoRelocationIsAdded) {
|
|
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";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 0;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
nameToKernelId["wrong_name"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(0u, relocations.size());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenInvalidTextSectionNameWhenDecodingElfTextRelocationsThenNoRelocationIsAdded) {
|
|
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] = ".invalid.abc";
|
|
sectionNames[1] = ".data.const";
|
|
sectionNames[2] = ".text.hello";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 0;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(0u, relocations.size());
|
|
}
|
|
|
|
TEST(LinkerInputTests, whenSymbolHasShndxReferringToSectionOfUnknownTypehenDoNotAddItToLinkerInput) {
|
|
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] = ".invalid.aaa";
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
elf64.overrideSymbolName = true;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
|
|
symbol.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_OBJECT | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol.name = 0x20;
|
|
symbol.other = 0;
|
|
symbol.shndx = 0;
|
|
symbol.size = 0x8;
|
|
symbol.value = 0x4000;
|
|
|
|
elf64.symbolTable.emplace_back(symbol);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto symbols = linkerInput.getSymbols();
|
|
ASSERT_EQ(0u, symbols.size());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenValidZebinRelocationTypesWhenDecodingElfTextRelocationsThenCorrectTypeIsSet) {
|
|
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";
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc1;
|
|
reloc1.offset = 0;
|
|
reloc1.relocType = NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_NONE;
|
|
reloc1.symbolName = "symbol1";
|
|
reloc1.symbolSectionIndex = 1;
|
|
reloc1.symbolTableIndex = 0;
|
|
reloc1.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc1);
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc2;
|
|
reloc2.offset = 0;
|
|
reloc2.relocType = NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_SYM_ADDR;
|
|
reloc2.symbolName = "symbol2";
|
|
reloc2.symbolSectionIndex = 1;
|
|
reloc2.symbolTableIndex = 0;
|
|
reloc2.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc2);
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc3;
|
|
reloc3.offset = 0;
|
|
reloc3.relocType = NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_SYM_ADDR_32;
|
|
reloc3.symbolName = "symbol3";
|
|
reloc3.symbolSectionIndex = 1;
|
|
reloc3.symbolTableIndex = 0;
|
|
reloc3.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc3);
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc4;
|
|
reloc4.offset = 0;
|
|
reloc4.relocType = NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_SYM_ADDR_32_HI;
|
|
reloc4.symbolName = "symbol4";
|
|
reloc4.symbolSectionIndex = 1;
|
|
reloc4.symbolTableIndex = 0;
|
|
reloc4.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc4);
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc5;
|
|
reloc5.offset = 0;
|
|
reloc5.relocType = NEO::Elf::RELOC_TYPE_ZEBIN::R_PER_THREAD_PAYLOAD_OFFSET;
|
|
reloc5.symbolName = "symbol5";
|
|
reloc5.symbolSectionIndex = 1;
|
|
reloc5.symbolTableIndex = 0;
|
|
reloc5.targetSectionIndex = 0;
|
|
elf64.relocations.emplace_back(reloc5);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
nameToKernelId["abc"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(1u, relocations.size());
|
|
ASSERT_EQ(5u, relocations[0].size());
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Unknown, relocations[0][0].type);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[0][1].type);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::AddressLow, relocations[0][2].type);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::AddressHigh, relocations[0][3].type);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::PerThreadPayloadOffset, relocations[0][4].type);
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenInvalidRelocationTypeWhenDecodingElfTextRelocationsThenUnknownTypeIsSet) {
|
|
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));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = 0xffffffff; // invalid type
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 0;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getRelocationsInInstructionSegments();
|
|
ASSERT_EQ(1u, relocations.size());
|
|
ASSERT_EQ(1u, relocations[0].size());
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Unknown, relocations[0][0].type);
|
|
}
|
|
|
|
TEST(LinkerInputTests, WhenDecodingElfGlobalDataRelocationsThenCorrectRelocationsAreAdded) {
|
|
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.global";
|
|
sectionNames[2] = ".data.const";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 1;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
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 = "symbol2";
|
|
reloc2.symbolSectionIndex = 2;
|
|
reloc2.symbolTableIndex = 1;
|
|
reloc2.targetSectionIndex = 1;
|
|
|
|
elf64.relocations.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 = "symbol3";
|
|
reloc3.symbolSectionIndex = 0;
|
|
reloc3.symbolTableIndex = 2;
|
|
reloc3.targetSectionIndex = 1;
|
|
|
|
elf64.relocations.emplace_back(reloc3);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getDataRelocations();
|
|
ASSERT_EQ(3u, relocations.size());
|
|
|
|
EXPECT_EQ(64u, relocations[0].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalVariables, relocations[0].relocationSegment);
|
|
EXPECT_EQ("symbol1", relocations[0].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[0].type);
|
|
|
|
EXPECT_EQ(32u, relocations[1].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalVariables, relocations[1].relocationSegment);
|
|
EXPECT_EQ("symbol2", relocations[1].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[1].type);
|
|
|
|
EXPECT_EQ(0u, relocations[2].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalVariables, relocations[2].relocationSegment);
|
|
EXPECT_EQ("symbol3", relocations[2].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[2].type);
|
|
}
|
|
|
|
TEST(LinkerInputTests, WhenDecodingElfConstantDataRelocationsThenCorrectRelocationsAreAdded) {
|
|
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.global";
|
|
sectionNames[2] = ".data.const";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 2;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
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 = "symbol2";
|
|
reloc2.symbolSectionIndex = 2;
|
|
reloc2.symbolTableIndex = 0;
|
|
reloc2.targetSectionIndex = 2;
|
|
|
|
elf64.relocations.emplace_back(reloc2);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getDataRelocations();
|
|
ASSERT_EQ(2u, relocations.size());
|
|
|
|
EXPECT_EQ(64u, relocations[0].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalConstants, relocations[0].relocationSegment);
|
|
EXPECT_EQ("symbol1", relocations[0].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[0].type);
|
|
|
|
EXPECT_EQ(32u, relocations[1].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalConstants, relocations[1].relocationSegment);
|
|
EXPECT_EQ("symbol2", relocations[1].symbolName);
|
|
EXPECT_EQ(NEO::LinkerInput::RelocationInfo::Type::Address, relocations[1].type);
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenUnsupportedDataSegmentWhenDecodingElfDataRelocationThenNoRelocationsAreAdded) {
|
|
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";
|
|
sectionNames[2] = ".data.const";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 2;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 1;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getDataRelocations();
|
|
ASSERT_EQ(0u, relocations.size());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenUnsupportedSymbolSegmentWhenDecodingElfDataRelocationThenNoRelocationsAreAdded) {
|
|
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] = ".unknown_section";
|
|
sectionNames[1] = ".data";
|
|
sectionNames[2] = ".data.const";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
|
|
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64>::RelocationInfo reloc;
|
|
reloc.offset = 64;
|
|
reloc.relocType = static_cast<uint32_t>(Elf::RELOCATION_X8664_TYPE::R_X8664_64);
|
|
reloc.symbolName = "symbol1";
|
|
reloc.symbolSectionIndex = 1;
|
|
reloc.symbolTableIndex = 0;
|
|
reloc.targetSectionIndex = 2;
|
|
|
|
elf64.relocations.emplace_back(reloc);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto relocations = linkerInput.getDataRelocations();
|
|
ASSERT_EQ(0u, relocations.size());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenGlobalAndLocalElfSymbolsWhenDecodingThenOnlyGlobalSymbolsAreAdded) {
|
|
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;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
|
|
symbol.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_OBJECT | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol.name = 0;
|
|
symbol.other = 0;
|
|
symbol.shndx = 1;
|
|
symbol.size = 8;
|
|
symbol.value = 0x1234000;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol2;
|
|
symbol2.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol2.name = 8;
|
|
symbol2.other = 0;
|
|
symbol2.shndx = 0;
|
|
symbol2.size = 16;
|
|
symbol2.value = 0x5000;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol3;
|
|
symbol3.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_LOCAL << 4;
|
|
symbol3.name = 16;
|
|
symbol3.other = 0;
|
|
symbol3.shndx = 0;
|
|
symbol3.size = 8;
|
|
symbol3.value = 0;
|
|
|
|
elf64.symbolTable.emplace_back(symbol);
|
|
elf64.symbolTable.emplace_back(symbol2);
|
|
elf64.symbolTable.emplace_back(symbol3);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto symbols = linkerInput.getSymbols();
|
|
ASSERT_EQ(2u, symbols.size());
|
|
|
|
EXPECT_EQ(0x1234000u, symbols[std::to_string(symbol.name)].offset);
|
|
EXPECT_EQ(NEO::SegmentType::GlobalConstants, symbols[std::to_string(symbol.name)].segment);
|
|
EXPECT_EQ(8u, symbols[std::to_string(symbol.name)].size);
|
|
|
|
EXPECT_EQ(0x5000u, symbols[std::to_string(symbol2.name)].offset);
|
|
EXPECT_EQ(NEO::SegmentType::Instructions, symbols[std::to_string(symbol2.name)].segment);
|
|
EXPECT_EQ(16u, symbols[std::to_string(symbol2.name)].size);
|
|
|
|
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
|
|
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
|
|
EXPECT_TRUE(linkerInput.getTraits().exportsFunctions);
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenGlobalFunctionsInTwoSegmentsWhenDecodingThenUnrecoverableIsCalled) {
|
|
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;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
|
|
symbol.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol.name = 0;
|
|
symbol.other = 0;
|
|
symbol.shndx = 0;
|
|
symbol.size = 8;
|
|
symbol.value = 0x1234000;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol2;
|
|
symbol2.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol2.name = 8;
|
|
symbol2.other = 0;
|
|
symbol2.shndx = 2;
|
|
symbol2.size = 16;
|
|
symbol2.value = 0x5000;
|
|
|
|
elf64.symbolTable.emplace_back(symbol);
|
|
elf64.symbolTable.emplace_back(symbol2);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
EXPECT_THROW(linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId), std::exception);
|
|
|
|
auto symbols = linkerInput.getSymbols();
|
|
ASSERT_EQ(1u, symbols.size());
|
|
EXPECT_EQ(0, linkerInput.getExportedFunctionsSegmentId());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenNoKernelNameToIdWhenDecodingGlobalFunctionThenExportedFunctionsSegmentIsNotSet) {
|
|
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";
|
|
|
|
elf64.setupSecionNames(std::move(sectionNames));
|
|
elf64.overrideSymbolName = true;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
|
|
symbol.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol.name = 0;
|
|
symbol.other = 0;
|
|
symbol.shndx = 0;
|
|
symbol.size = 8;
|
|
symbol.value = 0x1234000;
|
|
|
|
elf64.symbolTable.emplace_back(symbol);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto symbols = linkerInput.getSymbols();
|
|
ASSERT_EQ(1u, symbols.size());
|
|
EXPECT_EQ(-1, linkerInput.getExportedFunctionsSegmentId());
|
|
}
|
|
|
|
TEST(LinkerInputTests, GivenGlobalElfSymbolOfNoTypeWhenDecodingThenDebugBreakCalled) {
|
|
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;
|
|
|
|
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbol;
|
|
symbol.info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_NOTYPE | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
|
|
symbol.name = 0;
|
|
symbol.other = 0;
|
|
symbol.shndx = 1;
|
|
symbol.size = 8;
|
|
symbol.value = 0x1234000;
|
|
|
|
elf64.symbolTable.emplace_back(symbol);
|
|
|
|
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
|
|
|
|
nameToKernelId["abc"] = 0;
|
|
nameToKernelId["hello"] = 1;
|
|
|
|
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
|
|
|
|
auto symbols = linkerInput.getSymbols();
|
|
ASSERT_EQ(0u, symbols.size());
|
|
|
|
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
|
|
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
|
|
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
|
|
}
|
|
|
|
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::Elf::SectionsNamesZebin::externalFunctions.str());
|
|
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
|
|
|
|
relocInfo.offset = 0x10U;
|
|
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Elf::SectionsNamesZebin::externalFunctions.str());
|
|
EXPECT_TRUE(mockLinkerInput.extFunDependencies.empty());
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
|
|
EXPECT_EQ(0U, unresolvedExternals.size());
|
|
auto relocatedSymbols = linker.extractRelocatedSymbols();
|
|
EXPECT_EQ(0U, relocatedSymbols.size());
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, kernelDescriptors, extFuncs);
|
|
EXPECT_EQ(NEO::LinkingStatus::Error, linkResult);
|
|
}
|
|
|
|
TEST(LinkerTests, givenUnresolvedExternalSymbolsWhenResolveBuiltinsIsCalledThenSubDeviceIDSymbolsAreRmoved) {
|
|
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);
|
|
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
linker.resolveBuiltins(device.get(), unresolvedExternals, instructionsSegments);
|
|
|
|
EXPECT_EQ(2U, unresolvedExternals.size());
|
|
for (auto &symbol : unresolvedExternals) {
|
|
EXPECT_NE(NEO::Linker::subDeviceID, symbol.unresolvedRelocation.symbolName);
|
|
}
|
|
|
|
auto gpuAddressAs64bit = device->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));
|
|
}
|
|
|
|
TEST(LinkerTests, givenUnresolvedExternalsWhenLinkThenSubDeviceIDSymbolsAreRemoved) {
|
|
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);
|
|
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
|
|
linker.link(
|
|
globalVar, globalConst, exportedFunc, {},
|
|
patchableGlobalVarSeg, patchableConstVarSeg, instructionsSegments,
|
|
unresolvedExternals, device.get(), nullptr, nullptr, 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 = device->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));
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, 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));
|
|
}
|
|
|
|
TEST(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 relocIgnore = {};
|
|
relocIgnore.r_symbol[0] = 'X';
|
|
relocIgnore.r_offset = 36;
|
|
relocIgnore.r_type = vISA::GenRelocType::R_PER_THREAD_PAYLOAD_OFFSET_32;
|
|
|
|
vISA::GenRelocEntry relocs[] = {relocA, relocB, relocC, relocCPartHigh, relocCPartLow, relocIgnore};
|
|
constexpr uint32_t numRelocations = sizeof(relocs) / sizeof(relocs[0]);
|
|
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;
|
|
|
|
std::vector<char> instructionSegment;
|
|
uint32_t initData = 0x77777777;
|
|
instructionSegment.resize(64, static_cast<char>(initData));
|
|
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;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
|
|
auto linkResult = linker.link(
|
|
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
|
|
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments, unresolvedExternals,
|
|
nullptr, nullptr, nullptr, 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(symGlobalVariable.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)));
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, 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,
|
|
nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, 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, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, 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, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
|
|
}
|
|
|
|
TEST(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,
|
|
nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
|
|
EXPECT_EQ(0U, unresolvedExternals.size());
|
|
EXPECT_EQ(1U, linker.extractRelocatedSymbols().size());
|
|
|
|
uintptr_t strAddr = stringSegment.gpuAddress + strSymbol.offset;
|
|
uintptr_t patchAddr = reinterpret_cast<uintptr_t>(instructionSegment) + static_cast<uintptr_t>(relocation.offset);
|
|
|
|
EXPECT_EQ(static_cast<size_t>(strAddr), *reinterpret_cast<const size_t *>(patchAddr));
|
|
}
|
|
|
|
TEST(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.segment = SegmentType::Instructions;
|
|
fun1.offset = 0U;
|
|
fun1.size = 8U;
|
|
|
|
auto &fun2 = linkerInput.symbols["fun2"];
|
|
fun2.segment = SegmentType::Instructions;
|
|
fun2.offset = 8U;
|
|
fun2.size = 8U;
|
|
|
|
auto &var1 = linkerInput.symbols["var1"];
|
|
var1.segment = SegmentType::GlobalVariables;
|
|
var1.offset = 8U;
|
|
var1.size = 8U;
|
|
|
|
auto &const1 = linkerInput.symbols["const1"];
|
|
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);
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, exportedFunctionsSegmentInfo, {},
|
|
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
|
|
unresolvedExternals, device.get(), initGlobalConstantData, 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));
|
|
}
|
|
}
|
|
|
|
TEST(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);
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
|
|
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
|
|
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
|
|
EXPECT_EQ(1U, unresolvedExternals.size());
|
|
}
|
|
|
|
TEST(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);
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
|
|
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
|
|
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
|
|
EXPECT_EQ(1U, unresolvedExternals.size());
|
|
}
|
|
|
|
TEST(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);
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
auto linkResult = linker.link({}, {}, {}, {},
|
|
nullptr, nullptr, {},
|
|
unresolvedExternals, device.get(), nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
|
|
EXPECT_EQ(1U, unresolvedExternals.size());
|
|
}
|
|
|
|
TEST(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);
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
NEO::Linker::KernelDescriptorsT kernelDescriptors;
|
|
NEO::Linker::ExternalFunctionsT externalFunctions;
|
|
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
|
|
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
|
|
unresolvedExternals, device.get(), initGlobalConstantData, 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(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({§ionHeader0, {&storage[80], 80}});
|
|
elf64.sectionHeaders.push_back({§ionHeader1, {&storage[160], 80}});
|
|
elf64.sectionHeaders.push_back({§ionHeader2, {&storage[240], 80}});
|
|
elf64.sectionHeaders.push_back({§ionHeader3, {&storage[320], 80}});
|
|
elf64.sectionHeaders.push_back({§ionHeader4, {&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(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, nullptr, 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(LinkerTests, givenImplicitArgRelocationAndEnabledDebuggerThenPatchRelocationWithSizeOfImplicitArgStructAndUpdateKernelDescriptor) {
|
|
if (!defaultHwInfo->capabilityTable.debuggerSupported) {
|
|
GTEST_SKIP();
|
|
}
|
|
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;
|
|
|
|
DebugManagerStateRestore restorer;
|
|
DebugManager.flags.EnableMockSourceLevelDebugger.set(1);
|
|
UltDeviceFactory deviceFactory{1, 0};
|
|
auto device = deviceFactory.rootDevices[0];
|
|
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, nullptr, 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(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, nullptr, 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(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, nullptr, 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(LinkerTests, givenNoImplicitArgRelocationAndEnabledDebuggerThenImplicitArgsAreNotRequired) {
|
|
if (!defaultHwInfo->capabilityTable.debuggerSupported) {
|
|
GTEST_SKIP();
|
|
}
|
|
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;
|
|
|
|
DebugManagerStateRestore restorer;
|
|
DebugManager.flags.EnableMockSourceLevelDebugger.set(1);
|
|
UltDeviceFactory deviceFactory{1, 0};
|
|
auto device = deviceFactory.rootDevices[0];
|
|
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, nullptr, 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(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, nullptr, 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);
|
|
}
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(LinkingStatus::Error, linkResult);
|
|
}
|
|
|
|
TEST(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, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
|
|
EXPECT_EQ(LinkingStatus::LinkedFully, linkResult);
|
|
}
|
|
|
|
TEST(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;
|
|
linker.patchInstructionsSegments({segmentToPatch}, unresolvedExternals);
|
|
EXPECT_EQ(static_cast<uint64_t>(rela.addend + symValue), segmentData);
|
|
}
|
|
|
|
TEST(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;
|
|
|
|
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
|
|
NEO::Linker::UnresolvedExternals unresolvedExternals;
|
|
linker.patchDataSegments({}, globalConstantsSegmentInfo, {}, &globalConstantsPatchableSegment, unresolvedExternals, device.get(), &globalConstantSegmentData, nullptr);
|
|
EXPECT_EQ(static_cast<uint64_t>(rela.addend + symValue), globalConstantSegmentData);
|
|
}
|