Skip to content

Commit 05e0a55

Browse files
committed
ldd: add support for NOCROSSREFS(_TO)
Patch introduces supprot for NOCROSSREFS_(TO) linker script commands. These commands specify which cross-section references should be threated as errors. See more in ld documenmtation [0] Implementation is straightforward -- traverse all relocations in all object files and report an error if there is prohibited one. [0] https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html Closes: #41825 Signed-off-by: Pavel Skripkin <[email protected]>
1 parent 253c28f commit 05e0a55

File tree

7 files changed

+277
-0
lines changed

7 files changed

+277
-0
lines changed

lld/ELF/LinkerScript.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,3 +1714,14 @@ bool LinkerScript::shouldAddProvideSym(StringRef symName) {
17141714
Symbol *sym = symtab.find(symName);
17151715
return sym && !sym->isDefined() && !sym->isCommon();
17161716
}
1717+
1718+
bool NoCrossRefList::matchesRefToSection(const OutputSection *section) const {
1719+
if (toSection)
1720+
return toSection.value() == section->name;
1721+
1722+
return llvm::is_contained(outputSections, section->name);
1723+
}
1724+
1725+
bool NoCrossRefList::matchesRefFromSection(const OutputSection *section) const {
1726+
return llvm::is_contained(outputSections, section->name);
1727+
}

lld/ELF/LinkerScript.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,19 @@ struct InsertCommand {
256256
StringRef where;
257257
};
258258

