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

Eleanor Bonnici via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 8 02:23:54 PDT 2024


https://github.com/eleanor-arm updated https://github.com/llvm/llvm-project/pull/95431

>From 56a3e22b02bfc38b4231a5e3cb43643203ab4361 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Fri, 7 Jun 2024 13:08:16 +0100
Subject: [PATCH 1/6] [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  4 ++
 llvm/include/llvm/ObjCopy/CommonConfig.h      |  3 ++
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |  7 +++
 .../ELF/change-section-lma-multiple-load.test | 52 +++++++++++++++++++
 .../ELF/change-section-lma-single-load.test   | 45 ++++++++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    | 35 +++++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |  8 +++
 7 files changed, 154 insertions(+)
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index a62acfc8fdcd8..d1e2ddda6ec36 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -528,6 +528,10 @@ them.
 
  Mark all defined global symbols as weak in the output.
 
+.. option:: --change-section-lma \*{+-}<offset>
+
+ Currently only supports changing LMA for all sections by the same offset.
+
 MACH-O-SPECIFIC OPTIONS
 -----------------------
 
diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index ae08d4032736e..7f9d90d528b3e 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/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index f343d1447e055..32e6f5f751a03 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -670,6 +670,13 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
     }
   }
 
+  if (Config.ChangeSectionLMAValAll != 0) {
+    for (auto &Seg : Obj.segments()) {
+      if (Seg.FileSize > 0)
+        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-multiple-load.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
new file mode 100644
index 0000000000000..70aa8ef386ea2
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
@@ -0,0 +1,52 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
+# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
+# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
+# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
+# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t  2>&1 | FileCheck %s --check-prefix=ERR
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_AARCH64
+Sections:
+  - Name:            .text1
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "DEADBEEF"
+    Size:            0x1000
+  - Name:            .text2
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x2000
+    AddressAlign:    0x1000
+    Content:         "32323232"
+    Size:            0x1000
+  - Name:            .text3
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x3000
+    AddressAlign:    0x1000
+    Content:         "c3c3c3c3"
+    Size:            0x1000
+ProgramHeaders:
+  - Type:     PT_LOAD
+    Flags:    [ PF_R ]
+    VAddr:    0x1000
+    FirstSec: .text1
+    LastSec:  .text2
+# CHECK-PLUS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
+# CHECK-PLUS:  LOAD           0x001000 0x0000000000001000 0x0000000000001020 0x002000 0x002000
+# CHECK-MINUS: LOAD           0x001000 0x0000000000001000 0x0000000000001010 0x002000 0x002000
+  - Type:     PT_LOAD
+    Flags:    [ PF_R ]
+    VAddr:    0x7000
+    FirstSec: .text3
+    LastSec:  .text3
+# CHECK-PLUS:  LOAD           0x003000 0x0000000000007000 0x0000000000007020 0x001000 0x001000
+# CHECK-MINUS: LOAD           0x003000 0x0000000000007000 0x0000000000007010 0x001000 0x001000
+# ERR: error: bad format for --change-section-lma: it is required that all sections are either incremented, or decremented at the same time; use *+val, or *-val
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test
new file mode 100644
index 0000000000000..439ea81bf0b0f
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test
@@ -0,0 +1,45 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
+# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
+# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
+# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
+# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t  2>&1 | FileCheck %s --check-prefix=ERR
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_AARCH64
+Sections:
+  - Name:            .text1
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "DEADBEEF"
+    Size:            0x1000
+  - Name:            .text2
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x2000
+    AddressAlign:    0x1000
+    Content:         "32323232"
+    Size:            0x1000
+  - Name:            .text3
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x3000
+    AddressAlign:    0x1000
+    Content:         "c3c3c3c3"
+    Size:            0x1000
+ProgramHeaders:
+  - Type:     PT_LOAD
+    Flags:    [ PF_R ]
+    VAddr:    0x1000
+    FirstSec: .text1
+    LastSec:  .text3
+# CHECK-PLUS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
+# CHECK-PLUS:  LOAD           0x001000 0x0000000000001000 0x0000000000001020 0x003000 0x003000
+# CHECK-MINUS: LOAD           0x001000 0x0000000000001000 0x0000000000001010 0x003000 0x003000
+# ERR: error: bad format for --change-section-lma: it is required that all sections are either incremented, or decremented at the same time; use *+val, or *-val
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 4ab3b7265f2f6..c5f3c971a5599 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -552,6 +552,33 @@ static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName,
   return Error::success();
 }
 
+static Expected<int64_t> parseAdjustSectionLMA(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 {
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": it is required that all sections "
+                                 "are either incremented, or decremented at "
+                                 "the same time; use *+val, "
+                                 "or *-val");
+  }
+  if (StringValue.empty())
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": missing offset of LMA");
+
+  auto SLMAV = getAsInteger<int64_t>(StringValue);
+  if (!SLMAV)
+    return createStringError(SLMAV.getError(),
+                             "Unable to parse adjustment value");
+  return *SLMAV;
+}
+
 // 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 +860,14 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     Config.PadTo = *Addr;
   }
 
