[llvm] 61835d1 - [llvm-objcopy] Initial XCOFF32 support.

via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 28 02:00:15 PST 2022


Author: esmeyi
Date: 2022-02-28T04:59:46-05:00
New Revision: 61835d19a848ecd3530d9b86deb6b15f336ae6d6

URL: https://github.com/llvm/llvm-project/commit/61835d19a848ecd3530d9b86deb6b15f336ae6d6
DIFF: https://github.com/llvm/llvm-project/commit/61835d19a848ecd3530d9b86deb6b15f336ae6d6.diff

LOG: [llvm-objcopy] Initial XCOFF32 support.

Summary: This is an initial implementation of lvm-objcopy for XCOFF32.
Currently only supports simple copying, op-passthrough to follow.

Reviewed By: jhenderson, shchenz

Differential Revision: https://reviews.llvm.org/D97656

Added: 
    llvm/include/llvm/ObjCopy/XCOFF/XCOFFConfig.h
    llvm/include/llvm/ObjCopy/XCOFF/XCOFFObjcopy.h
    llvm/lib/ObjCopy/XCOFF/XCOFFObjcopy.cpp
    llvm/lib/ObjCopy/XCOFF/XCOFFObject.h
    llvm/lib/ObjCopy/XCOFF/XCOFFReader.cpp
    llvm/lib/ObjCopy/XCOFF/XCOFFReader.h
    llvm/lib/ObjCopy/XCOFF/XCOFFWriter.cpp
    llvm/lib/ObjCopy/XCOFF/XCOFFWriter.h
    llvm/test/tools/llvm-objcopy/XCOFF/basic-copy.test
    llvm/test/tools/llvm-objcopy/XCOFF/invalid-read.test

Modified: 
    llvm/include/llvm/ObjCopy/ConfigManager.h
    llvm/include/llvm/ObjCopy/MultiFormatConfig.h
    llvm/include/llvm/Object/XCOFFObjectFile.h
    llvm/lib/ObjCopy/CMakeLists.txt
    llvm/lib/ObjCopy/ConfigManager.cpp
    llvm/lib/ObjCopy/ObjCopy.cpp
    llvm/lib/Object/XCOFFObjectFile.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ObjCopy/ConfigManager.h b/llvm/include/llvm/ObjCopy/ConfigManager.h
index 3aac601fca9a3..2962cf99b270d 100644
--- a/llvm/include/llvm/ObjCopy/ConfigManager.h
+++ b/llvm/include/llvm/ObjCopy/ConfigManager.h
@@ -15,6 +15,7 @@
 #include "llvm/ObjCopy/MachO/MachOConfig.h"
 #include "llvm/ObjCopy/MultiFormatConfig.h"
 #include "llvm/ObjCopy/wasm/WasmConfig.h"
