[llvm] 3320036 - [llvm-objcopy] Add change-section-lma *+/-offset (#95431)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 8 09:17:11 PDT 2024


Author: Eleanor Bonnici
Date: 2024-07-08T17:17:09+01:00
New Revision: 3320036370ba3719471ab243f253237e88996495

URL: https://github.com/llvm/llvm-project/commit/3320036370ba3719471ab243f253237e88996495
DIFF: https://github.com/llvm/llvm-project/commit/3320036370ba3719471ab243f253237e88996495.diff

LOG: [llvm-objcopy] Add change-section-lma *+/-offset (#95431)

llvm-objcopy did not support change-section-lma argument.

This patch adds support for a use case of change-section-lma, that is
shifting load address of all sections by the same offset. This seems to
be the only practical use case of change-section-lma, found in other
software such as Zephyr RTOS's build system.

This is an option that could possibly be supported in some other than
ELF formats, however this change only implements it for ELF. When used
with other formats an error message is raised.

In comparison, the behavior of GNU objcopy is inconsistent. For some ELF
files it behaves the same as described above. For others, it copies the
file without modifying the p_paddr fields when it would be expected. In
some experiments it modifies arbitrary fields in section or program
headers. It is unclear what exactly determines this.
The executable file generated by yaml2obj in this test is not parsable
by GNU objcopy. With Machine set to EM_AARCH64, the file can be parsed
and the first test in the test file completes with 0 exit code. However,
the result is rather arbitrary. AArch64 GNU objcopy subtracts 0x1000
from p_filesz and p_memsz of the first LOAD section and 0x1000 from
p_offset of the second LOAD section. It does not look meaningful.

Added: 
    llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test

Modified: 
    llvm/docs/CommandGuide/llvm-objcopy.rst
    llvm/include/llvm/ObjCopy/CommonConfig.h
    llvm/lib/ObjCopy/ConfigManager.cpp
    llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
    llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
    llvm/tools/llvm-objcopy/ObjcopyOpts.td

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index a62acfc8fdcd80..eaeb19e26c7b67 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -299,6 +299,10 @@ them.
  Allow :program:`llvm-objcopy` to remove sections even if it would leave invalid
  section references. Any invalid sh_link fields will be set to zero.
 
+.. option:: --change-section-lma \*{+-}<val>
+
+ Shift LMA of non-zero-sized segments by ``<val>``.
+
 .. option:: --change-start <incr>, --adjust-start
 
  Add ``<incr>`` to the program's start address. Can be specified multiple

diff  --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index ae08d4032736e2..7f9d90d528b3ee 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -245,6 +245,9 @@ struct CommonConfig {
   // Symbol info specified by --add-symbol option.
   SmallVector<NewSymbolInfo, 0> SymbolsToAdd;
 
+  // Integer options
+  int64_t ChangeSectionLMAValAll = 0;
+
   // Boolean options
   bool DeterministicArchives = true;
   bool ExtractDWO = false;

diff  --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp
index 6442f1b958fb4d..c542c4e5f07430 100644
--- a/llvm/lib/ObjCopy/ConfigManager.cpp
+++ b/llvm/lib/ObjCopy/ConfigManager.cpp
@@ -25,7 +25,8 @@ Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
       Common.StripNonAlloc || Common.StripSections || Common.Weaken ||
       Common.DecompressDebugSections ||
       Common.DiscardMode == DiscardType::Locals ||
-      !Common.SymbolsToAdd.empty() || Common.GapFill != 0 || Common.PadTo != 0)
+      !Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
     return createStringError(llvm::errc::invalid_argument,
                              "option is not supported for COFF");
 
@@ -46,7 +47,8 @@ Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
       Common.StripNonAlloc || Common.StripSections ||
       Common.DecompressDebugSections || Common.StripUnneeded ||
       Common.DiscardMode == DiscardType::Locals ||
-      !Common.SymbolsToAdd.empty() || Common.GapFill != 0 || Common.PadTo != 0)
+      !Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
     return createStringError(llvm::errc::invalid_argument,
                              "option is not supported for MachO");
 
@@ -66,7 +68,7 @@ Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
       !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
       !Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() ||
       !Common.SymbolsToRename.empty() || Common.GapFill != 0 ||
-      Common.PadTo != 0)
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
     return createStringError(llvm::errc::invalid_argument,
                              "only flags for section dumping, removal, and "
                              "addition are supported");
@@ -94,7 +96,8 @@ Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
       Common.PreserveDates || Common.StripAllGNU || Common.StripDWO ||
       Common.StripDebug || Common.StripNonAlloc || Common.StripSections ||
       Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections ||