+  if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_adjust_section_lma)) {
+    Expected<int64_t> SLMAV =
+        parseAdjustSectionLMA(Arg->getValue(), Arg->getSpelling());
+    if (!SLMAV)
+      return SLMAV.takeError();
+    Config.ChangeSectionLMAValAll = *SLMAV;
+  }
+
   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 4bc80eba05f8e..44e9963ec1cb0 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -254,6 +254,14 @@ def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
                    Alias<change_start>,
                    HelpText<"Alias for --change-start">;
 
+defm adjust_section_lma
+    : Eq<"change-section-lma",
+         "Change LMA of all sections by <val>. LMA is the physical address where the section is "
+         "loaded in the memory; as opoosed to VMA, which is the virtual address at which the "
+         "section resides during program execution. Ussually LMA and VMA are the same, but some "
+         "platforms support these to be different resulting in runtime copying">,
+      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, "

>From ae970b411687adafe850802ed1626e14106fbdec Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Tue, 18 Jun 2024 12:58:16 +0100
Subject: [PATCH 2/6] fixup1! [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  4 +-
 llvm/lib/ObjCopy/ConfigManager.cpp            | 11 ++--
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |  2 +-
 .../ELF/change-section-lma-multiple-load.test | 52 ------------------
 .../ELF/change-section-lma-single-load.test   | 45 ---------------
 .../llvm-objcopy/ELF/change-section-lma.test  | 55 +++++++++++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    | 43 ++++++++-------
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |  8 +--
 8 files changed, 91 insertions(+), 129 deletions(-)
 delete mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
 delete mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index d1e2ddda6ec36..72f0596034e77 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -528,9 +528,9 @@ them.
 
  Mark all defined global symbols as weak in the output.
 
-.. option:: --change-section-lma \*{+-}<offset>
+.. option:: --change-section-lma \*{+-}<val>
 
- Currently only supports changing LMA for all sections by the same offset.
+ Shift LMA of non-zero-sized sections in the program header by ``<val>``
 
 MACH-O-SPECIFIC OPTIONS
 -----------------------
diff --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp
index 6442f1b958fb4..c542c4e5f0743 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 32e6f5f751a03..8dd6e1a40bc28 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -671,7 +671,7 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
   }
 
   if (Config.ChangeSectionLMAValAll != 0) {
-    for (auto &Seg : Obj.segments()) {
+    for (Segment &Seg : Obj.segments()) {
       if (Seg.FileSize > 0)
         Seg.PAddr += Config.ChangeSectionLMAValAll;
     }
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
deleted file mode 100644
index 70aa8ef386ea2..0000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test
+++ /dev/null
@@ -1,52 +0,0 @@
-# RUN: yaml2obj %s -o %t
-# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
-# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
-# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
-# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
-# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t  2>&1 | FileCheck %s --check-prefix=ERR
-
-!ELF
-FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
-  Machine:         EM_AARCH64
-Sections:
-  - Name:            .text1
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x1000
-    AddressAlign:    0x1000
-    Content:         "DEADBEEF"
-    Size:            0x1000
-  - Name:            .text2
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x2000
-    AddressAlign:    0x1000
-    Content:         "32323232"
-    Size:            0x1000
-  - Name:            .text3
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x3000
-    AddressAlign:    0x1000
-    Content:         "c3c3c3c3"
-    Size:            0x1000
-ProgramHeaders:
-  - Type:     PT_LOAD
-    Flags:    [ PF_R ]
-    VAddr:    0x1000
-    FirstSec: .text1
-    LastSec:  .text2
-# CHECK-PLUS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
-# CHECK-PLUS:  LOAD           0x001000 0x0000000000001000 0x0000000000001020 0x002000 0x002000
-# CHECK-MINUS: LOAD           0x001000 0x0000000000001000 0x0000000000001010 0x002000 0x002000
-  - Type:     PT_LOAD
-    Flags:    [ PF_R ]
-    VAddr:    0x7000
-    FirstSec: .text3
-    LastSec:  .text3
-# CHECK-PLUS:  LOAD           0x003000 0x0000000000007000 0x0000000000007020 0x001000 0x001000
-# CHECK-MINUS: LOAD           0x003000 0x0000000000007000 0x0000000000007010 0x001000 0x001000
-# ERR: error: bad format for --change-section-lma: it is required that all sections are either incremented, or decremented at the same time; use *+val, or *-val
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test
deleted file mode 100644
index 439ea81bf0b0f..0000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test
+++ /dev/null
@@ -1,45 +0,0 @@
-# RUN: yaml2obj %s -o %t
-# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
-# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
-# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
-# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
-# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t  2>&1 | FileCheck %s --check-prefix=ERR
-
-!ELF
-FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
-  Machine:         EM_AARCH64
-Sections:
-  - Name:            .text1
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x1000
-    AddressAlign:    0x1000
-    Content:         "DEADBEEF"
-    Size:            0x1000
-  - Name:            .text2
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x2000
-    AddressAlign:    0x1000
-    Content:         "32323232"
-    Size:            0x1000
-  - Name:            .text3
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x3000
-    AddressAlign:    0x1000
-    Content:         "c3c3c3c3"
-    Size:            0x1000
-ProgramHeaders:
-  - Type:     PT_LOAD
-    Flags:    [ PF_R ]
-    VAddr:    0x1000
-    FirstSec: .text1
-    LastSec:  .text3
-# CHECK-PLUS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
-# CHECK-PLUS:  LOAD           0x001000 0x0000000000001000 0x0000000000001020 0x003000 0x003000
-# CHECK-MINUS: LOAD           0x001000 0x0000000000001000 0x0000000000001010 0x003000 0x003000
-# ERR: error: bad format for --change-section-lma: it is required that all sections are either incremented, or decremented at the same time; use *+val, or *-val
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 0000000000000..8375f3aecf12c
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -0,0 +1,55 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
+# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
+# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
+# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
+# 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
+
+# CHECK-PLUS:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
+# CHECK-PLUS:  PHDR           0x000002 0x0000000000001002 0x0000000000001022 0x000038 0x000000
+# CHECK-PLUS:  LOAD           0x000000 0x0000000000001000 0x0000000000001020 0x001120 0x001120
+# CHECK-PLUS:  LOAD           0x001120 0x0000000000005000 0x0000000000006020 0x001000 0x001000
+# CHECK-PLUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+
+# CHECK-MINUS:  PHDR           0x000002 0x0000000000001002 0x0000000000001012 0x000038 0x000000
+# CHECK-MINUS:  LOAD           0x000000 0x0000000000001000 0x0000000000001010 0x001120 0x001120
+# CHECK-MINUS:  LOAD           0x001120 0x0000000000005000 0x0000000000006010 0x001000 0x001000
+# CHECK-MINUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+
+# 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 integer
+# ERR-MISSING-OFFSET: error: bad format for --change-section-lma: missing LMA offset
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+Sections:
+  - Name:          .text1
+    Type:          SHT_PROGBITS
+    Size:          0x1000
+  - Name:          .text2
+    Type:          SHT_PROGBITS
+    Size:          0x1000
+ProgramHeaders:
+  - Type:          PT_PHDR
+    FileSize:      0x38
+    Offset:        0x2
+    VAddr:         0x1002
+  - Type:          PT_LOAD
+    Offset:        0x0
+    VAddr:         0x1000
+    FirstSec:      .text1
+    LastSec:       .text1
+  - Type:          PT_LOAD
+    VAddr:         0x5000
+    PAddr:         0x6000
+    FirstSec:      .text2
+    LastSec:       .text2
+  - Type:          PT_NOTE
+    FileSize:      0x0
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index c5f3c971a5599..38b09f61860a6 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -552,31 +552,36 @@ static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName,
   return Error::success();
 }
 
-static Expected<int64_t> parseAdjustSectionLMA(StringRef ArgValue,
+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 {
+  } 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 +
-                                 ": it is required that all sections "
-                                 "are either incremented, or decremented at "
-                                 "the same time; use *+val, "
-                                 "or *-val");
+                                 ": 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 offset of LMA");
-
-  auto SLMAV = getAsInteger<int64_t>(StringValue);
-  if (!SLMAV)
-    return createStringError(SLMAV.getError(),
-                             "Unable to parse adjustment value");
-  return *SLMAV;
+                                 ": 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 integer");
+  return *LMAValue;
 }
 
 // parseObjcopyOptions returns the config and sets the input arguments. If a
