Skip to content

[libomptarget] Support BE ELF files in plugins-nextgen #85246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class GenericGlobalHandlerTy {
virtual ~GenericGlobalHandlerTy() {}

/// Helper function for getting an ELF from a device image.
Expected<ELF64LEObjectFile> getELFObjectFile(DeviceImageTy &Image);
Expected<std::unique_ptr<ObjectFile>> getELFObjectFile(DeviceImageTy &Image);

/// Returns whether the symbol named \p SymName is present in the given \p
/// Image.
Expand Down
10 changes: 4 additions & 6 deletions openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,15 @@ bool isELF(llvm::StringRef Buffer);
llvm::Expected<bool> checkMachine(llvm::StringRef Object, uint16_t EMachine);

/// Returns a pointer to the given \p Symbol inside of an ELF object.
llvm::Expected<const void *> getSymbolAddress(
const llvm::object::ELFObjectFile<llvm::object::ELF64LE> &ELFObj,
const llvm::object::ELF64LE::Sym &Symbol);
llvm::Expected<const void *>
getSymbolAddress(const llvm::object::ELFSymbolRef &Symbol);

/// Returns the symbol associated with the \p Name in the \p ELFObj. It will
/// first search for the hash sections to identify symbols from the hash table.
/// If that fails it will fall back to a linear search in the case of an
/// executable file without a hash table.
llvm::Expected<const typename llvm::object::ELF64LE::Sym *>
getSymbol(const llvm::object::ELFObjectFile<llvm::object::ELF64LE> &ELFObj,
llvm::StringRef Name);
llvm::Expected<std::optional<llvm::object::ELFSymbolRef>>
getSymbol(const llvm::object::ObjectFile &ELFObj, llvm::StringRef Name);

} // namespace elf
} // namespace utils
Expand Down
16 changes: 7 additions & 9 deletions openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@ using namespace omp;
using namespace target;
using namespace plugin;

Expected<ELF64LEObjectFile>
Expected<std::unique_ptr<ObjectFile>>
GenericGlobalHandlerTy::getELFObjectFile(DeviceImageTy &Image) {
assert(utils::elf::isELF(Image.getMemoryBuffer().getBuffer()) &&
"Input is not an ELF file");

Expected<ELF64LEObjectFile> ElfOrErr =
ELF64LEObjectFile::create(Image.getMemoryBuffer());
return ElfOrErr;
return ELFObjectFileBase::createELFObjectFile(Image.getMemoryBuffer());
}

Error GenericGlobalHandlerTy::moveGlobalBetweenDeviceAndHost(
Expand Down Expand Up @@ -91,13 +89,13 @@ bool GenericGlobalHandlerTy::isSymbolInImage(GenericDeviceTy &Device,
}

// Search the ELF symbol using the symbol name.
auto SymOrErr = utils::elf::getSymbol(*ELFObjOrErr, SymName);
auto SymOrErr = utils::elf::getSymbol(**ELFObjOrErr, SymName);
if (!SymOrErr) {
consumeError(SymOrErr.takeError());
return false;
}

return *SymOrErr;
return SymOrErr->has_value();
}

