1//===-- Utils/ELF.cpp - Common ELF functionality --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Common ELF functionality for target plugins.
10//
11//===----------------------------------------------------------------------===//
12
13#include "Utils/ELF.h"
14
15#include "llvm/BinaryFormat/Magic.h"
16#include "llvm/Object/Binary.h"
17#include "llvm/Object/ELFObjectFile.h"
18#include "llvm/Object/ELFTypes.h"
19#include "llvm/Object/ObjectFile.h"
20#include "llvm/Support/MemoryBuffer.h"
21
22using namespace llvm;
23using namespace llvm::ELF;
24using namespace llvm::object;
25
26bool utils::elf::isELF(StringRef Buffer) {
27 switch (identify_magic(Buffer)) {
28 case file_magic::elf:
29 case file_magic::elf_relocatable:
30 case file_magic::elf_executable:
31 case file_magic::elf_shared_object:
32 case file_magic::elf_core:
33 return true;
34 default:
35 return false;
36 }
37}
38
39template <class ELFT>
40static Expected<bool>
41checkMachineImpl(const object::ELFObjectFile<ELFT> &ELFObj, uint16_t EMachine) {
42 const auto Header = ELFObj.getELFFile().getHeader();
43 if (Header.e_type != ET_EXEC && Header.e_type != ET_DYN)
44 return createError(Err: "Only executable ELF files are supported");
45
46 if (Header.e_machine == EM_AMDGPU) {
47 if (Header.e_ident[EI_OSABI] != ELFOSABI_AMDGPU_HSA)
48 return createError(Err: "Invalid AMD OS/ABI, must be AMDGPU_HSA");
49 if (Header.e_ident[EI_ABIVERSION] != ELFABIVERSION_AMDGPU_HSA_V4 &&
50 Header.e_ident[EI_ABIVERSION] != ELFABIVERSION_AMDGPU_HSA_V5)
51 return createError(Err: "Invalid AMD ABI version, must be version 4 or 5");
52 if ((Header.e_flags & EF_AMDGPU_MACH) < EF_AMDGPU_MACH_AMDGCN_GFX700 ||
53 (Header.e_flags & EF_AMDGPU_MACH) > EF_AMDGPU_MACH_AMDGCN_GFX1201)
54 return createError(Err: "Unsupported AMDGPU architecture");
55 } else if (Header.e_machine == EM_CUDA) {
56 if (~Header.e_flags & EF_CUDA_64BIT_ADDRESS)
57 return createError(Err: "Invalid CUDA addressing mode");
58 if ((Header.e_flags & EF_CUDA_SM) < EF_CUDA_SM35 ||
59 (Header.e_flags & EF_CUDA_SM) > EF_CUDA_SM90)
60 return createError(Err: "Unsupported NVPTX architecture");
61 }
62
63 return Header.e_machine == EMachine;
64}
65
66Expected<bool> utils::elf::checkMachine(StringRef Object, uint16_t EMachine) {
67 assert(isELF(Object) && "Input is not an ELF!");
68
69 Expected<std::unique_ptr<ObjectFile>> ElfOrErr =
70 ObjectFile::createELFObjectFile(
71 MemoryBufferRef(Object, /*Identifier=*/""),
72 /*InitContent=*/false);
73 if (!ElfOrErr)
74 return ElfOrErr.takeError();
75
76 if (const ELF64LEObjectFile *ELFObj =
77 dyn_cast<ELF64LEObjectFile>(&**ElfOrErr))
78 return checkMachineImpl(*ELFObj, EMachine);
79 if (const ELF64BEObjectFile *ELFObj =
80 dyn_cast<ELF64BEObjectFile>(&**ElfOrErr))
81 return checkMachineImpl(*ELFObj, EMachine);
82 return createError("Only 64-bit ELF files are supported");
83}
84
85template <class ELFT>
86static Expected<const typename ELFT::Sym *>
87getSymbolFromGnuHashTable(StringRef Name, const typename ELFT::GnuHash &HashTab,
88 ArrayRef<typename ELFT::Sym> SymTab,
89 StringRef StrTab) {
90 const uint32_t NameHash = hashGnu(Name);
91 const typename ELFT::Word NBucket = HashTab.nbuckets;
92 const typename ELFT::Word SymOffset = HashTab.symndx;
93 ArrayRef<typename ELFT::Off> Filter = HashTab.filter();
94 ArrayRef<typename ELFT::Word> Bucket = HashTab.buckets();
95 ArrayRef<typename ELFT::Word> Chain = HashTab.values(SymTab.size());
96
97 // Check the bloom filter and exit early if the symbol is not present.
98 uint64_t ElfClassBits = ELFT::Is64Bits ? 64 : 32;
99 typename ELFT::Off Word =
100 Filter[(NameHash / ElfClassBits) % HashTab.maskwords];
101 uint64_t Mask = (0x1ull << (NameHash % ElfClassBits)) |
102 (0x1ull << ((NameHash >> HashTab.shift2) % ElfClassBits));
103 if ((Word & Mask) != Mask)
104 return nullptr;
105
106 // The symbol may or may not be present, check the hash values.
107 for (typename ELFT::Word I = Bucket[NameHash % NBucket];
108 I >= SymOffset && I < SymTab.size(); I = I + 1) {
109 const uint32_t ChainHash = Chain[I - SymOffset];
110
111 if ((NameHash | 0x1) != (ChainHash | 0x1))
112 continue;
113
114 if (SymTab[I].st_name >= StrTab.size())
115 return createError(Err: "symbol [index " + Twine(I) +
116 "] has invalid st_name: " + Twine(SymTab[I].st_name));
117 if (StrTab.drop_front(N: SymTab[I].st_name).data() == Name)
118 return &SymTab[I];
119
120 if (ChainHash & 0x1)
121 return nullptr;
122 }
123 return nullptr;
124}
125
126template <class ELFT>
127static Expected<const typename ELFT::Sym *>
128getSymbolFromSysVHashTable(StringRef Name, const typename ELFT::Hash &HashTab,
129 ArrayRef<typename ELFT::Sym> SymTab,
130 StringRef StrTab) {
131 const uint32_t Hash = hashSysV(SymbolName: Name);
132 const typename ELFT::Word NBucket = HashTab.nbucket;
133 ArrayRef<typename ELFT::Word> Bucket = HashTab.buckets();
134 ArrayRef<typename ELFT::Word> Chain = HashTab.chains();
135 for (typename ELFT::Word I = Bucket[Hash % NBucket]; I != ELF::STN_UNDEF;
136 I = Chain[I]) {
137 if (I >= SymTab.size())
138 return createError(
139 Err: "symbol [index " + Twine(I) +
140 "] is greater than the number of symbols: " + Twine(SymTab.size()));
141 if (SymTab[I].st_name >= StrTab.size())
142 return createError(Err: "symbol [index " + Twine(I) +
143 "] has invalid st_name: " + Twine(SymTab[I].st_name));
144
145 if (StrTab.drop_front(N: SymTab[I].st_name).data() == Name)
146 return &SymTab[I];
147 }
148 return nullptr;
149}
150
151template <class ELFT>
152static Expected<std::optional<ELFSymbolRef>>
153getHashTableSymbol(const ELFObjectFile<ELFT> &ELFObj,
154 const typename ELFT::Shdr &Sec, StringRef Name) {
155 const ELFFile<ELFT> &Elf = ELFObj.getELFFile();
156 if (Sec.sh_type != ELF::SHT_HASH && Sec.sh_type != ELF::SHT_GNU_HASH)
157 return createError(
158 Err: "invalid sh_type for hash table, expected SHT_HASH or SHT_GNU_HASH");
159 Expected<typename ELFT::ShdrRange> SectionsOrError = Elf.sections();
160 if (!SectionsOrError)
161 return SectionsOrError.takeError();
162
163 auto SymTabOrErr = getSection<ELFT>(*SectionsOrError, Sec.sh_link);
164 if (!SymTabOrErr)
165 return SymTabOrErr.takeError();
166
167 auto StrTabOrErr =
168 Elf.getStringTableForSymtab(**SymTabOrErr, *SectionsOrError);
169 if (!StrTabOrErr)
170 return StrTabOrErr.takeError();
171 StringRef StrTab = *StrTabOrErr;
172
173 auto SymsOrErr = Elf.symbols(*SymTabOrErr);
174 if (!SymsOrErr)
175 return SymsOrErr.takeError();
176 ArrayRef<typename ELFT::Sym> SymTab = *SymsOrErr;
177
178 // If this is a GNU hash table we verify its size and search the symbol
179 // table using the GNU hash table format.
180 if (Sec.sh_type == ELF::SHT_GNU_HASH) {
181 const typename ELFT::GnuHash *HashTab =
182 reinterpret_cast<const typename ELFT::GnuHash *>(Elf.base() +
183 Sec.sh_offset);
184 if (Sec.sh_offset + Sec.sh_size >= Elf.getBufSize())
185 return createError(Err: "section has invalid sh_offset: " +
186 Twine(Sec.sh_offset));
187 if (Sec.sh_size < sizeof(typename ELFT::GnuHash) ||
188 Sec.sh_size <
189 sizeof(typename ELFT::GnuHash) +
190 sizeof(typename ELFT::Word) * HashTab->maskwords +
191 sizeof(typename ELFT::Word) * HashTab->nbuckets +
192 sizeof(typename ELFT::Word) * (SymTab.size() - HashTab->symndx))
193 return createError(Err: "section has invalid sh_size: " + Twine(Sec.sh_size));
194 auto Sym = getSymbolFromGnuHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
195 if (!Sym)
196 return Sym.takeError();
197 if (!*Sym)
198 return std::nullopt;
199 return ELFObj.toSymbolRef(*SymTabOrErr, *Sym - &SymTab[0]);
200 }
201
202 // If this is a Sys-V hash table we verify its size and search the symbol
203 // table using the Sys-V hash table format.
204 if (Sec.sh_type == ELF::SHT_HASH) {
205 const typename ELFT::Hash *HashTab =
206 reinterpret_cast<const typename ELFT::Hash *>(Elf.base() +
207 Sec.sh_offset);
208 if (Sec.sh_offset + Sec.sh_size >= Elf.getBufSize())
209 return createError(Err: "section has invalid sh_offset: " +
210 Twine(Sec.sh_offset));
211 if (Sec.sh_size < sizeof(typename ELFT::Hash) ||
212 Sec.sh_size < sizeof(typename ELFT::Hash) +
213 sizeof(typename ELFT::Word) * HashTab->nbucket +
214 sizeof(typename ELFT::Word) * HashTab->nchain)
215 return createError(Err: "section has invalid sh_size: " + Twine(Sec.sh_size));
216
217 auto Sym = getSymbolFromSysVHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
218 if (!Sym)
219 return Sym.takeError();
220 if (!*Sym)
221 return std::nullopt;
222 return ELFObj.toSymbolRef(*SymTabOrErr, *Sym - &SymTab[0]);
223 }
224
225 return std::nullopt;
226}
227
228template <class ELFT>
229static Expected<std::optional<ELFSymbolRef>>
230getSymTableSymbol(const ELFObjectFile<ELFT> &ELFObj,
231 const typename ELFT::Shdr &Sec, StringRef Name) {
232 const ELFFile<ELFT> &Elf = ELFObj.getELFFile();
233 if (Sec.sh_type != ELF::SHT_SYMTAB && Sec.sh_type != ELF::SHT_DYNSYM)
234 return createError(
235 Err: "invalid sh_type for hash table, expected SHT_SYMTAB or SHT_DYNSYM");
236 Expected<typename ELFT::ShdrRange> SectionsOrError = Elf.sections();
237 if (!SectionsOrError)
238 return SectionsOrError.takeError();
239
240 auto StrTabOrErr = Elf.getStringTableForSymtab(Sec, *SectionsOrError);
241 if (!StrTabOrErr)
242 return StrTabOrErr.takeError();
243 StringRef StrTab = *StrTabOrErr;
244
245 auto SymsOrErr = Elf.symbols(&Sec);
246 if (!SymsOrErr)
247 return SymsOrErr.takeError();
248 ArrayRef<typename ELFT::Sym> SymTab = *SymsOrErr;
249
250 for (const typename ELFT::Sym &Sym : SymTab)
251 if (StrTab.drop_front(N: Sym.st_name).data() == Name)
252 return ELFObj.toSymbolRef(&Sec, &Sym - &SymTab[0]);
253
254 return std::nullopt;
255}
256
257template <class ELFT>
258static Expected<std::optional<ELFSymbolRef>>
259getSymbolImpl(const ELFObjectFile<ELFT> &ELFObj, StringRef Name) {
260 // First try to look up the symbol via the hash table.
261 for (ELFSectionRef Sec : ELFObj.sections()) {
262 if (Sec.getType() != SHT_HASH && Sec.getType() != SHT_GNU_HASH)
263 continue;
264
265 auto HashTabOrErr = ELFObj.getELFFile().getSection(Sec.getIndex());
266 if (!HashTabOrErr)
267 return HashTabOrErr.takeError();
268 return getHashTableSymbol<ELFT>(ELFObj, **HashTabOrErr, Name);
269 }
270
271 // If this is an executable file check the entire standard symbol table.
272 for (ELFSectionRef Sec : ELFObj.sections()) {
273 if (Sec.getType() != SHT_SYMTAB)
274 continue;
275
276 auto SymTabOrErr = ELFObj.getELFFile().getSection(Sec.getIndex());
277 if (!SymTabOrErr)
278 return SymTabOrErr.takeError();
279 return getSymTableSymbol<ELFT>(ELFObj, **SymTabOrErr, Name);
280 }
281
282 return std::nullopt;
283}
284
285Expected<std::optional<ELFSymbolRef>>
286utils::elf::getSymbol(const ObjectFile &Obj, StringRef Name) {
287 if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(&Obj))
288 return getSymbolImpl(*ELFObj, Name);
289 if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(&Obj))
290 return getSymbolImpl(*ELFObj, Name);
291 return createError("Only 64-bit ELF files are supported");
292}
293
294template <class ELFT>
295static Expected<const void *>
296getSymbolAddressImpl(const ELFObjectFile<ELFT> &ELFObj,
297 const ELFSymbolRef &SymRef) {
298 const ELFFile<ELFT> &ELFFile = ELFObj.getELFFile();
299
300 auto SymOrErr = ELFObj.getSymbol(SymRef.getRawDataRefImpl());
301 if (!SymOrErr)
302 return SymOrErr.takeError();
303 const auto &Symbol = **SymOrErr;
304
305 auto SecOrErr = ELFFile.getSection(Symbol.st_shndx);
306 if (!SecOrErr)
307 return SecOrErr.takeError();
308 const auto &Section = *SecOrErr;
309
310 // A section with SHT_NOBITS occupies no space in the file and has no
311 // offset.
312 if (Section->sh_type == ELF::SHT_NOBITS)
313 return createError(
314 Err: "invalid sh_type for symbol lookup, cannot be SHT_NOBITS");
315
316 uint64_t Offset = Section->sh_offset - Section->sh_addr + Symbol.st_value;
317 if (Offset > ELFFile.getBufSize())
318 return createError(Err: "invalid offset [" + Twine(Offset) +
319 "] into ELF file of size [" +
320 Twine(ELFFile.getBufSize()) + "]");
321
322 return ELFFile.base() + Offset;
323}
324
325Expected<const void *>
326utils::elf::getSymbolAddress(const ELFSymbolRef &SymRef) {
327 const ObjectFile *Obj = SymRef.getObject();
328 if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
329 return getSymbolAddressImpl(*ELFObj, SymRef);
330 if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
331 return getSymbolAddressImpl(*ELFObj, SymRef);
332 return createError("Only 64-bit ELF files are supported");
333}
334

source code of offload/plugins-nextgen/common/src/Utils/ELF.cpp