Skip to content

Commit ae970b4

Browse files
committed
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.
1 parent 56a3e22 commit ae970b4

File tree

8 files changed

+91
-129
lines changed

8 files changed

+91
-129
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,9 +528,9 @@ them.
528528

529529
Mark all defined global symbols as weak in the output.
530530

531-
.. option:: --change-section-lma \*{+-}<offset>
531+
.. option:: --change-section-lma \*{+-}<val>
532532

533-
Currently only supports changing LMA for all sections by the same offset.
533+
Shift LMA of non-zero-sized sections in the program header by ``<val>``
534534

535535
MACH-O-SPECIFIC OPTIONS
536536
-----------------------

llvm/lib/ObjCopy/ConfigManager.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
2525
Common.StripNonAlloc || Common.StripSections || Common.Weaken ||
2626
Common.DecompressDebugSections ||
2727
Common.DiscardMode == DiscardType::Locals ||
28-
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 || Common.PadTo != 0)
28+
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
29+
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
2930
return createStringError(llvm::errc::invalid_argument,
3031
"option is not supported for COFF");
3132

@@ -46,7 +47,8 @@ Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
4647
Common.StripNonAlloc || Common.StripSections ||
4748
Common.DecompressDebugSections || Common.StripUnneeded ||
4849
Common.DiscardMode == DiscardType::Locals ||
49-
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 || Common.PadTo != 0)
50+
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
51+
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
5052
return createStringError(llvm::errc::invalid_argument,
5153
"option is not supported for MachO");
5254

@@ -66,7 +68,7 @@ Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
6668
!Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
6769
!Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() ||
6870
!Common.SymbolsToRename.empty() || Common.GapFill != 0 ||
69-
Common.PadTo != 0)
71+
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
7072
return createStringError(llvm::errc::invalid_argument,
7173
"only flags for section dumping, removal, and "
7274
"addition are supported");
@@ -94,7 +96,8 @@ Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
9496
Common.PreserveDates || Common.StripAllGNU || Common.StripDWO ||
9597
Common.StripDebug || Common.StripNonAlloc || Common.StripSections ||
9698
Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections ||
97-
Common.GapFill != 0 || Common.PadTo != 0) {
99+
Common.GapFill != 0 || Common.PadTo != 0 ||
100+
Common.ChangeSectionLMAValAll != 0) {
98101
return createStringError(
99102
llvm::errc::invalid_argument,
100103
"no flags are supported yet, only basic copying is allowed");

llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
671671
}
672672

