Skip to content

Commit 3320036

Browse files
authored
[llvm-objcopy] Add change-section-lma *+/-offset (#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 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 2f37a22 commit 3320036

File tree

7 files changed

+164
-4
lines changed

7 files changed

+164
-4
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ them.
299299
Allow :program:`llvm-objcopy` to remove sections even if it would leave invalid
300300
section references. Any invalid sh_link fields will be set to zero.
301301

302+
.. option:: --change-section-lma \*{+-}<val>
303+
304+
Shift LMA of non-zero-sized segments by ``<val>``.
305+
302306
.. option:: --change-start <incr>, --adjust-start
303307

304308
Add ``<incr>`` to the program's start address. Can be specified multiple

llvm/include/llvm/ObjCopy/CommonConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ struct CommonConfig {
245245
// Symbol info specified by --add-symbol option.
246246
SmallVector<NewSymbolInfo, 0> SymbolsToAdd;
247247

248+
// Integer options
249+
int64_t ChangeSectionLMAValAll = 0;
250+
248251
// Boolean options
249252
bool DeterministicArchives = true;
250253
bool ExtractDWO = false;

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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,33 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
670670
}
671671
}
672672

673+
if (Config.ChangeSectionLMAValAll != 0) {
674+
for (Segment &Seg : Obj.segments()) {
675+
if (Seg.FileSize > 0) {
676+
if (Config.ChangeSectionLMAValAll > 0 &&
677+
Seg.PAddr > std::numeric_limits<uint64_t>::max() -
678+
Config.ChangeSectionLMAValAll) {
679+
return createStringError(
680+
errc::invalid_argument,
681+
"address 0x" + Twine::utohexstr(Seg.PAddr) +
682+
" cannot be increased by 0x" +
683+
Twine::utohexstr(Config.ChangeSectionLMAValAll) +
684+
". The result would overflow");
685+
} else if (Config.ChangeSectionLMAValAll < 0 &&
686+
Seg.PAddr < std::numeric_limits<uint64_t>::min() -
687+
Config.ChangeSectionLMAValAll) {
688+
return createStringError(
689+
errc::invalid_argument,
690+
"address 0x" + Twine::utohexstr(Seg.PAddr) +
691+
" cannot be decreased by 0x" +
692+
Twine::utohexstr(std::abs(Config.ChangeSectionLMAValAll)) +
693+
". The result would underflow");
694+
}
695+
Seg.PAddr += Config.ChangeSectionLMAValAll;
696+
}
697+
}
698+
}
699+
673700
if (Config.OnlyKeepDebug)
674701
for (auto &Sec : Obj.sections())
675702
if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# RUN: yaml2obj %s -o %t
2+
# RUN: llvm-objcopy --change-section-lma *+0x20 %t %t2
3+
# RUN: llvm-readelf --program-headers %t2 | FileCheck %s --check-prefix=CHECK-PLUS-PROGRAMS
4+
# RUN: llvm-readelf --section-headers %t2 | FileCheck %s --check-prefix=CHECK-PLUS-SECTIONS
5+
# RUN: llvm-objcopy --change-section-lma *-0x30 %t %t3
6+
# RUN: llvm-readelf --program-headers %t3 | FileCheck %s --check-prefix=CHECK-MINUS-PROGRAMS
7+
# RUN: llvm-readelf --section-headers %t3 | FileCheck %s --check-prefix=CHECK-MINUS-SECTIONS
8+
# RUN: not llvm-objcopy --change-section-lma .text3=0x5000 %t 2>&1 | FileCheck %s --check-prefix=ERR-SET-ADDRESS
9+
# RUN: not llvm-objcopy --change-section-lma .text3+0x30 %t 2>&1 | FileCheck %s --check-prefix=ERR-SPECIFIC-SEC
10+
# RUN: not llvm-objcopy --change-section-lma *+0c50 %t 2>&1 | FileCheck %s --check-prefix=ERR-INVALID-VAL
11+
# RUN: not llvm-objcopy --change-section-lma 0 %t 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-OFFSET
12+
# RUN: not llvm-objcopy --change-section-lma *-0x2000 %t 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
13+
# RUN: not llvm-objcopy --change-section-lma *+0x100000000 %t 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
14+
15+
# CHECK-PLUS-PROGRAMS: Type Offset VirtAddr PhysAddr FileSiz MemSiz
16+
# CHECK-PLUS-PROGRAMS: PHDR 0x000002 0x0000000000001102 0x0000000000001122 0x000038 0x000000
17+
# CHECK-PLUS-PROGRAMS: LOAD 0x000000 0x0000000000001100 0x0000000000001120 0x000258 0x000258
18+
# CHECK-PLUS-PROGRAMS: LOAD 0x000258 0xffffffff00005100 0xffffffff00006120 0x000100 0x000100
19+
# CHECK-PLUS-PROGRAMS: NOTE 0x000358 0x0000000000001200 0x0000000000001220 0x000010 0x000000
20+
# CHECK-PLUS-PROGRAMS: NOTE 0x000368 0x0000000000000000 0x0000000000000000 0x000000 0x000000
21+
22+
# CHECK-MINUS-PROGRAMS: PHDR 0x000002 0x0000000000001102 0x00000000000010d2 0x000038 0x000000
23+
# CHECK-MINUS-PROGRAMS: LOAD 0x000000 0x0000000000001100 0x00000000000010d0 0x000258 0x000258
24+
# CHECK-MINUS-PROGRAMS: LOAD 0x000258 0xffffffff00005100 0xffffffff000060d0 0x000100 0x000100
25+
# CHECK-MINUS-PROGRAMS: NOTE 0x000358 0x0000000000001200 0x00000000000011d0 0x000010 0x000000
26+
# CHECK-MINUS-PROGRAMS: NOTE 0x000368 0x0000000000000000 0x0000000000000000 0x000000 0x000000
27+
28+
# CHECK-PLUS-SECTIONS: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
29+
# CHECK-PLUS-SECTIONS: .text1
30+
# CHECK-PLUS-SECTIONS-SAME: 0000000000000000
31+
# CHECK-PLUS-SECTIONS: .text2
32+
# CHECK-PLUS-SECTIONS-SAME: 0000000000000000
33+
34+
# CHECK-MINUS-SECTIONS: .text1
35+
# CHECK-MINUS-SECTIONS-SAME: 0000000000000000
36+
# CHECK-MINUS-SECTIONS: .text2
37+
# CHECK-MINUS-SECTIONS-SAME: 0000000000000000
38+
39+
# ERR-SET-ADDRESS: error: bad format for --change-section-lma: changing LMA to a specific value is not supported. Use *+val or *-val instead
40+
# ERR-SPECIFIC-SEC: error: bad format for --change-section-lma: changing a specific section LMA is not supported. Use *+val or *-val instead
41+
# ERR-INVALID-VAL: error: bad format for --change-section-lma: value after *+ is 0c50 when it should be an integer
42+
# ERR-MISSING-OFFSET: error: bad format for --change-section-lma: missing LMA offset
43+
# ERR-UNDERFLOW: : address 0x1102 cannot be decreased by 0x2000. The result would underflow
44+
# ERR-OVERFLOW: address 0xffffffff00006100 cannot be increased by 0x100000000. The result would overflow
45+
46+
!ELF
47+
FileHeader:
48+
Class: ELFCLASS64
49+
Data: ELFDATA2LSB
50+
Type: ET_EXEC
51+
Sections:
52+
- Name: .text1
53+
Type: SHT_PROGBITS
54+
Size: 0x100
55+
- Name: .text2
56+
Type: SHT_PROGBITS
57+
Size: 0x100
58+
ProgramHeaders:
59+
- Type: PT_PHDR
60+
FileSize: 0x38
61+
Offset: 0x2
62+
VAddr: 0x1102
63+
- Type: PT_LOAD
64+
Offset: 0x0
65+
VAddr: 0x1100
66+
FirstSec: .text1
67+
LastSec: .text1
68+
- Type: PT_LOAD
69+
VAddr: 0xFFFFFFFF00005100
70+
PAddr: 0xFFFFFFFF00006100
71+
FirstSec: .text2
72+
LastSec: .text2
73+
- Type: PT_NOTE
74+
FileSize: 0x10
75+
VAddr: 0x1200
76+
Offset: 0x358
77+
- Type: PT_NOTE
78+
FileSize: 0x0
79+
Offset: 0x368

