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