[llvm] Segment based binary output format for objcopy (PR #68569)

via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 11 03:57:11 PDT 2023


https://github.com/quic-subhkedi updated https://github.com/llvm/llvm-project/pull/68569

>From e4a2341b16cfa16fdd953ab575eb6b3b05296217 Mon Sep 17 00:00:00 2001
From: SUBHAM KEDIA <quic_subhkedi at quicinc.com>
Date: Mon, 9 Oct 2023 14:55:45 +0530
Subject: [PATCH] Segment based binary output format for objcopy

1. Adds support for a new output format
for objcopy that is segments based binary output
formats. In this format the multiple output files
are created based on the number of loadable program
header count in the input elf with each files having
the content from the sections mapped into it.

2. Adds a new tool called llvm-fromelf where the required
options/features of ARM's fromelf can be added to.
---
 llvm/include/llvm/ObjCopy/CommonConfig.h      |   9 +-
 llvm/include/llvm/Object/ELFObjectFile.h      |   9 ++
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |   2 +
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            |  26 +++++
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  58 +++++++++-
 llvm/test/lit.cfg.py                          |   2 +
 .../tools/llvm-objcopy/tool-help-message.test |  12 ++
 llvm/test/tools/llvm-objcopy/tool-name.test   |  10 ++
 .../test/tools/llvm-objcopy/tool-version.test |   3 +
 llvm/tools/llvm-objcopy/CMakeLists.txt        |   9 +-
 llvm/tools/llvm-objcopy/FromElfOpts.td        |  37 ++++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    | 108 +++++++++++++++++-
 llvm/tools/llvm-objcopy/ObjcopyOptions.h      |   5 +
 llvm/tools/llvm-objcopy/llvm-objcopy.cpp      |  85 ++++++++------
 14 files changed, 334 insertions(+), 41 deletions(-)
 create mode 100644 llvm/tools/llvm-objcopy/FromElfOpts.td

diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index e7ce1e6f2c54d75..f1e10a663ab70a2 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -27,12 +27,7 @@
 namespace llvm {
 namespace objcopy {
 
-enum class FileFormat {
-  Unspecified,
-  ELF,
-  Binary,
-  IHex,
-};
+enum class FileFormat { Unspecified, ELF, Binary, IHex, SegBin };
 
 // This type keeps track of the machine info for various architectures. This
 // lets us map architecture names to ELF types and the e_machine value of the
@@ -213,6 +208,8 @@ struct CommonConfig {
   StringRef AddGnuDebugLink;
   // Cached gnu_debuglink's target CRC
   uint32_t GnuDebugLinkCRC32;
+  // Segment Index to be dumped
+  uint32_t SegmentIndex;
   std::optional<StringRef> ExtractPartition;
   StringRef SplitDWO;
   StringRef SymbolsPrefix;
diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h
index dc3d6bb58710c74..ffa17867cabab1e 100644
--- a/llvm/include/llvm/Object/ELFObjectFile.h
+++ b/llvm/include/llvm/Object/ELFObjectFile.h
@@ -114,6 +114,8 @@ class ELFObjectFileBase : public ObjectFile {
   // corresponding to the section with that index.
   Expected<std::vector<BBAddrMap>>
   readBBAddrMap(std::optional<unsigned> TextSectionIndex = std::nullopt) const;
+
+  virtual uint32_t getProgramHeaderCount() const = 0;
 };
 
 class ELFSectionRef : public SectionRef {
@@ -467,6 +469,8 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
   bool isRelocatableObject() const override;
 
   void createFakeSections() { EF.createFakeSections(); }
+
+  uint32_t getProgramHeaderCount() const override;
 };
 
 using ELF32LEObjectFile = ELFObjectFile<ELF32LE>;
@@ -553,6 +557,11 @@ uint64_t ELFObjectFile<ELFT>::getSectionOffset(DataRefImpl Sec) const {
   return getSection(Sec)->sh_offset;
 }
 
+template <class ELFT>
+uint32_t ELFObjectFile<ELFT>::getProgramHeaderCount() const {
+  return EF.getHeader().e_phnum;
+}
+
 template <class ELFT>
 uint64_t ELFObjectFile<ELFT>::getSymbolValueImpl(DataRefImpl Symb) const {
   Expected<const Elf_Sym *> SymOrErr = getSymbol(Symb);
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 9d02ba051a0a84b..36bc88a1cbd8c18 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -183,6 +183,8 @@ static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
     return std::make_unique<BinaryWriter>(Obj, Out);
   case FileFormat::IHex:
     return std::make_unique<IHexWriter>(Obj, Out);
+  case FileFormat::SegBin:
+    return std::make_unique<SegBinWriter>(Obj, Out, Config.SegmentIndex);
   default:
     return createELFWriter(Config, Obj, Out, OutputElfType);
   }
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index eaeef11b127e487..ac1c70714f1c777 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -174,6 +174,13 @@ Error SectionWriter::visit(const Section &Sec) {
   return Error::success();
 }
 
+Error BinarySegmentWriter::visit(const Segment &Seg) {
+  if (Seg.Type == PT_LOAD)
+    llvm::copy(Seg.Contents, Out.getBufferStart());
+
+  return Error::success();
+}
+
 static bool addressOverflows32bit(uint64_t Addr) {
   // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok
   return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX;
@@ -2679,6 +2686,25 @@ Error BinaryWriter::finalize() {
   return Error::success();
 }
 
+Error SegBinWriter::write() {
+  const Segment &Seg = Obj.getSegmentForIndex(SegmentIndex);
+  if (Error Err = Seg.accept(*SegmentWriter))
+    return Err;
+  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+  return Error::success();
+}
+
+Error SegBinWriter::finalize() {
+  const Segment &Seg = Obj.getSegmentForIndex(SegmentIndex);
+  Buf = WritableMemoryBuffer::getNewMemBuffer(Seg.FileSize);
+  if (!Buf)
+    return createStringError(errc::not_enough_memory,
+                             "failed to allocate memory buffer of " +
+                                 Twine::utohexstr(Seg.FileSize) + " bytes");
+  SegmentWriter = std::make_unique<BinarySegmentWriter>(*Buf);
+  return Error::success();
+}
+
 bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
                                             const SectionBase *Rhs) const {
   return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 89a03b3fe0ee354..802eb3999d5a2e9 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -87,6 +87,13 @@ class SectionVisitor {
   virtual Error visit(const DecompressedSection &Sec) = 0;
 };
 
+class SegmentVisitor {
+public:
+  virtual ~SegmentVisitor() = default;
+
+  virtual Error visit(const Segment &Seg) = 0;
+};
+
 class MutableSectionVisitor {
 public:
   virtual ~MutableSectionVisitor() = default;
@@ -126,6 +133,18 @@ class SectionWriter : public SectionVisitor {
   explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
 };
 
+class SegmentWriter : public SegmentVisitor {
+protected:
+  WritableMemoryBuffer &Out;
+
+public:
+  virtual ~SegmentWriter() = default;
+
+  Error visit(const Segment &Seg) override = 0;
+
+  explicit SegmentWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
+};
+
 template <class ELFT> class ELFSectionWriter : public SectionWriter {
 private:
   using Elf_Word = typename ELFT::Word;
@@ -191,6 +210,16 @@ class BinarySectionWriter : public SectionWriter {
       : SectionWriter(Buf) {}
 };
 
+class BinarySegmentWriter : public SegmentWriter {
+public:
+  virtual ~BinarySegmentWriter() {}
+
+  Error visit(const Segment &Seg) override;
+
+  explicit BinarySegmentWriter(WritableMemoryBuffer &Buf)
+      : SegmentWriter(Buf) {}
+};
+
 using IHexLineData = SmallVector<char, 64>;
 
 struct IHexRecord {
@@ -387,6 +416,19 @@ class IHexWriter : public Writer {
   IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
 };
 
+class SegBinWriter : public Writer {
+private:
+  std::unique_ptr<BinarySegmentWriter> SegmentWriter;
+  uint32_t SegmentIndex = 0;
+
+public:
+  ~SegBinWriter() {}
+  Error finalize() override;
+  Error write() override;
+  SegBinWriter(Object &Obj, raw_ostream &Out, uint32_t SegmentIndex)
+      : Writer(Obj, Out), SegmentIndex(SegmentIndex) {}
+};
+
 class SectionBase {
 public:
   std::string Name;
@@ -476,7 +518,7 @@ class Segment {
 
   void removeSection(const SectionBase *Sec) { Sections.erase(Sec); }
   void addSection(const SectionBase *Sec) { Sections.insert(Sec); }
-
+  Error accept(SegmentVisitor &Visitor) const { return Visitor.visit(*this); }
   ArrayRef<uint8_t> getContents() const { return Contents; }
 };
 
@@ -1036,6 +1078,9 @@ class Object {
   static bool sectionIsAlloc(const SectionBase &Sec) {
     return Sec.Flags & ELF::SHF_ALLOC;
   };
+  static bool segmentIsLoadable(const Segment &Seg) {
+    return Seg.Type & ELF::PT_LOAD;
+  };
 
 public:
   template <class T>
@@ -1087,6 +1132,17 @@ class Object {
   }
   SectionTableRef removedSections() { return SectionTableRef(RemovedSections); }
 
+  iterator_range<
+      filter_iterator<pointee_iterator<std::vector<SegPtr>::const_iterator>,
+                      decltype(&segmentIsLoadable)>>
+  loadableSegments() const {
+    return make_filter_range(make_pointee_range(Segments), segmentIsLoadable);
+  }
+
+  const Segment &getSegmentForIndex(uint32_t Index) const {
+    return *(Segments[Index]);
+  }
+
   ConstRange<Segment> segments() const { return make_pointee_range(Segments); }
 
   Error removeSections(bool AllowBrokenLinks,
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 7a1e30b49b0c3d1..3dd99cab3f41912 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -166,6 +166,7 @@ def get_asan_rtlib():
     ToolSubst("%llvm-strip", FindTool("llvm-strip")),
     ToolSubst("%llvm-install-name-tool", FindTool("llvm-install-name-tool")),
     ToolSubst("%llvm-bitcode-strip", FindTool("llvm-bitcode-strip")),
+    ToolSubst("%llvm-fromelf", FindTool("llvm-fromelf")),
     ToolSubst("%split-file", FindTool("split-file")),
 ]
 
@@ -180,6 +181,7 @@ def get_asan_rtlib():
         "llvm-addr2line",
         "llvm-bcanalyzer",
         "llvm-bitcode-strip",
+        "llvm-fromelf",
         "llvm-config",
         "llvm-cov",
         "llvm-cxxdump",
diff --git a/llvm/test/tools/llvm-objcopy/tool-help-message.test b/llvm/test/tools/llvm-objcopy/tool-help-message.test
index 7e72acbbe50733d..b3dc4015b8749ac 100644
--- a/llvm/test/tools/llvm-objcopy/tool-help-message.test
+++ b/llvm/test/tools/llvm-objcopy/tool-help-message.test
@@ -5,6 +5,7 @@
 # RUN: not llvm-objcopy -abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG-SHORT %s
 # RUN: not llvm-objcopy --abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG-LONG %s
 # RUN: not llvm-objcopy --strip-debug 2>&1 | FileCheck %s --check-prefix=NO-INPUT-FILES
+# RUN: not llvm-objcopy --input-target binary --output-target sbin f1 2>&1 | FileCheck %s --check-prefix=SEGBIN
 
 # RUN: llvm-strip -h | FileCheck --check-prefix=STRIP-USAGE %s --match-full-lines
 # RUN: llvm-strip --help | FileCheck --check-prefix=STRIP-USAGE %s --match-full-lines
@@ -29,6 +30,13 @@
 # RUN: not llvm-bitcode-strip --abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG-LONG %s
 # RUN: not llvm-bitcode-strip f1 f2 2>&1 | FileCheck %s --check-prefix=MULTIPLE-INPUT-FILES
 
+# RUN: llvm-fromelf -h | FileCheck --check-prefix=FROMELF-USAGE %s --match-full-lines
+# RUN: llvm-fromelf --help | FileCheck --check-prefix=FROMELF-USAGE %s --match-full-lines
+# RUN: not llvm-fromelf 2>&1 | FileCheck --check-prefix=FROMELF-USAGE %s --match-full-lines
+# RUN: not llvm-fromelf -- 2>&1 | FileCheck --check-prefix=FROMELF-USAGE %s --match-full-lines
+# RUN: not llvm-fromelf -abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG-LONG %s
+# RUN: not llvm-fromelf --abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG-LONG %s
+
 # OBJCOPY-USAGE:  USAGE: llvm-objcopy [options] input [output]
 # OBJCOPY-USAGE:  Pass @FILE as argument to read options from FILE.
 
@@ -41,7 +49,11 @@
 # BITCODE-STRIP-USAGE: USAGE: llvm-bitcode-strip [options] input
 # BITCODE-STRIP-USAGE: Pass @FILE as argument to read options from FILE.
 
+# FROMELF-USAGE:  USAGE: llvm-fromelf [options] input [output]
+# FROMELF-USAGE:  Pass @FILE as argument to read options from FILE.
+
 # UNKNOWN-ARG-SHORT: unknown argument '-a'
 # UNKNOWN-ARG-LONG: unknown argument '{{-+}}abcabc'
 # NO-INPUT-FILES: no input file specified
 # MULTIPLE-INPUT-FILES: expects a single input file
+# SEGBIN: --output-target segbin can be used with only --input-target elf
diff --git a/llvm/test/tools/llvm-objcopy/tool-name.test b/llvm/test/tools/llvm-objcopy/tool-name.test
index 6a1f72588e23d8f..ab0a5abcb2c9952 100644
--- a/llvm/test/tools/llvm-objcopy/tool-name.test
+++ b/llvm/test/tools/llvm-objcopy/tool-name.test
@@ -41,3 +41,13 @@
 # RUN: %t/bitcode_strip.exe --help | FileCheck --check-prefix=BITCODE-STRIP %s
 
 # BITCODE-STRIP: OVERVIEW: llvm-bitcode-strip tool
+
+## This driver emulates fromelf from ARM.
+# RUN: ln -s llvm-fromelf %t/llvm-fromelf-10
+# RUN: ln -s llvm-fromelf %t/fromelf.exe
+
+# RUN: llvm-fromelf --help | FileCheck --check-prefix=FROM-ELF %s
+# RUN: %t/llvm-fromelf-10 --help | FileCheck --check-prefix=FROM-ELF %s
+# RUN: %t/fromelf.exe --help | FileCheck --check-prefix=FROM-ELF %s
+
+# FROM-ELF: OVERVIEW: llvm-fromelf tool
\ No newline at end of file
diff --git a/llvm/test/tools/llvm-objcopy/tool-version.test b/llvm/test/tools/llvm-objcopy/tool-version.test
index e2fb551ad79514c..a62f1e20b81b477 100644
--- a/llvm/test/tools/llvm-objcopy/tool-version.test
+++ b/llvm/test/tools/llvm-objcopy/tool-version.test
@@ -10,6 +10,9 @@
 # RUN: llvm-bitcode-strip --version | FileCheck %s
 # RUN: llvm-bitcode-strip -V | FileCheck %s
 
+# RUN: llvm-fromelf --version | FileCheck %s
+# RUN: llvm-fromelf -V | FileCheck %s
+
 # OBJCOPY-DAG: {{ version }}
 # OBJCOPY-DAG: GNU objcopy
 
diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt
index 1c73a1781e80184..27750c2b6bbf069 100644
--- a/llvm/tools/llvm-objcopy/CMakeLists.txt
+++ b/llvm/tools/llvm-objcopy/CMakeLists.txt
@@ -24,6 +24,10 @@ set(LLVM_TARGET_DEFINITIONS StripOpts.td)
 tablegen(LLVM StripOpts.inc -gen-opt-parser-defs)
 add_public_tablegen_target(StripOptsTableGen)
 
+set(LLVM_TARGET_DEFINITIONS FromElfOpts.td)
+tablegen(LLVM FromElfOpts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(FromElfOptsTableGen)
+
 add_llvm_tool(llvm-objcopy
   ObjcopyOptions.cpp
   llvm-objcopy.cpp
@@ -31,16 +35,19 @@ add_llvm_tool(llvm-objcopy
   ObjcopyOptsTableGen
   InstallNameToolOptsTableGen
   StripOptsTableGen
+  FromElfOptsTableGen
   GENERATE_DRIVER
-  )
+)
 
 add_llvm_tool_symlink(llvm-install-name-tool llvm-objcopy)
 add_llvm_tool_symlink(llvm-bitcode-strip llvm-objcopy)
 add_llvm_tool_symlink(llvm-strip llvm-objcopy)
+add_llvm_tool_symlink(llvm-fromelf llvm-objcopy)
 
 if(LLVM_INSTALL_BINUTILS_SYMLINKS)
   add_llvm_tool_symlink(objcopy llvm-objcopy)
   add_llvm_tool_symlink(strip llvm-objcopy)
+  add_llvm_tool_symlink(fromelf llvm-objcopy)
 endif()
 
 if(LLVM_INSTALL_CCTOOLS_SYMLINKS)
diff --git a/llvm/tools/llvm-objcopy/FromElfOpts.td b/llvm/tools/llvm-objcopy/FromElfOpts.td
new file mode 100644
index 000000000000000..1ac7e354a3fecbd
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/FromElfOpts.td
@@ -0,0 +1,37 @@
+//===-- FromElfOpts.td - llvm-fromelf options  ---------------*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes the command line options of llvm-fromelf.
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Option/OptParser.td"
+
+multiclass Eq<string name, string help> {
+  def NAME : Separate<["--"], name>;
+  def NAME #_eq : Joined<["--"], name #"=">,
+                  Alias<!cast<Separate>(NAME)>,
+                  HelpText<help>;
+}
+
+def help : Flag<["--"], "help">;
+
+def h : Flag<["-"], "h">, Alias<help>;
+
+def version : Flag<["--"], "version">,
+              HelpText<"Print the version and exit">;
+
+def V : Flag<["-"], "V">,
+        Alias<version>,
+        HelpText<"Alias for --version">;
+
+defm output_target : Eq<"output-target", "Format of the output file">,
+                     Values<"binary">;
+def O : JoinedOrSeparate<["-"], "O">,
+        Alias<output_target>,
+        HelpText<"Alias for --output-target">;
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index d33adb0b6a3e478..036397320138fb8 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -150,6 +150,35 @@ class StripOptTable : public opt::GenericOptTable {
   }
 };
 
+enum FromElfID {
+  FROM_ELF_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(FROM_ELF_, __VA_ARGS__),
+#include "FromElfOpts.inc"
+#undef OPTION
+};
+
+namespace from_elf {
+
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
+  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
+                                                std::size(NAME##_init) - 1);
+#include "FromElfOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info FromElfInfoTable[] = {
+#define OPTION(...)                                                            \
+  LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(FROM_ELF_, __VA_ARGS__),
+#include "FromElfOpts.inc"
+#undef OPTION
+};
+} // namespace from_elf
+
+class FromElfOptTable : public opt::GenericOptTable {
+public:
+  FromElfOptTable() : opt::GenericOptTable(from_elf::FromElfInfoTable) {}
+};
+
 } // namespace
 
 static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
@@ -385,7 +414,7 @@ template <class T> static ErrorOr<T> getAsInteger(StringRef Val) {
 
 namespace {
 
-enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip };
+enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip, FromElf };
 
 } // anonymous namespace
 
@@ -409,6 +438,10 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
     ToolName = "llvm-bitcode-strip";
     HelpText = " [options] input";
     break;
+  case ToolType::FromElf:
+    ToolName = "llvm-fromelf";
+    HelpText = " [options] input [output]";
+    break;
   }
   OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(),
                      (ToolName + " tool").str().c_str());
@@ -688,6 +721,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
   Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
                             .Case("binary", FileFormat::Binary)
                             .Case("ihex", FileFormat::IHex)
+                            .Case("sbin", FileFormat::SegBin)
                             .Default(FileFormat::Unspecified);
   if (Config.OutputFormat == FileFormat::Unspecified) {
     if (OutputFormat.empty()) {
@@ -702,6 +736,12 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     }
   }
 
+  if (Config.OutputFormat == FileFormat::SegBin &&
+      Config.InputFormat != FileFormat::ELF)
+    return createStringError(
+        errc::invalid_argument,
+        "--output-target segbin can be used with only --input-target elf");
+
   if (const auto *A = InputArgs.getLastArg(OBJCOPY_compress_debug_sections)) {
     Config.CompressionType = StringSwitch<DebugCompressionType>(A->getValue())
                                  .Case("zlib", DebugCompressionType::Zlib)
@@ -1192,6 +1232,72 @@ objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr,
   return std::move(DC);
 }
 
+Expected<DriverConfig>
+objcopy::parseFromElfOptions(ArrayRef<const char *> RawArgsArr) {
+  DriverConfig DC;
+  FromElfOptTable T;
+  const char *const *DashDash =
+      llvm::find_if(RawArgsArr, [](StringRef Str) { return Str == "--"; });
+  ArrayRef<const char *> ArgsArr = ArrayRef(RawArgsArr.begin(), DashDash);
+  if (DashDash != RawArgsArr.end())
+    DashDash = std::next(DashDash);
+
+  unsigned MissingArgumentIndex, MissingArgumentCount;
+  llvm::opt::InputArgList InputArgs =
+      T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+  if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
+    printHelp(T, errs(), ToolType::FromElf);
+    exit(1);
+  }
+
+  if (InputArgs.hasArg(FROM_ELF_help)) {
+    printHelp(T, outs(), ToolType::FromElf);
+    exit(0);
+  }
+
+  if (InputArgs.hasArg(FROM_ELF_version)) {
+    outs() << "llvm-fromelf, compatible with ARM fromelf\n";
+    cl::PrintVersionMessage();
+    exit(0);
+  }
+
+  SmallVector<const char *, 2> Positional;
+
+  for (auto *Arg : InputArgs.filtered(FROM_ELF_UNKNOWN))
+    return createStringError(errc::invalid_argument, "unknown argument '%s'",
+                             Arg->getAsString(InputArgs).c_str());
+
+  for (auto *Arg : InputArgs.filtered(FROM_ELF_INPUT))
+    Positional.push_back(Arg->getValue());
+  std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
+
+  if (Positional.empty())
+    return createStringError(errc::invalid_argument, "no input file specified");
+
+  if (Positional.size() > 2)
+    return createStringError(errc::invalid_argument,
+                             "too many positional arguments");
+  ConfigManager ConfigMgr;
+  CommonConfig &Config = ConfigMgr.Common;
+  Config.InputFilename = Positional[0];
+  Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
+  Config.InputFormat = FileFormat::ELF;
+  StringRef OutputFormat;
+  OutputFormat = InputArgs.getLastArgValue(FROM_ELF_output_target);
+  if (OutputFormat.empty())
+    Config.OutputFormat = FileFormat::SegBin;
+  else {
+    Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
+                              .Case("sbin", FileFormat::SegBin)
+                              .Default(FileFormat::Unspecified);
+  }
+  if (Config.OutputFormat == FileFormat::Unspecified)
+    return createStringError(errc::invalid_argument, "unknown output format");
+  DC.CopyConfigs.push_back(std::move(ConfigMgr));
+  return std::move(DC);
+}
+
 // parseStripOptions returns the config and sets the input arguments. If a
 // help flag is set then parseStripOptions will print the help messege and
 // exit.
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.h b/llvm/tools/llvm-objcopy/ObjcopyOptions.h
index f7fa2af304d7731..c5ba6bb4fccedda 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.h
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.h
@@ -45,6 +45,11 @@ Expected<DriverConfig>
 parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr,
                          llvm::function_ref<Error(Error)> ErrorCallback);
 
+// ParseFromElfOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseFromElfOptions will print the help
+// messege and exit.
+Expected<DriverConfig> parseFromElfOptions(ArrayRef<const char *> ArgsArr);
+
 // ParseStripOptions returns the config and sets the input arguments. If a
 // help flag is set then ParseStripOptions will print the help messege and
 // exit. ErrorCallback is used to handle recoverable errors. An Error returned
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 2afa97601f5cfd8..8a61c732b5d6f86 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -9,6 +9,7 @@
 #include "ObjcopyOptions.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/ELF.h"
@@ -93,6 +94,8 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {
     return parseStripOptions(Args, reportWarning);
   else if (Is("install-name-tool") || Is("install_name_tool"))
     return parseInstallNameToolOptions(Args);
+  else if (Is("fromelf"))
+    return parseFromElfOptions(Args);
   else
     return parseObjcopyOptions(Args, reportWarning);
 }
@@ -123,6 +126,7 @@ static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
   case FileFormat::Binary:
   case FileFormat::IHex:
   case FileFormat::Unspecified:
+  case FileFormat::SegBin:
     Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
     if (!ELFConfig)
       return ELFConfig.takeError();
@@ -133,6 +137,36 @@ static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
   llvm_unreachable("unsupported output format");
 }
 
+static Error emitOutput(CommonConfig &Config,
+                        FilePermissionsApplier &PermsApplierOrErr,
+                        std::function<Error(raw_ostream &)> ObjcopyFunc) {
+  std::string OutputFilename =
+      Config.OutputFilename.str() + ((Config.OutputFormat == FileFormat::SegBin)
+                                         ? std::to_string(Config.SegmentIndex)
+                                         : "");
+  if (ObjcopyFunc) {
+    if (Config.SplitDWO.size()) {
+      // remove .dwo tables
+      Config.ExtractDWO = false;
+      Config.StripDWO = true;
+    }
+    // Apply transformations described by Config and store result into
+    // Config.OutputFilename using specified ObjcopyFunc function.
+    if (Error E = writeToOutput(OutputFilename, ObjcopyFunc))
+      return E;
+  }
+
+  if (Error E = PermsApplierOrErr.apply(OutputFilename, Config.PreserveDates))
+    return E;
+
+  if (!Config.SplitDWO.empty())
+    if (Error E = PermsApplierOrErr.apply(Config.SplitDWO, Config.PreserveDates,
+                                          static_cast<sys::fs::perms>(0666)))
+      return E;
+
+  return Error::success();
+}
+
 /// The function executeObjcopy does the higher level dispatch based on the type
 /// of input (raw binary, archive or single object file) and takes care of the
 /// format-agnostic modifications, i.e. preserving dates.
@@ -145,6 +179,7 @@ static Error executeObjcopy(ConfigManager &ConfigMgr) {
     return PermsApplierOrErr.takeError();
 
   std::function<Error(raw_ostream & OutFile)> ObjcopyFunc;
+  uint32_t SegmentCount = 0;
 
   OwningBinary<llvm::object::Binary> BinaryHolder;
   std::unique_ptr<MemoryBuffer> MemoryBufferHolder;
@@ -181,47 +216,33 @@ static Error executeObjcopy(ConfigManager &ConfigMgr) {
         return E;
     } else {
       // Handle llvm::object::Binary.
+      if (ELFObjectFileBase *ElfFile =
+              dyn_cast<ELFObjectFileBase>(BinaryHolder.getBinary()))
+        SegmentCount = ElfFile->getProgramHeaderCount();
+
       ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
         return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(),
                                       OutFile);
       };
     }
   }
-
-  if (ObjcopyFunc) {
-    if (Config.SplitDWO.empty()) {
-      // Apply transformations described by Config and store result into
-      // Config.OutputFilename using specified ObjcopyFunc function.
-      if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
-        return E;
-    } else {
-      Config.ExtractDWO = true;
-      Config.StripDWO = false;
-      // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO
-      // file using specified ObjcopyFunc function.
-      if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))
-        return E;
-      Config.ExtractDWO = false;
-      Config.StripDWO = true;
-      // Apply transformations described by Config, remove .dwo tables and
-      // store result into Config.OutputFilename using specified ObjcopyFunc
-      // function.
-      if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
+  if (Config.SplitDWO.size()) {
+    Config.ExtractDWO = true;
+    Config.StripDWO = false;
+    // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO
+    // file using specified ObjcopyFunc function.
+    if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))
+      return E;
+  }
+  if (Config.OutputFormat == FileFormat::SegBin) {
+    for (uint32_t SegmentIdx = 0; SegmentIdx < SegmentCount; SegmentIdx++) {
+      Config.SegmentIndex = SegmentIdx;
+      if (Error E = emitOutput(Config, PermsApplierOrErr.get(), ObjcopyFunc))
         return E;
     }
+    return Error::success();
   }
-
-  if (Error E =
-          PermsApplierOrErr->apply(Config.OutputFilename, Config.PreserveDates))
-    return E;
-
-  if (!Config.SplitDWO.empty())
-    if (Error E =
-            PermsApplierOrErr->apply(Config.SplitDWO, Config.PreserveDates,
-                                     static_cast<sys::fs::perms>(0666)))
-      return E;
-
-  return Error::success();
+  return emitOutput(Config, PermsApplierOrErr.get(), ObjcopyFunc);
 }
 
 int llvm_objcopy_main(int argc, char **argv, const llvm::ToolContext &) {



More information about the llvm-commits mailing list