[llvm] [llvm-objcopy] Add --change-section-address (PR #98664)

Eleanor Bonnici via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 29 12:08:00 PDT 2024


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

>From 1c5a984c4351c618d24d73a85d340f4ee10e10d2 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Wed, 19 Jun 2024 14:31:11 +0100
Subject: [PATCH 1/4] [llvm-objcopy] Add --change-section-address

--change-section address and its alias --adjust-section-vma allows modification
of section addresses in a relocatable file. This used to be used, for example,
in Fiasco microkernel.

On a relocatable file this option behaves the same as GNU objcopy, apart from
the fact that it does not issue any warnings, for example, when an argument is
not used.
GNU objcopy does not produce an error when passed an executable file but the
usecase for this is not clear, and the behaviour is inconsistent. The idea of
GNU objcopy --change-section-address is that the option should change both LMA
and VMA in an executable file. Since this patch does not implement executable
file support, only VMA is changed.
---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |   9 ++
 llvm/include/llvm/ObjCopy/CommonConfig.h      |  96 ++++++++------
 llvm/lib/ObjCopy/ConfigManager.cpp            |  12 +-
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |  46 +++++++
 .../ELF/change-section-address.test           | 120 ++++++++++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  69 ++++++++++
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |   8 ++
 7 files changed, 317 insertions(+), 43 deletions(-)
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/change-section-address.test

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 8ccb025a3f0f3..78663fb1cef23 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -303,6 +303,15 @@ them.
 
  Shift LMA of non-zero-sized segments by ``<val>``.
 
+.. option:: --change-section-address <sectionpattern>{=+-}<val>, --adjust-section-vma
+
+ Change the address of ``<sectionpattern>`` to the specified value, or apply
+ offset to the current value. Can be specified multiple times to specify multiple
+ patterns. Each section is only modified by one --change-section-address
+ argument. Changes apply from the right of the command line. If a section name
+ matches multiple patterns, the rightmost change applies. Object file needs to be
+ relocatable.
+
 .. 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 7f9d90d528b3e..5e447670c4f99 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -44,45 +44,6 @@ struct MachineInfo {
   bool IsLittleEndian;
 };
 
-// Flags set by --set-section-flags or --rename-section. Interpretation of these
-// is format-specific and not all flags are meaningful for all object file
-// formats. This is a bitmask; many section flags may be set.
-enum SectionFlag {
-  SecNone = 0,
-  SecAlloc = 1 << 0,
-  SecLoad = 1 << 1,
-  SecNoload = 1 << 2,
-  SecReadonly = 1 << 3,
-  SecDebug = 1 << 4,
-  SecCode = 1 << 5,
-  SecData = 1 << 6,
-  SecRom = 1 << 7,
-  SecMerge = 1 << 8,
-  SecStrings = 1 << 9,
-  SecContents = 1 << 10,
-  SecShare = 1 << 11,
-  SecExclude = 1 << 12,
-  SecLarge = 1 << 13,
-  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
-};
-
-struct SectionRename {
-  StringRef OriginalName;
-  StringRef NewName;
-  std::optional<SectionFlag> NewFlags;
-};
-
-struct SectionFlagsUpdate {
-  StringRef Name;
-  SectionFlag NewFlags;
-};
-
-enum class DiscardType {
-  None,   // Default
-  All,    // --discard-all (-x)
-  Locals, // --discard-locals (-X)
-};
-
 enum class MatchStyle {
   Literal,  // Default for symbols.
   Wildcard, // Default for sections, or enabled with --wildcard (-w).
@@ -191,6 +152,61 @@ struct NewSectionInfo {
   std::shared_ptr<MemoryBuffer> SectionData;
 };
 
+// Flags set by --set-section-flags or --rename-section. Interpretation of these
+// is format-specific and not all flags are meaningful for all object file
+// formats. This is a bitmask; many section flags may be set.
+enum SectionFlag {
+  SecNone = 0,
+  SecAlloc = 1 << 0,
+  SecLoad = 1 << 1,
+  SecNoload = 1 << 2,
+  SecReadonly = 1 << 3,
+  SecDebug = 1 << 4,
+  SecCode = 1 << 5,
+  SecData = 1 << 6,
+  SecRom = 1 << 7,
+  SecMerge = 1 << 8,
+  SecStrings = 1 << 9,
+  SecContents = 1 << 10,
+  SecShare = 1 << 11,
+  SecExclude = 1 << 12,
+  SecLarge = 1 << 13,
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
+};
+
+struct SectionRename {
+  StringRef OriginalName;
+  StringRef NewName;
+  std::optional<SectionFlag> NewFlags;
+};
+
+struct SectionFlagsUpdate {
+  StringRef Name;
+  SectionFlag NewFlags;
+};
+
+struct AddressUpdate {
+  uint64_t Value = 0;
+  bool Absolute = false;
+  bool Negative = false;
+};
+
+struct SectionPatternAddressUpdate {
+  NameMatcher SectionPattern;
+  AddressUpdate Update;
+};
+
+struct SectionNameAddressUpdate {
+  StringRef Name;
+  AddressUpdate Update;
+};
+
+enum class DiscardType {
+  None,   // Default
+  All,    // --discard-all (-x)
+  Locals, // --discard-locals (-X)
+};
+
 // Configuration for copying/stripping a single file.
 struct CommonConfig {
   // Main input/output options
@@ -219,6 +235,7 @@ struct CommonConfig {
   SmallVector<NewSectionInfo, 0> AddSection;
   SmallVector<StringRef, 0> DumpSection;
   SmallVector<NewSectionInfo, 0> UpdateSection;
+  SmallVector<SectionPatternAddressUpdate, 0> ChangeSectionAddress;
 
   // Section matchers
   NameMatcher KeepSection;
@@ -241,6 +258,7 @@ struct CommonConfig {
   StringMap<SectionFlagsUpdate> SetSectionFlags;
   StringMap<uint64_t> SetSectionType;
   StringMap<StringRef> SymbolsToRename;
+  StringMap<AddressUpdate> SectionsToUpdateAddress;
 
   // Symbol info specified by --add-symbol option.
   SmallVector<NewSymbolInfo, 0> SymbolsToAdd;
diff --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp
index c542c4e5f0743..78fc0c451e1a3 100644
--- a/llvm/lib/ObjCopy/ConfigManager.cpp
+++ b/llvm/lib/ObjCopy/ConfigManager.cpp
@@ -26,7 +26,8 @@ Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
       Common.DecompressDebugSections ||
       Common.DiscardMode == DiscardType::Locals ||
       !Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
-      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
+      !Common.ChangeSectionAddress.empty())
     return createStringError(llvm::errc::invalid_argument,
                              "option is not supported for COFF");
 
@@ -48,7 +49,8 @@ Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
       Common.DecompressDebugSections || Common.StripUnneeded ||
       Common.DiscardMode == DiscardType::Locals ||
       !Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
-      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
+      !Common.ChangeSectionAddress.empty())
     return createStringError(llvm::errc::invalid_argument,
                              "option is not supported for MachO");
 
@@ -68,7 +70,8 @@ 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.ChangeSectionLMAValAll != 0)
+      Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
+      !Common.ChangeSectionAddress.empty())
     return createStringError(llvm::errc::invalid_argument,
                              "only flags for section dumping, removal, and "
                              "addition are supported");
@@ -97,7 +100,8 @@ Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
       Common.StripDebug || Common.StripNonAlloc || Common.StripSections ||
       Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections ||
       Common.GapFill != 0 || Common.PadTo != 0 ||
-      Common.ChangeSectionLMAValAll != 0) {
+      Common.ChangeSectionLMAValAll != 0 ||
+      !Common.ChangeSectionAddress.empty()) {
     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 075455c034154..718d6f2739382 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -745,6 +745,52 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
     }
   }
 