-      Common.GapFill != 0 || Common.PadTo != 0) {
+      Common.GapFill != 0 || Common.PadTo != 0 ||
+      Common.ChangeSectionLMAValAll != 0) {
     return createStringError(
         llvm::errc::invalid_argument,
         "no flags are supported yet, only basic copying is allowed");

diff  --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index f343d1447e0554..60a85f9a49c590 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -670,6 +670,33 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
     }
   }
 
+  if (Config.ChangeSectionLMAValAll != 0) {
+    for (Segment &Seg : Obj.segments()) {
+      if (Seg.FileSize > 0) {
+        if (Config.ChangeSectionLMAValAll > 0 &&
+            Seg.PAddr > std::numeric_limits<uint64_t>::max() -
+                            Config.ChangeSectionLMAValAll) {
+          return createStringError(
+              errc::invalid_argument,
+              "address 0x" + Twine::utohexstr(Seg.PAddr) +
+                  " cannot be increased by 0x" +
+                  Twine::utohexstr(Config.ChangeSectionLMAValAll) +
+                  ". The result would overflow");
+        } else if (Config.ChangeSectionLMAValAll < 0 &&
+                   Seg.PAddr < std::numeric_limits<uint64_t>::min() -
+                                   Config.ChangeSectionLMAValAll) {
+          return createStringError(
+              errc::invalid_argument,
+              "address 0x" + Twine::utohexstr(Seg.PAddr) +
+                  " cannot be decreased by 0x" +
+                  Twine::utohexstr(std::abs(Config.ChangeSectionLMAValAll)) +
+                  ". The result would underflow");
+        }
+        Seg.PAddr += Config.ChangeSectionLMAValAll;
+      }
+    }
+  }
+
   if (Config.OnlyKeepDebug)
     for (auto &Sec : Obj.sections())
       if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)

diff  --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
new file mode 100644
index 00000000000000..c1cd1eb46d9496
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -0,0 +1,79 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
+# RUN: llvm-readelf --program-headers %t2 | FileCheck %s --check-prefix=CHECK-PLUS-PROGRAMS
+# RUN: llvm-readelf --section-headers %t2 | FileCheck %s --check-prefix=CHECK-PLUS-SECTIONS
+# RUN: llvm-objcopy --change-section-lma *-0x30 %t %t3
+# RUN: llvm-readelf --program-headers %t3 | FileCheck %s --check-prefix=CHECK-MINUS-PROGRAMS
+# RUN: llvm-readelf --section-headers %t3 | FileCheck %s --check-prefix=CHECK-MINUS-SECTIONS
+# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t  2>&1 | FileCheck %s --check-prefix=ERR-SET-ADDRESS
+# RUN: not llvm-objcopy --change-section-lma .text3+0x30 %t  2>&1 | FileCheck %s --check-prefix=ERR-SPECIFIC-SEC
+# RUN: not llvm-objcopy --change-section-lma *+0c50 %t  2>&1 | FileCheck %s --check-prefix=ERR-INVALID-VAL
+# RUN: not llvm-objcopy --change-section-lma 0 %t  2>&1 | FileCheck %s --check-prefix=ERR-MISSING-OFFSET
+# RUN: not llvm-objcopy --change-section-lma *-0x2000 %t  2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
+# RUN: not llvm-objcopy --change-section-lma *+0x100000000 %t  2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
+
+# CHECK-PLUS-PROGRAMS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
+# CHECK-PLUS-PROGRAMS:  PHDR           0x000002 0x0000000000001102 0x0000000000001122 0x000038 0x000000
+# CHECK-PLUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x0000000000001120 0x000258 0x000258
+# CHECK-PLUS-PROGRAMS:  LOAD           0x000258 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
+# CHECK-PLUS-PROGRAMS:  NOTE           0x000358 0x0000000000001200 0x0000000000001220 0x000010 0x000000
+# CHECK-PLUS-PROGRAMS:  NOTE           0x000368 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+
+# CHECK-MINUS-PROGRAMS:  PHDR           0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000258 0x000258
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000258 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
+# CHECK-MINUS-PROGRAMS:  NOTE           0x000358 0x0000000000001200 0x00000000000011d0 0x000010 0x000000
+# CHECK-MINUS-PROGRAMS:  NOTE           0x000368 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+
+# CHECK-PLUS-SECTIONS:      [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+# CHECK-PLUS-SECTIONS:           .text1
+# CHECK-PLUS-SECTIONS-SAME:                                        0000000000000000
+# CHECK-PLUS-SECTIONS:           .text2
+# CHECK-PLUS-SECTIONS-SAME:                                        0000000000000000
+
+# CHECK-MINUS-SECTIONS:           .text1
+# CHECK-MINUS-SECTIONS-SAME:                                        0000000000000000
+# CHECK-MINUS-SECTIONS:           .text2
+# CHECK-MINUS-SECTIONS-SAME:                                        0000000000000000
+
+# ERR-SET-ADDRESS: error: bad format for --change-section-lma: changing LMA to a specific value is not supported. Use *+val or *-val instead
+# ERR-SPECIFIC-SEC: error: bad format for --change-section-lma: changing a specific section LMA is not supported. Use *+val or *-val instead
+# ERR-INVALID-VAL: error: bad format for --change-section-lma: value after *+ is 0c50 when it should be an integer
+# ERR-MISSING-OFFSET: error: bad format for --change-section-lma: missing LMA offset
+# ERR-UNDERFLOW: : address 0x1102 cannot be decreased by 0x2000. The result would underflow
+# ERR-OVERFLOW: address 0xffffffff00006100 cannot be increased by 0x100000000. The result would overflow
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+Sections:
+  - Name:          .text1
+    Type:          SHT_PROGBITS
+    Size:          0x100
+  - Name:          .text2
+    Type:          SHT_PROGBITS
+    Size:          0x100
+ProgramHeaders:
+  - Type:          PT_PHDR
+    FileSize:      0x38
+    Offset:        0x2
+    VAddr:         0x1102
+  - Type:          PT_LOAD
+    Offset:        0x0
+    VAddr:         0x1100
+    FirstSec:      .text1
+    LastSec:       .text1
+  - Type:          PT_LOAD
+    VAddr:         0xFFFFFFFF00005100
+    PAddr:         0xFFFFFFFF00006100
+    FirstSec:      .text2
+    LastSec:       .text2
+  - Type:          PT_NOTE
+    FileSize:      0x10
+    VAddr:         0x1200
+    Offset:        0x358
+  - Type:          PT_NOTE
+    FileSize:      0x0
+    Offset:        0x368