llvm/tools/llvm-objcopy/ObjcopyOptions.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,38 @@ static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName,
552552
return Error::success();
553553
}
554554

555+
static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
556+
StringRef OptionName) {
557+
StringRef StringValue;
558+
if (ArgValue.starts_with("*+")) {
559+
StringValue = ArgValue.slice(2, StringRef::npos);
560+
} else if (ArgValue.starts_with("*-")) {
561+
StringValue = ArgValue.slice(1, StringRef::npos);
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("-")) {
568+
return createStringError(errc::invalid_argument,
569+
"bad format for " + OptionName +
570+
": changing a specific section LMA is not "
571+
"supported. Use *+val or *-val instead");
572+
}
573+
if (StringValue.empty())
574+
return createStringError(errc::invalid_argument,
575+
"bad format for " + OptionName +
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 an integer");
584+
return *LMAValue;
585+
}
586+
555587
// parseObjcopyOptions returns the config and sets the input arguments. If a
556588
// help flag is set then parseObjcopyOptions will print the help messege and
557589
// exit.
@@ -833,6 +865,14 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
833865
Config.PadTo = *Addr;
834866
}
835867

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;
874+
}
875+
836876
for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
837877
if (!StringRef(Arg->getValue()).contains('='))
838878
return createStringError(errc::invalid_argument,

llvm/tools/llvm-objcopy/ObjcopyOpts.td

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

257+
defm change_section_lma
258+
: Eq<"change-section-lma", "Shift LMA of non-zero-sized sections in the program header by <val>">,
259+
MetaVarName<"*{+|-}val">;
260+
257261
defm add_symbol
258262
: Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: "
259263
"global, local, weak, default, hidden, protected, file, section, object, "

0 commit comments

Comments
 (0)