Linker: add support for symbols with local binding

This commits add support for relocating
symbols with local binding and of functional type
(STB_LOCAL, STT_FUNC).

Related-To: NEO-7299
Signed-off-by: Kacper Nowak <kacper.nowak@intel.com>
This commit is contained in:
Kacper Nowak
2022-09-06 00:10:18 +00:00
committed by Compute-Runtime-Automation
parent 824c781ab5
commit 710c8cf5ef
10 changed files with 272 additions and 35 deletions

View File

@@ -203,6 +203,14 @@ void LinkerInput::decodeElfSymbolTableAndRelocations(Elf::Elf<Elf::EI_CLASS_64>
} break;
}
symbols.insert({elf.getSymbolName(symbol.name), symbolInfo});
} else if (type == Elf::SYMBOL_TABLE_TYPE::STT_FUNC) {
DEBUG_BREAK_IF(Elf::SYMBOL_TABLE_BIND::STB_LOCAL != bind);
LocalFuncSymbolInfo localSymbolInfo;
localSymbolInfo.offset = static_cast<uint32_t>(symbol.value);
localSymbolInfo.size = static_cast<uint32_t>(symbol.size);
auto symbolSectionName = elf.getSectionName(symbol.shndx);
localSymbolInfo.targetedKernelSectionName = symbolSectionName.substr(Elf::SectionsNamesZebin::textPrefix.length());
localSymbols.insert({elf.getSymbolName(symbol.name), localSymbolInfo});
}
}
@@ -267,7 +275,8 @@ void LinkerInput::parseRelocationForExtFuncUsage(const RelocationInfo &relocInfo
}
}
bool Linker::processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings) {
bool Linker::processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings,
const PatchableSegments &instructionsSegments) {
relocatedSymbols.reserve(data.getSymbols().size());
for (auto &symbol : data.getSymbols()) {
const SegmentInfo *seg = nullptr;
@@ -295,6 +304,15 @@ bool Linker::processRelocations(const SegmentInfo &globalVariables, const Segmen
}
relocatedSymbols[symbol.first] = {symbol.second, gpuAddress};
}
localRelocatedSymbols.reserve(data.getLocalSymbols().size());
for (auto &localSymbol : data.getLocalSymbols()) {
for (auto &s : instructionsSegments) {
if (s.kernelName == localSymbol.second.targetedKernelSectionName) {
uintptr_t gpuAddress = s.gpuAddress + localSymbol.second.offset;
localRelocatedSymbols[localSymbol.first] = {localSymbol.second, gpuAddress};
}
}
}
return true;
}
@@ -347,6 +365,20 @@ void Linker::patchInstructionsSegments(const std::vector<PatchableSegment> &inst
continue;
}
auto symbolIt = relocatedSymbols.find(relocation.symbolName);
if (symbolIt == relocatedSymbols.end()) {
auto localSymbolIt = localRelocatedSymbols.find(relocation.symbolName);
if (localRelocatedSymbols.end() != localSymbolIt) {
if (localSymbolIt->first == kernelDescriptors[segId]->kernelMetadata.kernelName) {
uint64_t patchValue = localSymbolIt->second.gpuAddress + relocation.addend;
patchAddress(relocAddress, patchValue, relocation);
continue;
}
} else if (relocation.symbolName.empty()) {
uint64_t patchValue = 0;
patchAddress(relocAddress, patchValue, relocation);
continue;
}
}
bool unresolvedExternal = (symbolIt == relocatedSymbols.end());
if (invalidOffset || unresolvedExternal) {
uint32_t segId = static_cast<uint32_t>(segIt - instructionsSegments.begin());
@@ -536,7 +568,7 @@ void Linker::resolveBuiltins(Device *pDevice, UnresolvedExternals &outUnresolved
int vecIndex = static_cast<int>(outUnresolvedExternals.size() - 1u);
for (; vecIndex >= 0; --vecIndex) {
if (outUnresolvedExternals[vecIndex].unresolvedRelocation.symbolName == subDeviceID) {
RelocatedSymbol symbol;
RelocatedSymbol<SymbolInfo> symbol;
symbol.gpuAddress = static_cast<uintptr_t>(pDevice->getDefaultEngine().commandStreamReceiver->getWorkPartitionAllocationGpuAddress());
auto relocAddress = ptrOffset(instructionsSegments[outUnresolvedExternals[vecIndex].instructionsSegmentId].hostPointer,
static_cast<uintptr_t>(outUnresolvedExternals[vecIndex].unresolvedRelocation.offset));

View File

@@ -55,6 +55,12 @@ struct SymbolInfo {
SegmentType segment = SegmentType::Unknown;
};
struct LocalFuncSymbolInfo {
uint32_t offset = std::numeric_limits<uint32_t>::max();
uint32_t size = std::numeric_limits<uint32_t>::max();
std::string targetedKernelSectionName;
};
struct LinkerInput {
union Traits {
enum PointerSize : uint8_t {
@@ -97,6 +103,7 @@ struct LinkerInput {
using SectionNameToSegmentIdMap = std::unordered_map<std::string, uint32_t>;
using Relocations = std::vector<RelocationInfo>;
using SymbolMap = std::unordered_map<std::string, SymbolInfo>;
using LocalSymbolMap = std::unordered_map<std::string, LocalFuncSymbolInfo>;
using RelocationsPerInstSegment = std::vector<Relocations>;
virtual ~LinkerInput() = default;
@@ -123,6 +130,10 @@ struct LinkerInput {
return symbols;
}
const LocalSymbolMap &getLocalSymbols() const {
return localSymbols;
}
void addSymbol(const std::string &symbolName, const SymbolInfo &symbolInfo) {
symbols.emplace(std::make_pair(symbolName, symbolInfo));
}
@@ -156,6 +167,7 @@ struct LinkerInput {
Traits traits;
SymbolMap symbols;
LocalSymbolMap localSymbols;
RelocationsPerInstSegment textRelocations;
Relocations dataRelocations;
std::vector<std::pair<std::string, SymbolInfo>> extFuncSymbols;
@@ -177,7 +189,9 @@ struct Linker {
struct PatchableSegment {
void *hostPointer = nullptr;
uintptr_t gpuAddress = 0;
size_t segmentSize = std::numeric_limits<size_t>::max();
std::string kernelName;
};
struct UnresolvedExternal {
@@ -186,12 +200,14 @@ struct Linker {
bool internalError = false;
};
template <typename T>
struct RelocatedSymbol {
SymbolInfo symbol;
T symbol;
uintptr_t gpuAddress = std::numeric_limits<uintptr_t>::max();
};
using RelocatedSymbolsMap = std::unordered_map<std::string, RelocatedSymbol>;
using RelocatedSymbolsMap = std::unordered_map<std::string, RelocatedSymbol<SymbolInfo>>;
using LocalsRelocatedSymbolsMap = std::unordered_map<std::string, RelocatedSymbol<LocalFuncSymbolInfo>>;
using PatchableSegments = std::vector<PatchableSegment>;
using UnresolvedExternals = std::vector<UnresolvedExternal>;
using KernelDescriptorsT = std::vector<KernelDescriptor *>;
@@ -207,7 +223,7 @@ struct Linker {
const KernelDescriptorsT &kernelDescriptors, ExternalFunctionsT &externalFunctions) {
bool success = data.isValid();
auto initialUnresolvedExternalsCount = outUnresolvedExternals.size();
success = success && processRelocations(globalVariablesSegInfo, globalConstantsSegInfo, exportedFunctionsSegInfo, globalStringsSegInfo);
success = success && processRelocations(globalVariablesSegInfo, globalConstantsSegInfo, exportedFunctionsSegInfo, globalStringsSegInfo, instructionsSegments);
if (!success) {
return LinkingStatus::Error;
}
@@ -238,8 +254,9 @@ struct Linker {
protected:
const LinkerInput &data;
RelocatedSymbolsMap relocatedSymbols;
LocalsRelocatedSymbolsMap localRelocatedSymbols;
bool processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings);
bool processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings, const PatchableSegments &instructionsSegments);
void patchInstructionsSegments(const std::vector<PatchableSegment> &instructionsSegments, std::vector<UnresolvedExternal> &outUnresolvedExternals, const KernelDescriptorsT &kernelDescriptors);

View File

@@ -27,6 +27,7 @@ struct WhiteBox<NEO::LinkerInput> : NEO::LinkerInput {
using BaseClass::extFuncSymbols;
using BaseClass::extFunDependencies;
using BaseClass::kernelDependencies;
using BaseClass::localSymbols;
using BaseClass::parseRelocationForExtFuncUsage;
using BaseClass::symbols;
using BaseClass::textRelocations;
@@ -38,8 +39,10 @@ template <>
struct WhiteBox<NEO::Linker> : NEO::Linker {
using BaseClass = NEO::Linker;
using BaseClass::BaseClass;
using BaseClass::localRelocatedSymbols;
using BaseClass::patchDataSegments;
using BaseClass::patchInstructionsSegments;
using BaseClass::processRelocations;
using BaseClass::relocatedSymbols;
using BaseClass::resolveExternalFunctions;
};

View File

@@ -2518,4 +2518,184 @@ TEST(LinkerTests, givenPerThreadPayloadOffsetRelocationWhenPatchingInstructionSe
linker.patchInstructionsSegments({segmentToPatch}, unresolvedExternals, kernelDescriptors);
auto perThreadPayloadOffsetPatchedValue = reinterpret_cast<uint32_t *>(ptrOffset(segmentToPatch.hostPointer, static_cast<size_t>(rel.offset)));
EXPECT_EQ(kd.kernelAttributes.crossThreadDataSize, static_cast<uint32_t>(*perThreadPayloadOffsetPatchedValue));
}
}
TEST(LinkerTests, givenRelocationToInstructionSegmentWithLocalSymbolPointingToSameSegmentThenItIsPatched) {
std::string kernelName{"test_kernel"};
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 = kernelName;
rela.relocationSegment = NEO::SegmentType::Instructions;
linkerInput.textRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
constexpr uint64_t symValue = 64U;
linker.localRelocatedSymbols[kernelName].gpuAddress = symValue;
uint64_t instructionSegmentData{std::numeric_limits<uint64_t>::max()};
NEO::Linker::PatchableSegment instructionSegmentToPatch;
instructionSegmentToPatch.hostPointer = reinterpret_cast<void *>(&instructionSegmentData);
instructionSegmentToPatch.segmentSize = sizeof(instructionSegmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
ASSERT_EQ(0u, unresolvedExternals.size());
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kd;
kd.kernelMetadata.kernelName = kernelName;
kernelDescriptors.push_back(&kd);
linker.patchInstructionsSegments({instructionSegmentToPatch}, unresolvedExternals, kernelDescriptors);
auto instructionSegmentPatchedData = reinterpret_cast<uint64_t *>(ptrOffset(instructionSegmentToPatch.hostPointer, static_cast<size_t>(rela.offset)));
EXPECT_EQ(symValue + rela.addend, static_cast<uint64_t>(*instructionSegmentPatchedData));
EXPECT_EQ(0u, unresolvedExternals.size());
}
TEST(LinkerTests, givenRelocationToInstructionSegmentWithLocalSymbolPointingToDifferentSegmentThenItIsNotPatched) {
std::string kernelName{"test_kernel"};
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 = kernelName;
rela.relocationSegment = NEO::SegmentType::Instructions;
linkerInput.textRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
constexpr uint64_t symValue = 64U;
linker.localRelocatedSymbols[kernelName].gpuAddress = symValue;
uint64_t instructionSegmentData{std::numeric_limits<uint64_t>::max()};
NEO::Linker::PatchableSegment instructionSegmentToPatch;
instructionSegmentToPatch.hostPointer = reinterpret_cast<void *>(&instructionSegmentData);
instructionSegmentToPatch.segmentSize = sizeof(instructionSegmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
ASSERT_EQ(0u, unresolvedExternals.size());
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kd;
kd.kernelMetadata.kernelName = "not_matching";
kernelDescriptors.push_back(&kd);
linker.patchInstructionsSegments({instructionSegmentToPatch}, unresolvedExternals, kernelDescriptors);
auto instructionSegmentPatchedData = reinterpret_cast<uint64_t *>(ptrOffset(instructionSegmentToPatch.hostPointer, static_cast<size_t>(rela.offset)));
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), static_cast<uint64_t>(*instructionSegmentPatchedData));
EXPECT_EQ(1u, unresolvedExternals.size());
}
TEST(LinkerTests, givenRelocationToInstructionSegmentWithLocalUndefinedSymbolThenItIsPatchedWithZeroes) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.traits.requiresPatchingOfInstructionSegments = true;
NEO::LinkerInput::RelocationInfo rela;
rela.offset = 0U;
rela.addend = 128U;
rela.type = NEO::LinkerInput::RelocationInfo::Type::Address;
rela.symbolName = "";
rela.relocationSegment = NEO::SegmentType::Instructions;
linkerInput.textRelocations.push_back({rela});
WhiteBox<NEO::Linker> linker(linkerInput);
EXPECT_TRUE(linker.relocatedSymbols.empty());
EXPECT_TRUE(linker.localRelocatedSymbols.empty());
uint64_t instructionSegmentData{std::numeric_limits<uint64_t>::max()};
NEO::Linker::PatchableSegment instructionSegmentToPatch;
instructionSegmentToPatch.hostPointer = reinterpret_cast<void *>(&instructionSegmentData);
instructionSegmentToPatch.segmentSize = sizeof(instructionSegmentData);
NEO::Linker::UnresolvedExternals unresolvedExternals;
ASSERT_EQ(0u, unresolvedExternals.size());
NEO::Linker::KernelDescriptorsT kernelDescriptors;
linker.patchInstructionsSegments({instructionSegmentToPatch}, unresolvedExternals, kernelDescriptors);
auto instructionSegmentPatchedData = reinterpret_cast<uint64_t *>(ptrOffset(instructionSegmentToPatch.hostPointer, static_cast<size_t>(rela.offset)));
EXPECT_EQ(0u, static_cast<uint64_t>(*instructionSegmentPatchedData));
EXPECT_EQ(0u, unresolvedExternals.size());
}
TEST(LinkerTests, givenElfWithLocalSymbolsWhenDecodingElfSymbolTableAndRelocationsThenOnlySymbolsWithTypeFunctionArePopulated) {
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;
std::string kernelName = "test_kernel";
sectionNames[0] = NEO::Elf::SectionsNamesZebin::textPrefix.str() + kernelName;
elf64.setupSecionNames(std::move(sectionNames));
elf64.overrideSymbolName = true;
elf64.symbolTable.reserve(2);
auto &localFuncSymbol = elf64.symbolTable.emplace_back();
localFuncSymbol.setBinding(NEO::Elf::SYMBOL_TABLE_BIND::STB_LOCAL);
localFuncSymbol.setType(NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC);
localFuncSymbol.name = 0x20;
localFuncSymbol.other = 0;
localFuncSymbol.shndx = 0;
localFuncSymbol.size = 0x8;
localFuncSymbol.value = 0x4000;
auto &localIgnoredSymbol = elf64.symbolTable.emplace_back();
localIgnoredSymbol.setBinding(NEO::Elf::SYMBOL_TABLE_BIND::STB_LOCAL);
localIgnoredSymbol.setType(NEO::Elf::SYMBOL_TABLE_TYPE::STT_OBJECT);
localIgnoredSymbol.name = std::numeric_limits<uint32_t>::max();
localIgnoredSymbol.other = std::numeric_limits<uint8_t>::max();
localIgnoredSymbol.shndx = std::numeric_limits<uint16_t>::max();
localIgnoredSymbol.size = std::numeric_limits<uint64_t>::max();
localIgnoredSymbol.value = std::numeric_limits<uint64_t>::max();
NEO::LinkerInput::SectionNameToSegmentIdMap nameToKernelId;
linkerInput.decodeElfSymbolTableAndRelocations(elf64, nameToKernelId);
const auto &symbols = linkerInput.getSymbols();
EXPECT_EQ(0u, symbols.size());
const auto &localSymbols = linkerInput.getLocalSymbols();
EXPECT_EQ(1u, localSymbols.size());
const auto &retrievedSymbol = localSymbols.at(std::to_string(localFuncSymbol.name));
EXPECT_EQ(retrievedSymbol.offset, localFuncSymbol.value);
EXPECT_EQ(retrievedSymbol.size, localFuncSymbol.size);
EXPECT_STREQ(retrievedSymbol.targetedKernelSectionName.c_str(), kernelName.c_str());
}
TEST(LinkerTest, givenLocalFuncSymbolsWhenProcessingRelocationsThenLocalSymbolsAreRelocated) {
std::string kernelName{"test_kernel"};
WhiteBox<NEO::LinkerInput> linkerInput;
LocalFuncSymbolInfo localSymInfo;
localSymInfo.offset = 0x20;
localSymInfo.size = 0x30;
localSymInfo.targetedKernelSectionName = kernelName;
LocalFuncSymbolInfo ignoredSymInfo;
ignoredSymInfo.offset = 0x40;
ignoredSymInfo.size = 0x50;
ignoredSymInfo.targetedKernelSectionName = "mismatched_kernel";
linkerInput.localSymbols[localSymInfo.targetedKernelSectionName] = localSymInfo;
linkerInput.localSymbols[ignoredSymInfo.targetedKernelSectionName] = ignoredSymInfo;
WhiteBox<NEO::Linker> linker(linkerInput);
const NEO::Linker::SegmentInfo gVariables{}, gConstants{}, expFuncs{}, gStrings{};
NEO::Linker::PatchableSegments insSegments;
insSegments.reserve(2);
auto &emplacedTargeted = insSegments.emplace_back();
emplacedTargeted.gpuAddress = 0x1000;
emplacedTargeted.kernelName = kernelName;
auto &emplacedOther = insSegments.emplace_back();
emplacedOther.gpuAddress = 0x2000;
emplacedOther.kernelName = "other_kernel";
auto res = linker.processRelocations(gVariables, gConstants, expFuncs, gStrings, insSegments);
EXPECT_TRUE(res);
EXPECT_EQ(1u, linker.localRelocatedSymbols.size());
const auto &localRelocatedSymbolInfo = linker.localRelocatedSymbols.at(kernelName);
EXPECT_EQ(emplacedTargeted.gpuAddress + localSymInfo.offset, localRelocatedSymbolInfo.gpuAddress);
}