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 | template <class ELFT> |
40 | static Expected<bool> |
41 | checkMachineImpl(const object::ELFObjectFile<ELFT> &ELFObj, uint16_t EMachine) { |
42 | const auto = 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 | |
66 | Expected<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 | |
85 | template <class ELFT> |
86 | static Expected<const typename ELFT::Sym *> |
87 | getSymbolFromGnuHashTable(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 | |
126 | template <class ELFT> |
127 | static Expected<const typename ELFT::Sym *> |
128 | getSymbolFromSysVHashTable(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 | |
151 | template <class ELFT> |
152 | static Expected<std::optional<ELFSymbolRef>> |
153 | getHashTableSymbol(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 | |
228 | template <class ELFT> |
229 | static Expected<std::optional<ELFSymbolRef>> |
230 | getSymTableSymbol(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 | |
257 | template <class ELFT> |
258 | static Expected<std::optional<ELFSymbolRef>> |
259 | getSymbolImpl(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 | |
285 | Expected<std::optional<ELFSymbolRef>> |
286 | utils::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 | |
294 | template <class ELFT> |
295 | static Expected<const void *> |
296 | getSymbolAddressImpl(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 | |
325 | Expected<const void *> |
326 | utils::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 | |