[llvm] [llvm-objcopy] Add change-section-lma *+/-offset (PR #95431)
Eleanor Bonnici via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 13 09:28:49 PDT 2024
https://github.com/eleanor-arm created https://github.com/llvm/llvm-project/pull/95431
llvm-objcopy did not support change-section-lma argument.
This patch adds support for a use case of change-section-lma, that is
shifting load address of all sections by the same offset. This seems to
be the only practical use case of change-section-lma, found in other
software.
>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] [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, "
More information about the llvm-commits
mailing list