360 lines · cpp
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-exception6//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#else53#warning "Unknown ELF compilation target architecture"54 return EM_NONE;55#endif56}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("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_V5 &&69 Header.e_ident[EI_ABIVERSION] != ELFABIVERSION_AMDGPU_HSA_V6)70 return createError("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("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("invalid CUDA addressing mode");79 if ((Header.e_flags & EF_CUDA_SM) < EF_CUDA_SM35)80 return createError("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("unsupported NVPTX architecture");84 } else {85 return createError("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("symbol [index " + Twine(I) +142 "] has invalid st_name: " + Twine(SymTab[I].st_name));143 if (StrTab.drop_front(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(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 "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("symbol [index " + Twine(I) +169 "] has invalid st_name: " + Twine(SymTab[I].st_name));170 171 if (StrTab.drop_front(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 "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 symbol205 // 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("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("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 symbol229 // 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("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("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 "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(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 no337 // offset.338 if (Section->sh_type == ELF::SHT_NOBITS)339 return createError(340 "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("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