Skip to content

Commit 0778f5c

Browse files
authored
[ELF] Support NOCROSSREFS and NOCROSSERFS_TO
Implement the two commands described by https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html After `outputSections` is available, check each output section described by at least one `NOCROSSREFS`/`NOCROSSERFS_TO` command. For each checked output section, scan relocations from its input sections. This step is slow, therefore utilize `parallelForEach(isd->sections, ...)`. To support non SHF_ALLOC sections, `InputSectionBase::relocations` (empty) cannot be used. In addition, we may explore eliminating this member to speed up relocation scanning. Some parse code is adapted from #95714. Close #41825 Pull Request: #98773
1 parent 0bb68b5 commit 0778f5c

File tree

7 files changed

+195
-0
lines changed

7 files changed

+195
-0
lines changed

lld/ELF/LinkerScript.h

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

259+
// A NOCROSSREFS/NOCROSSREFS_TO command that prohibits references between
260+
// certain output sections.
261+
struct NoCrossRefCommand {
262+
SmallVector<StringRef, 0> outputSections;
263+
264+
// When true, this describes a NOCROSSREFS_TO command that probits references
265+
// to the first output section from any of the other sections.
266+
bool toFirst = false;
267+
};
268+
259269
struct PhdrsCommand {
260270
StringRef name;
261271
unsigned type = llvm::ELF::PT_NULL;
@@ -397,6 +407,9 @@ class LinkerScript final {
397407
// OutputSections specified by OVERWRITE_SECTIONS.
398408
SmallVector<OutputDesc *, 0> overwriteSections;
399409

410+
// NOCROSSREFS(_TO) commands.
411+
SmallVector<NoCrossRefCommand, 0> noCrossRefs;
412+
400413
// Sections that will be warned/errored by --orphan-handling.
401414
SmallVector<const InputSectionBase *, 0> orphanSections;
402415

lld/ELF/Relocations.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,7 +2367,65 @@ void elf::hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections) {
23672367
});
23682368
}
23692369

2370+
static bool matchesRefTo(const NoCrossRefCommand &cmd, StringRef osec) {
2371+
if (cmd.toFirst)
2372+
return cmd.outputSections[0] == osec;
2373+
return llvm::is_contained(cmd.outputSections, osec);
2374+
}
2375+
2376+
template <class ELFT, class Rels>
2377+
static void scanCrossRefs(const NoCrossRefCommand &cmd, OutputSection *osec,
2378+
InputSection *sec, Rels rels) {
2379+
for (const auto &r : rels) {
2380+
Symbol &sym = sec->file->getSymbol(r.getSymbol(config->isMips64EL));
2381+
// A legal cross-reference is when the destination output section is
2382+
// nullptr, osec for a self-reference, or a section that is described by the
2383+
// NOCROSSREFS/NOCROSSREFS_TO command.
2384+
auto *dstOsec = sym.getOutputSection();
2385+
if (!dstOsec || dstOsec == osec || !matchesRefTo(cmd, dstOsec->name))
2386+
continue;
2387+
2388+
std::string toSymName;
2389+
if (!sym.isSection())
2390+
toSymName = toString(sym);
2391+
else if (auto *d = dyn_cast<Defined>(&sym))
2392+
toSymName = d->section->name;
2393+
errorOrWarn(sec->getLocation(r.r_offset) +
2394+
": prohibited cross reference from '" + osec->name + "' to '" +
2395+
toSymName + "' in '" + dstOsec->name + "'");
2396+
}
2397+
}
2398+
2399+
// For each output section described by at least one NOCROSSREFS(_TO) command,
2400+
// scan relocations from its input sections for prohibited cross references.
2401+
template <class ELFT> void elf::checkNoCrossRefs() {
2402+
for (OutputSection *osec : outputSections) {
2403+
for (const NoCrossRefCommand &noxref : script->noCrossRefs) {
2404+
if (!llvm::is_contained(noxref.outputSections, osec->name) ||
2405+
(noxref.toFirst && noxref.outputSections[0] == osec->name))
2406+
continue;
2407+
for (SectionCommand *cmd : osec->commands) {
2408+
auto *isd = dyn_cast<InputSectionDescription>(cmd);
2409+
if (!isd)
2410+
continue;
2411+
parallelForEach(isd->sections, [&](InputSection *sec) {
2412+
const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
2413+
if (rels.areRelocsRel())
2414+
scanCrossRefs<ELFT>(noxref, osec, sec, rels.rels);
2415+
else
2416+
scanCrossRefs<ELFT>(noxref, osec, sec, rels.relas);
2417+
});
2418+
}
2419+
}
2420+
}
2421+
}
2422+
23702423
template void elf::scanRelocations<ELF32LE>();
23712424
template void elf::scanRelocations<ELF32BE>();
23722425
template void elf::scanRelocations<ELF64LE>();
23732426
template void elf::scanRelocations<ELF64BE>();
2427+
2428+
template void elf::checkNoCrossRefs<ELF32LE>();
2429+
template void elf::checkNoCrossRefs<ELF32BE>();
2430+
template void elf::checkNoCrossRefs<ELF64LE>();
2431+
template void elf::checkNoCrossRefs<ELF64BE>();

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
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
142142
// the diagnostics.
143143
template <class ELFT> void scanRelocations();
144+
template <class ELFT> void checkNoCrossRefs();
144145
void reportUndefinedSymbols();
145146
void postScanRelocations();
146147
void addGotEntry(Symbol &sym);