673673
if (Config.ChangeSectionLMAValAll != 0) {
674-
for (auto &Seg : Obj.segments()) {
674+
for (Segment &Seg : Obj.segments()) {
675675
if (Seg.FileSize > 0)
676676
Seg.PAddr += Config.ChangeSectionLMAValAll;
677677
}

llvm/test/tools/llvm-objcopy/ELF/change-section-lma-multiple-load.test

Lines changed: 0 additions & 52 deletions
This file was deleted.

llvm/test/tools/llvm-objcopy/ELF/change-section-lma-single-load.test

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# RUN: yaml2obj %s -o %t
2+
# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
3+
# RUN: llvm-readelf -l %t2 | FileCheck %s --check-prefix=CHECK-PLUS
4+
# RUN: llvm-objcopy --change-section-lma *-0x10 %t2 %t3
5+
# RUN: llvm-readelf -l %t3 | FileCheck %s --check-prefix=CHECK-MINUS
6+
# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t 2>&1 | FileCheck %s --check-prefix=ERR-SET-ADDRESS
7+
# RUN: not llvm-objcopy --change-section-lma .text3+0x30 %t 2>&1 | FileCheck %s --check-prefix=ERR-SPECIFIC-SEC
8+
# RUN: not llvm-objcopy --change-section-lma *+0c50 %t 2>&1 | FileCheck %s --check-prefix=ERR-INVALID-VAL
9+
# RUN: not llvm-objcopy --change-section-lma 0 %t 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-OFFSET
10+
11+
# CHECK-PLUS: Type Offset VirtAddr PhysAddr FileSiz MemSiz
12+
# CHECK-PLUS: PHDR 0x000002 0x0000000000001002 0x0000000000001022 0x000038 0x000000
13+
# CHECK-PLUS: LOAD 0x000000 0x0000000000001000 0x0000000000001020 0x001120 0x001120
14+
# CHECK-PLUS: LOAD 0x001120 0x0000000000005000 0x0000000000006020 0x001000 0x001000
15+
# CHECK-PLUS: NOTE 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
16+
17+
# CHECK-MINUS: PHDR 0x000002 0x0000000000001002 0x0000000000001012 0x000038 0x000000
18+
# CHECK-MINUS: LOAD 0x000000 0x0000000000001000 0x0000000000001010 0x001120 0x001120
19+
# CHECK-MINUS: LOAD 0x001120 0x0000000000005000 0x0000000000006010 0x001000 0x001000
20+
# CHECK-MINUS: NOTE 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000
21+
22+
# ERR-SET-ADDRESS: error: bad format for --change-section-lma: changing LMA to a specific value is not supported. Use *+val or *-val instead
23+
# ERR-SPECIFIC-SEC: error: bad format for --change-section-lma: changing a specific section LMA is not supported. Use *+val or *-val instead
24+
# ERR-INVALID-VAL: error: bad format for --change-section-lma: value after *+ is 0c50 when it should be integer
25+
# ERR-MISSING-OFFSET: error: bad format for --change-section-lma: missing LMA offset
26+
27+
!ELF
28+
FileHeader:
29+
Class: ELFCLASS64
30+
Data: ELFDATA2LSB
31+
Type: ET_EXEC
32+
Sections:
33+
- Name: .text1
34+
Type: SHT_PROGBITS
35+
Size: 0x1000
36+
- Name: .text2
37+
Type: SHT_PROGBITS
38+
Size: 0x1000
39+
ProgramHeaders:
40+
- Type: PT_PHDR
41+
FileSize: 0x38
42+
Offset: 0x2
43+
VAddr: 0x1002
44+
- Type: PT_LOAD
45+
Offset: 0x0
46+
VAddr: 0x1000
47+
FirstSec: .text1
48+
LastSec: .text1
49+
- Type: PT_LOAD
50+
VAddr: 0x5000
51+
PAddr: 0x6000
52+
FirstSec: .text2
53+
LastSec: .text2
54+
- Type: PT_NOTE
55+
FileSize: 0x0

llvm/tools/llvm-objcopy/ObjcopyOptions.cpp

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -552,31 +552,36 @@ static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName,
552552
return Error::success();
553553
}
554554

555-
static Expected<int64_t> parseAdjustSectionLMA(StringRef ArgValue,
555+
static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
556556
StringRef OptionName) {
557557
StringRef StringValue;
558558
if (ArgValue.starts_with("*+")) {
559559
StringValue = ArgValue.slice(2, StringRef::npos);
560560
} else if (ArgValue.starts_with("*-")) {
561561
StringValue = ArgValue.slice(1, StringRef::npos);
562-
} else {
562+
} else if (ArgValue.contains("=")) {
563+
return createStringError(errc::invalid_argument,
564+
"bad format for " + OptionName +
565+
": changing LMA to a specific value is not "
566+
"supported. Use *+val or *-val instead");
567+
} else if (ArgValue.contains("+") || ArgValue.contains("-")) {
563568
return createStringError(errc::invalid_argument,
564569
"bad format for " + OptionName +
565-
": it is required that all sections "
566-
"are either incremented, or decremented at "
567-
"the same time; use *+val, "
568-
"or *-val");
570+
": changing a specific section LMA is not "
571+
"supported. Use *+val or *-val instead");
569572
}
570573
if (StringValue.empty())
571574
return createStringError(errc::invalid_argument,
572575
"bad format for " + OptionName +
573-
": missing offset of LMA");
574-
575-
auto SLMAV = getAsInteger<int64_t>(StringValue);
576-
if (!SLMAV)
577-
return createStringError(SLMAV.getError(),
578-
"Unable to parse adjustment value");
579-
return *SLMAV;
576+
": missing LMA offset");
577+
578+
auto LMAValue = getAsInteger<int64_t>(StringValue);
579+
if (!LMAValue)
580+
return createStringError(LMAValue.getError(),
581+
"bad format for " + OptionName + ": value after " +
582+
ArgValue.slice(0, 2) + " is " + StringValue +
583+
" when it should be integer");
584+
return *LMAValue;
580585
}
581586

582587
// parseObjcopyOptions returns the config and sets the input arguments. If a
@@ -860,12 +865,12 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
860865
Config.PadTo = *Addr;
861866
}
862867

863-
if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_adjust_section_lma)) {
864-
Expected<int64_t> SLMAV =
865-
parseAdjustSectionLMA(Arg->getValue(), Arg->getSpelling());
866-
if (!SLMAV)
867-
return SLMAV.takeError();
868-
Config.ChangeSectionLMAValAll = *SLMAV;
868+
if (const auto *Arg = InputArgs.getLastArg(OBJCOPY_change_section_lma)) {
869+
Expected<int64_t> LMAValue =
870+
parseChangeSectionLMA(Arg->getValue(), Arg->getSpelling());
871+
if (!LMAValue)
872+
return LMAValue.takeError();
873+
Config.ChangeSectionLMAValAll = *LMAValue;
869874
}
870875

871876
for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {

llvm/tools/llvm-objcopy/ObjcopyOpts.td

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,8 @@ def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
254254
Alias<change_start>,
255255
HelpText<"Alias for --change-start">;
256256

257-
defm adjust_section_lma
258-
: Eq<"change-section-lma",
259-
"Change LMA of all sections by <val>. LMA is the physical address where the section is "
260-
"loaded in the memory; as opoosed to VMA, which is the virtual address at which the "
261-
"section resides during program execution. Ussually LMA and VMA are the same, but some "
262-
"platforms support these to be different resulting in runtime copying">,
257+
defm change_section_lma
258+
: Eq<"change-section-lma", "Shift LMA of non-zero-sized sections in the program header by <val>">,
263259
MetaVarName<"*{+|-}val">;
264260

265261
defm add_symbol

0 commit comments

Comments
 (0)