Skip to content

Commit

Permalink
[libomptarget] Support BE ELF files in plugins-nextgen
Browse files Browse the repository at this point in the history
Code in plugins-nextgen reading ELF files is currently hard-coded
to assume a 64-bit little-endian ELF format.  Unfortunately, this
assumption is even embedded in the interface between GlobalHandler
and Utils/ELF routines, which use ELF64LE types.

To fix this, I've refactored the interface to push all ELF specific
types into Utils/ELF.  Specifically, this patch removes both the
getSymbol and getSymbolAddress routines and replaces them with a
single findSymbolInImage, which gets a MemoryBufferRef identifying
the raw object file image as input, and returns a StringRef covering
the data addressed by the symbol (address and size) if found, or an
empty StringRef otherwise.

This allows properly templating over multiple ELF format variants
inside Utils/ELF; specifically, this patch adds support for 64-bit
big-endian ELF files in addition to 64-bit little-endian files.
  • Loading branch information
uweigand committed Mar 5, 2024
1 parent ff66e9b commit 407ac26
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 71 deletions.
Expand Up @@ -103,9 +103,6 @@ class GenericGlobalHandlerTy {
public:
virtual ~GenericGlobalHandlerTy() {}

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

/// Returns whether the symbol named \p SymName is present in the given \p
/// Image.
bool isSymbolInImage(GenericDeviceTy &Device, DeviceImageTy &Image,
Expand Down
16 changes: 6 additions & 10 deletions openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h
Expand Up @@ -27,18 +27,14 @@ bool isELF(llvm::StringRef Buffer);
/// Checks if the given \p Object is a valid ELF matching the e_machine value.
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);

/// Returns the symbol associated with the \p Name in the \p ELFObj. It will
/// Returns the symbol associated with the \p Name in the \p Obj. 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);
/// executable file without a hash table. If the symbol is not found, returns
/// an empty StringRef; otherwise, returns a StringRef covering the symbol's
/// data in the Obj buffer, based on its address and size
llvm::Expected<llvm::StringRef>
findSymbolInImage(const llvm::MemoryBufferRef Obj, llvm::StringRef Name);

} // namespace elf
} // namespace utils
Expand Down
45 changes: 8 additions & 37 deletions openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp
Expand Up @@ -25,16 +25,6 @@ using namespace omp;
using namespace target;
using namespace plugin;

Expected<ELF64LEObjectFile>
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;
}

