[Openmp-commits] [openmp] [libomptarget] Support BE ELF files in plugins-nextgen (PR #83976)

Ulrich Weigand via Openmp-commits openmp-commits at lists.llvm.org
Tue Mar 5 01:12:43 PST 2024


https://github.com/uweigand updated https://github.com/llvm/llvm-project/pull/83976

>From 407ac26bf8cf46f9c0760f7c73021b343a6dc1bc Mon Sep 17 00:00:00 2001
From: Ulrich Weigand <ulrich.weigand at de.ibm.com>
Date: Tue, 5 Mar 2024 09:52:14 +0100
Subject: [PATCH] [libomptarget] Support BE ELF files in plugins-nextgen

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.
---
 .../common/include/GlobalHandler.h            |  3 -
 .../common/include/Utils/ELF.h                | 16 ++-
 .../common/src/GlobalHandler.cpp              | 45 ++-------
 .../plugins-nextgen/common/src/Utils/ELF.cpp  | 98 +++++++++++++++----
 4 files changed, 91 insertions(+), 71 deletions(-)

diff --git a/openmp/libomptarget/plugins-nextgen/common/include/GlobalHandler.h b/openmp/libomptarget/plugins-nextgen/common/include/GlobalHandler.h
index 5c767995126b77..57d93b88afcaa9 100644
--- a/openmp/libomptarget/plugins-nextgen/common/include/GlobalHandler.h
+++ b/openmp/libomptarget/plugins-nextgen/common/include/GlobalHandler.h
@@ -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,
diff --git a/openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h b/openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h
index 140a6b6b84aa12..367c05369dd934 100644
--- a/openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h
+++ b/openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h
@@ -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
diff --git a/openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp b/openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp
index d398f60c55bd13..d3c883ce7d0c4a 100644
--- a/openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp
+++ b/openmp/libomptarget/plugins-nextgen/common/src/GlobalHandler.cpp
@@ -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) {
@@ -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();
 }
diff --git a/openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp b/openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp
index c84c3bad5def0a..d172d20dbc34d9 100644
--- a/openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp
+++ b/openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.cpp
@@ -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");
 
@@ -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,
@@ -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)
@@ -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.
@@ -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)
@@ -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");
+}



More information about the Openmp-commits mailing list