+  if (!Config.ChangeSectionAddress.empty()) {
+    if (Obj.isRelocatable()) {
+      StringMap<AddressUpdate> SectionsToUpdateAddress;
+      for (const SectionPatternAddressUpdate &PatternUpdate :
+           make_range(Config.ChangeSectionAddress.rbegin(),
+                      Config.ChangeSectionAddress.rend())) {
+        for (SectionBase &Sec : Obj.sections()) {
+          if (PatternUpdate.SectionPattern.matches(Sec.Name)) {
+            if (SectionsToUpdateAddress
+                    .try_emplace(Sec.Name, PatternUpdate.Update)
+                    .second) {
+              if (PatternUpdate.Update.Absolute) {
+                Sec.Addr = PatternUpdate.Update.Value;
+              } else if (PatternUpdate.Update.Negative &&
+                         Sec.Addr < PatternUpdate.Update.Value) {
+                return createStringError(
+                    errc::invalid_argument,
+                    "address 0x" + Twine::utohexstr(Sec.Addr) +
+                        " cannot be decreased by 0x" +
+                        Twine::utohexstr(PatternUpdate.Update.Value) +
+                        ". The result would underflow");
+              } else if (!PatternUpdate.Update.Negative &&
+                         Sec.Addr > std::numeric_limits<uint64_t>::max() -
+                                        PatternUpdate.Update.Value) {
+                return createStringError(
+                    errc::invalid_argument,
+                    "address 0x" + Twine::utohexstr(Sec.Addr) +
+                        " cannot be increased by 0x" +
+                        Twine::utohexstr(PatternUpdate.Update.Value) +
+                        ". The result would overflow");
+              } else if (PatternUpdate.Update.Negative) {
+                Sec.Addr -= PatternUpdate.Update.Value;
+              } else {
+                Sec.Addr += PatternUpdate.Update.Value;
+              }
+            }
+          }
+        }
+      }
+    } else {
+      return createStringError(
+          object_error::invalid_file_type,
+          "cannot change section address in a non-relocatable file");
+    }
+  }
+
   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-address.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
new file mode 100644
index 0000000000000..0a5a6a4b025c1
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
@@ -0,0 +1,120 @@
+# RUN: yaml2obj --docnum=1 %s -o %ti
+
+# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
+
+# RUN: llvm-objcopy --change-section-address *+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
+
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUB-SECTION
+
+# RUN: llvm-objcopy --change-section-address .text*+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
+
+# RUN: llvm-objcopy --change-section-address .text*=0x10 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SET-PATTERN
+
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30  --change-section-address .anotherone+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-USE-LAST
+
+# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
+
+# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
+
+# CHECK-ADD-ALL:          [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+# CHECK-ADD-ALL:               .text1
+# CHECK-ADD-ALL-SAME:                                            0000000000000120
+# CHECK-ADD-ALL:               .text2
+# CHECK-ADD-ALL:               .anotherone
+# CHECK-ADD-ALL-SAME:                                            0000000000000320
+
+# CHECK-SUB-SECTION:           .text1
+# CHECK-SUB-SECTION-SAME:                                        0000000000000100
+# CHECK-SUB-SECTION:           .text2
+# CHECK-SUB-SECTION-SAME:                                        0000000000000200
+# CHECK-SUB-SECTION:           .anotherone
+# CHECK-SUB-SECTION-SAME:                                        00000000000002d0
+
+# CHECK-ADD-PATTERN:           .text1
+# CHECK-ADD-PATTERN-SAME:                                        0000000000000120
+# CHECK-ADD-PATTERN:           .text2
+# CHECK-ADD-PATTERN-SAME:                                        0000000000000220
+# CHECK-ADD-PATTERN:           .anotherone
+# CHECK-ADD-PATTERN-SAME:                                        0000000000000300
+
+# CHECK-SET-PATTERN:           .text1
+# CHECK-SET-PATTERN-SAME:                                        0000000000000010
+# CHECK-SET-PATTERN:           .text2
+# CHECK-SET-PATTERN-SAME:                                        0000000000000010
+# CHECK-SET-PATTERN:           .anotherone
+# CHECK-SET-PATTERN-SAME:                                        0000000000000300
+
+# CHECK-USE-LAST:              .anotherone
+# CHECK-USE-LAST-SAME:                                           0000000000000320
+
+# CHECK-NOTSUPERSET-SET:       .text1
+# CHECK-NOTSUPERSET-SET-SAME:                                    0000000000000120
+# CHECK-NOTSUPERSET-SET:       .text2
+# CHECK-NOTSUPERSET-SET-SAME:                                    0000000000000220
+# CHECK-NOTSUPERSET-SET:       .anotherone
+# CHECK-NOTSUPERSET-SET-SAME:                                    0000000000000320
+
+# CHECK-SUPERSET-SET:          .text1
+# CHECK-SUPERSET-SET-SAME:                                       0000000000000120
+# CHECK-SUPERSET-SET:          .text2
+# CHECK-SUPERSET-SET-SAME:                                       0000000000000220
+# CHECK-SUPERSET-SET:          .anotherone
+# CHECK-SUPERSET-SET-SAME:                                       0000000000000455
+
+# RUN: not llvm-objcopy --change-section-address .anotherone+0xffffffffffffff00 %ti 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
+# RUN: not llvm-objcopy --change-section-address *-0x2000 %ti 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
+# RUN: not llvm-objcopy --change-section-address 0 %ti 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
+# RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
+# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
+# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
+# RUN: not llvm-objcopy --change-section-address .text1- %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
+# RUN: not llvm-objcopy --change-section-address .text1+ %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
+# RUN: not llvm-objcopy --change-section-address .text1= %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
+
+# ERR-OVERFLOW: address 0x300 cannot be increased by 0xffffffffffffff00. The result would overflow
+# ERR-UNDERFLOW: address 0x100 cannot be decreased by 0x2000. The result would underflow
+# ERR-IVALID-VAL: error: bad format for --change-section-address: argument value 0 is invalid. See help
+# ERR-NOT-INTEGER: error: bad format for --change-section-address: value after + is 0c50 when it should be an integer
+# ERR-MISSING-SECTION: error: bad format for --change-section-address: missing section pattern to apply address change to
+# ERR-MISSING-VALUE-MINUS: error: bad format for --change-section-address: missing value of offset after '-'
+# ERR-MISSING-VALUE-PLUS: error: bad format for --change-section-address: missing value of offset after '+'
+# ERR-MISSING-VALUE-EQUAL: error: bad format for --change-section-address: missing address value after '='
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+Sections:
+  - Name:          .text1
+    Type:          SHT_PROGBITS
+    Size:          0x100
+    Address:       0x100
+  - Name:          .text2
+    Type:          SHT_PROGBITS
+    Size:          0x100
+    Address:       0x200
+  - Name:          .anotherone
+    Type:          SHT_PROGBITS
+    Size:          0x100
+    Address:       0x300
+
+# RUN: yaml2obj --docnum=2 %s -o %ti
+
+# RUN: not llvm-objcopy --change-section-address *+0x20 %ti  2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
+
+# ERR-FILE-TYPE: cannot change section address in a non-relocatable file
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index a54feb7644bda..be4511c1d7c00 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -584,6 +584,65 @@ static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
   return *LMAValue;
 }
 
+static Expected<SectionPatternAddressUpdate>
+parseChangeSectionAddr(StringRef ArgValue, StringRef OptionName,
+                       MatchStyle SectionMatchStyle,
+                       function_ref<Error(Error)> ErrorCallback) {
+
+  SectionPatternAddressUpdate PatternUpdate;
+  std::pair<StringRef, StringRef> UpdateParts;
+  std::string UpdateSymbol;
+
+  if (ArgValue.contains("+")) {
+    UpdateParts = ArgValue.split("+");
+    UpdateSymbol = '+';
+  } else if (ArgValue.contains("-")) {
+    UpdateParts = ArgValue.split("-");
+    UpdateSymbol = '-';
+    PatternUpdate.Update.Negative = true;
+  } else if (ArgValue.contains("=")) {
+    UpdateParts = ArgValue.split("=");
+    UpdateSymbol = '=';
+    PatternUpdate.Update.Absolute = true;
+  } else {
+    return createStringError(errc::invalid_argument,
+                             "bad format for " + OptionName +
+                                 ": argument value " + ArgValue +
+                                 " is invalid. See help");
+  }
+
+  if (UpdateParts.second.empty()) {
+    if (UpdateSymbol == "+" || UpdateSymbol == "-")
+      return createStringError(errc::invalid_argument,
+                               "bad format for " + OptionName +
+                                   ": missing value of offset after '" +
+                                   UpdateSymbol + "'");
+    else if (UpdateSymbol == "=")
+      return createStringError(errc::invalid_argument,
+                               "bad format for " + OptionName +
+                                   ": missing address value after '='");
+  }
+  if (UpdateParts.first.empty())
+    return createStringError(
+        errc::invalid_argument,
+        "bad format for " + OptionName +
+            ": missing section pattern to apply address change to");
+
+  if (Error E = PatternUpdate.SectionPattern.addMatcher(NameOrPattern::create(
+          UpdateParts.first, SectionMatchStyle, ErrorCallback)))
+    return std::move(E);
+
+  auto AddrValue = getAsInteger<uint64_t>(UpdateParts.second);
+  if (!AddrValue)
+    return createStringError(AddrValue.getError(),
+                             "bad format for " + OptionName + ": value after " +
+                                 UpdateSymbol + " is " + UpdateParts.second +
+                                 " when it should be an integer");
+
+  PatternUpdate.Update.Value = *AddrValue;
+  return PatternUpdate;
+}
+
 // parseObjcopyOptions returns the config and sets the input arguments. If a
 // help flag is set then parseObjcopyOptions will print the help messege and
 // exit.
