[llvm] [llvm-objcopy][ELF] Disable huge section offset (PR #97036)

Djordje Todorovic via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 13 02:09:42 PDT 2024


https://github.com/djtodoro updated https://github.com/llvm/llvm-project/pull/97036

>From 548f073ebd8f39e97508013e482b35cc5395e303 Mon Sep 17 00:00:00 2001
From: Djordje Todorovic <djordje.todorovic at htecgroup.com>
Date: Thu, 20 Jun 2024 18:08:40 +0200
Subject: [PATCH 1/2] [llvm-objcopy][ELF] Disable huge section offset

By default, GNU objcopy issues a warning for large offsets in ELF
files. To align with this behavior, we introduce an option to
define a maximum file offset for a section. This allows us to skip
creating excessively large files by specifying a threshold beyond
which the file offset will trigger an error.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  5 ++++
 llvm/include/llvm/ObjCopy/ELF/ELFConfig.h     |  1 +
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           | 15 ++++++------
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            |  9 ++++++++
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  9 ++++++--
 .../ELF/set-max-section-offset.yaml           | 23 +++++++++++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  5 ++++
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |  6 +++++
 8 files changed, 64 insertions(+), 9 deletions(-)
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 7f20a76550c833..f9a5f67ff6267b 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -350,6 +350,11 @@ them.
 
  Extract the named partition from the output.
 
+.. option:: --set-max-section-offset <value>
+
+ Emit an error if input section has a file offset greater than the specified
+ ``<value>``.
+
 .. option:: --gap-fill <value>
 
  For binary outputs, fill the gaps between sections with ``<value>`` instead
diff --git a/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h b/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h
index 59960b65307430..4e5f898de8efbb 100644
--- a/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h
+++ b/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h
@@ -31,6 +31,7 @@ struct ELFConfig {
   bool KeepFileSymbols = false;
   bool LocalizeHidden = false;
   bool VerifyNoteSections = true;
+  std::optional<uint64_t> MaxSectionOffset;
 };
 
 } // namespace objcopy
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 40598861d42731..22e376bf11d6ca 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -176,11 +176,12 @@ static std::unique_ptr<Writer> createELFWriter(const CommonConfig &Config,
 }
 
 static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
+                                            const ELFConfig &ELFConfig,
                                             Object &Obj, raw_ostream &Out,
                                             ElfType OutputElfType) {
   switch (Config.OutputFormat) {
   case FileFormat::Binary:
-    return std::make_unique<BinaryWriter>(Obj, Out, Config);
+    return std::make_unique<BinaryWriter>(Obj, Out, Config, ELFConfig);
   case FileFormat::IHex:
     return std::make_unique<IHexWriter>(Obj, Out, Config.OutputFilename);
   case FileFormat::SREC:
@@ -926,10 +927,10 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
   return Error::success();
 }
 
-static Error writeOutput(const CommonConfig &Config, Object &Obj,
-                         raw_ostream &Out, ElfType OutputElfType) {
+static Error writeOutput(const CommonConfig &Config, const ELFConfig &ELFConfig,
+                         Object &Obj, raw_ostream &Out, ElfType OutputElfType) {
   std::unique_ptr<Writer> Writer =
-      createWriter(Config, Obj, Out, OutputElfType);
+      createWriter(Config, ELFConfig, Obj, Out, OutputElfType);
   if (Error E = Writer->finalize())
     return E;
   return Writer->write();
@@ -947,7 +948,7 @@ Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config,
       getOutputElfType(Config.OutputArch.value_or(MachineInfo()));
   if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
     return E;
-  return writeOutput(Config, **Obj, Out, OutputElfType);
+  return writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType);
 }
 
 Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config,
@@ -965,7 +966,7 @@ Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config,
       getOutputElfType(Config.OutputArch.value_or(MachineInfo()));
   if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
     return E;
-  return writeOutput(Config, **Obj, Out, OutputElfType);
+  return writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType);
 }
 
 Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config,
@@ -985,7 +986,7 @@ Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config,
   if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
     return createFileError(Config.InputFilename, std::move(E));
 
