-
Notifications
You must be signed in to change notification settings - Fork 14.6k
lld: add support for NOCROSSREFS(_TO) #95714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write If you have received no comments on your PR for a week, you can request a review If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-lld-elf Author: Pavel Skripkin (pskrgag) ChangesPatch 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 Full diff: https://github.com/llvm/llvm-project/pull/95714.diff 6 Files Affected:
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 734d4e7498aa2..5821b0fc587aa 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -256,6 +256,11 @@ struct InsertCommand {
StringRef where;
};
+struct CrossRefList {
+ SmallVector<StringRef, 2> refs;
+ bool firstOnly;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
@@ -394,6 +399,9 @@ class LinkerScript final {
// OutputSections specified by OVERWRITE_SECTIONS.
SmallVector<OutputDesc *, 0> overwriteSections;
+ // Nocrossrefs sections.
+ SmallVector<CrossRefList, 0> nocrossrefs;
+
// 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..400013adf53dd 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2358,3 +2358,62 @@ template void elf::scanRelocations<ELF32LE>();
template void elf::scanRelocations<ELF32BE>();
template void elf::scanRelocations<ELF64LE>();
template void elf::scanRelocations<ELF64BE>();
+
+static bool isNoCrefFromSection(const CrossRefList &list,
+ const OutputSection *section) {
+ const auto *begin =
+ list.firstOnly ? list.refs.begin() + 1 : list.refs.begin();
+
+ return std::find(begin, list.refs.end(), section->name) != list.refs.end();
+}
+
+static bool isNoCrefToSection(const CrossRefList &list,
+ const OutputSection *section) {
+ if (list.firstOnly)
+ return list.refs[0] == section->name;
+
+ return std::find(list.refs.begin(), list.refs.end(), section->name) !=
+ list.refs.end();
+}
+
+void elf::checkNoCrossRefs() {
+ // Basic brute-force algorithm, since in reality NOCROSSRES lists are quite
+ // small.
+ //
+ // Idea is to traverse all relocations in all sections and report if
+ // prohibited reference was found. Note that NOCROSSREFS works with output section
+ // names.
+ for (ELFFileBase *file : ctx.objectFiles) {
+ auto sections = file->getSections();
+ std::string message = "";
+
+ for (size_t i = 0; i < sections.size(); ++i) {
+ if (sections[i]) {
+ StringRef sectionName = sections[i]->name;
+
+ if (const auto *outSec = dyn_cast_or_null<OutputSection>(sections[i]->parent)) {
+ for (const auto &refs : script->nocrossrefs) {
+ if (isNoCrefFromSection(refs, outSec)) {
+
+ for (const auto &j : sections[i]->relocations) {
+ if (auto *def = dyn_cast<Defined>(j.sym)) {
+ const auto *outSecSym =
+ cast<InputSection>(def->section)->getParent();
+
+ if (isNoCrefToSection(refs, outSecSym)) {
+ message +=
+ (sections[i]->getLocation(j.offset) +
+ ": prohibited cross reference from " +
+ sectionName.str() + " to " + def->getName().str() +
+ " in " + outSecSym->name.str());
+ error(message);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
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..0c14f43fc5f05 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,21 @@ void ScriptParser::readVersionScriptCommand() {
}
}
+void ScriptParser::readNoCrossRefs(bool to) {
+ expect("(");
+
+ script->nocrossrefs.push_back({});
+
+ script->nocrossrefs.back().firstOnly = to;
+
+ while (!atEOF() && !errorCount() && peek() != ")") {
+ StringRef section = next();
+ script->nocrossrefs.back().refs.push_back(section);
+ }
+
+ expect(")");
+}
+
void ScriptParser::readVersion() {
expect("{");
readVersionScriptCommand();
@@ -279,6 +295,10 @@ void ScriptParser::readLinkerScript() {
readTarget();
} else if (tok == "VERSION") {
readVersion();
+ } else if (tok == "NOCROSSREFS") {
+ readNoCrossRefs();
+ } else if (tok == "NOCROSSREFS_TO") {
+ readNoCrossRefs(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..9466d383607c4 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->nocrossrefs.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..67d98482cad78
--- /dev/null
+++ b/lld/test/ELF/gnu-nocrossrefs.s
@@ -0,0 +1,110 @@
+// REQUIRES: x86
+
+// 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
+
+.global _start
+_start:
+ nop
+
+.section .text
+test:
+ call test1
+
+.section .text2
+test2:
+ nop
+
+.section .text1
+test1:
+ nop
|
@llvm/pr-subscribers-lld Author: Pavel Skripkin (pskrgag) ChangesPatch 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 Full diff: https://github.com/llvm/llvm-project/pull/95714.diff 6 Files Affected:
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 734d4e7498aa2..5821b0fc587aa 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -256,6 +256,11 @@ struct InsertCommand {
StringRef where;
};
+struct CrossRefList {
+ SmallVector<StringRef, 2> refs;
+ bool firstOnly;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
@@ -394,6 +399,9 @@ class LinkerScript final {
// OutputSections specified by OVERWRITE_SECTIONS.
SmallVector<OutputDesc *, 0> overwriteSections;
+ // Nocrossrefs sections.
+ SmallVector<CrossRefList, 0> nocrossrefs;
+
// 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..400013adf53dd 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2358,3 +2358,62 @@ template void elf::scanRelocations<ELF32LE>();
template void elf::scanRelocations<ELF32BE>();
template void elf::scanRelocations<ELF64LE>();
template void elf::scanRelocations<ELF64BE>();
+
+static bool isNoCrefFromSection(const CrossRefList &list,
+ const OutputSection *section) {
+ const auto *begin =
+ list.firstOnly ? list.refs.begin() + 1 : list.refs.begin();
+
+ return std::find(begin, list.refs.end(), section->name) != list.refs.end();
+}
+
+static bool isNoCrefToSection(const CrossRefList &list,
+ const OutputSection *section) {
+ if (list.firstOnly)
+ return list.refs[0] == section->name;
+
+ return std::find(list.refs.begin(), list.refs.end(), section->name) !=
+ list.refs.end();
+}
+
+void elf::checkNoCrossRefs() {
+ // Basic brute-force algorithm, since in reality NOCROSSRES lists are quite
+ // small.
+ //
+ // Idea is to traverse all relocations in all sections and report if
+ // prohibited reference was found. Note that NOCROSSREFS works with output section
+ // names.
+ for (ELFFileBase *file : ctx.objectFiles) {
+ auto sections = file->getSections();
+ std::string message = "";
+
+ for (size_t i = 0; i < sections.size(); ++i) {
+ if (sections[i]) {
+ StringRef sectionName = sections[i]->name;
+
+ if (const auto *outSec = dyn_cast_or_null<OutputSection>(sections[i]->parent)) {
+ for (const auto &refs : script->nocrossrefs) {
+ if (isNoCrefFromSection(refs, outSec)) {
+
+ for (const auto &j : sections[i]->relocations) {
+ if (auto *def = dyn_cast<Defined>(j.sym)) {
+ const auto *outSecSym =
+ cast<InputSection>(def->section)->getParent();
+
+ if (isNoCrefToSection(refs, outSecSym)) {
+ message +=
+ (sections[i]->getLocation(j.offset) +
+ ": prohibited cross reference from " +
+ sectionName.str() + " to " + def->getName().str() +
+ " in " + outSecSym->name.str());
+ error(message);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
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..0c14f43fc5f05 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,21 @@ void ScriptParser::readVersionScriptCommand() {
}
}
+void ScriptParser::readNoCrossRefs(bool to) {
+ expect("(");
+
+ script->nocrossrefs.push_back({});
+
+ script->nocrossrefs.back().firstOnly = to;
+
+ while (!atEOF() && !errorCount() && peek() != ")") {
+ StringRef section = next();
+ script->nocrossrefs.back().refs.push_back(section);
+ }
+
+ expect(")");
+}
+
void ScriptParser::readVersion() {
expect("{");
readVersionScriptCommand();
@@ -279,6 +295,10 @@ void ScriptParser::readLinkerScript() {
readTarget();
} else if (tok == "VERSION") {
readVersion();
+ } else if (tok == "NOCROSSREFS") {
+ readNoCrossRefs();
+ } else if (tok == "NOCROSSREFS_TO") {
+ readNoCrossRefs(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..9466d383607c4 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->nocrossrefs.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..67d98482cad78
--- /dev/null
+++ b/lld/test/ELF/gnu-nocrossrefs.s
@@ -0,0 +1,110 @@
+// REQUIRES: x86
+
+// 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
+
+.global _start
+_start:
+ nop
+
+.section .text
+test:
+ call test1
+
+.section .text2
+test2:
+ nop
+
+.section .text1
+test1:
+ nop
|
You can test this locally with the following command:git-clang-format --diff 253c28fa829cee0104c2fc59ed1a958980b5138c d7baadb71a0590e1950cd4eb744103ece8690eb3 -- lld/ELF/LinkerScript.h lld/ELF/Relocations.cpp lld/ELF/Relocations.h lld/ELF/ScriptParser.cpp lld/ELF/Writer.cpp View the diff from clang-format here.diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 400013adf5..008b8f7efb 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2381,8 +2381,8 @@ void elf::checkNoCrossRefs() {
// small.
//
// Idea is to traverse all relocations in all sections and report if
- // prohibited reference was found. Note that NOCROSSREFS works with output section
- // names.
+ // prohibited reference was found. Note that NOCROSSREFS works with output
+ // section names.
for (ELFFileBase *file : ctx.objectFiles) {
auto sections = file->getSections();
std::string message = "";
@@ -2391,7 +2391,8 @@ void elf::checkNoCrossRefs() {
if (sections[i]) {
StringRef sectionName = sections[i]->name;
- if (const auto *outSec = dyn_cast_or_null<OutputSection>(sections[i]->parent)) {
+ if (const auto *outSec =
+ dyn_cast_or_null<OutputSection>(sections[i]->parent)) {
for (const auto &refs : script->nocrossrefs) {
if (isNoCrefFromSection(refs, outSec)) {
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for submitting this. I'd like to see NOCROSSREFS support as OVERLAY is difficult to use without it.
I've not had a lot of time to go through the code in detail, but I've put some initial comments there based on a quick read. My apologies if I've misinterpreted anything.
lld/ELF/Relocations.cpp
Outdated
cast<InputSection>(def->section)->getParent(); | ||
|
||
if (isNoCrefToSection(refs, outSecSym)) { | ||
message += |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could give you an awful lot of error messsages for large sections with lots of relocations. Does GNU ld give an error per relocation or only one per output section cross-reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GNU ld reports error per-symbol reference. I.e if symbol is referenced 2+ times in one place, it will be reported once.
Will try to modify reporting scheme, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I misunderstood ld output.
For input like
int __section(bla) test(void)
{
return 0;
}
int test1(void)
{
test();
return test();
}
GNU ld built locally from master outputs following:
/home/paskripkin/Documents/git/binutils-gdb/ld/ld-new: test.o: in function `test1':
test.c:(.text+0x5): prohibited cross reference from .text to `test' in .bla
/home/paskripkin/Documents/git/binutils-gdb/ld/ld-new: test.c:(.text+0xa): prohibited cross reference from .text to `test' in .bla
So looks like it indeed reports per-relocation.
lld/ELF/ScriptParser.cpp
Outdated
script->nocrossrefs.back().firstOnly = to; | ||
|
||
while (!atEOF() && !errorCount() && peek() != ")") { | ||
StringRef section = next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we do any checking that the token is a valid OutputSection name?
Although not possible at this point, it may be worth validating that the names given actually match the names of OutputSections in the file (probably warning if it doesn't match).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GNU ld silently ignores unknown section names. Maybe this is because some sections may appear only depending on some build options and warning may abort build process with --fatal-warnings
specified.
Anyway, i don't have strong opinion here, I just think it might be a good idea to be fully compatible with GNU ld.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For an initial version I agree that being compatible with GNU ld is worthwhile.
Oh, forgot to mention this with the code review. Big +1 to having |
@mysterymath @smithp35 Thank you a lot for a review! I hope, I understood correctly your comments. I apologize for first "dirty" version, I should have worked more with names and comments. |
lld/ELF/Relocations.cpp
Outdated
|
||
for (const auto &inSection : inSecDescr->sections) { | ||
for (const auto &relocation : inSection->relocations) { | ||
if (auto *destOutSec = relocation.sym->getOutputSection()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (auto *destOutSec = relocation.sym->getOutputSection()) { | |
auto *destOutSec = relocation.sym->getOutputSection()); | |
if (!destOutSec) | |
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update, looks a lot better now, I've not got any more significant comments.
lld/ELF/ScriptParser.cpp
Outdated
script->nocrossrefs.back().firstOnly = to; | ||
|
||
while (!atEOF() && !errorCount() && peek() != ")") { | ||
StringRef section = next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For an initial version I agree that being compatible with GNU ld is worthwhile.
Thank you for a review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with this change on my end too, pending a few style nits.
lld/test/ELF/gnu-nocrossrefs.s
Outdated
@@ -0,0 +1,150 @@ | |||
// REQUIRES: x86 | |||
// UNSUPPORTED: system-windows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is windows prohibited? The test doesn't seem to use backslashes.
A better place is linkerscript/nocrossrefs.test
using rm -rf %t && split-file %s %t && cd %t
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason this test failed on windows with smth like unsupported file type gnu-crossrefs.o
during link stage.
Maybe you have an idea why that happened?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rewrote test and dropped windows part. Let's see if it will work
bbc0b3b
to
fe3f494
Compare
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Omit ld.lld:
. We don't test the string (which might change) before error:
If you use the # RUN: rm -rf %t && split-file %s %t && cd %t
pattern, which is popular in new tests, you can replace {{.*}}
with the exact name.
The section offset is important. Don't omit it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried split-file pattern, but it has problems on windows.
In this test split-file + rm should be run before each test, since each script file has its own NOCROSSREFS. On windows this fails, since it's not possible to remove cwd.
I will add checks for offsets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this test split-file + rm should be run before each test, since each script file has its own NOCROSSREFS. On windows this fails, since it's not possible to remove cwd.
You currently create scripts with echo. With split-file, you place scripts as split-file parts. See addr.test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess, I get it, thanks for the tip.
Please check newest version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to bother, but did you have time to look into new version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay sorry to bother you, but did you have time to check newest version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaskRay sorry to bother you again, but did you have time to check newest version?
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: llvm#41825 Signed-off-by: Pavel Skripkin <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for implementing the feature and sorry for the delay.
I think I would leave a large number of style comments. I realized that I probably should just provide the fixes in my branch for you to cherry pick.
# 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove -unknown-linux
. make it clear this doesn't use Linux specific features.
# 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-o
is unneeded when using not ld.lld
(Since we have done cd %t
, the unused output file will not clutter the current directory)
|
||
# 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something is omitted after to
@@ -235,6 +236,26 @@ void ScriptParser::readVersionScriptCommand() { | |||
} | |||
} | |||
|
|||
void ScriptParser::readNoCrossRefs(bool to) { | |||
expect("("); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our code style is quite terse. We only blank lines when there is a significant "pause" need.
@@ -2358,3 +2358,45 @@ template void elf::scanRelocations<ELF32LE>(); | |||
template void elf::scanRelocations<ELF32BE>(); | |||
template void elf::scanRelocations<ELF64LE>(); | |||
template void elf::scanRelocations<ELF64BE>(); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't add code after explicit template instantiations. I would place the checks to LinkerScript.cpp.
Relocations.cpp code should minimize uses of outputSections
.
Sorry, with closer inspection, I believe a large of portion of code and tests need reworking. I think it's best if I create a different PR, but I'll give you credits as a co-author. My current draft is at https://github.com/MaskRay/llvm-project/tree/nocrossrefs , which still needs a lot of work. |
!list.matchesRefToSection(destOutSec)) | ||
continue; | ||
|
||
error(inSection->getLocation(relocation.offset) + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my branch I'll use this:
+ std::string toSymName;
+ if (!r.sym->isSection())
+ toSymName = toString(*r.sym);
+ else if (auto *d = dyn_cast<Defined>(r.sym))
+ toSymName = d->section->name;
+ errorOrWarn(sec->getLocation(r.offset) +
+ ": prohibited cross reference to '" + toSymName + "' in " +
+ dstOsec->name.str());
I think from section
can be omitted since getLocation
carries the location information.
#--- script1.ld | ||
NOCROSSREFS(.text .text1); | ||
SECTIONS { | ||
.text : { *(.text) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll clean up the tests. We actually don't need SECTIONS
since the default orphan section placement does the right thing. We need to make these sections SHF_ALLOC, though ("a"
).
ArrayRef<OutputSection *> outputSections, | ||
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> fn) { | ||
for (OutputSection *os : outputSections) { | ||
if (!(os->flags & SHF_ALLOC)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GNU ld reports violations due to non-SHF_ALLOC sections. I'll fix it in my branch.
Note: |
Ok, I am fine with that. Thanks for guidance anyway, maybe next time I would do better. =)
Should I just close this PR, or you'd like to keep it open for a while? |
I think this feature requires more experience with the internals. Perhaps it's not the ideal starter project. However, I appreciate you suggesting the patch! I think the PR can be closed. Created #98773 |
Sure. Thank you for patience and explanations! I learned a lot =) |
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
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
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