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

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