Skip to content

Commit b5edd2b

Browse files
committed
[LLD] Added STARTUP linker script directive
The STARTUP linker script directive works similar to the INPUT directive with one file specified, but it also puts the specified file as the first file to be parsed, similar to putting an object file to be the first parameter on the command line. Errors are emitted if two STARTUP directives were found (there can't be two or more first files) and if archive files are passed as the argument (not yet supported).
1 parent 86e9abc commit b5edd2b

File tree

5 files changed

+93
-22
lines changed

5 files changed

+93
-22
lines changed

lld/ELF/Config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ struct VersionDefinition {
105105
class LinkerDriver {
106106
public:
107107
void linkerMain(ArrayRef<const char *> args);
108-
void addFile(StringRef path, bool withLOption);
108+
void addFile(StringRef path, bool withLOption, bool toBeginning);
109109
void addLibrary(StringRef name);
110110

111111
private:

lld/ELF/Driver.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,18 @@ static bool isBitcode(MemoryBufferRef mb) {
225225
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
226226
}
227227

228+
// Adds a file to the end or beginning of the file vector
229+
inline static void addFileToVec(std::vector<InputFile *> &files,
230+
InputFile *file, bool toBeginning) {
231+
if (LLVM_UNLIKELY(toBeginning)) {
232+
files.insert(files.begin(), file);
233+
} else {
234+
files.push_back(file);
235+
}
236+
}
237+
228238
// Opens a file and create a file object. Path has to be resolved already.
229-
void LinkerDriver::addFile(StringRef path, bool withLOption) {
239+
void LinkerDriver::addFile(StringRef path, bool withLOption, bool toBeginning) {
230240
using namespace sys::fs;
231241

232242
std::optional<MemoryBufferRef> buffer = readFile(path);
@@ -235,7 +245,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
235245
MemoryBufferRef mbref = *buffer;
236246

237247
if (config->formatBinary) {
238-
files.push_back(make<BinaryFile>(mbref));
248+
addFileToVec(files, make<BinaryFile>(mbref), toBeginning);
239249
return;
240250
}
241251

@@ -244,6 +254,10 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
244254
readLinkerScript(mbref);
245255
return;
246256
case file_magic::archive: {
257+
if (LLVM_UNLIKELY(toBeginning))
258+
error(
259+
"Adding archive files with STARTUP directive not supported, file: " +
260+
path);
247261
auto members = getArchiveMembers(mbref);
248262
if (inWholeArchive) {
249263
for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
@@ -300,14 +314,14 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
300314
auto *f =
301315
make<SharedFile>(mbref, withLOption ? path::filename(path) : path);
302316
f->init();
303-
files.push_back(f);
317+
addFileToVec(files, f, toBeginning);
304318
return;
305319
}
306320
case file_magic::bitcode:
307-
files.push_back(make<BitcodeFile>(mbref, "", 0, inLib));
321+
addFileToVec(files, make<BitcodeFile>(mbref, "", 0, inLib), toBeginning);
308322
break;
309323
case file_magic::elf_relocatable:
310-
files.push_back(createObjFile(mbref, "", inLib));
324+
addFileToVec(files, createObjFile(mbref, "", inLib), toBeginning);
311325
break;
312326
default:
313327
error(path + ": unknown file type");
@@ -317,7 +331,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
317331
// Add a given library by searching it from input search paths.
318332
void LinkerDriver::addLibrary(StringRef name) {
319333
if (std::optional<std::string> path = searchLibrary(name))
320-
addFile(saver().save(*path), /*withLOption=*/true);
334+
addFile(saver().save(*path), /*withLOption=*/true, /*toBeginning=*/false);
321335
else
322336
error("unable to find library -l" + name, ErrorTag::LibNotFound, {name});
323337
}
@@ -1624,7 +1638,7 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
16241638
hasInput = true;
16251639
break;
16261640
case OPT_INPUT:
1627-
addFile(arg->getValue(), /*withLOption=*/false);
1641+
addFile(arg->getValue(), /*withLOption=*/false, /*toBeginning=*/false);
16281642
hasInput = true;
16291643
break;
16301644
case OPT_defsym: {

lld/ELF/InputFiles.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,11 +358,13 @@ static void addDependentLibrary(StringRef specifier, const InputFile *f) {
358358
if (!config->dependentLibraries)
359359
return;
360360
if (std::optional<std::string> s = searchLibraryBaseName(specifier))
361-
ctx.driver.addFile(saver().save(*s), /*withLOption=*/true);
361+
ctx.driver.addFile(saver().save(*s), /*withLOption=*/true,
362+
/*toBeginning=*/false);
362363
else if (std::optional<std::string> s = findFromSearchPaths(specifier))
363-
ctx.driver.addFile(saver().save(*s), /*withLOption=*/true);
364+
ctx.driver.addFile(saver().save(*s), /*withLOption=*/true,
365+
/*toBeginning=*/false);
364366
else if (fs::exists(specifier))
365-
ctx.driver.addFile(specifier, /*withLOption=*/false);
367+
ctx.driver.addFile(specifier, /*withLOption=*/false, /*toBeginning=*/false);
366368
else
367369
error(toString(f) +
368370
": unable to find library from dependent library specifier: " +

lld/ELF/ScriptParser.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ class ScriptParser final : ScriptLexer {
6666
void readDefsym(StringRef name);
6767

6868
private:
69-
void addFile(StringRef path);
69+
void addFile(StringRef path, bool toBeginning);
7070

7171
void readAsNeeded();
7272
void readEntry();
7373
void readExtern();
7474
void readGroup();
7575
void readInclude();
7676
void readInput();
77+
void readStartupInput();
7778
void readMemory();
7879
void readOutput();
7980
void readOutputArch();
@@ -141,6 +142,9 @@ class ScriptParser final : ScriptLexer {
141142

142143
// A set to detect an INCLUDE() cycle.
143144
StringSet<> seen;
145+
146+
// Two startup directives don't make sense, error if this happens
147+
bool seenStartup = false;
144148
};
145149
} // namespace
146150

@@ -267,6 +271,8 @@ void ScriptParser::readLinkerScript() {
267271
readSearchDir();
268272
} else if (tok == "SECTIONS") {
269273
readSections();
274+
} else if (tok == "STARTUP") {
275+
readStartupInput();
270276
} else if (tok == "TARGET") {
271277
readTarget();
272278
} else if (tok == "VERSION") {
@@ -289,27 +295,28 @@ void ScriptParser::readDefsym(StringRef name) {
289295
script->sectionCommands.push_back(cmd);
290296
}
291297

292-
void ScriptParser::addFile(StringRef s) {
298+
void ScriptParser::addFile(StringRef s, bool toBeginning) {
293299
if (isUnderSysroot && s.startswith("/")) {
294300
SmallString<128> pathData;
295301
StringRef path = (config->sysroot + s).toStringRef(pathData);
296302
if (sys::fs::exists(path))
297-
ctx.driver.addFile(saver().save(path), /*withLOption=*/false);
303+
ctx.driver.addFile(saver().save(path), /*withLOption=*/false,
304+
toBeginning);
298305
else
299306
setError("cannot find " + s + " inside " + config->sysroot);
300307
return;
301308
}
302309

303310
if (s.startswith("/")) {
304311
// Case 1: s is an absolute path. Just open it.
305-
ctx.driver.addFile(s, /*withLOption=*/false);
312+
ctx.driver.addFile(s, /*withLOption=*/false, toBeginning);
306313
} else if (s.startswith("=")) {
307314
// Case 2: relative to the sysroot.
308315
if (config->sysroot.empty())
309-
ctx.driver.addFile(s.substr(1), /*withLOption=*/false);
316+
ctx.driver.addFile(s.substr(1), /*withLOption=*/false, toBeginning);
310317
else
311318
ctx.driver.addFile(saver().save(config->sysroot + "/" + s.substr(1)),
312-
/*withLOption=*/false);
319+
/*withLOption=*/false, toBeginning);
313320
} else if (s.startswith("-l")) {
314321
// Case 3: search in the list of library paths.
315322
ctx.driver.addLibrary(s.substr(2));
@@ -321,17 +328,18 @@ void ScriptParser::addFile(StringRef s) {
321328
SmallString<0> path(directory);
322329
sys::path::append(path, s);
323330
if (sys::fs::exists(path)) {
324-
ctx.driver.addFile(path, /*withLOption=*/false);
331+
ctx.driver.addFile(path, /*withLOption=*/false, toBeginning);
325332
return;
326333
}
327334
}
328335
// Then search in the current working directory.
329336
if (sys::fs::exists(s)) {
330-
ctx.driver.addFile(s, /*withLOption=*/false);
337+
ctx.driver.addFile(s, /*withLOption=*/false, toBeginning);
331338
} else {
332339
// Finally, search in the list of library paths.
333340
if (std::optional<std::string> path = findFromSearchPaths(s))
334-
ctx.driver.addFile(saver().save(*path), /*withLOption=*/true);
341+
ctx.driver.addFile(saver().save(*path), /*withLOption=*/true,
342+
toBeginning);
335343
else
336344
setError("unable to find " + s);
337345
}
@@ -343,7 +351,7 @@ void ScriptParser::readAsNeeded() {
343351
bool orig = config->asNeeded;
344352
config->asNeeded = true;
345353
while (!errorCount() && !consume(")"))
346-
addFile(unquote(next()));
354+
addFile(unquote(next()), /*toBeginning=*/false);
347355
config->asNeeded = orig;
348356
}
349357

@@ -393,10 +401,22 @@ void ScriptParser::readInput() {
393401
if (consume("AS_NEEDED"))
394402
readAsNeeded();
395403
else
396-
addFile(unquote(next()));
404+
addFile(unquote(next()), /*toBeginning=*/false);
397405
}
398406
}
399407

408+
// Works similar to the INPUT directive, but the file specified
409+
// in STARTUP is put as the first file to be parsed. Doesn't work
410+
// for archive files (error is emitted then).
411+
void ScriptParser::readStartupInput() {
412+
if (this->seenStartup)
413+
setError("multiple STARTUP directives seen");
414+
this->seenStartup = true;
415+
expect("(");
416+
addFile(unquote(next()), /*toBeginning=*/true);
417+
expect(")");
418+
}
419+
400420
void ScriptParser::readOutput() {
401421
// -o <file> takes predecence over OUTPUT(<file>).
402422
expect("(");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# REQUIRES: x86
2+
3+
## New linker script directive, STARTUP, takes a file an adds it to the files
4+
## that should be parsed list (like INPUT). It also specifies that that file
5+
## (object or bitcode) is treated first, as if it were specified as the first
6+
## file on the command line. This doesn't work for archives.
7+
8+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o
9+
# RUN: echo '.globl b; b:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
10+
11+
# RUN: echo 'STARTUP(%t1.o)' > %t.script
12+
# RUN: ld.lld %t2.o -T %t.script -o %t
13+
# RUN: llvm-objdump -t %t | FileCheck %s
14+
15+
# RUN: echo 'STARTUP(%t2.o)' >> %t.script
16+
17+
# RUN: not ld.lld -T %t.script -o /dev/null 2>&1 | FileCheck %s \
18+
# RUN: --check-prefix=MULTIPLE-STARTUPS
19+
20+
# RUN: llvm-ar rc %t2.a %t2.o
21+
# RUN: echo 'STARTUP(%t2.a)' > %t.script
22+
23+
# RUN: not ld.lld -T %t.script -o /dev/null %t1.o 2>&1 | FileCheck %s \
24+
# RUN: --check-prefix=ARCHIVE-ERROR
25+
26+
# CHECK: _start
27+
# CHECK: b
28+
29+
# MULTIPLE-STARTUPS: multiple STARTUP directives seen
30+
31+
# ARCHIVE-ERROR: Adding archive files with STARTUP directive not supported
32+
33+
.globl _start
34+
_start:
35+
call b

0 commit comments

Comments
 (0)