@@ -860,12 +865,12 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     Config.PadTo = *Addr;
   }
 
-  if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_adjust_section_lma)) {
-    Expected<int64_t> SLMAV =
-        parseAdjustSectionLMA(Arg->getValue(), Arg->getSpelling());
-    if (!SLMAV)
-      return SLMAV.takeError();
-    Config.ChangeSectionLMAValAll = *SLMAV;
+  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)) {
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index 44e9963ec1cb0..f3d14abee43bd 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -254,12 +254,8 @@ def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
                    Alias<change_start>,
                    HelpText<"Alias for --change-start">;
 
-defm adjust_section_lma
-    : Eq<"change-section-lma",
-         "Change LMA of all sections by <val>. LMA is the physical address where the section is "
-         "loaded in the memory; as opoosed to VMA, which is the virtual address at which the "
-         "section resides during program execution. Ussually LMA and VMA are the same, but some "
-         "platforms support these to be different resulting in runtime copying">,
+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

>From 7e90cddd6a4a0400e1ec3ddc8f446ad9524dafda Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Thu, 27 Jun 2024 14:02:54 +0100
Subject: [PATCH 3/6] fixup2! [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  8 ++---
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           | 18 +++++++++--
 .../llvm-objcopy/ELF/change-section-lma.test  | 30 +++++++++++--------
 3 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 72f0596034e77..c0be99973d7e5 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 sections in the program header table by ``<val>``
+
 .. option:: --change-start <incr>, --adjust-start
 
  Add ``<incr>`` to the program's start address. Can be specified multiple
@@ -528,10 +532,6 @@ them.
 
  Mark all defined global symbols as weak in the output.
 
-.. option:: --change-section-lma \*{+-}<val>
-
- Shift LMA of non-zero-sized sections in the program header by ``<val>``
-
 MACH-O-SPECIFIC OPTIONS
 -----------------------
 
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 8dd6e1a40bc28..f05191e470d54 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -672,8 +672,22 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
 
   if (Config.ChangeSectionLMAValAll != 0) {
     for (Segment &Seg : Obj.segments()) {
-      if (Seg.FileSize > 0)
-        Seg.PAddr += Config.ChangeSectionLMAValAll;
+      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) +
+                                       " 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) +
+                                       " would underflow");
+        else
+          Seg.PAddr += Config.ChangeSectionLMAValAll;
+      }
     }
   }
 
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
index 8375f3aecf12c..20fd985d2d3a7 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -1,28 +1,32 @@
 # RUN: yaml2obj %s -o %t
 # RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
 # RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
-# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
+# RUN: llvm-objcopy --change-section-lma *-0x30 %t %t3
 # RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
 # 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:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
-# CHECK-PLUS:  PHDR           0x000002 0x0000000000001002 0x0000000000001022 0x000038 0x000000
-# CHECK-PLUS:  LOAD           0x000000 0x0000000000001000 0x0000000000001020 0x001120 0x001120
-# CHECK-PLUS:  LOAD           0x001120 0x0000000000005000 0x0000000000006020 0x001000 0x001000
+# CHECK-PLUS:  PHDR           0x000002 0x0000000000001102 0x0000000000001122 0x000038 0x000000
+# CHECK-PLUS:  LOAD           0x000000 0x0000000000001100 0x0000000000001120 0x000220 0x000220
+# CHECK-PLUS:  LOAD           0x000220 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
 # CHECK-PLUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
 
-# CHECK-MINUS:  PHDR           0x000002 0x0000000000001002 0x0000000000001012 0x000038 0x000000
-# CHECK-MINUS:  LOAD           0x000000 0x0000000000001000 0x0000000000001010 0x001120 0x001120
-# CHECK-MINUS:  LOAD           0x001120 0x0000000000005000 0x0000000000006010 0x001000 0x001000
+# CHECK-MINUS:  PHDR           0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
+# CHECK-MINUS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000220 0x000220
+# CHECK-MINUS:  LOAD           0x000220 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
 # CHECK-MINUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
 
 # 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 integer
 # ERR-MISSING-OFFSET: error: bad format for --change-section-lma: missing LMA offset
