Skip to content

Commit 2b2f4ae

Browse files
authored
[llvm-objcopy] Add --change-section-address (llvm#98664)
--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.
1 parent ca8a411 commit 2b2f4ae

File tree

7 files changed

+357
-4
lines changed

7 files changed

+357
-4
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,15 @@ them.
303303

304304
Shift LMA of non-zero-sized segments by ``<val>``.
305305

306+
.. option:: --change-section-address <section>{=+-}<val>, --adjust-section-vma
307+
308+
Change the address of sections that match ``<section>`` pattern to the
309+
specified value, or apply ``+<val>``/``-<val>`` to the current value. Can be
310+
specified multiple times to specify multiple patterns. Each section is only
311+
modified by one ``--change-section-address`` argument. If a section name
312+
matches multiple patterns, the rightmost change applies. The object file needs
313+
to be of ET_REL type.
314+
306315
.. option:: --change-start <incr>, --adjust-start
307316

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

llvm/include/llvm/ObjCopy/CommonConfig.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,18 @@ class NameMatcher {
151151
}
152152
};
153153

154+
enum class AdjustKind { Set, Add, Subtract };
155+
156+
struct AddressUpdate {
157+
uint64_t Value = 0;
158+
AdjustKind Kind = AdjustKind::Add;
159+
};
160+
161+
struct SectionPatternAddressUpdate {
162+
NameMatcher SectionPattern;
163+
AddressUpdate Update;
164+
};
165+
154166
enum class SymbolFlag {
155167
Global,
156168
Local,
@@ -219,6 +231,7 @@ struct CommonConfig {
219231
SmallVector<NewSectionInfo, 0> AddSection;
220232
SmallVector<StringRef, 0> DumpSection;
221233
SmallVector<NewSectionInfo, 0> UpdateSection;
234+
SmallVector<SectionPatternAddressUpdate, 0> ChangeSectionAddress;
222235

223236
// Section matchers
224237
NameMatcher KeepSection;

llvm/lib/ObjCopy/ConfigManager.cpp

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

@@ -48,7 +49,8 @@ Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
4849
Common.DecompressDebugSections || Common.StripUnneeded ||
4950
Common.DiscardMode == DiscardType::Locals ||
5051
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
51-
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
52+
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
53+
!Common.ChangeSectionAddress.empty())
5254
return createStringError(llvm::errc::invalid_argument,
5355
"option is not supported for MachO");
5456

@@ -68,7 +70,8 @@ Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
6870
!Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
6971
!Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() ||
7072
!Common.SymbolsToRename.empty() || Common.GapFill != 0 ||
71-
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0)
73+
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
74+
!Common.ChangeSectionAddress.empty())
7275
return createStringError(llvm::errc::invalid_argument,
7376
"only flags for section dumping, removal, and "
7477
"addition are supported");
@@ -97,7 +100,8 @@ Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
97100
Common.StripDebug || Common.StripNonAlloc || Common.StripSections ||
98101
Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections ||
99102
Common.GapFill != 0 || Common.PadTo != 0 ||
100-
Common.ChangeSectionLMAValAll != 0) {
103+
Common.ChangeSectionLMAValAll != 0 ||
104+
!Common.ChangeSectionAddress.empty()) {
101105
return createStringError(
102106
llvm::errc::invalid_argument,
103107
"no flags are supported yet, only basic copying is allowed");

llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,56 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
745745
}
746746
}
747747

