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

Eleanor Bonnici via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 18 09:36:21 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/2] [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 304a839494c1198a32afa531d2e94536d2aa75ef 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/2] 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    | 31 ++++++-----
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |  8 +--
 8 files changed, 85 insertions(+), 123 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..6b9b525700c59 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -559,24 +559,29 @@ static Expected<int64_t> parseAdjustSectionLMA(StringRef ArgValue,
     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,7 +865,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     Config.PadTo = *Addr;
   }
 
-  if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_adjust_section_lma)) {
+  if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_change_section_lma)) {
     Expected<int64_t> SLMAV =
         parseAdjustSectionLMA(Arg->getValue(), Arg->getSpelling());
     if (!SLMAV)
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



More information about the llvm-commits mailing list