+# ERR-UNDERFLOW: : address 0x1102 would underflow
+# ERR-OVERFLOW: address 0xffffffff00006100 would overflow
 
 !ELF
 FileHeader:
@@ -32,23 +36,23 @@ FileHeader:
 Sections:
   - Name:          .text1
     Type:          SHT_PROGBITS
-    Size:          0x1000
+    Size:          0x100
   - Name:          .text2
     Type:          SHT_PROGBITS
-    Size:          0x1000
+    Size:          0x100
 ProgramHeaders:
   - Type:          PT_PHDR
     FileSize:      0x38
     Offset:        0x2
-    VAddr:         0x1002
+    VAddr:         0x1102
   - Type:          PT_LOAD
     Offset:        0x0
-    VAddr:         0x1000
+    VAddr:         0x1100
     FirstSec:      .text1
     LastSec:       .text1
   - Type:          PT_LOAD
-    VAddr:         0x5000
-    PAddr:         0x6000
+    VAddr:         0xFFFFFFFF00005100
+    PAddr:         0xFFFFFFFF00006100
     FirstSec:      .text2
     LastSec:       .text2
   - Type:          PT_NOTE

>From b053be3cec3d152a8ca24650eb237ac680c56561 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Mon, 1 Jul 2024 11:29:21 +0100
Subject: [PATCH 4/6] fixup3! [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  2 +-
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           | 24 +++++++----
 .../llvm-objcopy/ELF/change-section-lma.test  | 41 ++++++++++++-------
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  2 +-
 4 files changed, 44 insertions(+), 25 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index c0be99973d7e5..b688ff2e6b40f 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -301,7 +301,7 @@ them.
 
 .. option:: --change-section-lma \*{+-}<val>
 
- Shift LMA of non-zero-sized sections in the program header table by ``<val>``
+ Shift LMA of non-zero-sized segments by ``<val>``
 
 .. option:: --change-start <incr>, --adjust-start
 
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index f05191e470d54..60a85f9a49c59 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -676,17 +676,23 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
         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) +
-                                       " would overflow");
+          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) +
-                                       " would underflow");
-        else
-          Seg.PAddr += Config.ChangeSectionLMAValAll;
+                                   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;
       }
     }
   }
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
index 20fd985d2d3a7..4fe6e367d8cd0 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -1,8 +1,10 @@
 # RUN: yaml2obj %s -o %t
 # RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
-# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
+# 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 -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
+# 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
@@ -10,23 +12,34 @@
 # 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:  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz
-# CHECK-PLUS:  PHDR           0x000002 0x0000000000001102 0x0000000000001122 0x000038 0x000000
-# CHECK-PLUS:  LOAD           0x000000 0x0000000000001100 0x0000000000001120 0x000220 0x000220
-# CHECK-PLUS:  LOAD           0x000220 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
-# CHECK-PLUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+# 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 0x000220 0x000220
+# CHECK-PLUS-PROGRAMS:  LOAD           0x000220 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
+# CHECK-PLUS-PROGRAMS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
 
