Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1c5a984

Browse files
committedJul 12, 2024·
[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.
1 parent 58bc98c commit 1c5a984

File tree

7 files changed

+317
-43
lines changed

7 files changed

+317
-43
lines changed
 

‎llvm/docs/CommandGuide/llvm-objcopy.rst

+9
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 <sectionpattern>{=+-}<val>, --adjust-section-vma
307+
308+
Change the address of ``<sectionpattern>`` to the specified value, or apply
309+
offset to the current value. Can be specified multiple times to specify multiple
310+
patterns. Each section is only modified by one --change-section-address
311+
argument. Changes apply from the right of the command line. If a section name
312+
matches multiple patterns, the rightmost change applies. Object file needs to be
313+
relocatable.
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

+57-39
Original file line numberDiff line numberDiff line change
@@ -44,45 +44,6 @@ struct MachineInfo {
4444
bool IsLittleEndian;
4545
};
4646

47-
// Flags set by --set-section-flags or --rename-section. Interpretation of these
48-
// is format-specific and not all flags are meaningful for all object file
49-
// formats. This is a bitmask; many section flags may be set.
50-
enum SectionFlag {
51-
SecNone = 0,
52-
SecAlloc = 1 << 0,
53-
SecLoad = 1 << 1,
54-
SecNoload = 1 << 2,
55-
SecReadonly = 1 << 3,
56-
SecDebug = 1 << 4,
57-
SecCode = 1 << 5,
58-
SecData = 1 << 6,
59-
SecRom = 1 << 7,
60-
SecMerge = 1 << 8,
61-
SecStrings = 1 << 9,
62-
SecContents = 1 << 10,
63-
SecShare = 1 << 11,
64-
SecExclude = 1 << 12,
65-
SecLarge = 1 << 13,
66-
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
67-
};
68-
69-
struct SectionRename {
70-
StringRef OriginalName;
71-
StringRef NewName;
72-
std::optional<SectionFlag> NewFlags;
73-
};
74-
75-
struct SectionFlagsUpdate {
76-
StringRef Name;
77-
SectionFlag NewFlags;
78-
};
79-
80-
enum class DiscardType {
81-
None, // Default
82-
All, // --discard-all (-x)
83-
Locals, // --discard-locals (-X)
84-
};
85-
8647
enum class MatchStyle {
8748
Literal, // Default for symbols.
8849
Wildcard, // Default for sections, or enabled with --wildcard (-w).
@@ -191,6 +152,61 @@ struct NewSectionInfo {
191152
std::shared_ptr<MemoryBuffer> SectionData;
192153
};
193154

155+
// Flags set by --set-section-flags or --rename-section. Interpretation of these
156+
// is format-specific and not all flags are meaningful for all object file
157+
// formats. This is a bitmask; many section flags may be set.
158+
enum SectionFlag {
159+
SecNone = 0,
160+
SecAlloc = 1 << 0,
161+
SecLoad = 1 << 1,
162+
SecNoload = 1 << 2,
163+
SecReadonly = 1 << 3,
164+
SecDebug = 1 << 4,
165+
SecCode = 1 << 5,
166+
SecData = 1 << 6,
167+
SecRom = 1 << 7,
168+
SecMerge = 1 << 8,
169+
SecStrings = 1 << 9,
170+
SecContents = 1 << 10,
171+
SecShare = 1 << 11,
172+
SecExclude = 1 << 12,
173+
SecLarge = 1 << 13,
174+
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge)
175+
};
176+
177+
struct SectionRename {
178+
StringRef OriginalName;
179+
StringRef NewName;
180+
std::optional<SectionFlag> NewFlags;
181+
};
182+
183+
struct SectionFlagsUpdate {
184+
StringRef Name;
185+
SectionFlag NewFlags;
186+
};
187+
188+
struct AddressUpdate {
189+
uint64_t Value = 0;
190+
bool Absolute = false;
191+
bool Negative = false;
192+
};
193+
194+
struct SectionPatternAddressUpdate {
195+
NameMatcher SectionPattern;
196+
AddressUpdate Update;
197+
};
198+
199+
struct SectionNameAddressUpdate {
200+
StringRef Name;
201+
AddressUpdate Update;
202+
};
203+
204+
enum class DiscardType {
205+
None, // Default
206+
All, // --discard-all (-x)
207+
Locals, // --discard-locals (-X)
208+
};
209+
194210
// Configuration for copying/stripping a single file.
195211
struct CommonConfig {
196212
// Main input/output options
@@ -219,6 +235,7 @@ struct CommonConfig {
219235
SmallVector<NewSectionInfo, 0> AddSection;
220236
SmallVector<StringRef, 0> DumpSection;
221237
SmallVector<NewSectionInfo, 0> UpdateSection;
238+
SmallVector<SectionPatternAddressUpdate, 0> ChangeSectionAddress;
222239

223240
// Section matchers
224241
NameMatcher KeepSection;
@@ -241,6 +258,7 @@ struct CommonConfig {
241258
StringMap<SectionFlagsUpdate> SetSectionFlags;
242259
StringMap<uint64_t> SetSectionType;
243260
StringMap<StringRef> SymbolsToRename;
261+
StringMap<AddressUpdate> SectionsToUpdateAddress;
244262

245263
// Symbol info specified by --add-symbol option.
246264
SmallVector<NewSymbolInfo, 0> SymbolsToAdd;

‎llvm/lib/ObjCopy/ConfigManager.cpp

+8-4
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

+46
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,52 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
745745
}
746746
}
747747

748+
if (!Config.ChangeSectionAddress.empty()) {
749+
if (Obj.isRelocatable()) {
750+
StringMap<AddressUpdate> SectionsToUpdateAddress;
751+
for (const SectionPatternAddressUpdate &PatternUpdate :
752+
make_range(Config.ChangeSectionAddress.rbegin(),
753+
Config.ChangeSectionAddress.rend())) {
754+
for (SectionBase &Sec : Obj.sections()) {
755+
if (PatternUpdate.SectionPattern.matches(Sec.Name)) {
756+
if (SectionsToUpdateAddress
757+
.try_emplace(Sec.Name, PatternUpdate.Update)
758+
.second) {
759+
if (PatternUpdate.Update.Absolute) {
760+
Sec.Addr = PatternUpdate.Update.Value;
761+
} else if (PatternUpdate.Update.Negative &&
762+
Sec.Addr < PatternUpdate.Update.Value) {
763+
return createStringError(
764+
errc::invalid_argument,
765+
"address 0x" + Twine::utohexstr(Sec.Addr) +
766+
" cannot be decreased by 0x" +
767+
Twine::utohexstr(PatternUpdate.Update.Value) +
768+
". The result would underflow");
769+
} else if (!PatternUpdate.Update.Negative &&
770+
Sec.Addr > std::numeric_limits<uint64_t>::max() -
771+
PatternUpdate.Update.Value) {
772+
return createStringError(
773+
errc::invalid_argument,
774+
"address 0x" + Twine::utohexstr(Sec.Addr) +
775+
" cannot be increased by 0x" +
776+
Twine::utohexstr(PatternUpdate.Update.Value) +
777+
". The result would overflow");
778+
} else if (PatternUpdate.Update.Negative) {
779+
Sec.Addr -= PatternUpdate.Update.Value;
780+
} else {
781+
Sec.Addr += PatternUpdate.Update.Value;
782+
}
783+
}
784+
}
785+
}
786+
}
787+
} else {
788+
return createStringError(
789+
object_error::invalid_file_type,
790+
"cannot change section address in a non-relocatable file");
791+
}
792+
}
793+
748794
if (Config.OnlyKeepDebug)
749795
for (auto &Sec : Obj.sections())
750796
if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# RUN: yaml2obj --docnum=1 %s -o %ti
2+
3+
# RUN: llvm-objcopy --adjust-section-vma *+0x20 %ti %to
4+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
5+
6+
# RUN: llvm-objcopy --change-section-address *+0x20 %ti %to
7+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-ALL
8+
9+
# RUN: llvm-objcopy --change-section-address .anotherone-0x30 %ti %to
10+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUB-SECTION
11+
12+
# RUN: llvm-objcopy --change-section-address .text*+0x20 %ti %to
13+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-ADD-PATTERN
14+
15+
# RUN: llvm-objcopy --change-section-address .text*=0x10 %ti %to
16+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SET-PATTERN
17+
18+
# RUN: llvm-objcopy --change-section-address .anotherone-0x30 --change-section-address .anotherone+0x20 %ti %to
19+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-USE-LAST
20+
21+
# RUN: llvm-objcopy --change-section-address .anotherone=0x455 --change-section-address *+0x20 %ti %to
22+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-NOTSUPERSET-SET
23+
24+
# RUN: llvm-objcopy --change-section-address *+0x20 --change-section-address .anotherone=0x455 %ti %to
25+
# RUN: llvm-readelf --section-headers %to | FileCheck %s --check-prefix=CHECK-SUPERSET-SET
26+
27+
# CHECK-ADD-ALL: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
28+
# CHECK-ADD-ALL: .text1
29+
# CHECK-ADD-ALL-SAME: 0000000000000120
30+
# CHECK-ADD-ALL: .text2
31+
# CHECK-ADD-ALL: .anotherone
32+
# CHECK-ADD-ALL-SAME: 0000000000000320
33+
34+
# CHECK-SUB-SECTION: .text1
35+
# CHECK-SUB-SECTION-SAME: 0000000000000100
36+
# CHECK-SUB-SECTION: .text2
37+
# CHECK-SUB-SECTION-SAME: 0000000000000200
38+
# CHECK-SUB-SECTION: .anotherone
39+
# CHECK-SUB-SECTION-SAME: 00000000000002d0
40+
41+
# CHECK-ADD-PATTERN: .text1
42+
# CHECK-ADD-PATTERN-SAME: 0000000000000120
43+
# CHECK-ADD-PATTERN: .text2
44+
# CHECK-ADD-PATTERN-SAME: 0000000000000220
45+
# CHECK-ADD-PATTERN: .anotherone
46+
# CHECK-ADD-PATTERN-SAME: 0000000000000300
47+
48+
# CHECK-SET-PATTERN: .text1
49+
# CHECK-SET-PATTERN-SAME: 0000000000000010
50+
# CHECK-SET-PATTERN: .text2
51+
# CHECK-SET-PATTERN-SAME: 0000000000000010
52+
# CHECK-SET-PATTERN: .anotherone
53+
# CHECK-SET-PATTERN-SAME: 0000000000000300
54+
55+
# CHECK-USE-LAST: .anotherone
56+
# CHECK-USE-LAST-SAME: 0000000000000320
57+
58+
# CHECK-NOTSUPERSET-SET: .text1
59+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000120
60+
# CHECK-NOTSUPERSET-SET: .text2
61+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000220
62+
# CHECK-NOTSUPERSET-SET: .anotherone
63+
# CHECK-NOTSUPERSET-SET-SAME: 0000000000000320
64+
65+
# CHECK-SUPERSET-SET: .text1
66+
# CHECK-SUPERSET-SET-SAME: 0000000000000120
67+
# CHECK-SUPERSET-SET: .text2
68+
# CHECK-SUPERSET-SET-SAME: 0000000000000220
69+
# CHECK-SUPERSET-SET: .anotherone
70+
# CHECK-SUPERSET-SET-SAME: 0000000000000455
71+
72+
# RUN: not llvm-objcopy --change-section-address .anotherone+0xffffffffffffff00 %ti 2>&1 | FileCheck %s --check-prefix=ERR-OVERFLOW
73+
# RUN: not llvm-objcopy --change-section-address *-0x2000 %ti 2>&1 | FileCheck %s --check-prefix=ERR-UNDERFLOW
74+
# RUN: not llvm-objcopy --change-section-address 0 %ti 2>&1 | FileCheck %s --check-prefix=ERR-IVALID-VAL
75+
# RUN: not llvm-objcopy --change-section-address .anotherone+0c50 %ti 2>&1 | FileCheck %s --check-prefix=ERR-NOT-INTEGER
76+
# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
77+
# RUN: not llvm-objcopy --change-section-address =0x10 %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-SECTION
78+
# RUN: not llvm-objcopy --change-section-address .text1- %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-MINUS
79+
# RUN: not llvm-objcopy --change-section-address .text1+ %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-PLUS
80+
# RUN: not llvm-objcopy --change-section-address .text1= %ti 2>&1 | FileCheck %s --check-prefix=ERR-MISSING-VALUE-EQUAL
81+
82+
# ERR-OVERFLOW: address 0x300 cannot be increased by 0xffffffffffffff00. The result would overflow
83+
# ERR-UNDERFLOW: address 0x100 cannot be decreased by 0x2000. The result would underflow
84+
# ERR-IVALID-VAL: error: bad format for --change-section-address: argument value 0 is invalid. See help
85+
# ERR-NOT-INTEGER: error: bad format for --change-section-address: value after + is 0c50 when it should be an integer
86+
# ERR-MISSING-SECTION: error: bad format for --change-section-address: missing section pattern to apply address change to
87+
# ERR-MISSING-VALUE-MINUS: error: bad format for --change-section-address: missing value of offset after '-'
88+
# ERR-MISSING-VALUE-PLUS: error: bad format for --change-section-address: missing value of offset after '+'
89+
# ERR-MISSING-VALUE-EQUAL: error: bad format for --change-section-address: missing address value after '='
90+
91+
--- !ELF
92+
FileHeader:
93+
Class: ELFCLASS64
94+
Data: ELFDATA2LSB
95+
Type: ET_REL
96+
Sections:
97+
- Name: .text1
98+
Type: SHT_PROGBITS
99+
Size: 0x100
100+
Address: 0x100
101+
- Name: .text2
102+
Type: SHT_PROGBITS
103+
Size: 0x100
104+
Address: 0x200
105+
- Name: .anotherone
106+
Type: SHT_PROGBITS
107+
Size: 0x100
108+
Address: 0x300
109+
110+
# RUN: yaml2obj --docnum=2 %s -o %ti
111+
112+
# RUN: not llvm-objcopy --change-section-address *+0x20 %ti 2>&1 | FileCheck %s --check-prefix=ERR-FILE-TYPE
113+
114+
# ERR-FILE-TYPE: cannot change section address in a non-relocatable file
115+
116+
--- !ELF
117+
FileHeader:
118+
Class: ELFCLASS64
119+
Data: ELFDATA2LSB
120+
Type: ET_EXEC

0 commit comments

Comments
 (0)
Please sign in to comment.