Error GenericGlobalHandlerTy::getGlobalMetadataFromImage(
Expand All @@ -110,7 +108,7 @@ Error GenericGlobalHandlerTy::getGlobalMetadataFromImage(
return ELFObj.takeError();

// Search the ELF symbol using the symbol name.
auto SymOrErr = utils::elf::getSymbol(*ELFObj, ImageGlobal.getName());
auto SymOrErr = utils::elf::getSymbol(**ELFObj, ImageGlobal.getName());
if (!SymOrErr)
return Plugin::error("Failed ELF lookup of global '%s': %s",
ImageGlobal.getName().data(),
Expand All @@ -120,7 +118,7 @@ Error GenericGlobalHandlerTy::getGlobalMetadataFromImage(
return Plugin::error("Failed to find global symbol '%s' in the ELF image",
ImageGlobal.getName().data());

auto AddrOrErr = utils::elf::getSymbolAddress(*ELFObj, **SymOrErr);
auto AddrOrErr = utils::elf::getSymbolAddress(**SymOrErr);
// Get the section to which the symbol belongs.
if (!AddrOrErr)
return Plugin::error("Failed to get ELF symbol from global '%s': %s",
Expand All @@ -129,7 +127,7 @@ Error GenericGlobalHandlerTy::getGlobalMetadataFromImage(

// Setup the global symbol's address and size.
ImageGlobal.setPtr(const_cast<void *>(*AddrOrErr));
ImageGlobal.setSize((*SymOrErr)->st_size);
ImageGlobal.setSize((*SymOrErr)->getSize());

return Plugin::success();
}
Expand Down
110 changes: 77 additions & 33 deletions openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,10 @@ bool utils::elf::isELF(StringRef Buffer) {
}
}

Expected<bool> utils::elf::checkMachine(StringRef Object, uint16_t EMachine) {
assert(isELF(Object) && "Input is not an ELF!");

Expected<ELF64LEObjectFile> ElfOrErr =
ELF64LEObjectFile::create(MemoryBufferRef(Object, /*Identifier=*/""),
/*InitContent=*/false);
if (!ElfOrErr)
return ElfOrErr.takeError();

const auto Header = ElfOrErr->getELFFile().getHeader();
if (Header.e_ident[EI_CLASS] != ELFCLASS64)
return createError("Only 64-bit ELF files are supported");
template <class ELFT>
static Expected<bool>
checkMachineImpl(const object::ELFObjectFile<ELFT> &ELFObj, uint16_t EMachine) {
const auto Header = ELFObj.getELFFile().getHeader();
if (Header.e_type != ET_EXEC && Header.e_type != ET_DYN)
return createError("Only executable ELF files are supported");

Expand All @@ -71,6 +63,25 @@ Expected<bool> utils::elf::checkMachine(StringRef Object, uint16_t EMachine) {
return Header.e_machine == EMachine;
}

Expected<bool> utils::elf::checkMachine(StringRef Object, uint16_t EMachine) {
assert(isELF(Object) && "Input is not an ELF!");

Expected<std::unique_ptr<ObjectFile>> ElfOrErr =
ObjectFile::createELFObjectFile(
MemoryBufferRef(Object, /*Identifier=*/""),
/*InitContent=*/false);
if (!ElfOrErr)
return ElfOrErr.takeError();

if (const ELF64LEObjectFile *ELFObj =
dyn_cast<ELF64LEObjectFile>(&**ElfOrErr))
return checkMachineImpl(*ELFObj, EMachine);
if (const ELF64BEObjectFile *ELFObj =
dyn_cast<ELF64BEObjectFile>(&**ElfOrErr))
return checkMachineImpl(*ELFObj, EMachine);
return createError("Only 64-bit ELF files are supported");
}

template <class ELFT>
static Expected<const typename ELFT::Sym *>
getSymbolFromGnuHashTable(StringRef Name, const typename ELFT::GnuHash &HashTab,
Expand Down Expand Up @@ -138,9 +149,10 @@ getSymbolFromSysVHashTable(StringRef Name, const typename ELFT::Hash &HashTab,
}

template <class ELFT>
static Expected<const typename ELFT::Sym *>
getHashTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,
StringRef Name) {
static Expected<std::optional<ELFSymbolRef>>
getHashTableSymbol(const ELFObjectFile<ELFT> &ELFObj,
const typename ELFT::Shdr &Sec, StringRef Name) {
const ELFFile<ELFT> &Elf = ELFObj.getELFFile();
Comment on lines +152 to +155
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this part changed? It's internal so it should be using the "actual" ELF format and not the Object file wrapper I would assume.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to at some point convert a ELFT::Sym pointer into a ELFSymbolRef. This requires access to both the symbol table (section header) containing the symbol, and the Object file wrapper (which provides the ELFObj.toSymbolRef routine that actually creates the ELFSymbolRef. We could move the call to toSymbolRef to the caller, but there we don't have ready access to the symbol table, so we'd have to re-compute it again. Doing it inside getHashTableSymbol seems the lesser change to me.

if (Sec.sh_type != ELF::SHT_HASH && Sec.sh_type != ELF::SHT_GNU_HASH)
return createError(
"invalid sh_type for hash table, expected SHT_HASH or SHT_GNU_HASH");
Expand Down Expand Up @@ -179,7 +191,10 @@ getHashTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,
sizeof(typename ELFT::Word) * HashTab->nbuckets +
sizeof(typename ELFT::Word) * (SymTab.size() - HashTab->symndx))
return createError("section has invalid sh_size: " + Twine(Sec.sh_size));
return getSymbolFromGnuHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
auto Sym = getSymbolFromGnuHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
if (!Sym)
return Sym.takeError();
return ELFObj.toSymbolRef(*SymTabOrErr, *Sym - &SymTab[0]);
}

// If this is a Sys-V hash table we verify its size and search the symbol
Expand All @@ -197,16 +212,20 @@ getHashTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,
sizeof(typename ELFT::Word) * HashTab->nchain)
return createError("section has invalid sh_size: " + Twine(Sec.sh_size));

return getSymbolFromSysVHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
auto Sym = getSymbolFromSysVHashTable<ELFT>(Name, *HashTab, SymTab, StrTab);
if (!Sym)
return Sym.takeError();
return ELFObj.toSymbolRef(*SymTabOrErr, *Sym - &SymTab[0]);
}

return nullptr;
return std::nullopt;
}

template <class ELFT>
static Expected<const typename ELFT::Sym *>
getSymTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,
StringRef Name) {
static Expected<std::optional<ELFSymbolRef>>
getSymTableSymbol(const ELFObjectFile<ELFT> &ELFObj,
const typename ELFT::Shdr &Sec, StringRef Name) {
const ELFFile<ELFT> &Elf = ELFObj.getELFFile();
if (Sec.sh_type != ELF::SHT_SYMTAB && Sec.sh_type != ELF::SHT_DYNSYM)
return createError(
"invalid sh_type for hash table, expected SHT_SYMTAB or SHT_DYNSYM");
Expand All @@ -226,13 +245,14 @@ getSymTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,

for (const typename ELFT::Sym &Sym : SymTab)
if (StrTab.drop_front(Sym.st_name).data() == Name)
return &Sym;
return ELFObj.toSymbolRef(&Sec, &Sym - &SymTab[0]);

return nullptr;
return std::nullopt;
}

Expected<const typename ELF64LE::Sym *>
utils::elf::getSymbol(const ELFObjectFile<ELF64LE> &ELFObj, StringRef Name) {
template <class ELFT>
static Expected<std::optional<ELFSymbolRef>>
getSymbolImpl(const ELFObjectFile<ELFT> &ELFObj, StringRef Name) {
// First try to look up the symbol via the hash table.
for (ELFSectionRef Sec : ELFObj.sections()) {
if (Sec.getType() != SHT_HASH && Sec.getType() != SHT_GNU_HASH)
Expand All @@ -241,8 +261,7 @@ utils::elf::getSymbol(const ELFObjectFile<ELF64LE> &ELFObj, StringRef Name) {
auto HashTabOrErr = ELFObj.getELFFile().getSection(Sec.getIndex());
if (!HashTabOrErr)
return HashTabOrErr.takeError();
return getHashTableSymbol<ELF64LE>(ELFObj.getELFFile(), **HashTabOrErr,
Name);
return getHashTableSymbol<ELFT>(ELFObj, **HashTabOrErr, Name);
}

// If this is an executable file check the entire standard symbol table.
Expand All @@ -253,16 +272,31 @@ utils::elf::getSymbol(const ELFObjectFile<ELF64LE> &ELFObj, StringRef Name) {
auto SymTabOrErr = ELFObj.getELFFile().getSection(Sec.getIndex());
if (!SymTabOrErr)
return SymTabOrErr.takeError();
return getSymTableSymbol<ELF64LE>(ELFObj.getELFFile(), **SymTabOrErr, Name);
return getSymTableSymbol<ELFT>(ELFObj, **SymTabOrErr, Name);
}

return nullptr;
return std::nullopt;
}

Expected<const void *> utils::elf::getSymbolAddress(
const object::ELFObjectFile<object::ELF64LE> &ELFObj,
const object::ELF64LE::Sym &Symbol) {
const ELFFile<ELF64LE> &ELFFile = ELFObj.getELFFile();
Expected<std::optional<ELFSymbolRef>>
utils::elf::getSymbol(const ObjectFile &Obj, StringRef Name) {
if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(&Obj))
return getSymbolImpl(*ELFObj, Name);
if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(&Obj))
return getSymbolImpl(*ELFObj, Name);
Comment on lines +283 to +286
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not just do getELFFile here like before?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the ObjectFile wrapper so we can call toSymbolRef, as discussed above.

return createError("Only 64-bit ELF files are supported");
}

template <class ELFT>
static Expected<const void *>
getSymbolAddressImpl(const ELFObjectFile<ELFT> &ELFObj,
const ELFSymbolRef &SymRef) {
const ELFFile<ELFT> &ELFFile = ELFObj.getELFFile();

auto SymOrErr = ELFObj.getSymbol(SymRef.getRawDataRefImpl());
if (!SymOrErr)
return SymOrErr.takeError();
const auto &Symbol = **SymOrErr;

auto SecOrErr = ELFFile.getSection(Symbol.st_shndx);
if (!SecOrErr)
Expand All @@ -283,3 +317,13 @@ Expected<const void *> utils::elf::getSymbolAddress(

return ELFFile.base() + Offset;
}

Expected<const void *>
utils::elf::getSymbolAddress(const ELFSymbolRef &SymRef) {
const ObjectFile *Obj = SymRef.getObject();
if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
return getSymbolAddressImpl(*ELFObj, SymRef);
if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
return getSymbolAddressImpl(*ELFObj, SymRef);
return createError("Only 64-bit ELF files are supported");
}
2 changes: 1 addition & 1 deletion openmp/libomptarget/plugins-nextgen/cuda/src/rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,7 @@ struct CUDADeviceTy : public GenericDeviceTy {

// Search for all symbols that contain a constructor or destructor.
SmallVector<std::pair<StringRef, uint16_t>> Funcs;
for (ELFSymbolRef Sym : ELFObjOrErr->symbols()) {
for (ELFSymbolRef Sym : (*ELFObjOrErr)->symbols()) {
auto NameOrErr = Sym.getName();
if (!NameOrErr)
return NameOrErr.takeError();
Expand Down