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

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