diff  --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 4ab3b7265f2f6a..d5f95f874ea6f5 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -552,6 +552,38 @@ static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName,
   return Error::success();
 }
 
+static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
+                                               StringRef OptionName) {
+  StringRef StringValue;
+  if (ArgValue.starts_with("*+")) {
+    StringValue = ArgValue.slice(2, StringRef::npos);
+  } else if (ArgValue.starts_with("*-")) {
+    StringValue = ArgValue.slice(1, StringRef::npos);
+  } else if (ArgValue.contains("=")) {
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": changing LMA to a specific value is not "
+                                 "supported. Use *+val or *-val instead");
+  } else if (ArgValue.contains("+") || ArgValue.contains("-")) {
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": changing a specific section LMA is not "
+                                 "supported. Use *+val or *-val instead");
+  }
+  if (StringValue.empty())
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": missing LMA offset");
+
+  auto LMAValue = getAsInteger<int64_t>(StringValue);
+  if (!LMAValue)
+    return createStringError(LMAValue.getError(),
+                             "bad format for " + OptionName + ": value after " +
+                                 ArgValue.slice(0, 2) + " is " + StringValue +
+                                 " when it should be an integer");
+  return *LMAValue;
+}
+
 // parseObjcopyOptions returns the config and sets the input arguments. If a
 // help flag is set then parseObjcopyOptions will print the help messege and
 // exit.
@@ -833,6 +865,14 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     Config.PadTo = *Addr;
   }
 
+  if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_change_section_lma)) {
+    Expected<int64_t> LMAValue =
+        parseChangeSectionLMA(Arg->getValue(), Arg->getSpelling());
+    if (!LMAValue)
+      return LMAValue.takeError();
+    Config.ChangeSectionLMAValAll = *LMAValue;
+  }
+
   for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
     if (!StringRef(Arg->getValue()).contains('='))
       return createStringError(errc::invalid_argument,

diff  --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index 4bc80eba05f8e6..f3d14abee43bdf 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -254,6 +254,10 @@ def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
                    Alias<change_start>,
                    HelpText<"Alias for --change-start">;
 
+defm change_section_lma
+    : Eq<"change-section-lma", "Shift LMA of non-zero-sized sections in the program header by <val>">,
+      MetaVarName<"*{+|-}val">;
+
 defm add_symbol
     : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: "
          "global, local, weak, default, hidden, protected, file, section, object, "


        


More information about the llvm-commits mailing list