[lld] lld: add support for NOCROSSREFS(_TO) (PR #95714)
Pavel Skripkin via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 18 09:01:23 PDT 2024
https://github.com/pskrgag updated https://github.com/llvm/llvm-project/pull/95714
>From 42d3cec1c8b08669232bcb2520b8f873eb60edba Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sun, 16 Jun 2024 20:02:23 +0300
Subject: [PATCH] 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 <paskripkin at gmail.com>
---
lld/ELF/LinkerScript.cpp | 13 +++
lld/ELF/LinkerScript.h | 16 ++++
lld/ELF/Relocations.cpp | 49 +++++++++++
lld/ELF/Relocations.h | 1 +
lld/ELF/ScriptParser.cpp | 31 +++++++
lld/ELF/Writer.cpp | 3 +
lld/test/ELF/gnu-nocrossrefs.s | 150 +++++++++++++++++++++++++++++++++
7 files changed, 263 insertions(+)
create mode 100644 lld/test/ELF/gnu-nocrossrefs.s
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 3ba59c112b8a8..a7614986e559d 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -1714,3 +1714,16 @@ 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 std::find(outputSections.begin(), outputSections.end(),
+ section->name) != outputSections.end();
+}
+
+bool NoCrossRefList::matchesRefFromSection(const OutputSection *section) const {
+ return std::find(outputSections.begin(), outputSections.end(),
+ section->name) != outputSections.end();
+}
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 734d4e7498aa2..4dcf10e27a2ba 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -256,6 +256,19 @@ struct InsertCommand {
StringRef where;
};
+struct NoCrossRefList {
+ SmallVector<StringRef, 2> 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<StringRef> 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<OutputDesc *, 0> overwriteSections;
+ // Nocrossrefs sections.
+ SmallVector<NoCrossRefList, 0> noCrossRefLists;
+
// Sections that will be warned/errored by --orphan-handling.
SmallVector<const InputSectionBase *, 0> orphanSections;
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 2c02c2e572bfd..56636bf511106 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2358,3 +2358,52 @@ template void elf::scanRelocations<ELF32LE>();
template void elf::scanRelocations<ELF32BE>();
template void elf::scanRelocations<ELF64LE>();
template void elf::scanRelocations<ELF64BE>();
+
+static void forEachAllocInputSectionDescription(
+ ArrayRef<OutputSection *> outputSections,
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> fn) {
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_ALLOC))
+ continue;
+ for (SectionCommand *bc : os->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(bc))
+ fn(os, isd);
+ }
+}
+
+static void checkSectionNoCrossRefs(OutputSection *outSec,
+ InputSectionDescription *inSecDescr) {
+
+ std::string message = "";
+
+ for (const auto &list : script->noCrossRefLists) {
+ if (!list.matchesRefFromSection(outSec)) {
+ continue;
+ }
+
+ for (const auto &inSection : inSecDescr->sections) {
+ for (const auto &relocation : inSection->relocations) {
+ if (auto *destOutSec = relocation.sym->getOutputSection()) {
+
+ // Relocations from section to itself are allowed.
+ if (destOutSec->name != outSec->name &&
+ list.matchesRefToSection(destOutSec)) {
+
+ message +=
+ (inSection->getLocation(relocation.offset) +
+ ": prohibited cross reference from " + inSection->name.str() +
+ " to " + relocation.sym->getName().str() + " in " +
+ destOutSec->name.str());
+ error(message);
+
+ message.clear();
+ }
+ }
+ }
+ }
+ }
+}
+
+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 <class ELFT> 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..77c1e06102750 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 = false);
SymbolAssignment *readSymbolAssignment(StringRef name);
ByteCommand *readByteCommand(StringRef tok);
@@ -235,6 +236,32 @@ void ScriptParser::readVersionScriptCommand() {
}
}
+void ScriptParser::readNoCrossRefs(bool to) {
+ expect("(");
+
+ script->noCrossRefLists.push_back({});
+ auto &list = script->noCrossRefLists.back();
+
+ if (to && peek() != ")") {
+ StringRef toSection = next();
+
+ list.toSection = toSection;
+ }
+
+ while (!atEOF() && !errorCount() && peek() != ")") {
+ StringRef section = next();
+
+ list.outputSections.push_back(section);
+ }
+
+ // Discard meaningless lists
+ if ((to && list.outputSections.size() < 1) ||
+ (!to && list.outputSections.size() < 2))
+ script->noCrossRefLists.pop_back();
+
+ expect(")");
+}
+
void ScriptParser::readVersion() {
expect("{");
readVersionScriptCommand();
@@ -279,6 +306,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 <class ELFT> void Writer<ELFT>::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/gnu-nocrossrefs.s b/lld/test/ELF/gnu-nocrossrefs.s
new file mode 100644
index 0000000000000..479b1765e7be9
--- /dev/null
+++ b/lld/test/ELF/gnu-nocrossrefs.s
@@ -0,0 +1,150 @@
+// REQUIRES: x86
+// UNSUPPORTED: system-windows
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text .text1); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: not ld.lld %t.o -o %t --script %t.script 2>&1 | FileCheck -check-prefix=ERR %s
+// ERR: ld.lld: error: {{.*}}.o:(.text+{{.*}}): prohibited cross reference from .text to in .text1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS_TO(.text1 .text); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: not ld.lld %t.o -o %t --script %t.script 2>&1 | FileCheck -check-prefix=ERR1 %s
+// ERR1: ld.lld: error: {{.*}}.o:(.text+{{.*}}): prohibited cross reference from .text to in .text1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text1 .text .text2); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: not ld.lld %t.o -o %t --script %t.script 2>&1 | FileCheck -check-prefix=ERR2 %s
+// ERR2: ld.lld: error: {{.*}}.o:(.text+{{.*}}): prohibited cross reference from .text to in .text1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS_TO(.text .text1); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS_TO(); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS_TO(.text); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS_TO(.text2 .text); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text .text2); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text .text2); \
+// RUN: SECTIONS { \
+// RUN: foo = ABSOLUTE(.); \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text .text2); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: .bss : { *(.unused) } \
+// RUN: } " > %t.script
+// RUN: ld.lld %t.o -o %t --script %t.script 2>&1
+
+// RUN: llvm-mc -filetype=obj -o %t.o %s
+// RUN: echo "NOCROSSREFS(.text .bss); \
+// RUN: SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .text1 : { *(.text1) } \
+// RUN: .text2 : { *(.text2) } \
+// RUN: .bss : { *(.unused) } \
+// RUN: } " > %t.script
+// RUN: not ld.lld %t.o -o %t --script %t.script 2>&1 | FileCheck -check-prefix=ERR3 %s
+// ERR3: ld.lld: error: {{.*}}.o:(.text+{{.*}}): prohibited cross reference from .text to unused in .bss
+
+.global _start
+_start:
+ call test
+
+ .type unused, at object
+ .comm unused,4,4
+
+ .section .noalloc,"", at 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
More information about the llvm-commits
mailing list