[lld] [ELF] Handle INCLUDE like a call stack (PR #193427)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 22 18:59:46 PDT 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/193427
>From 6a20a9371a8e03d373fb2c939f2c43f0fcb7b878 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 21 Apr 2026 00:04:52 -0700
Subject: [PATCH 1/3] [ELF] Handle INCLUDE like a call stack
The lexer maintains a stack of buffers, which allows a construct
started in an INCLUDE'd file to be closed by the parent. This produces
spurious acceptance of malformed scripts (e.g. a bare assignment with
no trailing `;` in the include, terminated by the parent's `;` after
`INCLUDE`) and undefined-behavior span computations in
`readAssignment`'s `commandString` (issue #190376).
Force each INCLUDE to fully parse its own content, similar to a call
stack frame. `ScriptLexer::lex` no longer auto-pops on EOF; the
`buffers` member is gone. `readInclude` takes a `function_ref<void()>`
callback, and the four call sites (top-level, SECTIONS, output
section, MEMORY) pass a context-appropriate parser.
With this, each buffer contains complete parser structures by
construction, so the `[oldS, curTok)` pointer range in
`readAssignment` no longer needs a guard.
---
lld/ELF/ScriptLexer.cpp | 28 +--
lld/ELF/ScriptLexer.h | 7 +-
lld/ELF/ScriptParser.cpp | 219 ++++++++++--------
.../ELF/linkerscript/include-mid-construct.s | 18 ++
lld/test/ELF/linkerscript/memory-include.test | 17 ++
.../linkerscript/output-section-include.test | 9 +
.../ELF/linkerscript/section-include.test | 17 ++
7 files changed, 198 insertions(+), 117 deletions(-)
create mode 100644 lld/test/ELF/linkerscript/include-mid-construct.s
diff --git a/lld/ELF/ScriptLexer.cpp b/lld/ELF/ScriptLexer.cpp
index c16a70ba1ce8f..4f4610434a534 100644
--- a/lld/ELF/ScriptLexer.cpp
+++ b/lld/ELF/ScriptLexer.cpp
@@ -52,7 +52,7 @@ ScriptLexer::Buffer::Buffer(Ctx &ctx, MemoryBufferRef mb)
}
ScriptLexer::ScriptLexer(Ctx &ctx, MemoryBufferRef mb)
- : ctx(ctx), curBuf(ctx, mb), mbs(1, mb) {
+ : ctx(ctx), curBuf(ctx, mb) {
activeFilenames.insert(mb.getBufferIdentifier());
}
@@ -93,15 +93,10 @@ void ScriptLexer::lex() {
StringRef &s = curBuf.s;
s = skipSpace(s);
if (s.empty()) {
- // If this buffer is from an INCLUDE command, switch to the "return
- // value"; otherwise, mark EOF.
- if (buffers.empty()) {
- eof = true;
- return;
- }
- activeFilenames.erase(curBuf.filename);
- curBuf = buffers.pop_back_val();
- continue;
+ // If this buffer is from an INCLUDE, the caller is responsible for
+ // popping to the parent buffer.
+ eof = true;
+ return;
}
curTokState = lexState;
@@ -276,16 +271,7 @@ ScriptLexer::Token ScriptLexer::till(StringRef tok) {
return {};
}
-// Returns true if S encloses T.
-static bool encloses(StringRef s, StringRef t) {
- return s.bytes_begin() <= t.bytes_begin() && t.bytes_end() <= s.bytes_end();
-}
-
MemoryBufferRef ScriptLexer::getCurrentMB() {
- // Find input buffer containing the current token.
- assert(!mbs.empty());
- for (MemoryBufferRef mb : mbs)
- if (encloses(mb.getBuffer(), curBuf.s))
- return mb;
- llvm_unreachable("getCurrentMB: failed to find a token");
+ return MemoryBufferRef(StringRef(curBuf.begin, curBuf.s.end() - curBuf.begin),
+ curBuf.filename);
}
diff --git a/lld/ELF/ScriptLexer.h b/lld/ELF/ScriptLexer.h
index ba49155b9dc88..9fce5734edea1 100644
--- a/lld/ELF/ScriptLexer.h
+++ b/lld/ELF/ScriptLexer.h
@@ -14,7 +14,6 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBufferRef.h"
-#include <vector>
namespace lld::elf {
struct Ctx;
@@ -34,9 +33,9 @@ class ScriptLexer {
Buffer(Ctx &ctx, MemoryBufferRef mb);
};
Ctx &ctx;
- // The current buffer and parent buffers due to INCLUDE.
+ // The currently lexed buffer. INCLUDE runs a nested parse on a new `Buffer`,
+ // similar to a call stack frame.
Buffer curBuf;
- SmallVector<Buffer, 0> buffers;
// Used to detect INCLUDE() cycles.
llvm::DenseSet<StringRef> activeFilenames;
@@ -82,8 +81,6 @@ class ScriptLexer {
std::string getCurrentLocation();
MemoryBufferRef getCurrentMB();
- std::vector<MemoryBufferRef> mbs;
-
private:
StringRef getLine();
size_t getColumnNumber();
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 508d8f40f5304..8b03da52950d4 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -59,9 +59,11 @@ class ScriptParser final : ScriptLexer {
void readEntry();
void readExtern();
void readGroup();
- void readInclude();
+ void readInclude(llvm::function_ref<void()> parse);
+ void readIncludedBody(llvm::function_ref<void(StringRef)> stmt);
void readInput();
void readMemory();
+ void readMemoryStmt(StringRef tok);
void readOutput();
void readOutputArch();
void readOutputFormat();
@@ -70,6 +72,8 @@ class ScriptParser final : ScriptLexer {
void readRegionAlias();
void readSearchDir();
void readSections();
+ void readSectionsStmt(SmallVectorImpl<SectionCommand *> &v, StringRef tok);
+ void readOutputSectionStmt(OutputSection &osec, StringRef tok);
void readTarget();
void readVersion();
void readVersionScriptCommand();
@@ -249,7 +253,7 @@ void ScriptParser::readLinkerScript() {
} else if (tok == "GROUP") {
readGroup();
} else if (tok == "INCLUDE") {
- readInclude();
+ readInclude([&] { readLinkerScript(); });
} else if (tok == "INPUT") {
readInput();
} else if (tok == "MEMORY") {
@@ -392,22 +396,41 @@ void ScriptParser::readGroup() {
++ctx.driver.nextGroupId;
}
-void ScriptParser::readInclude() {
+void ScriptParser::readInclude(llvm::function_ref<void()> parse) {
StringRef name = readName();
if (!activeFilenames.insert(name).second) {
setError("there is a cycle in linker script INCLUDEs");
return;
}
- if (std::optional<std::string> path = searchScript(ctx, name)) {
- if (std::optional<MemoryBufferRef> mb = readFile(ctx, *path)) {
- buffers.push_back(curBuf);
- curBuf = Buffer(ctx, *mb);
- mbs.push_back(*mb);
- }
+ std::optional<std::string> path = searchScript(ctx, name);
+ if (!path) {
+ setError("cannot find linker script " + name);
+ return;
+ }
+ std::optional<MemoryBufferRef> mb = readFile(ctx, *path);
+ if (!mb)
return;
+
+ SaveAndRestore savedBuf(curBuf, Buffer(ctx, *mb));
+ SaveAndRestore savedPrevTok(prevTok, StringRef());
+ SaveAndRestore savedPrevTokLine(prevTokLine, size_t(1));
+ parse();
+
+ // parse() leaves `eof` true on normal completion; reset so the parent
+ // buffer continues to be lexed.
+ eof = false;
+ activeFilenames.erase(name);
+}
+
+// Drive `stmt` until EOF of the nested buffer.
+void ScriptParser::readIncludedBody(llvm::function_ref<void(StringRef)> stmt) {
+ while (!atEOF()) {
+ StringRef tok = next();
+ if (atEOF())
+ return;
+ stmt(tok);
}
- setError("cannot find linker script " + name);
}
void ScriptParser::readInput() {
@@ -655,26 +678,8 @@ void ScriptParser::readOverwriteSections() {
void ScriptParser::readSections() {
expect("{");
SmallVector<SectionCommand *, 0> v;
- while (auto tok = till("}")) {
- if (tok == "OVERLAY") {
- for (SectionCommand *cmd : readOverlay())
- v.push_back(cmd);
- continue;
- }
- if (tok == "CLASS") {
- v.push_back(readSectionClassDescription());
- continue;
- }
- if (tok == "INCLUDE") {
- readInclude();
- continue;
- }
-
- if (SectionCommand *cmd = readAssignment(tok))
- v.push_back(cmd);
- else
- v.push_back(readOutputSectionDescription(tok));
- }
+ while (auto tok = till("}"))
+ readSectionsStmt(v, tok);
// If DATA_SEGMENT_RELRO_END is absent, for sections after DATA_SEGMENT_ALIGN,
// the relro fields should be cleared.
@@ -705,6 +710,29 @@ void ScriptParser::readSections() {
ctx.script->insertCommands.push_back({std::move(names), isAfter, where});
}
+void ScriptParser::readSectionsStmt(SmallVectorImpl<SectionCommand *> &v,
+ StringRef tok) {
+ if (tok == "OVERLAY") {
+ for (SectionCommand *cmd : readOverlay())
+ v.push_back(cmd);
+ return;
+ }
+ if (tok == "CLASS") {
+ v.push_back(readSectionClassDescription());
+ return;
+ }
+ if (tok == "INCLUDE") {
+ readInclude([&] {
+ readIncludedBody([&](StringRef t) { readSectionsStmt(v, t); });
+ });
+ return;
+ }
+ if (SectionCommand *cmd = readAssignment(tok))
+ v.push_back(cmd);
+ else
+ v.push_back(readOutputSectionDescription(tok));
+}
+
void ScriptParser::readTarget() {
// TARGET(foo) is an alias for "--format foo". Unlike GNU linkers,
// we accept only a limited set of BFD names (i.e. "elf" or "binary")
@@ -1033,44 +1061,8 @@ OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) {
osec->constraint = ConstraintKind::ReadWrite;
expect("{");
- while (auto tok = till("}")) {
- if (tok == ";") {
- // Empty commands are allowed. Do nothing here.
- } else if (SymbolAssignment *assign = readAssignment(tok)) {
- osec->commands.push_back(assign);
- } else if (ByteCommand *data = readByteCommand(tok)) {
- osec->commands.push_back(data);
- } else if (tok == "CONSTRUCTORS") {
- // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
- // by name. This is for very old file formats such as ECOFF/XCOFF.
- // For ELF, we should ignore.
- } else if (tok == "FILL") {
- // We handle the FILL command as an alias for =fillexp section attribute,
- // which is different from what GNU linkers do.
- // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
- if (peek() != "(")
- setError("( expected, but got " + peek());
- osec->filler = readFill();
- } else if (tok == "SORT") {
- readSort();
- } else if (tok == "INCLUDE") {
- readInclude();
- } else if (tok == "(" || tok == ")") {
- setError("expected filename pattern");
- } else if (peek() == "(") {
- osec->commands.push_back(readInputSectionDescription(tok));
- } else {
- // We have a file name and no input sections description. It is not a
- // commonly used syntax, but still acceptable. In that case, all sections
- // from the file will be included.
- // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not
- // handle this case here as it will already have been matched by the
- // case above.
- auto *isd = make<InputSectionDescription>(tok);
- isd->sectionPatterns.push_back({{}, StringMatcher("*")});
- osec->commands.push_back(isd);
- }
- }
+ while (auto tok = till("}"))
+ readOutputSectionStmt(*osec, tok);
if (consume(">"))
osec->memoryRegionName = std::string(readName());
@@ -1100,6 +1092,47 @@ OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) {
return cmd;
}
+void ScriptParser::readOutputSectionStmt(OutputSection &osec, StringRef tok) {
+ if (tok == ";") {
+ // Empty commands are allowed. Do nothing here.
+ } else if (SymbolAssignment *assign = readAssignment(tok)) {
+ osec.commands.push_back(assign);
+ } else if (ByteCommand *data = readByteCommand(tok)) {
+ osec.commands.push_back(data);
+ } else if (tok == "CONSTRUCTORS") {
+ // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
+ // by name. This is for very old file formats such as ECOFF/XCOFF.
+ // For ELF, we should ignore.
+ } else if (tok == "FILL") {
+ // We handle the FILL command as an alias for =fillexp section attribute,
+ // which is different from what GNU linkers do.
+ // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
+ if (peek() != "(")
+ setError("( expected, but got " + peek());
+ osec.filler = readFill();
+ } else if (tok == "SORT") {
+ readSort();
+ } else if (tok == "INCLUDE") {
+ readInclude([&] {
+ readIncludedBody([&](StringRef t) { readOutputSectionStmt(osec, t); });
+ });
+ } else if (tok == "(" || tok == ")") {
+ setError("expected filename pattern");
+ } else if (peek() == "(") {
+ osec.commands.push_back(readInputSectionDescription(tok));
+ } else {
+ // We have a file name and no input sections description. It is not a
+ // commonly used syntax, but still acceptable. In that case, all sections
+ // from the file will be included.
+ // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not
+ // handle this case here as it will already have been matched by the
+ // case above.
+ auto *isd = make<InputSectionDescription>(tok);
+ isd->sectionPatterns.push_back({{}, StringMatcher("*")});
+ osec.commands.push_back(isd);
+ }
+}
+
// Reads a `=<fillexp>` expression and returns its value as a big-endian number.
// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html
// We do not support using symbols in such expressions.
@@ -1835,32 +1868,36 @@ Expr ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
// MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... }
void ScriptParser::readMemory() {
expect("{");
- while (auto tok = till("}")) {
- if (tok == "INCLUDE") {
- readInclude();
- continue;
- }
-
- uint32_t flags = 0;
- uint32_t invFlags = 0;
- uint32_t negFlags = 0;
- uint32_t negInvFlags = 0;
- if (consume("(")) {
- readMemoryAttributes(flags, invFlags, negFlags, negInvFlags);
- expect(")");
- }
- expect(":");
+ while (auto tok = till("}"))
+ readMemoryStmt(tok);
+}
- Expr origin = readMemoryAssignment("ORIGIN", "org", "o");
- expect(",");
- Expr length = readMemoryAssignment("LENGTH", "len", "l");
+void ScriptParser::readMemoryStmt(StringRef tok) {
+ if (tok == "INCLUDE") {
+ readInclude(
+ [&] { readIncludedBody([&](StringRef t) { readMemoryStmt(t); }); });
+ return;
+ }
- // Add the memory region to the region map.
- MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, invFlags,
- negFlags, negInvFlags);
- if (!ctx.script->memoryRegions.insert({tok, mr}).second)
- setError("region '" + tok + "' already defined");
+ uint32_t flags = 0;
+ uint32_t invFlags = 0;
+ uint32_t negFlags = 0;
+ uint32_t negInvFlags = 0;
+ if (consume("(")) {
+ readMemoryAttributes(flags, invFlags, negFlags, negInvFlags);
+ expect(")");
}
+ expect(":");
+
+ Expr origin = readMemoryAssignment("ORIGIN", "org", "o");
+ expect(",");
+ Expr length = readMemoryAssignment("LENGTH", "len", "l");
+
+ // Add the memory region to the region map.
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, invFlags,
+ negFlags, negInvFlags);
+ if (!ctx.script->memoryRegions.insert({tok, mr}).second)
+ setError("region '" + tok + "' already defined");
}
// This function parses the attributes used to match against section
diff --git a/lld/test/ELF/linkerscript/include-mid-construct.s b/lld/test/ELF/linkerscript/include-mid-construct.s
new file mode 100644
index 0000000000000..951c72fc93860
--- /dev/null
+++ b/lld/test/ELF/linkerscript/include-mid-construct.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+
+## A stray ';' in the parent after INCLUDE cannot complete the inner assignment.
+# RUN: not ld.lld a.o -T top.lds 2>&1 | FileCheck %s --check-prefix=TOP
+# TOP: error: inc-top.lds:1: unexpected EOF
+
+#--- top.lds
+INCLUDE "inc-top.lds";
+
+#--- inc-top.lds
+foo = 1
+
+#--- a.s
+.globl _start
+_start:
+ ret
diff --git a/lld/test/ELF/linkerscript/memory-include.test b/lld/test/ELF/linkerscript/memory-include.test
index 6c6a89e45787a..2d938d6c7fa93 100644
--- a/lld/test/ELF/linkerscript/memory-include.test
+++ b/lld/test/ELF/linkerscript/memory-include.test
@@ -17,6 +17,17 @@
# EMPTY: LOAD {{.*}} 0x0000000000001000 0x0000000000001000 {{.*}} R E
# EMPTY-NEXT: LOAD {{.*}} 0x0000000000002000 0x0000000000002000 {{.*}} RW
+## A region declaration truncated mid-expression cannot be completed by the
+## parent MEMORY { ... }.
+# RUN: cp trunc.lds inc.lds
+# RUN: not ld.lld -T a.lds a.o 2>&1 | FileCheck %s --check-prefix=TRUNC
+# TRUNC: error: inc.lds:1: unexpected EOF
+
+## A stray '}' in the include cannot close the parent MEMORY { ... }.
+# RUN: cp brace.lds inc.lds
+# RUN: not ld.lld -T a.lds a.o 2>&1 | FileCheck %s --check-prefix=BRACE
+# BRACE: error: inc.lds:1: unexpected EOF
+
#--- a.s
.section .text,"ax"
.global _start
@@ -54,3 +65,9 @@ SECTIONS {
}
#--- inc-empty.lds
+
+#--- trunc.lds
+RAM3 : ORIGIN = 0x4000, LENGTH
+
+#--- brace.lds
+}
diff --git a/lld/test/ELF/linkerscript/output-section-include.test b/lld/test/ELF/linkerscript/output-section-include.test
index 6cdf07ca7007e..a6392b315870c 100644
--- a/lld/test/ELF/linkerscript/output-section-include.test
+++ b/lld/test/ELF/linkerscript/output-section-include.test
@@ -16,6 +16,12 @@
# RUN: llvm-objdump --section-headers a.out | FileCheck %s --check-prefix=CHECK2
# CHECK2: .data 00000010 0000000000002000 DATA
+## A BYTE() with an unclosed paren in the include cannot be completed by the
+## parent output-section body.
+# RUN: cp trunc.lds inc.lds
+# RUN: not ld.lld -T a.lds a.o 2>&1 | FileCheck %s --check-prefix=TRUNC
+# TRUNC: error: inc.lds:1: unexpected EOF
+
#--- a.s
.section .text,"ax"
.global _start
@@ -42,3 +48,6 @@ SECTIONS {
#--- full.lds
QUAD(0)
+
+#--- trunc.lds
+BYTE(42
diff --git a/lld/test/ELF/linkerscript/section-include.test b/lld/test/ELF/linkerscript/section-include.test
index 16ed19f455d50..88c5f84de0b5f 100644
--- a/lld/test/ELF/linkerscript/section-include.test
+++ b/lld/test/ELF/linkerscript/section-include.test
@@ -19,6 +19,17 @@
# CHECK2-NEXT: .data2 00000008 0000000000002008 DATA
# CHECK2-NEXT: .data3 00000008 0000000000002010 DATA
+## An unclosed output section in the include cannot be closed by the outer
+## SECTIONS { ... } '}'.
+# RUN: cp trunc.lds inc.lds
+# RUN: not ld.lld -T a.lds a.o 2>&1 | FileCheck %s --check-prefix=TRUNC
+# TRUNC: error: inc.lds:1: unexpected EOF
+
+## A stray '}' in the include cannot close the parent SECTIONS { ... }.
+# RUN: cp brace.lds inc.lds
+# RUN: not ld.lld -T a.lds a.o 2>&1 | FileCheck %s --check-prefix=BRACE
+# BRACE: error: inc.lds:1: unexpected EOF
+
#--- a.s
.global _start
_start: nop
@@ -43,3 +54,9 @@ SECTIONS {
#--- full.lds
.data2 : { QUAD(0) } > RAM
+
+#--- trunc.lds
+.text : { *(.text*)
+
+#--- brace.lds
+}
>From 5f71c6940298533445b499d303522f242604dba9 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 22 Apr 2026 10:03:55 -0700
Subject: [PATCH 2/3] rename parameter simplify readLinkerScript
---
lld/ELF/ScriptParser.cpp | 105 ++++++++++++++++++++-------------------
1 file changed, 54 insertions(+), 51 deletions(-)
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 8b03da52950d4..829d34b1b63b4 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -60,8 +60,8 @@ class ScriptParser final : ScriptLexer {
void readExtern();
void readGroup();
void readInclude(llvm::function_ref<void()> parse);
- void readIncludedBody(llvm::function_ref<void(StringRef)> stmt);
void readInput();
+ void readLinkerScriptStmt(StringRef tok);
void readMemory();
void readMemoryStmt(StringRef tok);
void readOutput();
@@ -74,6 +74,7 @@ class ScriptParser final : ScriptLexer {
void readSections();
void readSectionsStmt(SmallVectorImpl<SectionCommand *> &v, StringRef tok);
void readOutputSectionStmt(OutputSection &osec, StringRef tok);
+ void readIncludedBody(llvm::function_ref<void(StringRef)> readStmt);
void readTarget();
void readVersion();
void readVersionScriptCommand();
@@ -239,54 +240,55 @@ void ScriptParser::readVersion() {
}
void ScriptParser::readLinkerScript() {
- while (!atEOF()) {
- StringRef tok = next();
- if (atEOF())
- break;
- if (tok == ";")
- continue;
+ readIncludedBody([&](StringRef t) { readLinkerScriptStmt(t); });
+}
- if (tok == "ENTRY") {
- readEntry();
- } else if (tok == "EXTERN") {
- readExtern();
- } else if (tok == "GROUP") {
- readGroup();
- } else if (tok == "INCLUDE") {
- readInclude([&] { readLinkerScript(); });
- } else if (tok == "INPUT") {
- readInput();
- } else if (tok == "MEMORY") {
- readMemory();
- } else if (tok == "OUTPUT") {
- readOutput();
- } else if (tok == "OUTPUT_ARCH") {
- readOutputArch();
- } else if (tok == "OUTPUT_FORMAT") {
- readOutputFormat();
- } else if (tok == "OVERWRITE_SECTIONS") {
- readOverwriteSections();
- } else if (tok == "PHDRS") {
- readPhdrs();
- } else if (tok == "REGION_ALIAS") {
- readRegionAlias();
- } else if (tok == "SEARCH_DIR") {
- readSearchDir();
- } else if (tok == "SECTIONS") {
- readSections();
- } else if (tok == "TARGET") {
- 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)) {
- ctx.script->sectionCommands.push_back(cmd);
- } else {
- setError("unknown directive: " + tok);
- }
+void ScriptParser::readLinkerScriptStmt(StringRef tok) {
+ if (tok == ";")
+ return;
+
+ if (tok == "ENTRY") {
+ readEntry();
+ } else if (tok == "EXTERN") {
+ readExtern();
+ } else if (tok == "GROUP") {
+ readGroup();
+ } else if (tok == "INCLUDE") {
+ readInclude([&] {
+ readIncludedBody([&](StringRef t) { readLinkerScriptStmt(t); });
+ });
+ } else if (tok == "INPUT") {
+ readInput();
+ } else if (tok == "MEMORY") {
+ readMemory();
+ } else if (tok == "OUTPUT") {
+ readOutput();
+ } else if (tok == "OUTPUT_ARCH") {
+ readOutputArch();
+ } else if (tok == "OUTPUT_FORMAT") {
+ readOutputFormat();
+ } else if (tok == "OVERWRITE_SECTIONS") {
+ readOverwriteSections();
+ } else if (tok == "PHDRS") {
+ readPhdrs();
+ } else if (tok == "REGION_ALIAS") {
+ readRegionAlias();
+ } else if (tok == "SEARCH_DIR") {
+ readSearchDir();
+ } else if (tok == "SECTIONS") {
+ readSections();
+ } else if (tok == "TARGET") {
+ 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)) {
+ ctx.script->sectionCommands.push_back(cmd);
+ } else {
+ setError("unknown directive: " + tok);
}
}
@@ -423,13 +425,14 @@ void ScriptParser::readInclude(llvm::function_ref<void()> parse) {
activeFilenames.erase(name);
}
-// Drive `stmt` until EOF of the nested buffer.
-void ScriptParser::readIncludedBody(llvm::function_ref<void(StringRef)> stmt) {
+// Drive `readStmt` on each token until EOF of the current buffer.
+void ScriptParser::readIncludedBody(
+ llvm::function_ref<void(StringRef)> readStmt) {
while (!atEOF()) {
StringRef tok = next();
if (atEOF())
return;
- stmt(tok);
+ readStmt(tok);
}
}
>From 2d0f6ba64b1f458a6d6194e01f64bebe0895a60a Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 22 Apr 2026 10:06:53 -0700
Subject: [PATCH 3/3] rename readIncludedBody as it is reused by
readLinkerScript
---
lld/ELF/ScriptParser.cpp | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 829d34b1b63b4..8a7059d17cb2e 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -74,7 +74,7 @@ class ScriptParser final : ScriptLexer {
void readSections();
void readSectionsStmt(SmallVectorImpl<SectionCommand *> &v, StringRef tok);
void readOutputSectionStmt(OutputSection &osec, StringRef tok);
- void readIncludedBody(llvm::function_ref<void(StringRef)> readStmt);
+ void readStmts(llvm::function_ref<void(StringRef)> readStmt);
void readTarget();
void readVersion();
void readVersionScriptCommand();
@@ -240,7 +240,7 @@ void ScriptParser::readVersion() {
}
void ScriptParser::readLinkerScript() {
- readIncludedBody([&](StringRef t) { readLinkerScriptStmt(t); });
+ readStmts([&](StringRef t) { readLinkerScriptStmt(t); });
}
void ScriptParser::readLinkerScriptStmt(StringRef tok) {
@@ -254,9 +254,8 @@ void ScriptParser::readLinkerScriptStmt(StringRef tok) {
} else if (tok == "GROUP") {
readGroup();
} else if (tok == "INCLUDE") {
- readInclude([&] {
- readIncludedBody([&](StringRef t) { readLinkerScriptStmt(t); });
- });
+ readInclude(
+ [&] { readStmts([&](StringRef t) { readLinkerScriptStmt(t); }); });
} else if (tok == "INPUT") {
readInput();
} else if (tok == "MEMORY") {
@@ -426,8 +425,7 @@ void ScriptParser::readInclude(llvm::function_ref<void()> parse) {
}
// Drive `readStmt` on each token until EOF of the current buffer.
-void ScriptParser::readIncludedBody(
- llvm::function_ref<void(StringRef)> readStmt) {
+void ScriptParser::readStmts(llvm::function_ref<void(StringRef)> readStmt) {
while (!atEOF()) {
StringRef tok = next();
if (atEOF())
@@ -725,9 +723,8 @@ void ScriptParser::readSectionsStmt(SmallVectorImpl<SectionCommand *> &v,
return;
}
if (tok == "INCLUDE") {
- readInclude([&] {
- readIncludedBody([&](StringRef t) { readSectionsStmt(v, t); });
- });
+ readInclude(
+ [&] { readStmts([&](StringRef t) { readSectionsStmt(v, t); }); });
return;
}
if (SectionCommand *cmd = readAssignment(tok))
@@ -1117,7 +1114,7 @@ void ScriptParser::readOutputSectionStmt(OutputSection &osec, StringRef tok) {
readSort();
} else if (tok == "INCLUDE") {
readInclude([&] {
- readIncludedBody([&](StringRef t) { readOutputSectionStmt(osec, t); });
+ readStmts([&](StringRef t) { readOutputSectionStmt(osec, t); });
});
} else if (tok == "(" || tok == ")") {
setError("expected filename pattern");
@@ -1877,8 +1874,7 @@ void ScriptParser::readMemory() {
void ScriptParser::readMemoryStmt(StringRef tok) {
if (tok == "INCLUDE") {
- readInclude(
- [&] { readIncludedBody([&](StringRef t) { readMemoryStmt(t); }); });
+ readInclude([&] { readStmts([&](StringRef t) { readMemoryStmt(t); }); });
return;
}
More information about the llvm-commits
mailing list