lld/ELF/ScriptParser.cpp

Lines changed: 16 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);
@@ -280,6 +281,10 @@ void ScriptParser::readLinkerScript() {
280281
readTarget();
281282
} else if (tok == "VERSION") {
282283
readVersion();
284+
} else if (tok == "NOCROSSREFS") {
285+
readNoCrossRefs(/*to=*/false);
286+
} else if (tok == "NOCROSSREFS_TO") {
287+
readNoCrossRefs(/*to=*/true);
283288
} else if (SymbolAssignment *cmd = readAssignment(tok)) {
284289
script->sectionCommands.push_back(cmd);
285290
} else {
@@ -299,6 +304,17 @@ void ScriptParser::readDefsym(StringRef name) {
299304
script->sectionCommands.push_back(cmd);
300305
}
301306

307+
void ScriptParser::readNoCrossRefs(bool to) {
308+
expect("(");
309+
NoCrossRefCommand cmd{{}, to};
310+
while (!errorCount() && !consume(")"))
311+
cmd.outputSections.push_back(unquote(next()));
312+
if (cmd.outputSections.size() < 2)
313+
warn(getCurrentLocation() + ": ignored with fewer than 2 output sections");
314+
else
315+
script->noCrossRefs.push_back(std::move(cmd));
316+
}
317+
302318
void ScriptParser::addFile(StringRef s) {
303319
if (isUnderSysroot && s.starts_with("/")) {
304320
SmallString<128> pathData;

lld/ELF/Writer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
19431943
// have the headers, we can find out which sections they point to.
19441944
setReservedSymbolSections();
19451945

1946+
if (script->noCrossRefs.size()) {
1947+
llvm::TimeTraceScope timeScope("Check NOCROSSREFS");
1948+
checkNoCrossRefs<ELFT>();
1949+
}
1950+
19461951
{
19471952
llvm::TimeTraceScope timeScope("Finalize synthetic sections");
19481953

lld/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ ELF Improvements
8181
(`#87530 <https://github.com/llvm/llvm-project/pull/87530>`_)
8282
* ``OUTPUT_FORMAT(binary)`` is now supported.
8383
(`#98837 <https://github.com/llvm/llvm-project/pull/98837>`_)
84+
* ``NOCROSSREFS`` and ``NOCRFOSSREFS_TO`` commands now supported to prohibit
85+
cross references between certain output sections.
86+
(`#98773 <https://github.com/llvm/llvm-project/pull/98773>`_)
8487
* Orphan placement is refined to prefer the last similar section when its rank <= orphan's rank.
8588
(`#94099 <https://github.com/llvm/llvm-project/pull/94099>`_)
8689
Non-alloc orphan sections are now placed at the end.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
4+
# RUN: llvm-mc --triple=x86_64 -filetype=obj a.s -o a.o
5+
# RUN: llvm-mc --triple=x86_64 -filetype=obj data.s -o data.o
6+
# RUN: ld.lld a.o data.o -T 0.t 2>&1 | FileCheck %s --check-prefix=CHECK0 --implicit-check-not=warning:
7+
8+
# CHECK0: warning: 0.t:3: ignored with fewer than 2 output sections
9+
# CHECK0-NEXT: warning: 0.t:4: ignored with fewer than 2 output sections
10+
11+
# RUN: not ld.lld a.o data.o -T 1.t 2>&1 | FileCheck %s --check-prefix=CHECK1 --implicit-check-not=error:
12+
# CHECK1: error: a.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
13+
14+
## .text and .text1 are in two NOCROSSREFS commands. Violations are reported twice.
15+
# RUN: not ld.lld --threads=1 a.o data.o -T 2.t 2>&1 | FileCheck %s --check-prefix=CHECK2 --implicit-check-not=error:
16+
# CHECK2: error: a.o:(.text.start+0x6): prohibited cross reference from '.text' to '.text1' in '.text1'
17+
# CHECK2-NEXT: error: a.o:(.text.start+0x6): prohibited cross reference from '.text' to '.text1' in '.text1'
18+
# CHECK2-NEXT: error: a.o:(.text.start+0xb): prohibited cross reference from '.text' to 'foo2' in '.text2'
19+
# CHECK2-NEXT: error: a.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
20+
# CHECK2-NEXT: error: a.o:(.text.start+0x17): prohibited cross reference from '.text' to 'str1' in '.rodata'
21+
## .data occurs twice in the command, but the violation is only reported once.
22+
# CHECK2-NEXT: error: a.o:(.text1+0x1): prohibited cross reference from '.text1' to '_edata' in '.data'
23+
# CHECK2-NEXT: error: a.o:(.nonalloc+0x0): prohibited cross reference from '.nonalloc' to '.text' in '.text'
24+
# CHECK2-NEXT: error: a.o:(.nonalloc+0x10): prohibited cross reference from '.nonalloc' to 'data' in '.data'
25+
26+
# RUN: not ld.lld a.o data.o -T 3.t 2>&1 | FileCheck %s --check-prefix=CHECK3 --implicit-check-not=error:
27+
# CHECK3: error: a.o:(.nonalloc+0x0): prohibited cross reference from '.nonalloc' to '.text' in '.text'
28+
29+
#--- 0.t
30+
## Some cases that do not cause errors.
31+
abs = 42;
32+
NOCROSSREFS()
33+
NOCROSSREFS (.text)
34+
NOCROSSREFS( .text .text3 ); ## ; is ignored
35+
NOCROSSREFS_TO(.text .text2 .text3 .data );
36+
NOCROSSREFS_TO (.data .text2 .text3)
37+
38+
#--- 1.t
39+
abs = 42;
40+
NOCROSSREFS(.text ".data")
41+
42+
#--- 2.t
43+
abs = 42;
44+
NOCROSSREFS(.text ".text1" .text ".text1" )
45+
NOCROSSREFS(.text .text1 .text2 .data .rodata .data .nonalloc)
46+
47+
#--- 3.t
48+
abs = 42;
49+
NOCROSSREFS_TO(.text .text .text1 .text2 .data .nonalloc)
50+
51+
#--- err1.t
52+
NOCROSSREFS )
53+
54+
# RUN: not ld.lld a.o data.o -T err1.t 2>&1 | FileCheck %s --check-prefix=ERR1 --implicit-check-not=error:
55+
# ERR1: error: err1.t:1: ( expected, but got )
56+
57+
#--- err2.t
58+
NOCROSSREFS(.text
59+
60+
# RUN: not ld.lld a.o data.o -T err2.t 2>&1 | FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:
61+
# ERR2: error: err2.t:1: unexpected EOF
62+
63+
#--- a.s
64+
.global _start, foo1, foo2, foo3
65+
.section .text.start,"ax"
66+
_start:
67+
call _start
68+
call .text1
69+
call foo2
70+
movl data(%rip), %eax
71+
movl str1(%rip), %eax
72+
73+
.section .text1,"ax"
74+
foo1:
75+
call _edata
76+
77+
.section .text2,"ax"
78+
foo2:
79+
call foo3
80+
81+
.section .text3,"ax"
82+
foo3:
83+
call foo2
84+
85+
.section .rodata.str1.1,"aMS",@progbits,1
86+
str1:
87+
.asciz "abc"
88+
89+
.section .nonalloc,""
90+
.quad .text
91+
.quad .text3
92+
.quad data
93+
94+
#--- data.s
95+
.section .data,"aw"
96+
.globl data
97+
data:
98+
.byte 0
99+
.quad abs

0 commit comments

Comments
 (0)