+#include "llvm/ObjCopy/XCOFF/XCOFFConfig.h"
 
 namespace llvm {
 namespace objcopy {
@@ -32,12 +33,15 @@ struct ConfigManager : public MultiFormatConfig {
 
   Expected<const WasmConfig &> getWasmConfig() const override;
 
+  Expected<const XCOFFConfig &> getXCOFFConfig() const override;
+
   // All configs.
   CommonConfig Common;
   ELFConfig ELF;
   COFFConfig COFF;
   MachOConfig MachO;
   WasmConfig Wasm;
+  XCOFFConfig XCOFF;
 };
 
 } // namespace objcopy

diff  --git a/llvm/include/llvm/ObjCopy/MultiFormatConfig.h b/llvm/include/llvm/ObjCopy/MultiFormatConfig.h
index 022751b6228bb..180f2f82a908b 100644
--- a/llvm/include/llvm/ObjCopy/MultiFormatConfig.h
+++ b/llvm/include/llvm/ObjCopy/MultiFormatConfig.h
@@ -19,6 +19,7 @@ struct ELFConfig;
 struct COFFConfig;
 struct MachOConfig;
 struct WasmConfig;
+struct XCOFFConfig;
 
 class MultiFormatConfig {
 public:
@@ -29,6 +30,7 @@ class MultiFormatConfig {
   virtual Expected<const COFFConfig &> getCOFFConfig() const = 0;
   virtual Expected<const MachOConfig &> getMachOConfig() const = 0;
   virtual Expected<const WasmConfig &> getWasmConfig() const = 0;
+  virtual Expected<const XCOFFConfig &> getXCOFFConfig() const = 0;
 };
 
 } // namespace objcopy

diff  --git a/llvm/include/llvm/ObjCopy/XCOFF/XCOFFConfig.h b/llvm/include/llvm/ObjCopy/XCOFF/XCOFFConfig.h
new file mode 100644
index 0000000000000..adaeedc82b73b
--- /dev/null
+++ b/llvm/include/llvm/ObjCopy/XCOFF/XCOFFConfig.h
@@ -0,0 +1,21 @@
+//===- XCOFFConfig.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJCOPY_XCOFF_XCOFFCONFIG_H
+#define LLVM_OBJCOPY_XCOFF_XCOFFCONFIG_H
+
+namespace llvm {
+namespace objcopy {
+
+// XCOFF specific configuration for copying/stripping a single file.
+struct XCOFFConfig {};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_OBJCOPY_XCOFF_XCOFFCONFIG_H

diff  --git a/llvm/include/llvm/ObjCopy/XCOFF/XCOFFObjcopy.h b/llvm/include/llvm/ObjCopy/XCOFF/XCOFFObjcopy.h
new file mode 100644
index 0000000000000..9fc85cb39fa5b
--- /dev/null
+++ b/llvm/include/llvm/ObjCopy/XCOFF/XCOFFObjcopy.h
@@ -0,0 +1,35 @@
+//===- XCOFFObjcopy.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJCOPY_XCOFF_XCOFFOBJCOPY_H
+#define LLVM_OBJCOPY_XCOFF_XCOFFOBJCOPY_H
+
+namespace llvm {
+class Error;
+class raw_ostream;
+
+namespace object {
+class XCOFFObjectFile;
+} // end namespace object
+
+namespace objcopy {
+struct CommonConfig;
+struct XCOFFConfig;
+
+namespace xcoff {
+/// Apply the transformations described by \p Config and \p XCOFFConfig
+/// to \p In and writes the result into \p Out.
+/// \returns any Error encountered whilst performing the operation.
+Error executeObjcopyOnBinary(const CommonConfig &Config, const XCOFFConfig &,
+                             object::XCOFFObjectFile &In, raw_ostream &Out);
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_OBJCOPY_XCOFF_XCOFFOBJCOPY_H

diff  --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h
index 5aad03b888fc2..585ead214380f 100644
--- a/llvm/include/llvm/Object/XCOFFObjectFile.h
+++ b/llvm/include/llvm/Object/XCOFFObjectFile.h
@@ -451,9 +451,6 @@ class XCOFFObjectFile : public ObjectFile {
   const void *SymbolTblPtr = nullptr;
   XCOFFStringTable StringTable = {0, nullptr};
 
-  const XCOFFFileHeader32 *fileHeader32() const;
-  const XCOFFFileHeader64 *fileHeader64() const;
-
   const XCOFFSectionHeader32 *sectionHeaderTable32() const;
   const XCOFFSectionHeader64 *sectionHeaderTable64() const;
   template <typename T> const T *sectionHeaderTable() const;
@@ -551,6 +548,8 @@ class XCOFFObjectFile : public ObjectFile {
 
   // Below here is the non-inherited interface.
   bool is64Bit() const;
+  Expected<StringRef> getRawData(const char *Start, uint64_t Size,
+                                 StringRef Name) const;
 
   const XCOFFAuxiliaryHeader32 *auxiliaryHeader32() const;
   const XCOFFAuxiliaryHeader64 *auxiliaryHeader64() const;
@@ -562,6 +561,8 @@ class XCOFFObjectFile : public ObjectFile {
   XCOFFSymbolRef toSymbolRef(DataRefImpl Ref) const;
 
   // File header related interfaces.
+  const XCOFFFileHeader32 *fileHeader32() const;
+  const XCOFFFileHeader64 *fileHeader64() const;
   uint16_t getMagic() const;
   uint16_t getNumberOfSections() const;
   int32_t getTimeStamp() const;
@@ -690,6 +691,9 @@ class XCOFFSymbolRef {
       Entry32 = reinterpret_cast<const XCOFFSymbolEntry32 *>(SymEntDataRef.p);
   }
 
+  const XCOFFSymbolEntry32 *getSymbol32() { return Entry32; }
+  const XCOFFSymbolEntry64 *getSymbol64() { return Entry64; }
+
   uint64_t getValue() const { return Entry32 ? getValue32() : getValue64(); }
 
   uint32_t getValue32() const { return Entry32->Value; }

diff  --git a/llvm/lib/ObjCopy/CMakeLists.txt b/llvm/lib/ObjCopy/CMakeLists.txt
index 9b8365cd2f89c..54557f260f8d0 100644
--- a/llvm/lib/ObjCopy/CMakeLists.txt
+++ b/llvm/lib/ObjCopy/CMakeLists.txt
@@ -13,6 +13,9 @@ source_group("Header Files\\MachO" REGULAR_EXPRESSION
 source_group("Header Files\\wasm" REGULAR_EXPRESSION
   wasm/.*[.]h
 )
+source_group("Header Files\\XCOFF" REGULAR_EXPRESSION
+  XCOFF/.*[.]h
+)
 source_group("Source Files" REGULAR_EXPRESSION
   .*[.]cpp
 )
@@ -28,6 +31,9 @@ source_group("Source Files\\MachO" REGULAR_EXPRESSION
 source_group("Source Files\\wasm" REGULAR_EXPRESSION
   wasm/.*[.]cpp
 )
+source_group("Source Files\\XCOFF" REGULAR_EXPRESSION
+  XCOFF/.*[.]cpp
+)
 
 add_llvm_component_library(LLVMObjCopy
   Archive.cpp
@@ -48,6 +54,9 @@ add_llvm_component_library(LLVMObjCopy
   wasm/WasmReader.cpp
   wasm/WasmWriter.cpp
   wasm/WasmObjcopy.cpp
+  XCOFF/XCOFFObjcopy.cpp
+  XCOFF/XCOFFReader.cpp
+  XCOFF/XCOFFWriter.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCopy
@@ -55,10 +64,12 @@ add_llvm_component_library(LLVMObjCopy
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCopy/ELF
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCopy/MachO
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCopy/wasm
+  ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCopy/XCOFF
   COFF
   ELF
   MachO
   wasm
+  XCOFF
  
   DEPENDS
   intrinsics_gen

diff  --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp
index 6f6e1bd1a74f8..9d8883a15c0bf 100644
--- a/llvm/lib/ObjCopy/ConfigManager.cpp
+++ b/llvm/lib/ObjCopy/ConfigManager.cpp
@@ -66,5 +66,32 @@ Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
   return Wasm;
 }
 
+Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
+  if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition ||
+      !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
+      !Common.AllocSectionsPrefix.empty() ||
+      Common.DiscardMode != DiscardType::None || !Common.AddSection.empty() ||
+      !Common.DumpSection.empty() || !Common.SymbolsToAdd.empty() ||
+      !Common.KeepSection.empty() || !Common.OnlySection.empty() ||
+      !Common.ToRemove.empty() || !Common.SymbolsToGlobalize.empty() ||
+      !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() ||
+      !Common.SymbolsToRemove.empty() ||
+      !Common.UnneededSymbolsToRemove.empty() ||
+      !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
+      !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
+      !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty() ||
+      Common.ExtractDWO || Common.ExtractMainPartition ||
+      Common.OnlyKeepDebug || Common.PreserveDates || Common.StripAllGNU ||
+      Common.StripDWO || Common.StripDebug || Common.StripNonAlloc ||
+      Common.StripSections || Common.Weaken || Common.StripUnneeded ||
+      Common.DecompressDebugSections) {
+    return createStringError(
+        llvm::errc::invalid_argument,
+        "no flags are supported yet, only basic copying is allowed");
+  }
+
+  return XCOFF;
+}
+
 } // end namespace objcopy
 } // end namespace llvm

diff  --git a/llvm/lib/ObjCopy/ObjCopy.cpp b/llvm/lib/ObjCopy/ObjCopy.cpp
index a62f476567f56..16968d2022657 100644
--- a/llvm/lib/ObjCopy/ObjCopy.cpp
+++ b/llvm/lib/ObjCopy/ObjCopy.cpp
@@ -17,12 +17,15 @@
 #include "llvm/ObjCopy/MultiFormatConfig.h"
 #include "llvm/ObjCopy/wasm/WasmConfig.h"
 #include "llvm/ObjCopy/wasm/WasmObjcopy.h"
+#include "llvm/ObjCopy/XCOFF/XCOFFConfig.h"
+#include "llvm/ObjCopy/XCOFF/XCOFFObjcopy.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Object/Error.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Object/Wasm.h"
+#include "llvm/Object/XCOFFObjectFile.h"
 #include "llvm/Support/SmallVectorMemoryBuffer.h"
 
 namespace llvm {
@@ -71,6 +74,14 @@ Error executeObjcopyOnBinary(const MultiFormatConfig &Config,
     return objcopy::wasm::executeObjcopyOnBinary(Config.getCommonConfig(),
                                                  *WasmConfig, *WasmBinary, Out);
   }
+  if (auto *XCOFFBinary = dyn_cast<object::XCOFFObjectFile>(&In)) {
+    Expected<const XCOFFConfig &> XCOFFConfig = Config.getXCOFFConfig();
+    if (!XCOFFConfig)
+      return XCOFFConfig.takeError();
+
+    return xcoff::executeObjcopyOnBinary(Config.getCommonConfig(), *XCOFFConfig,
+                                         *XCOFFBinary, Out);
+  }
   return createStringError(object_error::invalid_file_type,
                            "unsupported object file format");
 }

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFObjcopy.cpp b/llvm/lib/ObjCopy/XCOFF/XCOFFObjcopy.cpp
new file mode 100644
index 0000000000000..f6e29bd315cb1
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFObjcopy.cpp
@@ -0,0 +1,45 @@
+//===- XCOFFObjcopy.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjCopy/CommonConfig.h"
+#include "llvm/ObjCopy/XCOFF/XCOFFConfig.h"
+#include "llvm/ObjCopy/XCOFF/XCOFFObjcopy.h"
+#include "llvm/Support/Errc.h"
+#include "XCOFFObject.h"
+#include "XCOFFReader.h"
+#include "XCOFFWriter.h"
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+using namespace object;
+
+static Error handleArgs(const CommonConfig &Config, Object &Obj) {
+  return Error::success();
+}
+
+Error executeObjcopyOnBinary(const CommonConfig &Config, const XCOFFConfig &,
+                             XCOFFObjectFile &In, raw_ostream &Out) {
+  XCOFFReader Reader(In);
+  Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
+  if (!ObjOrErr)
+    return createFileError(Config.InputFilename, ObjOrErr.takeError());
+  Object *Obj = ObjOrErr->get();
+  assert(Obj && "Unable to deserialize XCOFF object");
+  if (Error E = handleArgs(Config, *Obj))
+    return createFileError(Config.InputFilename, std::move(E));
+  XCOFFWriter Writer(*Obj, Out);
+  if (Error E = Writer.write())
+    return createFileError(Config.OutputFilename, std::move(E));
+  return Error::success();
+}
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFObject.h b/llvm/lib/ObjCopy/XCOFF/XCOFFObject.h
new file mode 100644
index 0000000000000..3c68b6d3878fc
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFObject.h
@@ -0,0 +1,48 @@
+//===- XCOFFObject.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_OBJCOPY_XCOFF_XCOFFOBJECT_H
+#define LLVM_LIB_OBJCOPY_XCOFF_XCOFFOBJECT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+using namespace object;
+
+struct Section {
+  XCOFFSectionHeader32 SectionHeader;
+  ArrayRef<uint8_t> Contents;
+  std::vector<XCOFFRelocation32> Relocations;
+};
+
+struct Symbol {
+  XCOFFSymbolEntry32 Sym;
+  // For now, each auxiliary symbol is only an opaque binary blob with no
+  // distinction.
+  StringRef AuxSymbolEntries;
+};
+
+struct Object {
+  XCOFFFileHeader32 FileHeader;
+  XCOFFAuxiliaryHeader32 OptionalFileHeader;
+  std::vector<Section> Sections;
+  std::vector<Symbol> Symbols;
+  StringRef StringTable;
+};
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_LIB_OBJCOPY_XCOFF_XCOFFOBJECT_H

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFReader.cpp b/llvm/lib/ObjCopy/XCOFF/XCOFFReader.cpp
new file mode 100644
index 0000000000000..8ad3021a03428
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFReader.cpp
@@ -0,0 +1,101 @@
+//===- XCOFFReader.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "XCOFFReader.h"
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+using namespace object;
+
+Error XCOFFReader::readSections(Object &Obj) const {
+  ArrayRef<XCOFFSectionHeader32> Sections = XCOFFObj.sections32();
+  for (const XCOFFSectionHeader32 &Sec : Sections) {
+    Section ReadSec;
+    // Section header.
+    ReadSec.SectionHeader = Sec;
+    DataRefImpl SectionDRI;
+    SectionDRI.p = reinterpret_cast<uintptr_t>(&Sec);
+
+    // Section data.
+    if (Sec.SectionSize) {
+      Expected<ArrayRef<uint8_t>> ContentsRef =
+          XCOFFObj.getSectionContents(SectionDRI);
+      if (!ContentsRef)
+        return ContentsRef.takeError();
+      ReadSec.Contents = ContentsRef.get();
+    }
+
+    // Relocations.
+    if (Sec.NumberOfRelocations) {
+      auto Relocations =
+          XCOFFObj.relocations<XCOFFSectionHeader32, XCOFFRelocation32>(Sec);
+      if (!Relocations)
+        return Relocations.takeError();
+      for (const XCOFFRelocation32 &Rel : Relocations.get())
+        ReadSec.Relocations.push_back(Rel);
+    }
+
+    Obj.Sections.push_back(std::move(ReadSec));
+  }
+  return Error::success();
+}
+
+Error XCOFFReader::readSymbols(Object &Obj) const {
+  std::vector<Symbol> Symbols;
+  Symbols.reserve(XCOFFObj.getNumberOfSymbolTableEntries());
+  for (SymbolRef Sym : XCOFFObj.symbols()) {
+    Symbol ReadSym;
+    DataRefImpl SymbolDRI = Sym.getRawDataRefImpl();
+    XCOFFSymbolRef SymbolEntRef = XCOFFObj.toSymbolRef(SymbolDRI);
+    ReadSym.Sym = *SymbolEntRef.getSymbol32();
+    // Auxiliary entries.
+    if (SymbolEntRef.getNumberOfAuxEntries()) {
+      const char *Start = reinterpret_cast<const char *>(
+          SymbolDRI.p + XCOFF::SymbolTableEntrySize);
+      Expected<StringRef> RawAuxEntriesOrError = XCOFFObj.getRawData(
+          Start,
+          XCOFF::SymbolTableEntrySize * SymbolEntRef.getNumberOfAuxEntries(),
+          StringRef("symbol"));
+      if (!RawAuxEntriesOrError)
+        return RawAuxEntriesOrError.takeError();
+      ReadSym.AuxSymbolEntries = RawAuxEntriesOrError.get();
+    }
+    Obj.Symbols.push_back(std::move(ReadSym));
+  }
+  return Error::success();
+}
+
+Expected<std::unique_ptr<Object>> XCOFFReader::create() const {
+  auto Obj = std::make_unique<Object>();
+  // Only 32-bit supported now.
+  if (XCOFFObj.is64Bit())
+    return createStringError(object_error::invalid_file_type,
+                             "64-bit XCOFF is not supported yet");
+  // Read the file header.
+  Obj->FileHeader = *XCOFFObj.fileHeader32();
+  // Read the optional header.
+  if (XCOFFObj.getOptionalHeaderSize())
+    Obj->OptionalFileHeader = *XCOFFObj.auxiliaryHeader32();
+  // Read each section.
+  Obj->Sections.reserve(XCOFFObj.getNumberOfSections());
+  if (Error E = readSections(*Obj))
+    return std::move(E);
+  // Read each symbol.
+  Obj->Symbols.reserve(XCOFFObj.getRawNumberOfSymbolTableEntries32());
+  if (Error E = readSymbols(*Obj))
+    return std::move(E);
+  // String table.
+  Obj->StringTable = XCOFFObj.getStringTable();
+  return std::move(Obj);
+}
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFReader.h b/llvm/lib/ObjCopy/XCOFF/XCOFFReader.h
new file mode 100644
index 0000000000000..63a8d8579d37b
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFReader.h
@@ -0,0 +1,35 @@
+//===- XCOFFReader.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H
+#define LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H
+
+#include "XCOFFObject.h"
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+using namespace object;
+
+class XCOFFReader {
+public:
+  explicit XCOFFReader(const XCOFFObjectFile &O) : XCOFFObj(O) {}
+  Expected<std::unique_ptr<Object>> create() const;
+
+private:
+  const XCOFFObjectFile &XCOFFObj;
+  Error readSections(Object &Obj) const;
+  Error readSymbols(Object &Obj) const;
+};
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.cpp b/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.cpp
new file mode 100644
index 0000000000000..bae3128822e22
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.cpp
@@ -0,0 +1,125 @@
+//===- XCOFFWriter.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Errc.h"
+#include "XCOFFWriter.h"
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+using namespace object;
+
+void XCOFFWriter::finalizeHeaders() {
+  // File header.
+  FileSize += sizeof(XCOFFFileHeader32);
+  // Optional file header.
+  FileSize += Obj.FileHeader.AuxHeaderSize;
+  // Section headers.
+  FileSize += sizeof(XCOFFSectionHeader32) * Obj.Sections.size();
+}
+
+void XCOFFWriter::finalizeSections() {
+  for (const Section &Sec : Obj.Sections) {
+    // Section data.
+    FileSize += Sec.Contents.size();
+    // Relocations.
+    FileSize +=
+        Sec.SectionHeader.NumberOfRelocations * sizeof(XCOFFRelocation32);
+  }
+}
+
+void XCOFFWriter::finalizeSymbolStringTable() {
+  assert(Obj.FileHeader.SymbolTableOffset >= FileSize);
+  FileSize = Obj.FileHeader.SymbolTableOffset;
+  // Symbols and auxiliary entries.
+  FileSize +=
+      Obj.FileHeader.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
+  // String table.
+  FileSize += Obj.StringTable.size();
+}
+
+void XCOFFWriter::finalize() {
+  FileSize = 0;
+  finalizeHeaders();
+  finalizeSections();
+  finalizeSymbolStringTable();
+}
+
+void XCOFFWriter::writeHeaders() {
+  // Write the file header.
+  uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+  memcpy(Ptr, &Obj.FileHeader, sizeof(XCOFFFileHeader32));
+  Ptr += sizeof(XCOFFFileHeader32);
+
+  // Write the optional header.
+  if (Obj.FileHeader.AuxHeaderSize) {
+    memcpy(Ptr, &Obj.OptionalFileHeader, Obj.FileHeader.AuxHeaderSize);
+    Ptr += Obj.FileHeader.AuxHeaderSize;
+  }
+
+  // Write section headers.
+  for (const Section &Sec : Obj.Sections) {
+    memcpy(Ptr, &Sec.SectionHeader, sizeof(XCOFFSectionHeader32));
+    Ptr += sizeof(XCOFFSectionHeader32);
+  }
+}
+
+void XCOFFWriter::writeSections() {
+  // Write section data.
+  for (const Section &Sec : Obj.Sections) {
+    uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+                   Sec.SectionHeader.FileOffsetToRawData;
+    Ptr = std::copy(Sec.Contents.begin(), Sec.Contents.end(), Ptr);
+  }
+
+  // Write relocations.
+  for (const Section &Sec : Obj.Sections) {
+    uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+                   Sec.SectionHeader.FileOffsetToRelocationInfo;
+    for (const XCOFFRelocation32 &Rel : Sec.Relocations) {
+      memcpy(Ptr, &Rel, sizeof(XCOFFRelocation32));
+      Ptr += sizeof(XCOFFRelocation32);
+    }
+  }
+}
+
+void XCOFFWriter::writeSymbolStringTable() {
+  // Write symbols.
+  uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+                 Obj.FileHeader.SymbolTableOffset;
+  for (const Symbol &Sym : Obj.Symbols) {
+    memcpy(Ptr, &Sym.Sym, XCOFF::SymbolTableEntrySize);
+    Ptr += XCOFF::SymbolTableEntrySize;
+    // Auxiliary symbols.
+    memcpy(Ptr, Sym.AuxSymbolEntries.data(), Sym.AuxSymbolEntries.size());
+    Ptr += Sym.AuxSymbolEntries.size();
+  }
+  // Write the string table.
+  memcpy(Ptr, Obj.StringTable.data(), Obj.StringTable.size());
+  Ptr += Obj.StringTable.size();
+}
+
+Error XCOFFWriter::write() {
+  finalize();
+  Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize);
+  if (!Buf)
+    return createStringError(errc::not_enough_memory,
+                             "failed to allocate memory buffer of " +
+                                 Twine::utohexstr(FileSize) + " bytes");
+
+  writeHeaders();
+  writeSections();
+  writeSymbolStringTable();
+  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+  return Error::success();
+}
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm

diff  --git a/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.h b/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.h
new file mode 100644
index 0000000000000..54c7b5f3ccbe7
--- /dev/null
+++ b/llvm/lib/ObjCopy/XCOFF/XCOFFWriter.h
@@ -0,0 +1,48 @@
+//===- XCOFFWriter.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H
+#define LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H
+
+#include "llvm/Support/MemoryBuffer.h"
+#include "XCOFFObject.h"
+
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace xcoff {
+
+class XCOFFWriter {
+public:
+  virtual ~XCOFFWriter() {}
+  XCOFFWriter(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {}
+  Error write();
+
+private:
+  Object &Obj;
+  raw_ostream &Out;
+  std::unique_ptr<WritableMemoryBuffer> Buf;
+  size_t FileSize;
+
+  void finalizeHeaders();
+  void finalizeSections();
+  void finalizeSymbolStringTable();
+  void finalize();
+
+  void writeHeaders();
+  void writeSections();
+  void writeSymbolStringTable();
+};
+
+} // end namespace xcoff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H

diff  --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp
index d9ecb0aff6bdc..5e699e97e052a 100644
--- a/llvm/lib/Object/XCOFFObjectFile.cpp
+++ b/llvm/lib/Object/XCOFFObjectFile.cpp
@@ -709,6 +709,19 @@ bool XCOFFObjectFile::is64Bit() const {
   return Binary::ID_XCOFF64 == getType();
 }
 
+Expected<StringRef> XCOFFObjectFile::getRawData(const char *Start,
+                                                uint64_t Size,
+                                                StringRef Name) const {
+  uintptr_t StartPtr = reinterpret_cast<uintptr_t>(Start);
+  // TODO: this path is untested.
+  if (Error E = Binary::checkOffset(Data, StartPtr, Size))
+    return createError(toString(std::move(E)) + ": " + Name.data() +
+                       " data with offset 0x" + Twine::utohexstr(StartPtr) +
+                       " and size 0x" + Twine::utohexstr(Size) +
+                       " goes past the end of the file");
+  return StringRef(Start, Size);
+}
+
 uint16_t XCOFFObjectFile::getMagic() const {
   return is64Bit() ? fileHeader64()->Magic : fileHeader32()->Magic;
 }

diff  --git a/llvm/test/tools/llvm-objcopy/XCOFF/basic-copy.test b/llvm/test/tools/llvm-objcopy/XCOFF/basic-copy.test
new file mode 100644
index 0000000000000..467efd9b5bbf9
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/XCOFF/basic-copy.test
@@ -0,0 +1,31 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy %t %t.out
+# RUN: cmp %t %t.out
+
+--- !XCOFF
+FileHeader:
+  MagicNumber: 0x1DF
+AuxiliaryHeader:
+  Magic: 0x10B
+Sections:
+  - Name:        .text
+    Flags:       [ STYP_TEXT ]
+    SectionData: "123456"
+  - Name:        .data
+    Flags:       [ STYP_DATA ]
+    SectionData: "067891"
+    Relocations:
+      - Address: 0x3A
+        Type:    0x02
+Symbols:
+  - Name:         aux_fcn_csect
+    StorageClass: C_EXT
+    Type:         0x20
+    AuxEntries:
+      - Type: AUX_FCN
+      - Type: AUX_CSECT
+  - Name:         aux_stat
+    StorageClass: C_STAT
+    AuxEntries:
+      - Type: AUX_STAT
+...

diff  --git a/llvm/test/tools/llvm-objcopy/XCOFF/invalid-read.test b/llvm/test/tools/llvm-objcopy/XCOFF/invalid-read.test
new file mode 100644
index 0000000000000..1df63406a4136
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/XCOFF/invalid-read.test
@@ -0,0 +1,47 @@
+## Check that llvm-objcopy reports a suitable error when it
+## encounters invalid input during reading.
+
+## Failed to read section data.
+# RUN: yaml2obj %s --docnum=1 -o %t1
+# RUN: not llvm-objcopy %t1 %t1.out 2>&1 | FileCheck %s -DFILE=%t1 --check-prefix=ERROR1
+
+# ERROR1: error: '[[FILE]]': The end of the file was unexpectedly encountered: section data with offset 0x70 and size 0x4 goes past the end of the file
+
+--- !XCOFF
+FileHeader:
+  MagicNumber: 0x01DF
+Sections:
+  - SectionData:      '00007400'
+    FileOffsetToData: 0x70
+
+## Failed to read relocations.
+# RUN: yaml2obj %s --docnum=2 -o %t2
+# RUN: not llvm-objcopy %t2 %t2.out 2>&1 | FileCheck %s -DFILE=%t2 --check-prefix=ERROR2
+
+# ERROR2: error: '[[FILE]]': The end of the file was unexpectedly encountered: relocations with offset 0x3c and size 0x1e go past the end of the file
+
+--- !XCOFF
+FileHeader:
+  MagicNumber: 0x01DF
+Sections:
+  - NumberOfRelocations: 0x3
+    Relocations:
+      - Address: 0xE
+        Symbol:  0x12
+        Info:    0xF
+        Type:    0x3
+
+## Failed to read the symbols.
+# RUN: yaml2obj %s --docnum=3 -o %t3
+# RUN: not llvm-objcopy %t3 %t3.out 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=ERROR3
+
+# ERROR3: error: '[[FILE]]': The end of the file was unexpectedly encountered: symbol table with offset 0x15 and size 0x24 goes past the end of the file
+
+--- !XCOFF
+FileHeader:
+  MagicNumber:         0x01DF
+  OffsetToSymbolTable: 0x15
+Symbols:
+  - Name:         foo
+    AuxEntries:
+      - Type: AUX_CSECT


        


More information about the llvm-commits mailing list