@@ -874,6 +933,16 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
     Config.ChangeSectionLMAValAll = *LMAValue;
   }
 
+  for (auto *Arg : InputArgs.filtered(OBJCOPY_change_section_address)) {
+    Expected<SectionPatternAddressUpdate> AddressUpdate =
+        parseChangeSectionAddr(Arg->getValue(), Arg->getSpelling(),
+                               SectionMatchStyle, ErrorCallback);
+    if (!AddressUpdate)
+      return AddressUpdate.takeError();
+    else
+      Config.ChangeSectionAddress.push_back(*AddressUpdate);
+  }
+
   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 b26c497ca3997..15bfbd7ab7b23 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -267,6 +267,14 @@ defm change_section_lma
     : Eq<"change-section-lma", "Shift LMA of non-zero-sized sections in the program header by <val>">,
       MetaVarName<"*{+|-}val">;
 
+defm change_section_address
+    : Eq<"change-section-address", "Set, or adjust the address of sections in <sectionpattern> to, "
+         "or by <val> in a relocatable file">,
+      MetaVarName<"sectionpattern{=|+|-}val">;
+def adjust_section_vma : JoinedOrSeparate<["--"], "adjust-section-vma">,
+                   Alias<change_section_address>,
+                   HelpText<"Alias for --change-section-address">;
+
 defm add_symbol
     : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: "
          "global, local, weak, default, hidden, protected, file, section, object, "

>From b5ab76d320367ebbbe9c6b88374422b40044f14c Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Wed, 24 Jul 2024 17:04:44 +0100
Subject: [PATCH 2/4] fixup! [llvm-objcopy] Add --change-section-address

---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  16 +--
 llvm/include/llvm/ObjCopy/CommonConfig.h      |  91 ++++++------
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |  76 +++++-----
 .../ELF/change-section-address.test           | 133 +++++++++++++++---
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  57 ++++----
 llvm/tools/llvm-objcopy/ObjcopyOpts.td        |   3 +-
 6 files changed, 231 insertions(+), 145 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 78663fb1cef23..0eb3539c025dc 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -303,14 +303,14 @@ them.
 
  Shift LMA of non-zero-sized segments by ``<val>``.
 
-.. option:: --change-section-address <sectionpattern>{=+-}<val>, --adjust-section-vma
-
- Change the address of ``<sectionpattern>`` to the specified value, or apply
- offset to the current value. Can be specified multiple times to specify multiple
- patterns. Each section is only modified by one --change-section-address
- argument. Changes apply from the right of the command line. If a section name
- matches multiple patterns, the rightmost change applies. Object file needs to be
- relocatable.
+.. option:: --change-section-address <section>{=+-}<val>, --adjust-section-vma
+
+ Change the address of sections that match``<section>`` pattern to the specified
+ value, or apply ``+-<val>`` to the current value. Can be specified multiple
+ times to specify multiple patterns. Each section is only modified by one
+ ``--change-section-address`` argument. Changes apply from the right of the
+ command line. If a section name matches multiple patterns, the rightmost change
+ applies. The object file needs to be of ET_REL type.
 
 .. option:: --change-start <incr>, --adjust-start
 
diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index 5e447670c4f99..5958fdc4a1470 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -112,46 +112,6 @@ class NameMatcher {
   }
 };
 
-enum class SymbolFlag {
-  Global,
-  Local,
-  Weak,
-  Default,
-  Hidden,
-  Protected,
-  File,
-  Section,
-  Object,
-  Function,
-  IndirectFunction,
-  Debug,
-  Constructor,
-  Warning,
-  Indirect,
-  Synthetic,
-  UniqueObject,
-};
-
-// Symbol info specified by --add-symbol option. Symbol flags not supported
-// by a concrete format should be ignored.
-struct NewSymbolInfo {
-  StringRef SymbolName;
-  StringRef SectionName;
-  uint64_t Value = 0;
-  SmallVector<SymbolFlag, 0> Flags;
-  SmallVector<StringRef, 0> BeforeSyms;
-};
-
-// Specify section name and section body for newly added or updated section.
-struct NewSectionInfo {
-  NewSectionInfo() = default;
-  NewSectionInfo(StringRef Name, std::unique_ptr<MemoryBuffer> &&Buffer)
-      : SectionName(Name), SectionData(std::move(Buffer)) {}
-
-  StringRef SectionName;
-  std::shared_ptr<MemoryBuffer> SectionData;
-};
-
 // Flags set by --set-section-flags or --rename-section. Interpretation of these
 // is format-specific and not all flags are meaningful for all object file
 // formats. This is a bitmask; many section flags may be set.
@@ -185,10 +145,11 @@ struct SectionFlagsUpdate {
   SectionFlag NewFlags;
 };
 
