[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: https://github.com/llvm/llvm-project/pull/98773
This commit is contained in:
parent
0bb68b5571
commit
0778f5c1f1
@ -256,6 +256,16 @@ struct InsertCommand {
|
||||
StringRef where;
|
||||
};
|
||||
|
||||
// A NOCROSSREFS/NOCROSSREFS_TO command that prohibits references between
|
||||
// certain output sections.
|
||||
struct NoCrossRefCommand {
|
||||
SmallVector<StringRef, 0> outputSections;
|
||||
|
||||
// When true, this describes a NOCROSSREFS_TO command that probits references
|
||||
// to the first output section from any of the other sections.
|
||||
bool toFirst = false;
|
||||
};
|
||||
|
||||
struct PhdrsCommand {
|
||||
StringRef name;
|
||||
unsigned type = llvm::ELF::PT_NULL;
|
||||
@ -397,6 +407,9 @@ public:
|
||||
// OutputSections specified by OVERWRITE_SECTIONS.
|
||||
SmallVector<OutputDesc *, 0> overwriteSections;
|
||||
|
||||
// NOCROSSREFS(_TO) commands.
|
||||
SmallVector<NoCrossRefCommand, 0> noCrossRefs;
|
||||
|
||||
// Sections that will be warned/errored by --orphan-handling.
|
||||
SmallVector<const InputSectionBase *, 0> orphanSections;
|
||||
|
||||
|
@ -2367,7 +2367,65 @@ void elf::hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections) {
|
||||
});
|
||||
}
|
||||
|
||||
static bool matchesRefTo(const NoCrossRefCommand &cmd, StringRef osec) {
|
||||
if (cmd.toFirst)
|
||||
return cmd.outputSections[0] == osec;
|
||||
return llvm::is_contained(cmd.outputSections, osec);
|
||||
}
|
||||
|
||||
template <class ELFT, class Rels>
|
||||
static void scanCrossRefs(const NoCrossRefCommand &cmd, OutputSection *osec,
|
||||
InputSection *sec, Rels rels) {
|
||||
for (const auto &r : rels) {
|
||||
Symbol &sym = sec->file->getSymbol(r.getSymbol(config->isMips64EL));
|
||||
// A legal cross-reference is when the destination output section is
|
||||
// nullptr, osec for a self-reference, or a section that is described by the
|
||||
// NOCROSSREFS/NOCROSSREFS_TO command.
|
||||
auto *dstOsec = sym.getOutputSection();
|
||||
if (!dstOsec || dstOsec == osec || !matchesRefTo(cmd, dstOsec->name))
|
||||
continue;
|
||||
|
||||
std::string toSymName;
|
||||
if (!sym.isSection())
|
||||
toSymName = toString(sym);
|
||||
else if (auto *d = dyn_cast<Defined>(&sym))
|
||||
toSymName = d->section->name;
|
||||
errorOrWarn(sec->getLocation(r.r_offset) +
|
||||
": prohibited cross reference from '" + osec->name + "' to '" +
|
||||
toSymName + "' in '" + dstOsec->name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// For each output section described by at least one NOCROSSREFS(_TO) command,
|
||||
// scan relocations from its input sections for prohibited cross references.
|
||||
template <class ELFT> void elf::checkNoCrossRefs() {
|
||||
for (OutputSection *osec : outputSections) {
|
||||
for (const NoCrossRefCommand &noxref : script->noCrossRefs) {
|
||||
if (!llvm::is_contained(noxref.outputSections, osec->name) ||
|
||||
(noxref.toFirst && noxref.outputSections[0] == osec->name))
|
||||
continue;
|
||||
for (SectionCommand *cmd : osec->commands) {
|
||||
auto *isd = dyn_cast<InputSectionDescription>(cmd);
|
||||
if (!isd)
|
||||
continue;
|
||||
parallelForEach(isd->sections, [&](InputSection *sec) {
|
||||
const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
|
||||
if (rels.areRelocsRel())
|
||||
scanCrossRefs<ELFT>(noxref, osec, sec, rels.rels);
|
||||
else
|
||||
scanCrossRefs<ELFT>(noxref, osec, sec, rels.relas);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void elf::scanRelocations<ELF32LE>();
|
||||
template void elf::scanRelocations<ELF32BE>();
|
||||
template void elf::scanRelocations<ELF64LE>();
|
||||
template void elf::scanRelocations<ELF64BE>();
|
||||
|
||||
template void elf::checkNoCrossRefs<ELF32LE>();
|
||||
template void elf::checkNoCrossRefs<ELF32BE>();
|
||||
template void elf::checkNoCrossRefs<ELF64LE>();
|
||||
template void elf::checkNoCrossRefs<ELF64BE>();
|
||||
|
@ -141,6 +141,7 @@ struct JumpInstrMod {
|
||||
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
|
||||
// the diagnostics.
|
||||
template <class ELFT> void scanRelocations();
|
||||
template <class ELFT> void checkNoCrossRefs();
|
||||
void reportUndefinedSymbols();
|
||||
void postScanRelocations();
|
||||
void addGotEntry(Symbol &sym);
|
||||
|
@ -87,6 +87,7 @@ private:
|
||||
void readTarget();
|
||||
void readVersion();
|
||||
void readVersionScriptCommand();
|
||||
void readNoCrossRefs(bool to);
|
||||
|
||||
SymbolAssignment *readSymbolAssignment(StringRef name);
|
||||
ByteCommand *readByteCommand(StringRef tok);
|
||||
@ -280,6 +281,10 @@ void ScriptParser::readLinkerScript() {
|
||||
readTarget();
|
||||
} else if (tok == "VERSION") {
|
||||
readVersion();
|
||||
} else if (tok == "NOCROSSREFS") {
|
||||
readNoCrossRefs(/*to=*/false);
|
||||
} else if (tok == "NOCROSSREFS_TO") {
|
||||
readNoCrossRefs(/*to=*/true);
|
||||
} else if (SymbolAssignment *cmd = readAssignment(tok)) {
|
||||
script->sectionCommands.push_back(cmd);
|
||||
} else {
|
||||
@ -299,6 +304,17 @@ void ScriptParser::readDefsym(StringRef name) {
|
||||
script->sectionCommands.push_back(cmd);
|
||||
}
|
||||
|
||||
void ScriptParser::readNoCrossRefs(bool to) {
|
||||
expect("(");
|
||||
NoCrossRefCommand cmd{{}, to};
|
||||
while (!errorCount() && !consume(")"))
|
||||
cmd.outputSections.push_back(unquote(next()));
|
||||
if (cmd.outputSections.size() < 2)
|
||||
warn(getCurrentLocation() + ": ignored with fewer than 2 output sections");
|
||||
else
|
||||
script->noCrossRefs.push_back(std::move(cmd));
|
||||
}
|
||||
|
||||
void ScriptParser::addFile(StringRef s) {
|
||||
if (isUnderSysroot && s.starts_with("/")) {
|
||||
SmallString<128> pathData;
|
||||
|
@ -1943,6 +1943,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
||||
// have the headers, we can find out which sections they point to.
|
||||
setReservedSymbolSections();
|
||||
|
||||
if (script->noCrossRefs.size()) {
|
||||
llvm::TimeTraceScope timeScope("Check NOCROSSREFS");
|
||||
checkNoCrossRefs<ELFT>();
|
||||
}
|
||||
|
||||
{
|
||||
llvm::TimeTraceScope timeScope("Finalize synthetic sections");
|
||||
|
||||
|
@ -81,6 +81,9 @@ ELF Improvements
|
||||
(`#87530 <https://github.com/llvm/llvm-project/pull/87530>`_)
|
||||
* ``OUTPUT_FORMAT(binary)`` is now supported.
|
||||
(`#98837 <https://github.com/llvm/llvm-project/pull/98837>`_)
|
||||
* ``NOCROSSREFS`` and ``NOCRFOSSREFS_TO`` commands now supported to prohibit
|
||||
cross references between certain output sections.
|
||||
(`#98773 <https://github.com/llvm/llvm-project/pull/98773>`_)
|
||||
* Orphan placement is refined to prefer the last similar section when its rank <= orphan's rank.
|
||||
(`#94099 <https://github.com/llvm/llvm-project/pull/94099>`_)
|
||||
Non-alloc orphan sections are now placed at the end.
|
||||
|
99
lld/test/ELF/linkerscript/nocrossrefs.test
Normal file
99
lld/test/ELF/linkerscript/nocrossrefs.test
Normal file
@ -0,0 +1,99 @@
|
||||
# REQUIRES: x86
|
||||
# RUN: rm -rf %t && split-file %s %t && cd %t
|
||||
|
||||
# RUN: llvm-mc --triple=x86_64 -filetype=obj a.s -o a.o
|
||||
# RUN: llvm-mc --triple=x86_64 -filetype=obj data.s -o data.o
|
||||
# RUN: ld.lld a.o data.o -T 0.t 2>&1 | FileCheck %s --check-prefix=CHECK0 --implicit-check-not=warning:
|
||||
|
||||
# CHECK0: warning: 0.t:3: ignored with fewer than 2 output sections
|
||||
# CHECK0-NEXT: warning: 0.t:4: ignored with fewer than 2 output sections
|
||||
|
||||
# RUN: not ld.lld a.o data.o -T 1.t 2>&1 | FileCheck %s --check-prefix=CHECK1 --implicit-check-not=error:
|
||||
# CHECK1: error: a.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
|
||||
|
||||
## .text and .text1 are in two NOCROSSREFS commands. Violations are reported twice.
|
||||
# RUN: not ld.lld --threads=1 a.o data.o -T 2.t 2>&1 | FileCheck %s --check-prefix=CHECK2 --implicit-check-not=error:
|
||||
# CHECK2: error: a.o:(.text.start+0x6): prohibited cross reference from '.text' to '.text1' in '.text1'
|
||||
# CHECK2-NEXT: error: a.o:(.text.start+0x6): prohibited cross reference from '.text' to '.text1' in '.text1'
|
||||
# CHECK2-NEXT: error: a.o:(.text.start+0xb): prohibited cross reference from '.text' to 'foo2' in '.text2'
|
||||
# CHECK2-NEXT: error: a.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
|
||||
# CHECK2-NEXT: error: a.o:(.text.start+0x17): prohibited cross reference from '.text' to 'str1' in '.rodata'
|
||||
## .data occurs twice in the command, but the violation is only reported once.
|
||||
# CHECK2-NEXT: error: a.o:(.text1+0x1): prohibited cross reference from '.text1' to '_edata' in '.data'
|
||||
# CHECK2-NEXT: error: a.o:(.nonalloc+0x0): prohibited cross reference from '.nonalloc' to '.text' in '.text'
|
||||
# CHECK2-NEXT: error: a.o:(.nonalloc+0x10): prohibited cross reference from '.nonalloc' to 'data' in '.data'
|
||||
|
||||
# RUN: not ld.lld a.o data.o -T 3.t 2>&1 | FileCheck %s --check-prefix=CHECK3 --implicit-check-not=error:
|
||||
# CHECK3: error: a.o:(.nonalloc+0x0): prohibited cross reference from '.nonalloc' to '.text' in '.text'
|
||||
|
||||
#--- 0.t
|
||||
## Some cases that do not cause errors.
|
||||
abs = 42;
|
||||
NOCROSSREFS()
|
||||
NOCROSSREFS (.text)
|
||||
NOCROSSREFS( .text .text3 ); ## ; is ignored
|
||||
NOCROSSREFS_TO(.text .text2 .text3 .data );
|
||||
NOCROSSREFS_TO (.data .text2 .text3)
|
||||
|
||||
#--- 1.t
|
||||
abs = 42;
|
||||
NOCROSSREFS(.text ".data")
|
||||
|
||||
#--- 2.t
|
||||
abs = 42;
|
||||
NOCROSSREFS(.text ".text1" .text ".text1" )
|
||||
NOCROSSREFS(.text .text1 .text2 .data .rodata .data .nonalloc)
|
||||
|
||||
#--- 3.t
|
||||
abs = 42;
|
||||
NOCROSSREFS_TO(.text .text .text1 .text2 .data .nonalloc)
|
||||
|
||||
#--- err1.t
|
||||
NOCROSSREFS )
|
||||
|
||||
# RUN: not ld.lld a.o data.o -T err1.t 2>&1 | FileCheck %s --check-prefix=ERR1 --implicit-check-not=error:
|
||||
# ERR1: error: err1.t:1: ( expected, but got )
|
||||
|
||||
#--- err2.t
|
||||
NOCROSSREFS(.text
|
||||
|
||||
# RUN: not ld.lld a.o data.o -T err2.t 2>&1 | FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:
|
||||
# ERR2: error: err2.t:1: unexpected EOF
|
||||
|
||||
#--- a.s
|
||||
.global _start, foo1, foo2, foo3
|
||||
.section .text.start,"ax"
|
||||
_start:
|
||||
call _start
|
||||
call .text1
|
||||
call foo2
|
||||
movl data(%rip), %eax
|
||||
movl str1(%rip), %eax
|
||||
|
||||
.section .text1,"ax"
|
||||
foo1:
|
||||
call _edata
|
||||
|
||||
.section .text2,"ax"
|
||||
foo2:
|
||||
call foo3
|
||||
|
||||
.section .text3,"ax"
|
||||
foo3:
|
||||
call foo2
|
||||
|
||||
.section .rodata.str1.1,"aMS",@progbits,1
|
||||
str1:
|
||||
.asciz "abc"
|
||||
|
||||
.section .nonalloc,""
|
||||
.quad .text
|
||||
.quad .text3
|
||||
.quad data
|
||||
|
||||
#--- data.s
|
||||
.section .data,"aw"
|
||||
.globl data
|
||||
data:
|
||||
.byte 0
|
||||
.quad abs
|
Loading…
x
Reference in New Issue
Block a user