1 /*
2 * Copyright (C) 2019-2021 Intel Corporation
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 */
7
8 #pragma once
9 #include "shared/source/device_binary_format/elf/elf_decoder.h"
10
11 #include <cstdint>
12 #include <limits>
13 #include <string>
14 #include <type_traits>
15 #include <unordered_map>
16 #include <vector>
17
18 namespace NEO {
19
20 class Device;
21 class GraphicsAllocation;
22 struct KernelDescriptor;
23
24 enum class SegmentType : uint32_t {
25 Unknown,
26 GlobalConstants,
27 GlobalStrings,
28 GlobalVariables,
29 Instructions,
30 };
31
32 enum class LinkingStatus : uint32_t {
33 Error,
34 LinkedFully,
35 LinkedPartially
36 };
37
asString(SegmentType segment)38 inline const char *asString(SegmentType segment) {
39 switch (segment) {
40 default:
41 return "UNKOWN";
42 case SegmentType::GlobalConstants:
43 return "GLOBAL_CONSTANTS";
44 case SegmentType::GlobalVariables:
45 return "GLOBAL_VARIABLES";
46 case SegmentType::Instructions:
47 return "INSTRUCTIONS";
48 }
49 }
50
51 struct SymbolInfo {
52 uint32_t offset = std::numeric_limits<uint32_t>::max();
53 uint32_t size = std::numeric_limits<uint32_t>::max();
54 SegmentType segment = SegmentType::Unknown;
55 };
56
57 struct LinkerInput {
58 union Traits {
59 enum PointerSize : uint8_t {
60 Ptr32bit = 0,
61 Ptr64bit = 1
62 };
Traits()63 Traits() : packed(0) {
64 pointerSize = (sizeof(void *) == 4) ? PointerSize::Ptr32bit : PointerSize::Ptr64bit;
65 }
66 struct {
67 bool exportsGlobalVariables : 1;
68 bool exportsGlobalConstants : 1;
69 bool exportsFunctions : 1;
70 bool requiresPatchingOfInstructionSegments : 1;
71 bool requiresPatchingOfGlobalVariablesBuffer : 1;
72 bool requiresPatchingOfGlobalConstantsBuffer : 1;
73 uint8_t pointerSize : 1;
74 };
75 uint32_t packed;
76 };
77 static_assert(sizeof(Traits) == sizeof(Traits::packed), "");
78
79 struct RelocationInfo {
80 enum class Type : uint32_t {
81 Unknown,
82 Address,
83 AddressLow,
84 AddressHigh,
85 PerThreadPayloadOffset,
86 RelocTypeMax
87 };
88
89 std::string symbolName;
90 uint64_t offset = std::numeric_limits<uint64_t>::max();
91 Type type = Type::Unknown;
92 SegmentType relocationSegment = SegmentType::Unknown;
93 };
94 using SectionNameToSegmentIdMap = std::unordered_map<std::string, uint32_t>;
95 using Relocations = std::vector<RelocationInfo>;
96 using SymbolMap = std::unordered_map<std::string, SymbolInfo>;
97 using RelocationsPerInstSegment = std::vector<Relocations>;
98
99 virtual ~LinkerInput() = default;
100
101 static SegmentType getSegmentForSection(ConstStringRef name);
102
103 MOCKABLE_VIRTUAL bool decodeGlobalVariablesSymbolTable(const void *data, uint32_t numEntries);
104 MOCKABLE_VIRTUAL bool decodeExportedFunctionsSymbolTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId);
105 MOCKABLE_VIRTUAL bool decodeRelocationTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId);
106 void addDataRelocationInfo(const RelocationInfo &relocationInfo);
107
108 void addElfTextSegmentRelocation(RelocationInfo relocationInfo, uint32_t instructionsSegmentId);
109 void decodeElfSymbolTableAndRelocations(Elf::Elf<Elf::EI_CLASS_64> &elf, const SectionNameToSegmentIdMap &nameToSegmentId);
110
getTraitsLinkerInput111 const Traits &getTraits() const {
112 return traits;
113 }
114
getExportedFunctionsSegmentIdLinkerInput115 int32_t getExportedFunctionsSegmentId() const {
116 return exportedFunctionsSegmentId;
117 }
118
getSymbolsLinkerInput119 const SymbolMap &getSymbols() const {
120 return symbols;
121 }
122
addSymbolLinkerInput123 void addSymbol(const std::string &symbolName, const SymbolInfo &symbolInfo) {
124 symbols.emplace(std::make_pair(symbolName, symbolInfo));
125 }
126
getRelocationsInInstructionSegmentsLinkerInput127 const RelocationsPerInstSegment &getRelocationsInInstructionSegments() const {
128 return relocations;
129 }
130
getDataRelocationsLinkerInput131 const Relocations &getDataRelocations() const {
132 return dataRelocations;
133 }
134
setPointerSizeLinkerInput135 void setPointerSize(Traits::PointerSize pointerSize) {
136 traits.pointerSize = pointerSize;
137 }
138
isValidLinkerInput139 bool isValid() const {
140 return valid;
141 }
142
143 bool undefinedSymbolsAllowed = false;
144
145 protected:
146 Traits traits;
147 SymbolMap symbols;
148 RelocationsPerInstSegment relocations;
149 Relocations dataRelocations;
150 int32_t exportedFunctionsSegmentId = -1;
151 bool valid = true;
152 };
153
154 struct Linker {
155 static constexpr std::string_view subDeviceID{"__SubDeviceID"};
156
157 using RelocationInfo = LinkerInput::RelocationInfo;
158
159 struct SegmentInfo {
160 uintptr_t gpuAddress = std::numeric_limits<uintptr_t>::max();
161 size_t segmentSize = std::numeric_limits<size_t>::max();
162 };
163
164 struct PatchableSegment {
165 void *hostPointer = nullptr;
166 size_t segmentSize = std::numeric_limits<size_t>::max();
167 };
168
169 struct UnresolvedExternal {
170 RelocationInfo unresolvedRelocation;
171 uint32_t instructionsSegmentId = std::numeric_limits<uint32_t>::max();
172 bool internalError = false;
173 };
174
175 struct RelocatedSymbol {
176 SymbolInfo symbol;
177 uintptr_t gpuAddress = std::numeric_limits<uintptr_t>::max();
178 };
179
180 using RelocatedSymbolsMap = std::unordered_map<std::string, RelocatedSymbol>;
181 using PatchableSegments = std::vector<PatchableSegment>;
182 using UnresolvedExternals = std::vector<UnresolvedExternal>;
183 using KernelDescriptorsT = std::vector<KernelDescriptor *>;
184
LinkerLinker185 Linker(const LinkerInput &data)
186 : data(data) {
187 }
188
linkLinker189 LinkingStatus link(const SegmentInfo &globalVariablesSegInfo, const SegmentInfo &globalConstantsSegInfo, const SegmentInfo &exportedFunctionsSegInfo, const SegmentInfo &globalStringsSegInfo,
190 GraphicsAllocation *globalVariablesSeg, GraphicsAllocation *globalConstantsSeg, const PatchableSegments &instructionsSegments,
191 UnresolvedExternals &outUnresolvedExternals, Device *pDevice, const void *constantsInitData, const void *variablesInitData, const KernelDescriptorsT &kernelDescriptors) {
192 bool success = data.isValid();
193 auto initialUnresolvedExternalsCount = outUnresolvedExternals.size();
194 success = success && processRelocations(globalVariablesSegInfo, globalConstantsSegInfo, exportedFunctionsSegInfo, globalStringsSegInfo);
195 if (!success) {
196 return LinkingStatus::Error;
197 }
198 patchInstructionsSegments(instructionsSegments, outUnresolvedExternals);
199 patchDataSegments(globalVariablesSegInfo, globalConstantsSegInfo, globalVariablesSeg, globalConstantsSeg,
200 outUnresolvedExternals, pDevice, constantsInitData, variablesInitData);
201 resolveImplicitArgs(kernelDescriptors, pDevice);
202 resolveBuiltins(pDevice, outUnresolvedExternals, instructionsSegments);
203 if (initialUnresolvedExternalsCount < outUnresolvedExternals.size()) {
204 return LinkingStatus::LinkedPartially;
205 }
206 return LinkingStatus::LinkedFully;
207 }
208 static void patchAddress(void *relocAddress, const RelocatedSymbol &symbol, const RelocationInfo &relocation);
extractRelocatedSymbolsLinker209 RelocatedSymbolsMap extractRelocatedSymbols() {
210 return RelocatedSymbolsMap(std::move(relocatedSymbols));
211 }
212
213 static void applyDebugDataRelocations(const NEO::Elf::Elf<NEO::Elf::EI_CLASS_64> &decodedElf, ArrayRef<uint8_t> inputOutputElf,
214 const SegmentInfo &text,
215 const SegmentInfo &globalData,
216 const SegmentInfo &constData);
217
218 protected:
219 const LinkerInput &data;
220 RelocatedSymbolsMap relocatedSymbols;
221
222 bool processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings);
223
224 void patchInstructionsSegments(const std::vector<PatchableSegment> &instructionsSegments, std::vector<UnresolvedExternal> &outUnresolvedExternals);
225
226 void patchDataSegments(const SegmentInfo &globalVariablesSegInfo, const SegmentInfo &globalConstantsSegInfo,
227 GraphicsAllocation *globalVariablesSeg, GraphicsAllocation *globalConstantsSeg,
228 std::vector<UnresolvedExternal> &outUnresolvedExternals, Device *pDevice,
229 const void *constantsInitData, const void *variablesInitData);
230
231 void resolveImplicitArgs(const KernelDescriptorsT &kernelDescriptors, Device *pDevice);
232 void resolveBuiltins(Device *pDevice, UnresolvedExternals &outUnresolvedExternals, const std::vector<PatchableSegment> &instructionsSegments);
233
234 template <typename PatchSizeT>
235 void patchIncrement(Device *pDevice, GraphicsAllocation *dstAllocation, size_t relocationOffset, const void *initData, uint64_t incrementValue);
236
237 std::unordered_map<uint32_t /*ISA segment id*/, uint32_t * /*implicit args relocation address to patch*/> pImplicitArgsRelocationAddresses;
238 };
239
240 std::string constructLinkerErrorMessage(const Linker::UnresolvedExternals &unresolvedExternals, const std::vector<std::string> &instructionsSegmentsNames);
241 std::string constructRelocationsDebugMessage(const Linker::RelocatedSymbolsMap &relocatedSymbols);
shouldIgnoreRelocation(const LinkerInput::RelocationInfo & relocation)242 constexpr bool shouldIgnoreRelocation(const LinkerInput::RelocationInfo &relocation) {
243 return LinkerInput::RelocationInfo::Type::PerThreadPayloadOffset == relocation.type;
244 }
245
246 } // namespace NEO
247