+enum class AdjustKind { Set, Add, Subtract };
+
 struct AddressUpdate {
   uint64_t Value = 0;
-  bool Absolute = false;
-  bool Negative = false;
+  AdjustKind Kind = AdjustKind::Add;
 };
 
 struct SectionPatternAddressUpdate {
@@ -196,17 +157,52 @@ struct SectionPatternAddressUpdate {
   AddressUpdate Update;
 };
 
-struct SectionNameAddressUpdate {
-  StringRef Name;
-  AddressUpdate Update;
-};
-
 enum class DiscardType {
   None,   // Default
   All,    // --discard-all (-x)
   Locals, // --discard-locals (-X)
 };
 
+enum class SymbolFlag {
+  Global,
+  Local,
+  Weak,
+  Default,
+  Hidden,
+  Protected,
+  File,
+  Section,
+  Object,
+  Function,
+  IndirectFunction,
+  Debug,
+  Constructor,
+  Warning,
+  Indirect,
+  Synthetic,
+  UniqueObject,
+};
+
+// Symbol info specified by --add-symbol option. Symbol flags not supported
+// by a concrete format should be ignored.
+struct NewSymbolInfo {
+  StringRef SymbolName;
+  StringRef SectionName;
+  uint64_t Value = 0;
+  SmallVector<SymbolFlag, 0> Flags;
+  SmallVector<StringRef, 0> BeforeSyms;
+};
+
+// Specify section name and section body for newly added or updated section.
+struct NewSectionInfo {
+  NewSectionInfo() = default;
+  NewSectionInfo(StringRef Name, std::unique_ptr<MemoryBuffer> &&Buffer)
+      : SectionName(Name), SectionData(std::move(Buffer)) {}
+
+  StringRef SectionName;
+  std::shared_ptr<MemoryBuffer> SectionData;
+};
+
 // Configuration for copying/stripping a single file.
 struct CommonConfig {
   // Main input/output options
@@ -258,7 +254,6 @@ struct CommonConfig {
   StringMap<SectionFlagsUpdate> SetSectionFlags;
   StringMap<uint64_t> SetSectionType;
   StringMap<StringRef> SymbolsToRename;
-  StringMap<AddressUpdate> SectionsToUpdateAddress;
 
   // Symbol info specified by --add-symbol option.
   SmallVector<NewSymbolInfo, 0> SymbolsToAdd;
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 718d6f2739382..adae84bc29fec 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -746,48 +746,46 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
   }
 
   if (!Config.ChangeSectionAddress.empty()) {
-    if (Obj.isRelocatable()) {
-      StringMap<AddressUpdate> SectionsToUpdateAddress;
-      for (const SectionPatternAddressUpdate &PatternUpdate :
-           make_range(Config.ChangeSectionAddress.rbegin(),
-                      Config.ChangeSectionAddress.rend())) {
-        for (SectionBase &Sec : Obj.sections()) {
-          if (PatternUpdate.SectionPattern.matches(Sec.Name)) {
-            if (SectionsToUpdateAddress
-                    .try_emplace(Sec.Name, PatternUpdate.Update)
-                    .second) {
-              if (PatternUpdate.Update.Absolute) {
-                Sec.Addr = PatternUpdate.Update.Value;
-              } else if (PatternUpdate.Update.Negative &&
-                         Sec.Addr < PatternUpdate.Update.Value) {
-                return createStringError(
-                    errc::invalid_argument,
-                    "address 0x" + Twine::utohexstr(Sec.Addr) +
-                        " cannot be decreased by 0x" +
-                        Twine::utohexstr(PatternUpdate.Update.Value) +
-                        ". The result would underflow");
-              } else if (!PatternUpdate.Update.Negative &&
-                         Sec.Addr > std::numeric_limits<uint64_t>::max() -
-                                        PatternUpdate.Update.Value) {
-                return createStringError(
-                    errc::invalid_argument,
-                    "address 0x" + Twine::utohexstr(Sec.Addr) +
-                        " cannot be increased by 0x" +
-                        Twine::utohexstr(PatternUpdate.Update.Value) +
-                        ". The result would overflow");
-              } else if (PatternUpdate.Update.Negative) {
-                Sec.Addr -= PatternUpdate.Update.Value;
-              } else {
-                Sec.Addr += PatternUpdate.Update.Value;
-              }
-            }
-          }
-        }
-      }
-    } else {
+    if (Obj.Type != ELF::ET_REL)
       return createStringError(
           object_error::invalid_file_type,
           "cannot change section address in a non-relocatable file");
+    StringMap<AddressUpdate> SectionsToUpdateAddress;
+    for (const SectionPatternAddressUpdate &PatternUpdate :
+         make_range(Config.ChangeSectionAddress.rbegin(),
+                    Config.ChangeSectionAddress.rend())) {
+      for (SectionBase &Sec : Obj.sections()) {
+        if (PatternUpdate.SectionPattern.matches(Sec.Name) &&
+            SectionsToUpdateAddress.try_emplace(Sec.Name, PatternUpdate.Update)
+                .second) {
+          if (PatternUpdate.Update.Kind == AdjustKind::Subtract &&
+              Sec.Addr < PatternUpdate.Update.Value) {
+            return createStringError(
+                errc::invalid_argument,
+                "address 0x" + Twine::utohexstr(Sec.Addr) +
+                    " cannot be decreased by 0x" +
+                    Twine::utohexstr(PatternUpdate.Update.Value) +
+                    ". The result would underflow");
+          }
+          if (PatternUpdate.Update.Kind == AdjustKind::Add &&
+              Sec.Addr > std::numeric_limits<uint64_t>::max() -
+                             PatternUpdate.Update.Value) {
+            return createStringError(
+                errc::invalid_argument,
+                "address 0x" + Twine::utohexstr(Sec.Addr) +
+                    " cannot be increased by 0x" +
+                    Twine::utohexstr(PatternUpdate.Update.Value) +
+                    ". The result would overflow");
+          }
+          if (PatternUpdate.Update.Kind == AdjustKind::Set) {
+            Sec.Addr = PatternUpdate.Update.Value;
+          } else if (PatternUpdate.Update.Kind == AdjustKind::Subtract) {
+            Sec.Addr -= PatternUpdate.Update.Value;
+          } else {
+            Sec.Addr += PatternUpdate.Update.Value;
+          }
+        }
+      }
     }
   }
 
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
index 0a5a6a4b025c1..ae14e4c91194c 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
@@ -1,35 +1,98 @@
-# RUN: yaml2obj --docnum=1 %s -o %ti
-
-# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti %to
+# Tests behavior of --change-section-address option
+# --------------------------------------------------
+# yaml2obj generates an object file %ti that looks like this:
+#
+# llvm-readelf --section-headers %ti
+# Section Headers:
+#   [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+#   [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
+#   [ 1] .text1            PROGBITS        0000000000000100 000040 000100 00      0   0  0
+#   [ 2] .text2            PROGBITS        0000000000000200 000140 000100 00      0   0  0
+#   [ 3] .anotherone       PROGBITS        0000000000000300 000240 000100 00      0   0  0
+#   [ 4] =a-b+c++d         PROGBITS        0000000000000400 000340 000100 00      0   0  0
+#   [ 5] .strtab           STRTAB          0000000000000000 000440 000001 00      0   0  1
+#   [ 6] .shstrtab         STRTAB          0000000000000000 000441 000037 00      0   0  1
+#
+# Various changes are applied to this file using llvm-objcopy --change-section-address and
+# results are checked for expected address values, or errors.
+#
+
+# RUN: yaml2obj -DTYPE=REL %s -o %ti
+
+# Basic check that the option processes wildcards and changes the address as expected
+# RUN: llvm-objcopy --change-section-address *+0x20 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
 
-# RUN: llvm-objcopy --change-section-address *+0x20 %ti %to
+# Check --change-section-address alias --adjust-section-vma
+# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
 
+# Check negative adjustment
 # RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUB-SECTION
 
+# Check wildcard that does not change all sections
 # RUN: llvm-objcopy --change-section-address .text*+0x20 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
 
+# Check regex
+# RUN: llvm-objcopy --regex --change-section-address .text.+0x20 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
+
+# Check setting the address to a specific value
 # RUN: llvm-objcopy --change-section-address .text*=0x10 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SET-PATTERN
 
-# RUN: llvm-objcopy --change-section-address .anotherone-0x30  --change-section-address .anotherone+0x20 %ti %to
+# Check adjustment to max possible value (UINT64_MAX)
+# RUN: llvm-objcopy --change-section-address .text2+0xfffffffffffffdff %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-MAX
+
+# Check adjustment to min possible value (0)
+# RUN: llvm-objcopy --change-section-address .text2-0x200 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ZERO
+
+# Check setting to max possible value (UINT64_MAX)
+# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-MAX
+
+# Check maximum negative adjustment
+# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti %to1
+# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to1 %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ZERO
+
+# Check two independent changes
+# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-INDEPENDENT
+
+# check two overlapping changes
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-USE-LAST
 
+# Check unused option
 # RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
 
+# Check partial overlap (.anotherone overlaps)
 # RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti %to
 # RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
 
-# CHECK-ADD-ALL:          [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+# Check more complex partial overlap (P1: .anotherone, .text2, P2: .text1, text2) (.text2 overlaps)
+# RUN: llvm-objcopy --regex  --change-section-address  ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30  %ti %to
+# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
+
+# CHECK-ADD-ALL:          [Nr] Name              Type            Address
 # CHECK-ADD-ALL:               .text1
 # CHECK-ADD-ALL-SAME:                                            0000000000000120
 # CHECK-ADD-ALL:               .text2
+# CHECK-ADD-ALL-SAME:                                            0000000000000220
 # CHECK-ADD-ALL:               .anotherone
 # CHECK-ADD-ALL-SAME:                                            0000000000000320
+# CHECK-ADD-ALL:               =a-b+c++d
+# CHECK-ADD-ALL-SAME:                                            0000000000000420
+# CHECK-ADD-ALL:               .strtab
+# CHECK-ADD_ALL-SAME:                                            0000000000000020
+# CHECK-ADD-ALL:               .shstrtab
+# CHECK-ADD-ALL-SAME:                                            0000000000000020
 
 # CHECK-SUB-SECTION:           .text1
 # CHECK-SUB-SECTION-SAME:                                        0000000000000100
@@ -52,6 +115,16 @@
 # CHECK-SET-PATTERN:           .anotherone
 # CHECK-SET-PATTERN-SAME:                                        0000000000000300
 
+# CHECK-MAX:                   .text2
+# CHECK-MAX-SAME:                                                ffffffffffffffff
+# CHECK-ZERO:                  .text2
+# CHECK-ZERO-SAME:                                               0000000000000000
+
+# CHECK-INDEPENDENT:           .text1
+# CHECK-INDEPENDENT-SAME:                                        0000000000000110
+# CHECK-INDEPENDENT:           .text2
+# CHECK-INDEPENDENT-SAME:                                        0000000000000210
+
 # CHECK-USE-LAST:              .anotherone
 # CHECK-USE-LAST-SAME:                                           0000000000000320
 
@@ -69,30 +142,49 @@
 # CHECK-SUPERSET-SET:          .anotherone
 # CHECK-SUPERSET-SET-SAME:                                       0000000000000455
 
-# RUN: not llvm-objcopy --change-section-address .anotherone+0xffffffffffffff00 %ti 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
-# RUN: not llvm-objcopy --change-section-address *-0x2000 %ti 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
+# CHECK-PARTIAL-OVERLAP:        .text1
+# CHECK-PARTIAL-OVERLAP-SAME:                                    0000000000000130
+# CHECK-PARTIAL-OVERLAP:        .text2
+# CHECK-PARTIAL-OVERLAP-SAME:                                    0000000000000230
+# CHECK-PARTIAL-OVERLAP:        .anotherone
+# CHECK-PARTIAL-OVERLAP-SAME:                                    0000000000000320
+
+# Overflow by 1
+# RUN: not llvm-objcopy --change-section-address .anotherone+0xfffffffffffffd00 %ti 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
+# Underflow by 1
+# RUN: not llvm-objcopy --change-section-address .text2-0x201 %ti 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
+# Invalid argument value
 # RUN: not llvm-objcopy --change-section-address 0 %ti 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
+# Invalid value in argument value
 # RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
+# Value does not fit in uint64_t
+# RUN  not llvm-objcopy --change-section-address .text1=0x10000000000000000 %ti %to 2>&1 | FileCheck %s --chack-prefix=ERR-NOT-INTEGER
+# Missing section pattern
 # RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
-# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
+# Missing value after -
 # RUN: not llvm-objcopy --change-section-address .text1- %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
+# Missing value after +
 # RUN: not llvm-objcopy --change-section-address .text1+ %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
+# Missing value after =
 # RUN: not llvm-objcopy --change-section-address .text1= %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
+# Invalid regex
+# RUN: not llvm-objcopy --regex --change-section-address  "ab**-0x20" %ti 2>&1 | FileCheck %s --check-prefix=ERR-MATCHER-FAILURE
 
-# ERR-OVERFLOW: address 0x300 cannot be increased by 0xffffffffffffff00. The result would overflow
-# ERR-UNDERFLOW: address 0x100 cannot be decreased by 0x2000. The result would underflow
-# ERR-IVALID-VAL: error: bad format for --change-section-address: argument value 0 is invalid. See help
-# ERR-NOT-INTEGER: error: bad format for --change-section-address: value after + is 0c50 when it should be an integer
+# ERR-OVERFLOW: address 0x300 cannot be increased by 0xfffffffffffffd00. The result would overflow
+# ERR-UNDERFLOW: address 0x200 cannot be decreased by 0x201. The result would underflow
+# ERR-IVALID-VAL: error: bad format for --change-section-address: argument value 0 is invalid. See --help
+# ERR-NOT-INTEGER: error: bad format for --change-section-address: value after + is 0c50 when it should be a 64-bit integer
 # ERR-MISSING-SECTION: error: bad format for --change-section-address: missing section pattern to apply address change to
 # ERR-MISSING-VALUE-MINUS: error: bad format for --change-section-address: missing value of offset after '-'
 # ERR-MISSING-VALUE-PLUS: error: bad format for --change-section-address: missing value of offset after '+'
 # ERR-MISSING-VALUE-EQUAL: error: bad format for --change-section-address: missing address value after '='
+# ERR-MATCHER-FAILURE: error: cannot compile regular expression 'ab**': repetition-operator operand invalid
 
 --- !ELF
 FileHeader:
   Class:           ELFCLASS64
   Data:            ELFDATA2LSB
-  Type:            ET_REL
+  Type:            ET_[[TYPE]]
 Sections:
   - Name:          .text1
     Type:          SHT_PROGBITS
@@ -106,15 +198,14 @@ Sections:
     Type:          SHT_PROGBITS
     Size:          0x100
     Address:       0x300
+  - Name:          =a-b+c++d
+    Type:          SHT_PROGBITS
+    Size:          0x100
+    Address:       0x400
 
-# RUN: yaml2obj --docnum=2 %s -o %ti
+# RUN: yaml2obj -DTYPE=EXEC %s -o %ti
 
+# Input file is not ET_REL
 # RUN: not llvm-objcopy --change-section-address *+0x20 %ti  2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
 
 # ERR-FILE-TYPE: cannot change section address in a non-relocatable file
-
---- !ELF
-FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index be4511c1d7c00..6f197cd220ec2 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -590,54 +590,58 @@ parseChangeSectionAddr(StringRef ArgValue, StringRef OptionName,
                        function_ref<Error(Error)> ErrorCallback) {
 
   SectionPatternAddressUpdate PatternUpdate;
-  std::pair<StringRef, StringRef> UpdateParts;
-  std::string UpdateSymbol;
-
-  if (ArgValue.contains("+")) {
-    UpdateParts = ArgValue.split("+");
-    UpdateSymbol = '+';
-  } else if (ArgValue.contains("-")) {
-    UpdateParts = ArgValue.split("-");
-    UpdateSymbol = '-';
-    PatternUpdate.Update.Negative = true;
-  } else if (ArgValue.contains("=")) {
-    UpdateParts = ArgValue.split("=");
-    UpdateSymbol = '=';
-    PatternUpdate.Update.Absolute = true;
-  } else {
+
+  size_t last_i = ArgValue.find_last_of("+-=");
+  StringRef Value = ArgValue.slice(last_i + 1, StringRef::npos);
+  StringRef SectionPattern = ArgValue.slice(0, last_i);
+
+  if (last_i == StringRef::npos)
     return createStringError(errc::invalid_argument,
                              "bad format for " + OptionName +
                                  ": argument value " + ArgValue +
-                                 " is invalid. See help");
+                                 " is invalid. See --help");
+
+  char UpdateSymbol = ArgValue[last_i];
+
+  switch (UpdateSymbol) {
+  case '+':
+    PatternUpdate.Update.Kind = AdjustKind::Add;
+    break;
+  case '-':
+    PatternUpdate.Update.Kind = AdjustKind::Subtract;
+    break;
+  case '=':
+    PatternUpdate.Update.Kind = AdjustKind::Set;
   }
 
-  if (UpdateParts.second.empty()) {
-    if (UpdateSymbol == "+" || UpdateSymbol == "-")
+  if (Value.empty()) {
+    if (UpdateSymbol == '+' || UpdateSymbol == '-')
       return createStringError(errc::invalid_argument,
                                "bad format for " + OptionName +
                                    ": missing value of offset after '" +
-                                   UpdateSymbol + "'");
-    else if (UpdateSymbol == "=")
+                                   std::string({UpdateSymbol}) + "'");
+    else if (UpdateSymbol == '=')
       return createStringError(errc::invalid_argument,
                                "bad format for " + OptionName +
                                    ": missing address value after '='");
   }
-  if (UpdateParts.first.empty())
+
+  if (SectionPattern.empty())
     return createStringError(
         errc::invalid_argument,
         "bad format for " + OptionName +
             ": missing section pattern to apply address change to");
 
   if (Error E = PatternUpdate.SectionPattern.addMatcher(NameOrPattern::create(
-          UpdateParts.first, SectionMatchStyle, ErrorCallback)))
+          SectionPattern, SectionMatchStyle, ErrorCallback)))
     return std::move(E);
 
-  auto AddrValue = getAsInteger<uint64_t>(UpdateParts.second);
+  auto AddrValue = getAsInteger<uint64_t>(Value);
   if (!AddrValue)
     return createStringError(AddrValue.getError(),
                              "bad format for " + OptionName + ": value after " +
-                                 UpdateSymbol + " is " + UpdateParts.second +
-                                 " when it should be an integer");
+                                 std::string({UpdateSymbol}) + " is " + Value +
+                                 " when it should be a 64-bit integer");
 
   PatternUpdate.Update.Value = *AddrValue;
   return PatternUpdate;