-# CHECK-MINUS:  PHDR           0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
-# CHECK-MINUS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000220 0x000220
-# CHECK-MINUS:  LOAD           0x000220 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
-# CHECK-MINUS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+# CHECK-MINUS-PROGRAMS:  PHDR           0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000220 0x000220
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000220 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
+# CHECK-MINUS-PROGRAMS:  NOTE           0x000000 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 integer
+# 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 would underflow
-# ERR-OVERFLOW: address 0xffffffff00006100 would overflow
+# 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:
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 38b09f61860a6..d5f95f874ea6f 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -580,7 +580,7 @@ static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
     return createStringError(LMAValue.getError(),
                              "bad format for " + OptionName + ": value after " +
                                  ArgValue.slice(0, 2) + " is " + StringValue +
-                                 " when it should be integer");
+                                 " when it should be an integer");
   return *LMAValue;
 }
 

>From 2e7afb7795a0aed11167f6f9d53220f9630d50b9 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Fri, 5 Jul 2024 15:45:03 +0100
Subject: [PATCH 5/6] fixup4! [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 .../tools/llvm-objcopy/ELF/change-section-lma.test  | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
index 4fe6e367d8cd0..7021f08a62ea3 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -14,14 +14,16 @@
 
 # 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 0x000220 0x000220
-# CHECK-PLUS-PROGRAMS:  LOAD           0x000220 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
+# CHECK-PLUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x0000000000001120 0x000258 0x000258
+# CHECK-PLUS-PROGRAMS:  LOAD           0x000258 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
 # CHECK-PLUS-PROGRAMS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+# CHECK-PLUS-PROGRAMS:  NOTE           0x000000 0x0000000000000100 0x0000000000000120 0x000010 0x000000
 
 # CHECK-MINUS-PROGRAMS:  PHDR           0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
-# CHECK-MINUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000220 0x000220
-# CHECK-MINUS-PROGRAMS:  LOAD           0x000220 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x00000000000010d0 0x000258 0x000258
+# CHECK-MINUS-PROGRAMS:  LOAD           0x000258 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
 # CHECK-MINUS-PROGRAMS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
+# CHECK-MINUS-PROGRAMS:  NOTE           0x000000 0x0000000000000100 0x00000000000000d0 0x000010 0x000000
 
 # CHECK-PLUS-SECTIONS:      [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
 # CHECK-PLUS-SECTIONS:           .text1
@@ -70,3 +72,6 @@ ProgramHeaders:
     LastSec:       .text2
   - Type:          PT_NOTE
     FileSize:      0x0
+  - Type:          PT_NOTE
+    FileSize:      0x10
+    VAddr:         0x100

>From dc6e748d1f11ed233d4ac16d523bb376209ec959 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Mon, 8 Jul 2024 10:19:50 +0100
Subject: [PATCH 6/6] fixup5! [llvm-objcopy] Add change-section-lma *+/-offset

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.
---
 llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
index 7021f08a62ea3..ace607fd83fdf 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-lma.test
@@ -17,13 +17,13 @@
 # CHECK-PLUS-PROGRAMS:  LOAD           0x000000 0x0000000000001100 0x0000000000001120 0x000258 0x000258
 # CHECK-PLUS-PROGRAMS:  LOAD           0x000258 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
 # CHECK-PLUS-PROGRAMS:  NOTE           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
-# CHECK-PLUS-PROGRAMS:  NOTE           0x000000 0x0000000000000100 0x0000000000000120 0x000010 0x000000
+# CHECK-PLUS-PROGRAMS:  NOTE           0x000358 0x0000000000001200 0x0000000000001220 0x000010 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           0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
-# CHECK-MINUS-PROGRAMS:  NOTE           0x000000 0x0000000000000100 0x00000000000000d0 0x000010 0x000000
+# CHECK-MINUS-PROGRAMS:  NOTE           0x000358 0x0000000000001200 0x00000000000011d0 0x000010 0x000000
 
 # CHECK-PLUS-SECTIONS:      [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
 # CHECK-PLUS-SECTIONS:           .text1
@@ -74,4 +74,5 @@ ProgramHeaders:
     FileSize:      0x0
   - Type:          PT_NOTE
     FileSize:      0x10
-    VAddr:         0x100
+    VAddr:         0x1200
+    Offset:        0x358



More information about the llvm-commits mailing list