259+
struct NoCrossRefList {
260+
SmallVector<StringRef, 2> outputSections;
261+
262+
// See documentation for NOCROSSREFS and NOCROSSREFS_TO. When toSection is
263+
// NONE outputSections are output section names that must not have any cross
264+
// references between them. Otherwise, toSection is tosection name and
265+
// outputSections are fromsections.
266+
std::optional<StringRef> toSection;
267+
268+
bool matchesRefFromSection(const OutputSection *section) const;
269+
bool matchesRefToSection(const OutputSection *section) const;
270+
};
271+
259272
struct PhdrsCommand {
260273
StringRef name;
261274
unsigned type = llvm::ELF::PT_NULL;
@@ -394,6 +407,9 @@ class LinkerScript final {
394407
// OutputSections specified by OVERWRITE_SECTIONS.
395408
SmallVector<OutputDesc *, 0> overwriteSections;
396409

410+
// OutputSections names specified by NOCROSSREFS(_TO).
411+
SmallVector<NoCrossRefList, 0> noCrossRefLists;
412+
397413
// Sections that will be warned/errored by --orphan-handling.
398414
SmallVector<const InputSectionBase *, 0> orphanSections;
399415

lld/ELF/Relocations.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2358,3 +2358,45 @@ template void elf::scanRelocations<ELF32LE>();
23582358
template void elf::scanRelocations<ELF32BE>();
23592359
template void elf::scanRelocations<ELF64LE>();
23602360
template void elf::scanRelocations<ELF64BE>();
2361+
2362+
static void forEachAllocInputSectionDescription(
2363+
ArrayRef<OutputSection *> outputSections,
2364+
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> fn) {
2365+
for (OutputSection *os : outputSections) {
2366+
if (!(os->flags & SHF_ALLOC))
2367+
continue;
2368+
for (SectionCommand *bc : os->commands)
2369+
if (auto *isd = dyn_cast<InputSectionDescription>(bc))
2370+
fn(os, isd);
2371+
}
2372+
}
2373+
2374+
static void checkSectionNoCrossRefs(OutputSection *outSec,
2375+
InputSectionDescription *inSecDescr) {
2376+
for (const auto &list : script->noCrossRefLists) {
2377+
if (!list.matchesRefFromSection(outSec))
2378+
continue;
2379+
2380+
for (const auto &inSection : inSecDescr->sections) {
2381+
for (const auto &relocation : inSection->relocations) {
2382+
auto *destOutSec = relocation.sym->getOutputSection();
2383+
if (!destOutSec)
2384+
continue;
2385+
2386+
// Relocations from section to itself are allowed.
2387+
if (destOutSec->name == outSec->name ||
2388+
!list.matchesRefToSection(destOutSec))
2389+
continue;
2390+
2391+
error(inSection->getLocation(relocation.offset) +
2392+
": prohibited cross reference from " + inSection->name.str() +
2393+
" to " + relocation.sym->getName().str() + " in " +
2394+
destOutSec->name.str());
2395+
}
2396+
}
2397+
}
2398+
}
2399+
2400+
void elf::checkNoCrossRefs() {
2401+
forEachAllocInputSectionDescription(outputSections, checkSectionNoCrossRefs);
2402+
}

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ struct JumpInstrMod {
141141
// the diagnostics.
142142
template <class ELFT> void scanRelocations();
143143
void reportUndefinedSymbols();
144+
void checkNoCrossRefs();
144145
void postScanRelocations();
145146
void addGotEntry(Symbol &sym);
146147

lld/ELF/ScriptParser.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ScriptParser final : ScriptLexer {
8787
void readTarget();
8888
void readVersion();
8989
void readVersionScriptCommand();
90+
void readNoCrossRefs(bool to);
9091

9192
SymbolAssignment *readSymbolAssignment(StringRef name);
9293
ByteCommand *readByteCommand(StringRef tok);
@@ -235,6 +236,26 @@ void ScriptParser::readVersionScriptCommand() {
235236
}
236237
}
237238

239+
void ScriptParser::readNoCrossRefs(bool to) {
240+
expect("(");
241+
242+
script->noCrossRefLists.push_back({});
243+
auto &list = script->noCrossRefLists.back();
244+
245+
if (to && peek() != ")")
246+
list.toSection = next();
247+
248+
while (!atEOF() && !errorCount() && peek() != ")")
249+
list.outputSections.push_back(next());
250+
251+
// Discard meaningless lists
252+
if ((to && list.outputSections.empty()) ||
253+
(!to && list.outputSections.size() < 2))
254+
script->noCrossRefLists.pop_back();
255+
256+
expect(")");
257+
}
258+
238259
void ScriptParser::readVersion() {
239260
expect("{");
240261
readVersionScriptCommand();
@@ -279,6 +300,10 @@ void ScriptParser::readLinkerScript() {
279300
readTarget();
280301
} else if (tok == "VERSION") {
281302
readVersion();
303+
} else if (tok == "NOCROSSREFS") {
304+
readNoCrossRefs(/*to=*/false);
305+
} else if (tok == "NOCROSSREFS_TO") {
306+
readNoCrossRefs(/*to=*/true);
282307
} else if (SymbolAssignment *cmd = readAssignment(tok)) {
283308
script->sectionCommands.push_back(cmd);
284309
} else {

lld/ELF/Writer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ template <class ELFT> void Writer<ELFT>::run() {
309309
finalizeSections();
310310
checkExecuteOnly();
311311

312+
if (script->noCrossRefLists.size())
313+
checkNoCrossRefs();
314+
312315
// If --compressed-debug-sections is specified, compress .debug_* sections.
313316
// Do it right now because it changes the size of output sections.
314317
for (OutputSection *sec : outputSections)
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
4+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
5+
# RUN: not ld.lld main.o -o main --script script1.ld 2>&1 | FileCheck -check-prefix=ERR %s
6+
# ERR: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1
7+
8+
#--- script1.ld
9+
NOCROSSREFS(.text .text1);
10+
SECTIONS {
11+
.text : { *(.text) }
12+
.text1 : { *(.text1) }
13+
.text2 : { *(.text2) }
14+
}
15+
16+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
17+
# RUN: not ld.lld main.o -o main --script script2.ld 2>&1 | FileCheck -check-prefix=ERR1 %s
18+
# ERR1: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1
19+
20+
#--- script2.ld
21+
NOCROSSREFS_TO(.text1 .text);
22+
SECTIONS {
23+
.text : { *(.text) }
24+
.text1 : { *(.text1) }
25+
.text2 : { *(.text2) }
26+
}
27+
28+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
29+
# RUN: not ld.lld main.o -o main --script script3.ld 2>&1 | FileCheck -check-prefix=ERR2 %s
30+
# ERR2: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1
31+
32+
#--- script3.ld
33+
NOCROSSREFS(.text1 .text .text2);
34+
SECTIONS {
35+
.text : { *(.text) }
36+
.text1 : { *(.text1) }
37+
.text2 : { *(.text2) }
38+
}
39+
40+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
41+
# RUN: ld.lld main.o -o main --script script4.ld 2>&1
42+
43+
#--- script4.ld
44+
NOCROSSREFS_TO(.text .text1);
45+
SECTIONS {
46+
.text : { *(.text) }
47+
.text1 : { *(.text1) }
48+
.text2 : { *(.text2) }
49+
}
50+
51+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
52+
# RUN: ld.lld main.o -o main --script script5.ld 2>&1
53+
54+
#--- script5.ld
55+
NOCROSSREFS_TO();
56+
SECTIONS {
57+
.text : { *(.text) }
58+
.text1 : { *(.text1) }
59+
.text2 : { *(.text2) }
60+
}
61+
62+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
63+
# RUN: ld.lld main.o -o main --script script6.ld 2>&1
64+
65+
#--- script6.ld
66+
NOCROSSREFS();
67+
SECTIONS {
68+
.text : { *(.text) }
69+
.text1 : { *(.text1) }
70+
.text2 : { *(.text2) }
71+
}
72+
73+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
74+
# RUN: ld.lld main.o -o main --script script7.ld 2>&1
75+
76+
#--- script7.ld
77+
NOCROSSREFS(.text);
78+
SECTIONS {
79+
.text : { *(.text) }
80+
.text1 : { *(.text1) }
81+
.text2 : { *(.text2) }
82+
}
83+
84+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
85+
# RUN: ld.lld main.o -o main --script script8.ld 2>&1
86+
87+
#--- script8.ld
88+
NOCROSSREFS_TO(.text);
89+
SECTIONS {
90+
.text : { *(.text) }
91+
.text1 : { *(.text1) }
92+
.text2 : { *(.text2) }
93+
}
94+
95+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
96+
# RUN: ld.lld main.o -o main --script script9.ld 2>&1
97+
98+
#--- script9.ld
99+
NOCROSSREFS_TO(.text2 .text);
100+
SECTIONS {
101+
.text : { *(.text) }
102+
.text1 : { *(.text1) }
103+
.text2 : { *(.text2) }
104+
}
105+
106+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
107+
# RUN: ld.lld main.o -o main --script script10.ld 2>&1
108+
109+
#--- script10.ld
110+
NOCROSSREFS(.text .text2);
111+
SECTIONS {
112+
.text : { *(.text) }
113+
.text1 : { *(.text1) }
114+
.text2 : { *(.text2) }
115+
}
116+
117+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
118+
# RUN: ld.lld main.o -o main --script script11.ld 2>&1
119+
120+
#--- script11.ld
121+
NOCROSSREFS(.text .text2);
122+
SECTIONS {
123+
foo = ABSOLUTE(.);
124+
.text : { *(.text) }
125+
.text1 : { *(.text1) }
126+
.text2 : { *(.text2) }
127+
}
128+
129+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
130+
# RUN: ld.lld main.o -o main --script script12.ld 2>&1
131+
132+
#--- script12.ld
133+
NOCROSSREFS(.text .text2);
134+
SECTIONS {
135+
foo = ABSOLUTE(.);
136+
.text : { *(.text) }
137+
.text1 : { *(.text1) }
138+
.text2 : { *(.text2) }
139+
.bss : { *(.unused) }
140+
}
141+
142+
# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s
143+
# RUN: not ld.lld main.o -o main --script script13.ld 2>&1 | FileCheck -check-prefix=ERR3 %s
144+
# ERR3: {{.*}} error: main.o:(.text+0x5): prohibited cross reference from .text to unused in .bss
145+
146+
#--- script13.ld
147+
NOCROSSREFS(.text .bss);
148+
SECTIONS {
149+
foo = ABSOLUTE(.);
150+
.text : { *(.text) }
151+
.text1 : { *(.text1) }
152+
.text2 : { *(.text2) }
153+
.bss : { *(.unused) }
154+
}
155+
156+
#--- main.s
157+
.global _start
158+
_start:
159+
call test
160+
161+
.type unused,@object
162+
.comm unused,4,4
163+
164+
.section .noalloc,"",@progbits
165+
.quad unused
166+
167+
.section .text
168+
test:
169+
.reloc ., R_X86_64_32, unused
170+
call test1
171+
172+
.section .text2
173+
test2:
174+
.reloc ., R_X86_64_32, foo
175+
nop
176+
177+
.section .text1
178+
test1:
179+
nop

0 commit comments

Comments
 (0)