@@ -939,8 +943,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
                                SectionMatchStyle, ErrorCallback);
     if (!AddressUpdate)
       return AddressUpdate.takeError();
-    else
-      Config.ChangeSectionAddress.push_back(*AddressUpdate);
+    Config.ChangeSectionAddress.push_back(*AddressUpdate);
   }
 
   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 15bfbd7ab7b23..434b5ff92324e 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -268,8 +268,7 @@ defm change_section_lma
       MetaVarName<"*{+|-}val">;
 
 defm change_section_address
-    : Eq<"change-section-address", "Set, or adjust the address of sections in <sectionpattern> to, "
-         "or by <val> in a relocatable file">,
+    : Eq<"change-section-address", "Set the address of the <section> to, or adjust it by, <val>">,
       MetaVarName<"sectionpattern{=|+|-}val">;
 def adjust_section_vma : JoinedOrSeparate<["--"], "adjust-section-vma">,
                    Alias<change_section_address>,

>From 26778dec3427b4634241a9237a46dacf9764c7a8 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Fri, 26 Jul 2024 17:39:38 +0100
Subject: [PATCH 3/4] fixup! fixup! [llvm-objcopy] Add --change-section-address

---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  12 +-
 llvm/include/llvm/ObjCopy/CommonConfig.h      |  78 +++----
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |  12 +-
 .../ELF/change-section-address.test           | 191 ++++++++----------
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |  61 +++---
 5 files changed, 171 insertions(+), 183 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 0eb3539c025dc..eff613404e313 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -305,12 +305,12 @@ them.
 
 .. option:: --change-section-address <section>{=+-}<val>, --adjust-section-vma
 
