diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 3ba59c112b8a8..f57e7ae7d4bf9 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1714,3 +1714,14 @@ bool LinkerScript::shouldAddProvideSym(StringRef symName) { Symbol *sym = symtab.find(symName); return sym && !sym->isDefined() && !sym->isCommon(); } + +bool NoCrossRefList::matchesRefToSection(const OutputSection *section) const { + if (toSection) + return toSection.value() == section->name; + + return llvm::is_contained(outputSections, section->name); +} + +bool NoCrossRefList::matchesRefFromSection(const OutputSection *section) const { + return llvm::is_contained(outputSections, section->name); +} diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 734d4e7498aa2..5f859a0b39eae 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -256,6 +256,19 @@ struct InsertCommand { StringRef where; }; +struct NoCrossRefList { + SmallVector outputSections; + + // See documentation for NOCROSSREFS and NOCROSSREFS_TO. When toSection is + // NONE outputSections are output section names that must not have any cross + // references between them. Otherwise, toSection is tosection name and + // outputSections are fromsections. + std::optional toSection; + + bool matchesRefFromSection(const OutputSection *section) const; + bool matchesRefToSection(const OutputSection *section) const; +}; + struct PhdrsCommand { StringRef name; unsigned type = llvm::ELF::PT_NULL; @@ -394,6 +407,9 @@ class LinkerScript final { // OutputSections specified by OVERWRITE_SECTIONS. SmallVector overwriteSections; + // OutputSections names specified by NOCROSSREFS(_TO). + SmallVector noCrossRefLists; + // Sections that will be warned/errored by --orphan-handling. SmallVector orphanSections; diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 2c02c2e572bfd..47081ad94b748 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -2358,3 +2358,45 @@ template void elf::scanRelocations(); template void elf::scanRelocations(); template void elf::scanRelocations(); template void elf::scanRelocations(); + +static void forEachAllocInputSectionDescription( + ArrayRef outputSections, + llvm::function_ref fn) { + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_ALLOC)) + continue; + for (SectionCommand *bc : os->commands) + if (auto *isd = dyn_cast(bc)) + fn(os, isd); + } +} + +static void checkSectionNoCrossRefs(OutputSection *outSec, + InputSectionDescription *inSecDescr) { + for (const auto &list : script->noCrossRefLists) { + if (!list.matchesRefFromSection(outSec)) + continue; + + for (const auto &inSection : inSecDescr->sections) { + for (const auto &relocation : inSection->relocations) { + auto *destOutSec = relocation.sym->getOutputSection(); + if (!destOutSec) + continue; + + // Relocations from section to itself are allowed. + if (destOutSec->name == outSec->name || + !list.matchesRefToSection(destOutSec)) + continue; + + error(inSection->getLocation(relocation.offset) + + ": prohibited cross reference from " + inSection->name.str() + + " to " + relocation.sym->getName().str() + " in " + + destOutSec->name.str()); + } + } + } +} + +void elf::checkNoCrossRefs() { + forEachAllocInputSectionDescription(outputSections, checkSectionNoCrossRefs); +} diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index b7b9c09e1b892..eabae26283c24 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -141,6 +141,7 @@ struct JumpInstrMod { // the diagnostics. template void scanRelocations(); void reportUndefinedSymbols(); +void checkNoCrossRefs(); void postScanRelocations(); void addGotEntry(Symbol &sym); diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index f90ce6fa74075..d92ea7b191bad 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -87,6 +87,7 @@ class ScriptParser final : ScriptLexer { void readTarget(); void readVersion(); void readVersionScriptCommand(); + void readNoCrossRefs(bool to); SymbolAssignment *readSymbolAssignment(StringRef name); ByteCommand *readByteCommand(StringRef tok); @@ -235,6 +236,26 @@ void ScriptParser::readVersionScriptCommand() { } } +void ScriptParser::readNoCrossRefs(bool to) { + expect("("); + + script->noCrossRefLists.push_back({}); + auto &list = script->noCrossRefLists.back(); + + if (to && peek() != ")") + list.toSection = next(); + + while (!atEOF() && !errorCount() && peek() != ")") + list.outputSections.push_back(next()); + + // Discard meaningless lists + if ((to && list.outputSections.empty()) || + (!to && list.outputSections.size() < 2)) + script->noCrossRefLists.pop_back(); + + expect(")"); +} + void ScriptParser::readVersion() { expect("{"); readVersionScriptCommand(); @@ -279,6 +300,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 { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index fe2e1900520a4..f8edd039c5779 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -309,6 +309,9 @@ template void Writer::run() { finalizeSections(); checkExecuteOnly(); + if (script->noCrossRefLists.size()) + checkNoCrossRefs(); + // If --compressed-debug-sections is specified, compress .debug_* sections. // Do it right now because it changes the size of output sections. for (OutputSection *sec : outputSections) diff --git a/lld/test/ELF/linkerscript/nocrossrefs.test b/lld/test/ELF/linkerscript/nocrossrefs.test new file mode 100644 index 0000000000000..cac12373c3939 --- /dev/null +++ b/lld/test/ELF/linkerscript/nocrossrefs.test @@ -0,0 +1,179 @@ +# REQUIRES: x86 +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: not ld.lld main.o -o main --script script1.ld 2>&1 | FileCheck -check-prefix=ERR %s +# ERR: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1 + +#--- script1.ld +NOCROSSREFS(.text .text1); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: not ld.lld main.o -o main --script script2.ld 2>&1 | FileCheck -check-prefix=ERR1 %s +# ERR1: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1 + +#--- script2.ld +NOCROSSREFS_TO(.text1 .text); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: not ld.lld main.o -o main --script script3.ld 2>&1 | FileCheck -check-prefix=ERR2 %s +# ERR2: {{.*}} error: main.o:(.text+0x6): prohibited cross reference from .text to in .text1 + +#--- script3.ld +NOCROSSREFS(.text1 .text .text2); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script4.ld 2>&1 + +#--- script4.ld +NOCROSSREFS_TO(.text .text1); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script5.ld 2>&1 + +#--- script5.ld +NOCROSSREFS_TO(); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script6.ld 2>&1 + +#--- script6.ld +NOCROSSREFS(); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script7.ld 2>&1 + +#--- script7.ld +NOCROSSREFS(.text); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script8.ld 2>&1 + +#--- script8.ld +NOCROSSREFS_TO(.text); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script9.ld 2>&1 + +#--- script9.ld +NOCROSSREFS_TO(.text2 .text); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script10.ld 2>&1 + +#--- script10.ld +NOCROSSREFS(.text .text2); +SECTIONS { + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script11.ld 2>&1 + +#--- script11.ld +NOCROSSREFS(.text .text2); +SECTIONS { + foo = ABSOLUTE(.); + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: ld.lld main.o -o main --script script12.ld 2>&1 + +#--- script12.ld +NOCROSSREFS(.text .text2); +SECTIONS { + foo = ABSOLUTE(.); + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } + .bss : { *(.unused) } +} + +# RUN: llvm-mc --triple=x86_64-unknown-linux -filetype=obj -o main.o main.s +# RUN: not ld.lld main.o -o main --script script13.ld 2>&1 | FileCheck -check-prefix=ERR3 %s +# ERR3: {{.*}} error: main.o:(.text+0x5): prohibited cross reference from .text to unused in .bss + +#--- script13.ld +NOCROSSREFS(.text .bss); +SECTIONS { + foo = ABSOLUTE(.); + .text : { *(.text) } + .text1 : { *(.text1) } + .text2 : { *(.text2) } + .bss : { *(.unused) } +} + +#--- main.s +.global _start +_start: + call test + + .type unused,@object + .comm unused,4,4 + + .section .noalloc,"",@progbits + .quad unused + +.section .text +test: + .reloc ., R_X86_64_32, unused + call test1 + +.section .text2 +test2: + .reloc ., R_X86_64_32, foo + nop + +.section .text1 +test1: + nop