748+
if (!Config.ChangeSectionAddress.empty()) {
749+
if (Obj.Type != ELF::ET_REL)
750+
return createStringError(
751+
object_error::invalid_file_type,
752+
"cannot change section address in a non-relocatable file");
753+
754+
StringMap<AddressUpdate> SectionsToUpdateAddress;
755+
for (const SectionPatternAddressUpdate &PatternUpdate :
756+
make_range(Config.ChangeSectionAddress.rbegin(),
757+
Config.ChangeSectionAddress.rend())) {
758+
for (SectionBase &Sec : Obj.sections()) {
759+
if (PatternUpdate.SectionPattern.matches(Sec.Name) &&
760+
SectionsToUpdateAddress.try_emplace(Sec.Name, PatternUpdate.Update)
761+
.second) {
762+
if (PatternUpdate.Update.Kind == AdjustKind::Subtract &&
763+
Sec.Addr < PatternUpdate.Update.Value) {
764+
return createStringError(
765+
errc::invalid_argument,
766+
"address 0x" + Twine::utohexstr(Sec.Addr) +
767+
" cannot be decreased by 0x" +
768+
Twine::utohexstr(PatternUpdate.Update.Value) +
769+
". The result would underflow");
770+
}
771+
if (PatternUpdate.Update.Kind == AdjustKind::Add &&
772+
Sec.Addr > std::numeric_limits<uint64_t>::max() -
773+
PatternUpdate.Update.Value) {
774+
return createStringError(
775+
errc::invalid_argument,
776+
"address 0x" + Twine::utohexstr(Sec.Addr) +
777+
" cannot be increased by 0x" +
778+
Twine::utohexstr(PatternUpdate.Update.Value) +
779+
". The result would overflow");
780+
}
781+
782+
switch (PatternUpdate.Update.Kind) {
783+
case (AdjustKind::Set):
784+
Sec.Addr = PatternUpdate.Update.Value;
785+
break;
786+
case (AdjustKind::Subtract):
787+
Sec.Addr -= PatternUpdate.Update.Value;
788+
break;
789+
case (AdjustKind::Add):
790+
Sec.Addr += PatternUpdate.Update.Value;
791+
break;
792+
}
793+
}
794+
}
795+
}
796+
}
797+
748798
if (Config.OnlyKeepDebug)
749799
for (auto &Sec : Obj.sections())
750800
if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
## This test tests the behavior of --change-section-address option.
2+
3+
# RUN: yaml2obj -DTYPE=REL %s -o %ti1
4+
5+
## Basic check that the option processes wildcards and changes the address as expected.
6+
# RUN: llvm-objcopy --change-section-address *+0x20 %ti1 %to1
7+
# RUN: llvm-readelf --section-headers %to1 | FileCheck %s --check-prefix=CHECK-ADD-ALL
8+
9+
## Check that --change-section-address alias --adjust-section-vma produces the same output as the test above.
10+
# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti1 %to2
11+
# RUN: cmp %to1 %to2
12+
13+
## Check that negative adjustment reduces the address by the specified value.
14+
# RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti1 %to3
15+
# RUN: llvm-readelf --section-headers %to3 | FileCheck %s --check-prefix=CHECK-SUB-SECTION
16+
17+
## Check that a wildcard pattern works and only the specified sections are updated.
18+
# RUN: llvm-objcopy --change-section-address .text*+0x20 %ti1 %to4
19+
# RUN: llvm-readelf --section-headers %to4 | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
20+
21+
## Check that regex pattern can be used with --change-section-address.
22+
# RUN: llvm-objcopy --regex --change-section-address .text.+0x20 %ti1 %to5
23+
# RUN: llvm-readelf --section-headers %to5 | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
24+
25+
## Check that a section address can be set to a specific value.
26+
# RUN: llvm-objcopy --change-section-address .text*=0x10 %ti1 %to6
27+
# RUN: llvm-readelf --section-headers %to6 | FileCheck %s --check-prefix=CHECK-SET-PATTERN
28+
29+
## Check setting that a section address can be set to the maximum possible value (UINT64_MAX).
30+
# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to7
31+
# RUN: llvm-readelf --section-headers %to7 | FileCheck %s --check-prefix=CHECK-MAX
32+
33+
## Check that a section address can be adjusted to the maximum possible value (UINT64_MAX).
34+
# RUN: llvm-objcopy --change-section-address .text2+0xfffffffffffffdff %ti1 %to8
35+
# RUN: llvm-readelf --section-headers %to8 | FileCheck %s --check-prefix=CHECK-MAX
36+
37+
## Check that the section address can be adjusted to the minimum possible value (0).
38+
# RUN: llvm-objcopy --change-section-address .text2-0x200 %ti1 %to9
39+
# RUN: llvm-readelf --section-headers %to9 | FileCheck %s --check-prefix=CHECK-ZERO
40+
41+
## Check that a section address can be adjusted by a maximum possible positive offset (UINT64_MAX).
42+
# RUN: llvm-objcopy --change-section-address .text2=0 %ti1 %to10
43+
# RUN: llvm-objcopy --change-section-address .text2+0xffffffffffffffff %to10 %to11
44+
# RUN: llvm-readelf --section-headers %to11 | FileCheck %s --check-prefix=CHECK-MAX
45+
46+
## Check that a section address can be adjusted by a maximum possible negative offset (UINT64_MIN).
47+
# RUN: llvm-objcopy --change-section-address .text2=0xffffffffffffffff %ti1 %to12
48+
# RUN: llvm-objcopy --change-section-address .text2-0xffffffffffffffff %to12 %to13
49+
# RUN: llvm-readelf --section-headers %to13 | FileCheck %s --check-prefix=CHECK-ZERO
50+
51+
## Check two independent changes.
52+
# RUN: llvm-objcopy --change-section-address .text1=0x110 --change-section-address .text2=0x210 %ti1 %to14
53+
# RUN: llvm-readelf --section-headers %to14 | FileCheck %s --check-prefix=CHECK-INDEPENDENT
54+
55+
## Check two overlapping changes.
56+
# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti1 %to15
57+
# RUN: llvm-readelf --section-headers %to15 | FileCheck %s --check-prefix=CHECK-USE-LAST
58+
59+
## Check unused option.
60+
# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti1 %to16
61+
# RUN: llvm-readelf --section-headers %to16 | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
62+
63+
## Check partial overlap (.anotherone overlaps).
64+
# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti1 %to17
65+
# RUN: llvm-readelf --section-headers %to17 | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
66+
67+
## Check more complex partial overlap (P1: .anotherone, .text2, P2: .text1, text2) (.text2 overlaps).
68+
# RUN: llvm-objcopy --regex --change-section-address ".(text2|anotherone)+0x20" --change-section-address .text.*+0x30 %ti1 %to18
69+
# RUN: llvm-readelf --section-headers %to18 | FileCheck %s --check-prefix=CHECK-PARTIAL-OVERLAP
70+
71+
# CHECK-ADD-ALL: [Nr] Name Type Address
72+
# CHECK-ADD-ALL: .text1
73+
# CHECK-ADD-ALL-SAME: 0000000000000120
74+
# CHECK-ADD-ALL: .text2
75+
# CHECK-ADD-ALL-SAME: 0000000000000220
76+
# CHECK-ADD-ALL: .anotherone
77+
# CHECK-ADD-ALL-SAME: 0000000000000320
78+
# CHECK-ADD-ALL: =a-b+c++d
79+
# CHECK-ADD-ALL-SAME: 0000000000000420
80+
# CHECK-ADD-ALL: .strtab
81+
# CHECK-ADD_ALL-SAME: 0000000000000020
82+
# CHECK-ADD-ALL: .shstrtab
83+
# CHECK-ADD-ALL-SAME: 0000000000000020
84+
85+
# CHECK-SUB-SECTION: .text1
86+
# CHECK-SUB-SECTION-SAME: 0000000000000100
87+
# CHECK-SUB-SECTION: .text2
88+
# CHECK-SUB-SECTION-SAME: 0000000000000200
89+
# CHECK-SUB-SECTION: .anotherone
90+
# CHECK-SUB-SECTION-SAME: 00000000000002d0
91+
92+
# CHECK-ADD-PATTERN: .text1
93+
# CHECK-ADD-PATTERN-SAME: 0000000000000120
94+
# CHECK-ADD-PATTERN: .text2
95+
# CHECK-ADD-PATTERN-SAME: 0000000000000220
96+
# CHECK-ADD-PATTERN: .anotherone
97+
# CHECK-ADD-PATTERN-SAME: 0000000000000300
98+
99+
# CHECK-SET-PATTERN: .text1
100+
# CHECK-SET-PATTERN-SAME: 0000000000000010
101+
# CHECK-SET-PATTERN: .text2
102+
# CHECK-SET-PATTERN-SAME: 0000000000000010
103+
# CHECK-SET-PATTERN: .anotherone
104+
# CHECK-SET-PATTERN-SAME: 0000000000000300
105+
106+
# CHECK-MAX: .text2
107+
# CHECK-MAX-SAME: ffffffffffffffff
108+
# CHECK-ZERO: .text2
109+
# CHECK-ZERO-SAME: 0000000000000000
110+
111+
# CHECK-INDEPENDENT: .text1
112+
# CHECK-INDEPENDENT-SAME: 0000000000000110
113+
# CHECK-INDEPENDENT: .text2
114+
# CHECK-INDEPENDENT-SAME: 0000000000000210
115+
116+
# CHECK-USE-LAST: .anotherone
117+
# CHECK-USE-LAST-SAME: 0000000000000320
118+
119+
# CHECK-NOTSUPERSET-SET: .text1
120+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000120
121+
# CHECK-NOTSUPERSET-SET: .text2
122+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000220
123+
# CHECK-NOTSUPERSET-SET: .anotherone
124+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000320
125+
126+
# CHECK-SUPERSET-SET: .text1
127+
# CHECK-SUPERSET-SET-SAME: 0000000000000120
128+
# CHECK-SUPERSET-SET: .text2
129+
# CHECK-SUPERSET-SET-SAME: 0000000000000220
130+
# CHECK-SUPERSET-SET: .anotherone
131+
# CHECK-SUPERSET-SET-SAME: 0000000000000455
132+
133+
# CHECK-PARTIAL-OVERLAP: .text1
134+
# CHECK-PARTIAL-OVERLAP-SAME: 0000000000000130
135+
# CHECK-PARTIAL-OVERLAP: .text2
136+
# CHECK-PARTIAL-OVERLAP-SAME: 0000000000000230
137+
# CHECK-PARTIAL-OVERLAP: .anotherone
138+
# CHECK-PARTIAL-OVERLAP-SAME: 0000000000000320
139+
140+
## Check overflow by 1.
141+
# RUN: not llvm-objcopy --change-section-address .anotherone+0xfffffffffffffd00 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
142+
## Check underflow by 1.
143+
# RUN: not llvm-objcopy --change-section-address .text2-0x201 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
144+
## Check error when argument value is invalid as a whole.
145+
# RUN: not llvm-objcopy --change-section-address 0 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
146+
## Check error when the value is invalid in the argument value.
147+
# RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
148+
## Check error when the value does not fit in uint64_t.
149+
# RUN not llvm-objcopy --change-section-address .text1=0x10000000000000000 %ti1 %to 2>&1 | FileCheck %s --chack-prefix=ERR-NOT-INTEGER
150+
## Check error when the section pattern is missing.
151+
# RUN: not llvm-objcopy --change-section-address =0x10 %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
152+
## Check error when the negative adjustment value is missing.
153+
# RUN: not llvm-objcopy --change-section-address .text1- %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
154+
## Check error when the positive adjustment value is missing.
155+
# RUN: not llvm-objcopy --change-section-address .text1+ %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
156+
## Check error when the value to set the address to is missing.
157+
# RUN: not llvm-objcopy --change-section-address .text1= %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
158+
## Check error when the provided regex is invalid.
159+
# RUN: not llvm-objcopy --regex --change-section-address "ab**-0x20" %ti1 2>&1 | FileCheck %s --check-prefix=ERR-MATCHER-FAILURE
160+
161+
# ERR-OVERFLOW: address 0x300 cannot be increased by 0xfffffffffffffd00. The result would overflow
162+
# ERR-UNDERFLOW: address 0x200 cannot be decreased by 0x201. The result would underflow
163+
# ERR-IVALID-VAL: error: bad format for --change-section-address: argument value 0 is invalid. See --help
164+
# ERR-NOT-INTEGER: error: bad format for --change-section-address: value after + is 0c50 when it should be a 64-bit integer
165+
# ERR-MISSING-SECTION: error: bad format for --change-section-address: missing section pattern to apply address change to
166+
# ERR-MISSING-VALUE-MINUS: error: bad format for --change-section-address: missing value of offset after '-'
167+
# ERR-MISSING-VALUE-PLUS: error: bad format for --change-section-address: missing value of offset after '+'
168+
# ERR-MISSING-VALUE-EQUAL: error: bad format for --change-section-address: missing address value after '='
169+
# ERR-MATCHER-FAILURE: error: cannot compile regular expression 'ab**': repetition-operator operand invalid
170+
171+
--- !ELF
172+
FileHeader:
173+
Class: ELFCLASS64
174+
Data: ELFDATA2LSB
175+
Type: ET_[[TYPE]]
176+
Sections:
177+
- Name: .text1
178+
Type: SHT_PROGBITS
179+
Size: 0x100
180+
Address: 0x100
181+
- Name: .text2
182+
Type: SHT_PROGBITS
183+
Size: 0x100
184+
Address: 0x200
185+
- Name: .anotherone
186+
Type: SHT_PROGBITS
187+
Size: 0x100
188+
Address: 0x300
189+
- Name: =a-b+c++d
190+
Type: SHT_PROGBITS
191+
Size: 0x100
192+
Address: 0x400
193+
194+
# RUN: yaml2obj -DTYPE=EXEC %s -o %ti2
195+
196+
## Input file is not ET_REL
197+
# RUN: not llvm-objcopy --change-section-address *+0x20 %ti2 2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
198+
199+
# ERR-FILE-TYPE: cannot change section address in a non-relocatable file

0 commit comments

Comments
 (0)