- Change the address of sections that match``<section>`` pattern to the specified
- value, or apply ``+-<val>`` to the current value. Can be specified multiple
- times to specify multiple patterns. Each section is only modified by one
- ``--change-section-address`` argument. Changes apply from the right of the
- command line. If a section name matches multiple patterns, the rightmost change
- applies. The object file needs to be of ET_REL type.
+ Change the address of sections that match ``<section>`` pattern to the
+ specified value, or apply ``+<val>``/``-<val>`` to the current value. Can be
+ specified multiple times to specify multiple patterns. Each section is only
+ modified by one ``--change-section-address`` argument. Changes apply from the
+ right of the command line. If a section name matches multiple patterns, the
+ rightmost change applies. The object file needs to be of ET_REL type.
 
 .. option:: --change-start <incr>, --adjust-start
 
diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index 5958fdc4a1470..5ae09760e9a54 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -44,6 +44,45 @@ struct MachineInfo {
   bool IsLittleEndian;
 };
 
+// Flags set by --set-section-flags or --rename-section. Interpretation of these
+// is format-specific and not all flags are meaningful for all object file
+// formats. This is a bitmask; many section flags may be set.
+enum SectionFlag {
+  SecNone = 0,
+  SecAlloc = 1 << 0,
+  SecLoad = 1 << 1,
+  SecNoload = 1 << 2,
+  SecReadonly = 1 << 3,
+  SecDebug = 1 << 4,
+  SecCode = 1 << 5,
+  SecData = 1 << 6,
+  SecRom = 1 << 7,
+  SecMerge = 1 << 8,
+  SecStrings = 1 << 9,
+  SecContents = 1 << 10,
+  SecShare = 1 << 11,
+  SecExclude = 1 << 12,
+  SecLarge = 1 << 13,
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
+};
+
+struct SectionRename {
+  StringRef OriginalName;
+  StringRef NewName;
+  std::optional<SectionFlag> NewFlags;
+};
+
+struct SectionFlagsUpdate {
+  StringRef Name;
+  SectionFlag NewFlags;
+};
+
+enum class DiscardType {
+  None,   // Default
+  All,    // --discard-all (-x)
+  Locals, // --discard-locals (-X)
+};
+
 enum class MatchStyle {
   Literal,  // Default for symbols.
   Wildcard, // Default for sections, or enabled with --wildcard (-w).
@@ -112,39 +151,6 @@ class NameMatcher {
   }
 };
 
