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