-  if (Error E = writeOutput(Config, **Obj, Out, OutputElfType))
+  if (Error E = writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType))
     return createFileError(Config.InputFilename, std::move(E));
 
   return Error::success();
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index fd55f974115a24..6987c18b966609 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2744,6 +2744,15 @@ Error BinaryWriter::finalize() {
     if (Sec.Type != SHT_NOBITS && Sec.Size > 0) {
       Sec.Offset = Sec.Addr - MinAddr;
       TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
+
+      if (MaxSectionOffset && Sec.Offset > *MaxSectionOffset) {
+        return createStringError(
+            errc::file_too_large,
+            "writing section " + Sec.Name + " at offset 0x" +
+                Twine::utohexstr(Sec.Offset) + " greater than max offset 0x" +
+                Twine::utohexstr(*MaxSectionOffset) +
+                " specified by --set-max-section-offset");
+      }
     }
 
   Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 6ccf85387131e4..5d24af3102e0a8 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -15,6 +15,7 @@
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/ObjCopy/CommonConfig.h"
+#include "llvm/ObjCopy/ELF/ELFConfig.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/FileOutputBuffer.h"
@@ -366,12 +367,16 @@ class BinaryWriter : public Writer {
 
   uint64_t TotalSize = 0;
 
+  std::optional<uint64_t> MaxSectionOffset;
+
 public:
   ~BinaryWriter() {}
   Error finalize() override;
   Error write() override;
-  BinaryWriter(Object &Obj, raw_ostream &Out, const CommonConfig &Config)
-      : Writer(Obj, Out), GapFill(Config.GapFill), PadTo(Config.PadTo) {}
+  BinaryWriter(Object &Obj, raw_ostream &Out, const CommonConfig &Config,
+               const ELFConfig &ELFConfig)
+      : Writer(Obj, Out), GapFill(Config.GapFill), PadTo(Config.PadTo),
+        MaxSectionOffset(ELFConfig.MaxSectionOffset) {}
 };
 
 // A base class for writing ascii hex formats such as srec and ihex.
diff --git a/llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml b/llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml
new file mode 100644
index 00000000000000..a782af430317c6
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml
@@ -0,0 +1,23 @@
+# RUN: yaml2obj %s --docnum=1 -o %t
+# RUN: not llvm-objcopy -O binary %t %t2 --set-max-section-offset=15 2>&1 | FileCheck -DFILE=%t %s --check-prefix=INVALID-COPY
+# RUN: llvm-objcopy -O binary %t %t2 --set-max-section-offset=16
+
+# INVALID-COPY: error: '[[FILE]]': writing section .high_addr at offset 0x10 greater than max offset 0xf specified by --set-max-section-offset
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x0
+    Content:         "10"
+  - Name:            .high_addr
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x10
+    Content:         "10"
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 26a888c628d9d3..26e04316bf83cc 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -1054,6 +1054,11 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
   Config.ExtractMainPartition =
       InputArgs.hasArg(OBJCOPY_extract_main_partition);
   ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
+
+  if (auto *Arg = InputArgs.getLastArg(OBJCOPY_set_max_section_offset)) {
+    ELFConfig.MaxSectionOffset = std::stoull(Arg->getValue());
+  }
+
   Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
   if (auto *Arg =
           InputArgs.getLastArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) {
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index 434b5ff92324eb..1853b597e7f8b1 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -146,6 +146,12 @@ def extract_main_partition
     : Flag<["--"], "extract-main-partition">,
       HelpText<"Extract main partition from the input file">;
 
+defm set_max_section_offset
+    : Eq<"set-max-section-offset",
+         "Emit an error if input section has a file offset greater than the "
+         "specified `offset`">,
+      MetaVarName<"offset">;
+
 def localize_hidden
     : Flag<["--"], "localize-hidden">,
       HelpText<

>From 983c28d2e4ae7008df28854c31cc98241ad89b29 Mon Sep 17 00:00:00 2001
From: Djordje Todorovic <djordje.todorovic at htecgroup.com>
Date: Fri, 13 Sep 2024 11:04:42 +0200
Subject: [PATCH 2/2] [NFC][llvm-objcopy] Cleanup -max-section-offset

---
 llvm/docs/CommandGuide/llvm-objcopy.rst                | 10 +++++-----
 ...max-section-offset.yaml => max-section-offset.yaml} |  4 ++--
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp             |  3 +--
 llvm/tools/llvm-objcopy/ObjcopyOpts.td                 |  4 ++--
 4 files changed, 10 insertions(+), 11 deletions(-)
 rename llvm/test/tools/llvm-objcopy/ELF/{set-max-section-offset.yaml => max-section-offset.yaml} (76%)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index f9a5f67ff6267b..2b8ef1482b8c00 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -350,11 +350,6 @@ them.
 
  Extract the named partition from the output.
 
-.. option:: --set-max-section-offset <value>
-
- Emit an error if input section has a file offset greater than the specified
- ``<value>``.
-
 .. option:: --gap-fill <value>
 
  For binary outputs, fill the gaps between sections with ``<value>`` instead
@@ -428,6 +423,11 @@ them.
  anything following a '#'. Can be specified multiple times to read names from
  multiple files.
 
+.. option:: --max-section-offset <value>
+
+ Emit an error if input section has a file offset greater than the specified
+ ``<value>``.
+
 .. option:: --new-symbol-visibility <visibility>
 
  Specify the visibility of the symbols automatically created when using binary
diff --git a/llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml b/llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml
similarity index 76%
rename from llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml
rename to llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml
index a782af430317c6..0cac628377bc21 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/set-max-section-offset.yaml
+++ b/llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %s --docnum=1 -o %t
-# RUN: not llvm-objcopy -O binary %t %t2 --set-max-section-offset=15 2>&1 | FileCheck -DFILE=%t %s --check-prefix=INVALID-COPY
-# RUN: llvm-objcopy -O binary %t %t2 --set-max-section-offset=16
+# RUN: not llvm-objcopy -O binary %t %t2 --max-section-offset=15 2>&1 | FileCheck -DFILE=%t %s --check-prefix=INVALID-COPY
+# RUN: llvm-objcopy -O binary %t %t2 --max-section-offset=16
 
 # INVALID-COPY: error: '[[FILE]]': writing section .high_addr at offset 0x10 greater than max offset 0xf specified by --set-max-section-offset
 
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 26e04316bf83cc..cfad65734e146b 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -1055,9 +1055,8 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
       InputArgs.hasArg(OBJCOPY_extract_main_partition);
   ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
 
-  if (auto *Arg = InputArgs.getLastArg(OBJCOPY_set_max_section_offset)) {
+  if (auto *Arg = InputArgs.getLastArg(OBJCOPY_max_section_offset))
     ELFConfig.MaxSectionOffset = std::stoull(Arg->getValue());
-  }
 
   Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
   if (auto *Arg =
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index 1853b597e7f8b1..bb27ef5f028902 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -146,8 +146,8 @@ def extract_main_partition
     : Flag<["--"], "extract-main-partition">,
       HelpText<"Extract main partition from the input file">;
 
-defm set_max_section_offset
-    : Eq<"set-max-section-offset",
+defm max_section_offset
+    : Eq<"max-section-offset",
          "Emit an error if input section has a file offset greater than the "
          "specified `offset`">,
       MetaVarName<"offset">;



More information about the llvm-commits mailing list