-// Flags set by --set-section-flags or --rename-section. Interpretation of these
-// is format-specific and not all flags are meaningful for all object file
-// formats. This is a bitmask; many section flags may be set.
-enum SectionFlag {
-  SecNone = 0,
-  SecAlloc = 1 << 0,
-  SecLoad = 1 << 1,
-  SecNoload = 1 << 2,
-  SecReadonly = 1 << 3,
-  SecDebug = 1 << 4,
-  SecCode = 1 << 5,
-  SecData = 1 << 6,
-  SecRom = 1 << 7,
-  SecMerge = 1 << 8,
-  SecStrings = 1 << 9,
-  SecContents = 1 << 10,
-  SecShare = 1 << 11,
-  SecExclude = 1 << 12,
-  SecLarge = 1 << 13,
-  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
-};
-
-struct SectionRename {
-  StringRef OriginalName;
-  StringRef NewName;
-  std::optional<SectionFlag> NewFlags;
-};
-
-struct SectionFlagsUpdate {
-  StringRef Name;
-  SectionFlag NewFlags;
-};
-
 enum class AdjustKind { Set, Add, Subtract };
 
 struct AddressUpdate {
@@ -157,12 +163,6 @@ struct SectionPatternAddressUpdate {
   AddressUpdate Update;
 };
 
-enum class DiscardType {
-  None,   // Default
-  All,    // --discard-all (-x)
-  Locals, // --discard-locals (-X)
-};
-
 enum class SymbolFlag {
   Global,
   Local,
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index adae84bc29fec..40598861d4273 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -750,6 +750,7 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
       return createStringError(
           object_error::invalid_file_type,
           "cannot change section address in a non-relocatable file");
+
     StringMap<AddressUpdate> SectionsToUpdateAddress;
     for (const SectionPatternAddressUpdate &PatternUpdate :
          make_range(Config.ChangeSectionAddress.rbegin(),
@@ -777,12 +778,17 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
                     Twine::utohexstr(PatternUpdate.Update.Value) +
                     ". The result would overflow");
           }
-          if (PatternUpdate.Update.Kind == AdjustKind::Set) {
+
+          switch (PatternUpdate.Update.Kind) {
+          case (AdjustKind::Set):
             Sec.Addr = PatternUpdate.Update.Value;
-          } else if (PatternUpdate.Update.Kind == AdjustKind::Subtract) {
+            break;
+          case (AdjustKind::Subtract):
             Sec.Addr -= PatternUpdate.Update.Value;
-          } else {
+            break;
+          case (AdjustKind::Add):
             Sec.Addr += PatternUpdate.Update.Value;
+            break;
           }
         }
       }
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
index ae14e4c91194c..6430d0e04ed7a 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
@@ -1,84 +1,67 @@
-# Tests behavior of --change-section-address option
-# --------------------------------------------------
-# yaml2obj generates an object file %ti that looks like this:
-#
-# llvm-readelf --section-headers %ti
-# Section Headers:
-#   [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
-#   [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
-#   [ 1] .text1            PROGBITS        0000000000000100 000040 000100 00      0   0  0
-#   [ 2] .text2            PROGBITS        0000000000000200 000140 000100 00      0   0  0
-#   [ 3] .anotherone       PROGBITS        0000000000000300 000240 000100 00      0   0  0
-#   [ 4] =a-b+c++d         PROGBITS        0000000000000400 000340 000100 00      0   0  0
-#   [ 5] .strtab           STRTAB          0000000000000000 000440 000001 00      0   0  1
-#   [ 6] .shstrtab         STRTAB          0000000000000000 000441 000037 00      0   0  1
-#
-# Various changes are applied to this file using llvm-objcopy --change-section-address and
-# results are checked for expected address values, or errors.
-#
-
-# RUN: yaml2obj -DTYPE=REL %s -o %ti
-
-# Basic check that the option processes wildcards and changes the address as expected
-# RUN: llvm-objcopy --change-section-address *+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
-
-# Check --change-section-address alias --adjust-section-vma
-# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
-
-# Check negative adjustment
-# RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUB-SECTION
-
-# Check wildcard that does not change all sections
-# RUN: llvm-objcopy --change-section-address .text*+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
-
-# Check regex
-# RUN: llvm-objcopy --regex --change-section-address .text.+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
-
-# Check setting the address to a specific value
-# RUN: llvm-objcopy --change-section-address .text*=0x10 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SET-PATTERN
-
-# Check adjustment to max possible value (UINT64_MAX)
-# RUN: llvm-objcopy --change-section-address .text2+0xfffffffffffffdff %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-MAX
-
-# Check adjustment to min possible value (0)
-# RUN: llvm-objcopy --change-section-address .text2-0x200 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ZERO
-
-# Check setting to max possible value (UINT64_MAX)
-# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-MAX
-
-# Check maximum negative adjustment
-# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti %to1
-# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to1 %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ZERO
-
-# Check two independent changes
-# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-INDEPENDENT
-
-# check two overlapping changes
-# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-USE-LAST
-
-# Check unused option
-# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
-
-# Check partial overlap (.anotherone overlaps)
-# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
-
-# Check more complex partial overlap (P1: .anotherone, .text2, P2: .text1, text2) (.text2 overlaps)
-# RUN: llvm-objcopy --regex  --change-section-address  ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30  %ti %to
-# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
+## This test tests the behavior of --change-section-address option.
+
+# RUN: yaml2obj -DTYPE=REL %s -o %ti1
+
+## Basic check that the option processes wildcards and changes the address as expected.
+# RUN: llvm-objcopy --change-section-address *+0x20 %ti1 %to1
+# RUN: llvm-readelf --section-headers %to1 | FileCheck %s --check-prefix=CHECK-ADD-ALL
+
+## Check that --change-section-address alias --adjust-section-vma produces the same output as the test above.
+# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti1 %to2
+# RUN: llvm-readelf --section-headers %to2 | FileCheck %s --check-prefix=CHECK-ADD-ALL
+
+## Check that negative adjustment reduces the address by the specified value.
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti1 %to3
+# RUN: llvm-readelf --section-headers %to3 | FileCheck %s --check-prefix=CHECK-SUB-SECTION
+
+## Check that a wildcard pattern works and only the specified sections are updated.
+# RUN: llvm-objcopy --change-section-address .text*+0x20 %ti1 %to4
+# RUN: llvm-readelf --section-headers %to4 | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
+
+## Check that regex pattern can be used with --change-section-address.
+# RUN: llvm-objcopy --regex --change-section-address .text.+0x20 %ti1 %to5
+# RUN: llvm-readelf --section-headers %to5 | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
+
+## Check that a section address can be set to a specific value.
+# RUN: llvm-objcopy --change-section-address .text*=0x10 %ti1 %to6
+# RUN: llvm-readelf --section-headers %to6 | FileCheck %s --check-prefix=CHECK-SET-PATTERN
+
+## Check setting that a section address can be set to the maximum possible value (UINT64_MAX).
+# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to7
+# RUN: llvm-readelf --section-headers %to7 | FileCheck %s --check-prefix=CHECK-MAX
+
+## Check that a section address can be adjusted to the maximum possible value (UINT64_MAX).
+# RUN: llvm-objcopy --change-section-address .text2+0xfffffffffffffdff %ti1 %to8
+# RUN: llvm-readelf --section-headers %to8 | FileCheck %s --check-prefix=CHECK-MAX
+
+## Check that the section address can be adjusted to the minimum possible value (0).
+# RUN: llvm-objcopy --change-section-address .text2-0x200 %ti1 %to9
+# RUN: llvm-readelf --section-headers %to9 | FileCheck %s --check-prefix=CHECK-ZERO
+
+## Check that a section address can be adjusted by a maximum possible negative offset (UINT64_MIN).
+# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to10
+# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to10 %to11
+# RUN: llvm-readelf --section-headers %to11 | FileCheck %s --check-prefix=CHECK-ZERO
+
+## Check two independent changes.
+# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti1 %to12
+# RUN: llvm-readelf --section-headers %to12 | FileCheck %s --check-prefix=CHECK-INDEPENDENT
+
+## Check two overlapping changes.
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti1 %to13
+# RUN: llvm-readelf --section-headers %to13 | FileCheck %s --check-prefix=CHECK-USE-LAST
+
+## Check unused option.
+# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti1 %to14
+# RUN: llvm-readelf --section-headers %to14 | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
+
+## Check partial overlap (.anotherone overlaps).
+# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti1 %to15
+# RUN: llvm-readelf --section-headers %to15 | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
+
+## Check more complex partial overlap (P1: .anotherone, .text2, P2: .text1, text2) (.text2 overlaps).
+# RUN: llvm-objcopy --regex  --change-section-address  ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30  %ti1 %to16
+# RUN: llvm-readelf --section-headers %to16 | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
 
 # CHECK-ADD-ALL:          [Nr] Name              Type            Address
 # CHECK-ADD-ALL:               .text1
@@ -149,26 +132,26 @@
 # CHECK-PARTIAL-OVERLAP:        .anotherone
 # CHECK-PARTIAL-OVERLAP-SAME:                                    0000000000000320
 
-# Overflow by 1
-# RUN: not llvm-objcopy --change-section-address .anotherone+0xfffffffffffffd00 %ti 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
-# Underflow by 1
-# RUN: not llvm-objcopy --change-section-address .text2-0x201 %ti 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
-# Invalid argument value
-# RUN: not llvm-objcopy --change-section-address 0 %ti 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
-# Invalid value in argument value
-# RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
-# Value does not fit in uint64_t
-# RUN  not llvm-objcopy --change-section-address .text1=0x10000000000000000 %ti %to 2>&1 | FileCheck %s --chack-prefix=ERR-NOT-INTEGER
-# Missing section pattern
-# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
-# Missing value after -
-# RUN: not llvm-objcopy --change-section-address .text1- %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
-# Missing value after +
-# RUN: not llvm-objcopy --change-section-address .text1+ %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
-# Missing value after =
-# RUN: not llvm-objcopy --change-section-address .text1= %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
-# Invalid regex
-# RUN: not llvm-objcopy --regex --change-section-address  "ab**-0x20" %ti 2>&1 | FileCheck %s --check-prefix=ERR-MATCHER-FAILURE
+## Check overflow by 1.
+# RUN: not llvm-objcopy --change-section-address .anotherone+0xfffffffffffffd00 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
+## Check underflow by 1.
+# RUN: not llvm-objcopy --change-section-address .text2-0x201 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
+## Check error when argument value is invalid as a whole.
+# RUN: not llvm-objcopy --change-section-address 0 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
+## Check error when the value is invalid in the argument value.
+# RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
+## Check error when the value does not fit in uint64_t.
+# RUN  not llvm-objcopy --change-section-address .text1=0x10000000000000000 %ti1 %to 2>&1 | FileCheck %s --chack-prefix=ERR-NOT-INTEGER
+## Check error when the section pattern is missing.
+# RUN: not llvm-objcopy --change-section-address =0x10 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
+## Check error when the negative adjustment value is missing.
+# RUN: not llvm-objcopy --change-section-address .text1- %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
+## Check error when the positive adjustment valu is missing.
+# RUN: not llvm-objcopy --change-section-address .text1+ %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
+## Check error when the value to set the address to is missing.
+# RUN: not llvm-objcopy --change-section-address .text1= %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
+## Check error when the provided regex is invalid.
+# RUN: not llvm-objcopy --regex --change-section-address "ab**-0x20" %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MATCHER-FAILURE
 
 # ERR-OVERFLOW: address 0x300 cannot be increased by 0xfffffffffffffd00. The result would overflow
 # ERR-UNDERFLOW: address 0x200 cannot be decreased by 0x201. The result would underflow
@@ -203,9 +186,9 @@ Sections:
     Size:          0x100
     Address:       0x400
 
-# RUN: yaml2obj -DTYPE=EXEC %s -o %ti
+# RUN: yaml2obj -DTYPE=EXEC %s -o %ti2
 
-# Input file is not ET_REL
-# RUN: not llvm-objcopy --change-section-address *+0x20 %ti  2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
+## Input file is not ET_REL
+# RUN: not llvm-objcopy --change-section-address *+0x20 %ti2 2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
 
 # ERR-FILE-TYPE: cannot change section address in a non-relocatable file
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index 6f197cd220ec2..d82ecc8b3d36e 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -588,54 +588,42 @@ static Expected<SectionPatternAddressUpdate>
 parseChangeSectionAddr(StringRef ArgValue, StringRef OptionName,
                        MatchStyle SectionMatchStyle,
                        function_ref<Error(Error)> ErrorCallback) {
-
   SectionPatternAddressUpdate PatternUpdate;
 
-  size_t last_i = ArgValue.find_last_of("+-=");
-  StringRef Value = ArgValue.slice(last_i + 1, StringRef::npos);
-  StringRef SectionPattern = ArgValue.slice(0, last_i);
-
-  if (last_i == StringRef::npos)
+  size_t LastSymbolIndex = ArgValue.find_last_of("+-=");
+  if (LastSymbolIndex == StringRef::npos)
     return createStringError(errc::invalid_argument,
                              "bad format for " + OptionName +
                                  ": argument value " + ArgValue +
                                  " is invalid. See --help");
+  char UpdateSymbol = ArgValue[LastSymbolIndex];
 
-  char UpdateSymbol = ArgValue[last_i];
-
-  switch (UpdateSymbol) {
-  case '+':
-    PatternUpdate.Update.Kind = AdjustKind::Add;
-    break;
-  case '-':
-    PatternUpdate.Update.Kind = AdjustKind::Subtract;
-    break;
-  case '=':
-    PatternUpdate.Update.Kind = AdjustKind::Set;
-  }
+  StringRef SectionPattern = ArgValue.slice(0, LastSymbolIndex);
+  if (SectionPattern.empty())
+    return createStringError(
+        errc::invalid_argument,
+        "bad format for " + OptionName +
+            ": missing section pattern to apply address change to");
+  if (Error E = PatternUpdate.SectionPattern.addMatcher(NameOrPattern::create(
+          SectionPattern, SectionMatchStyle, ErrorCallback)))
+    return std::move(E);
 
+  StringRef Value = ArgValue.slice(LastSymbolIndex + 1, StringRef::npos);
   if (Value.empty()) {
-    if (UpdateSymbol == '+' || UpdateSymbol == '-')
+    switch (UpdateSymbol) {
+    case '+':
+    case '-':
       return createStringError(errc::invalid_argument,
                                "bad format for " + OptionName +
                                    ": missing value of offset after '" +
                                    std::string({UpdateSymbol}) + "'");
-    else if (UpdateSymbol == '=')
+
+    case '=':
       return createStringError(errc::invalid_argument,
                                "bad format for " + OptionName +
                                    ": missing address value after '='");
+    }
   }
-
-  if (SectionPattern.empty())
-    return createStringError(
-        errc::invalid_argument,
-        "bad format for " + OptionName +
-            ": missing section pattern to apply address change to");
-
-  if (Error E = PatternUpdate.SectionPattern.addMatcher(NameOrPattern::create(
-          SectionPattern, SectionMatchStyle, ErrorCallback)))
-    return std::move(E);
-
   auto AddrValue = getAsInteger<uint64_t>(Value);
   if (!AddrValue)
     return createStringError(AddrValue.getError(),
@@ -643,6 +631,17 @@ parseChangeSectionAddr(StringRef ArgValue, StringRef OptionName,
                                  std::string({UpdateSymbol}) + " is " + Value +
                                  " when it should be a 64-bit integer");
 
+  switch (UpdateSymbol) {
+  case '+':
+    PatternUpdate.Update.Kind = AdjustKind::Add;
+    break;
+  case '-':
+    PatternUpdate.Update.Kind = AdjustKind::Subtract;
+    break;
+  case '=':
+    PatternUpdate.Update.Kind = AdjustKind::Set;
+  }
+
   PatternUpdate.Update.Value = *AddrValue;
   return PatternUpdate;
 }

>From 2853cbfe4138f4ea75e97ed078a88a0073e44266 Mon Sep 17 00:00:00 2001
From: Eleanor Bonnici <eleanor.bonnici at arm.com>
Date: Mon, 29 Jul 2024 20:05:52 +0100
Subject: [PATCH 4/4] fixup! fixup! fixup! [llvm-objcopy] Add
 --change-section-address

---
 llvm/docs/CommandGuide/llvm-objcopy.rst       |  6 ++--
 .../ELF/change-section-address.test           | 35 +++++++++++--------
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index eff613404e313..7f20a76550c83 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -308,9 +308,9 @@ them.
  Change the address of sections that match ``<section>`` pattern to the
  specified value, or apply ``+<val>``/``-<val>`` to the current value. Can be
  specified multiple times to specify multiple patterns. Each section is only
- modified by one ``--change-section-address`` argument. Changes apply from the
- right of the command line. If a section name matches multiple patterns, the
- rightmost change applies. The object file needs to be of ET_REL type.
+ modified by one ``--change-section-address`` argument. If a section name
+ matches multiple patterns, the rightmost change applies. The object file needs
+ to be of ET_REL type.
 
 .. option:: --change-start <incr>, --adjust-start
 
diff --git a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
index 6430d0e04ed7a..b17b1492690af 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/change-section-address.test
@@ -8,7 +8,7 @@
 
 ## Check that --change-section-address alias --adjust-section-vma produces the same output as the test above.
 # RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti1 %to2
-# RUN: llvm-readelf --section-headers %to2 | FileCheck %s --check-prefix=CHECK-ADD-ALL
+# RUN: cmp %to1 %to2
 
 ## Check that negative adjustment reduces the address by the specified value.
 # RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti1 %to3
@@ -38,30 +38,35 @@
 # RUN: llvm-objcopy --change-section-address .text2-0x200 %ti1 %to9
 # RUN: llvm-readelf --section-headers %to9 | FileCheck %s --check-prefix=CHECK-ZERO
 
+## Check that a section address can be adjusted by a maximum possible positive offset (UINT64_MAX).
+# RUN: llvm-objcopy --change-section-address .text2=0 %ti1 %to10
+# RUN: llvm-objcopy --change-section-address .text2+0xffffffffffffffff %to10 %to11
+# RUN: llvm-readelf --section-headers %to11 | FileCheck %s --check-prefix=CHECK-MAX
+
 ## Check that a section address can be adjusted by a maximum possible negative offset (UINT64_MIN).
-# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to10
-# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to10 %to11
-# RUN: llvm-readelf --section-headers %to11 | FileCheck %s --check-prefix=CHECK-ZERO
+# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to12
+# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to12 %to13
+# RUN: llvm-readelf --section-headers %to13 | FileCheck %s --check-prefix=CHECK-ZERO
 
 ## Check two independent changes.
-# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti1 %to12
-# RUN: llvm-readelf --section-headers %to12 | FileCheck %s --check-prefix=CHECK-INDEPENDENT
+# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti1 %to14
+# RUN: llvm-readelf --section-headers %to14 | FileCheck %s --check-prefix=CHECK-INDEPENDENT
 
 ## Check two overlapping changes.
-# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti1 %to13
-# RUN: llvm-readelf --section-headers %to13 | FileCheck %s --check-prefix=CHECK-USE-LAST
+# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti1 %to15
+# RUN: llvm-readelf --section-headers %to15 | FileCheck %s --check-prefix=CHECK-USE-LAST
 
 ## Check unused option.
-# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti1 %to14
-# RUN: llvm-readelf --section-headers %to14 | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
+# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti1 %to16
+# RUN: llvm-readelf --section-headers %to16 | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
 
 ## Check partial overlap (.anotherone overlaps).
-# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti1 %to15
-# RUN: llvm-readelf --section-headers %to15 | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
+# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti1 %to17
+# RUN: llvm-readelf --section-headers %to17 | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
 
 ## Check more complex partial overlap (P1: .anotherone, .text2, P2: .text1, text2) (.text2 overlaps).
-# RUN: llvm-objcopy --regex  --change-section-address  ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30  %ti1 %to16
-# RUN: llvm-readelf --section-headers %to16 | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
+# RUN: llvm-objcopy --regex  --change-section-address  ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30  %ti1 %to18
+# RUN: llvm-readelf --section-headers %to18 | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
 
 # CHECK-ADD-ALL:          [Nr] Name              Type            Address
 # CHECK-ADD-ALL:               .text1
@@ -146,7 +151,7 @@
 # RUN: not llvm-objcopy --change-section-address =0x10 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
 ## Check error when the negative adjustment value is missing.
 # RUN: not llvm-objcopy --change-section-address .text1- %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
-## Check error when the positive adjustment valu is missing.
+## Check error when the positive adjustment value is missing.
 # RUN: not llvm-objcopy --change-section-address .text1+ %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
 ## Check error when the value to set the address to is missing.
 # RUN: not llvm-objcopy --change-section-address .text1= %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL



More information about the llvm-commits mailing list