Error GenericGlobalHandlerTy::moveGlobalBetweenDeviceAndHost(
GenericDeviceTy &Device, DeviceImageTy &Image, const GlobalTy &HostGlobal,
bool Device2Host) {
Expand Down Expand Up @@ -81,55 +71,36 @@ Error GenericGlobalHandlerTy::moveGlobalBetweenDeviceAndHost(
bool GenericGlobalHandlerTy::isSymbolInImage(GenericDeviceTy &Device,
DeviceImageTy &Image,
StringRef SymName) {
// Get the ELF object file for the image. Notice the ELF object may already
// be created in previous calls, so we can reuse it. If this is unsuccessful
// just return false as we couldn't find it.
auto ELFObjOrErr = getELFObjectFile(Image);
if (!ELFObjOrErr) {
consumeError(ELFObjOrErr.takeError());
return false;
}

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

return *SymOrErr;
return !SymOrErr->empty();
}

Error GenericGlobalHandlerTy::getGlobalMetadataFromImage(
GenericDeviceTy &Device, DeviceImageTy &Image, GlobalTy &ImageGlobal) {

// Get the ELF object file for the image. Notice the ELF object may already
// be created in previous calls, so we can reuse it.
auto ELFObj = getELFObjectFile(Image);
if (!ELFObj)
return ELFObj.takeError();

// Search the ELF symbol using the symbol name.
auto SymOrErr = utils::elf::getSymbol(*ELFObj, ImageGlobal.getName());
auto SymOrErr = utils::elf::findSymbolInImage(Image.getMemoryBuffer(),
ImageGlobal.getName());
if (!SymOrErr)
return Plugin::error("Failed ELF lookup of global '%s': %s",
ImageGlobal.getName().data(),
toString(SymOrErr.takeError()).data());

if (!*SymOrErr)
if (SymOrErr->empty())
return Plugin::error("Failed to find global symbol '%s' in the ELF image",
ImageGlobal.getName().data());

auto AddrOrErr = utils::elf::getSymbolAddress(*ELFObj, **SymOrErr);
// Get the section to which the symbol belongs.
if (!AddrOrErr)
return Plugin::error("Failed to get ELF symbol from global '%s': %s",
ImageGlobal.getName().data(),
toString(AddrOrErr.takeError()).data());

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

return Plugin::success();
}
Expand Down
98 changes: 77 additions & 21 deletions openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp
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,27 @@ 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();

// Little-endian 64-bit
if (const ELF64LEObjectFile *ELFObj =
dyn_cast<ELF64LEObjectFile>(&**ElfOrErr))
return checkMachineImpl(*ELFObj, EMachine);
// Big-endian 64-bit
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 @@ -231,8 +244,9 @@ getSymTableSymbol(const ELFFile<ELFT> &Elf, const typename ELFT::Shdr &Sec,
return nullptr;
}

Expected<const typename ELF64LE::Sym *>
utils::elf::getSymbol(const ELFObjectFile<ELF64LE> &ELFObj, StringRef Name) {
template <class ELFT>
static Expected<const typename ELFT::Sym *>
getSymbol(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 +255,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.getELFFile(), **HashTabOrErr, Name);
}

// If this is an executable file check the entire standard symbol table.
Expand All @@ -253,16 +266,17 @@ 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.getELFFile(), **SymTabOrErr, Name);
}

return nullptr;
}

Expected<const void *> utils::elf::getSymbolAddress(
const object::ELFObjectFile<object::ELF64LE> &ELFObj,
const object::ELF64LE::Sym &Symbol) {
const ELFFile<ELF64LE> &ELFFile = ELFObj.getELFFile();
template <class ELFT>
static Expected<const void *>
getSymbolAddress(const object::ELFObjectFile<ELFT> &ELFObj,
const typename ELFT::Sym &Symbol) {
const ELFFile<ELFT> &ELFFile = ELFObj.getELFFile();

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

return ELFFile.base() + Offset;
}

template <class ELFT>
static Expected<StringRef>
findSymbolInImageImpl(const object::ELFObjectFile<ELFT> &ELFObj,
StringRef Name) {
// Search for the symbol by name.
auto SymOrErr = getSymbol(ELFObj, Name);
if (!SymOrErr)
return SymOrErr.takeError();
// If symbol not found, return an empty StringRef.
if (!*SymOrErr)
return StringRef();

// Retrieve the symbol address within the object's memory image.
auto AddrOrErr = getSymbolAddress(ELFObj, **SymOrErr);
if (!AddrOrErr)
return AddrOrErr.takeError();

// Return a StringRef covering the symbol's data, based on
// its address and size.
return StringRef(static_cast<const char *>(*AddrOrErr), (*SymOrErr)->st_size);
}

Expected<StringRef> utils::elf::findSymbolInImage(MemoryBufferRef Obj,
StringRef Name) {
assert(isELF(Obj.getBuffer()) && "Input is not an ELF!");

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

// Little-endian 64-bit
if (const ELF64LEObjectFile *ELFObj =
dyn_cast<ELF64LEObjectFile>(&**ElfOrErr))
return findSymbolInImageImpl(*ELFObj, Name);
// Big-endian 64-bit
if (const ELF64BEObjectFile *ELFObj =
dyn_cast<ELF64BEObjectFile>(&**ElfOrErr))
return findSymbolInImageImpl(*ELFObj, Name);
return createError("Only 64-bit ELF files are supported");
}

0 comments on commit 407ac26

Please sign in to comment.