1ece8a530Spatrick //===- DriverUtils.cpp ----------------------------------------------------===//
2ece8a530Spatrick //
3ece8a530Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ece8a530Spatrick // See https://llvm.org/LICENSE.txt for license information.
5ece8a530Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ece8a530Spatrick //
7ece8a530Spatrick //===----------------------------------------------------------------------===//
8ece8a530Spatrick //
9*05edf1c1Srobert // This file contains utility functions for the ctx.driver. Because there
10ece8a530Spatrick // are so many small functions, we created this separate file to make
11ece8a530Spatrick // Driver.cpp less cluttered.
12ece8a530Spatrick //
13ece8a530Spatrick //===----------------------------------------------------------------------===//
14ece8a530Spatrick
15*05edf1c1Srobert #include "Config.h"
16ece8a530Spatrick #include "Driver.h"
17*05edf1c1Srobert #include "lld/Common/CommonLinkerContext.h"
18ece8a530Spatrick #include "lld/Common/Reproduce.h"
19ece8a530Spatrick #include "llvm/ADT/Triple.h"
20ece8a530Spatrick #include "llvm/Option/Option.h"
21ece8a530Spatrick #include "llvm/Support/CommandLine.h"
22ece8a530Spatrick #include "llvm/Support/FileSystem.h"
23bb684c34Spatrick #include "llvm/Support/Host.h"
24ece8a530Spatrick #include "llvm/Support/Path.h"
25a0747c9fSpatrick #include "llvm/Support/TimeProfiler.h"
26*05edf1c1Srobert #include <optional>
27ece8a530Spatrick
28ece8a530Spatrick using namespace llvm;
29ece8a530Spatrick using namespace llvm::sys;
30ece8a530Spatrick using namespace llvm::opt;
31bb684c34Spatrick using namespace lld;
32bb684c34Spatrick using namespace lld::elf;
33ece8a530Spatrick
34ece8a530Spatrick // Create OptTable
35ece8a530Spatrick
36ece8a530Spatrick // Create prefix string literals used in Options.td
37*05edf1c1Srobert #define PREFIX(NAME, VALUE) \
38*05edf1c1Srobert static constexpr StringLiteral NAME##_init[] = VALUE; \
39*05edf1c1Srobert static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
40*05edf1c1Srobert std::size(NAME##_init) - 1);
41ece8a530Spatrick #include "Options.inc"
42ece8a530Spatrick #undef PREFIX
43ece8a530Spatrick
44ece8a530Spatrick // Create table mapping all options defined in Options.td
45*05edf1c1Srobert static constexpr opt::OptTable::Info optInfo[] = {
46ece8a530Spatrick #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
47ece8a530Spatrick {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
48ece8a530Spatrick X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
49ece8a530Spatrick #include "Options.inc"
50ece8a530Spatrick #undef OPTION
51ece8a530Spatrick };
52ece8a530Spatrick
ELFOptTable()53*05edf1c1Srobert ELFOptTable::ELFOptTable() : GenericOptTable(optInfo) {}
54ece8a530Spatrick
55*05edf1c1Srobert // Set color diagnostics according to --color-diagnostics={auto,always,never}
56*05edf1c1Srobert // or --no-color-diagnostics flags.
handleColorDiagnostics(opt::InputArgList & args)57ece8a530Spatrick static void handleColorDiagnostics(opt::InputArgList &args) {
58*05edf1c1Srobert auto *arg = args.getLastArg(OPT_color_diagnostics);
59ece8a530Spatrick if (!arg)
60ece8a530Spatrick return;
61ece8a530Spatrick StringRef s = arg->getValue();
62ece8a530Spatrick if (s == "always")
63ece8a530Spatrick lld::errs().enable_colors(true);
64ece8a530Spatrick else if (s == "never")
65ece8a530Spatrick lld::errs().enable_colors(false);
66ece8a530Spatrick else if (s != "auto")
67ece8a530Spatrick error("unknown option: --color-diagnostics=" + s);
68ece8a530Spatrick }
69ece8a530Spatrick
getQuotingStyle(opt::InputArgList & args)70ece8a530Spatrick static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
71ece8a530Spatrick if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
72ece8a530Spatrick StringRef s = arg->getValue();
73ece8a530Spatrick if (s != "windows" && s != "posix")
74ece8a530Spatrick error("invalid response file quoting: " + s);
75ece8a530Spatrick if (s == "windows")
76ece8a530Spatrick return cl::TokenizeWindowsCommandLine;
77ece8a530Spatrick return cl::TokenizeGNUCommandLine;
78ece8a530Spatrick }
79bb684c34Spatrick if (Triple(sys::getProcessTriple()).isOSWindows())
80ece8a530Spatrick return cl::TokenizeWindowsCommandLine;
81ece8a530Spatrick return cl::TokenizeGNUCommandLine;
82ece8a530Spatrick }
83ece8a530Spatrick
84ece8a530Spatrick // Gold LTO plugin takes a `--plugin-opt foo=bar` option as an alias for
85ece8a530Spatrick // `--plugin-opt=foo=bar`. We want to handle `--plugin-opt=foo=` as an
86ece8a530Spatrick // option name and `bar` as a value. Unfortunately, OptParser cannot
87ece8a530Spatrick // handle an option with a space in it.
88ece8a530Spatrick //
89ece8a530Spatrick // In this function, we concatenate command line arguments so that
90ece8a530Spatrick // `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a
91ece8a530Spatrick // bit hacky, but looks like it is still better than handling --plugin-opt
92ece8a530Spatrick // options by hand.
concatLTOPluginOptions(SmallVectorImpl<const char * > & args)93ece8a530Spatrick static void concatLTOPluginOptions(SmallVectorImpl<const char *> &args) {
94ece8a530Spatrick SmallVector<const char *, 256> v;
95ece8a530Spatrick for (size_t i = 0, e = args.size(); i != e; ++i) {
96ece8a530Spatrick StringRef s = args[i];
97ece8a530Spatrick if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) {
98*05edf1c1Srobert v.push_back(saver().save(s + "=" + args[i + 1]).data());
99ece8a530Spatrick ++i;
100ece8a530Spatrick } else {
101ece8a530Spatrick v.push_back(args[i]);
102ece8a530Spatrick }
103ece8a530Spatrick }
104ece8a530Spatrick args = std::move(v);
105ece8a530Spatrick }
106ece8a530Spatrick
107ece8a530Spatrick // Parses a given list of options.
parse(ArrayRef<const char * > argv)108ece8a530Spatrick opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
109ece8a530Spatrick // Make InputArgList from string vectors.
110ece8a530Spatrick unsigned missingIndex;
111ece8a530Spatrick unsigned missingCount;
112ece8a530Spatrick SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
113ece8a530Spatrick
114ece8a530Spatrick // We need to get the quoting style for response files before parsing all
115ece8a530Spatrick // options so we parse here before and ignore all the options but
116ece8a530Spatrick // --rsp-quoting.
117ece8a530Spatrick opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
118ece8a530Spatrick
119ece8a530Spatrick // Expand response files (arguments in the form of @<filename>)
120ece8a530Spatrick // and then parse the argument again.
121*05edf1c1Srobert cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec);
122ece8a530Spatrick concatLTOPluginOptions(vec);
123ece8a530Spatrick args = this->ParseArgs(vec, missingIndex, missingCount);
124ece8a530Spatrick
125ece8a530Spatrick handleColorDiagnostics(args);
126ece8a530Spatrick if (missingCount)
127ece8a530Spatrick error(Twine(args.getArgString(missingIndex)) + ": missing argument");
128ece8a530Spatrick
129a0747c9fSpatrick for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {
130ece8a530Spatrick std::string nearest;
131ece8a530Spatrick if (findNearest(arg->getAsString(args), nearest) > 1)
132ece8a530Spatrick error("unknown argument '" + arg->getAsString(args) + "'");
133ece8a530Spatrick else
134ece8a530Spatrick error("unknown argument '" + arg->getAsString(args) +
135ece8a530Spatrick "', did you mean '" + nearest + "'");
136ece8a530Spatrick }
137ece8a530Spatrick return args;
138ece8a530Spatrick }
139ece8a530Spatrick
printHelp()140bb684c34Spatrick void elf::printHelp() {
141a0747c9fSpatrick ELFOptTable().printHelp(
142ece8a530Spatrick lld::outs(), (config->progName + " [options] file...").str().c_str(),
143ece8a530Spatrick "lld", false /*ShowHidden*/, true /*ShowAllAliases*/);
144ece8a530Spatrick lld::outs() << "\n";
145ece8a530Spatrick
146*05edf1c1Srobert // Scripts generated by Libtool versions up to 2021-10 expect /: supported
147*05edf1c1Srobert // targets:.* elf/ in a message for the --help option. If it doesn't match,
148*05edf1c1Srobert // the scripts assume that the linker doesn't support very basic features
149*05edf1c1Srobert // such as shared libraries. Therefore, we need to print out at least "elf".
150ece8a530Spatrick lld::outs() << config->progName << ": supported targets: elf\n";
151ece8a530Spatrick }
152ece8a530Spatrick
rewritePath(StringRef s)153ece8a530Spatrick static std::string rewritePath(StringRef s) {
154ece8a530Spatrick if (fs::exists(s))
155ece8a530Spatrick return relativeToRoot(s);
156bb684c34Spatrick return std::string(s);
157ece8a530Spatrick }
158ece8a530Spatrick
159ece8a530Spatrick // Reconstructs command line arguments so that so that you can re-run
160ece8a530Spatrick // the same command with the same inputs. This is for --reproduce.
createResponseFile(const opt::InputArgList & args)161bb684c34Spatrick std::string elf::createResponseFile(const opt::InputArgList &args) {
162ece8a530Spatrick SmallString<0> data;
163ece8a530Spatrick raw_svector_ostream os(data);
164ece8a530Spatrick os << "--chroot .\n";
165ece8a530Spatrick
166ece8a530Spatrick // Copy the command line to the output while rewriting paths.
167ece8a530Spatrick for (auto *arg : args) {
168ece8a530Spatrick switch (arg->getOption().getID()) {
169ece8a530Spatrick case OPT_reproduce:
170ece8a530Spatrick break;
171ece8a530Spatrick case OPT_INPUT:
172ece8a530Spatrick os << quote(rewritePath(arg->getValue())) << "\n";
173ece8a530Spatrick break;
174ece8a530Spatrick case OPT_o:
175*05edf1c1Srobert case OPT_Map:
176*05edf1c1Srobert case OPT_print_archive_stats:
177*05edf1c1Srobert case OPT_why_extract:
178*05edf1c1Srobert // If an output path contains directories, "lld @response.txt" will
179*05edf1c1Srobert // likely fail because the archive we are creating doesn't contain empty
180ece8a530Spatrick // directories for the output path (-o doesn't create directories).
181ece8a530Spatrick // Strip directories to prevent the issue.
182*05edf1c1Srobert os << arg->getSpelling();
183*05edf1c1Srobert if (arg->getOption().getRenderStyle() == opt::Option::RenderSeparateStyle)
184*05edf1c1Srobert os << ' ';
185*05edf1c1Srobert os << quote(path::filename(arg->getValue())) << '\n';
186ece8a530Spatrick break;
187a0747c9fSpatrick case OPT_lto_sample_profile:
188a0747c9fSpatrick os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << "\n";
189a0747c9fSpatrick break;
190a0747c9fSpatrick case OPT_call_graph_ordering_file:
191ece8a530Spatrick case OPT_dynamic_list:
192*05edf1c1Srobert case OPT_export_dynamic_symbol_list:
193a0747c9fSpatrick case OPT_just_symbols:
194ece8a530Spatrick case OPT_library_path:
195a0747c9fSpatrick case OPT_retain_symbols_file:
196ece8a530Spatrick case OPT_rpath:
197ece8a530Spatrick case OPT_script:
198ece8a530Spatrick case OPT_symbol_ordering_file:
199ece8a530Spatrick case OPT_sysroot:
200ece8a530Spatrick case OPT_version_script:
201ece8a530Spatrick os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))
202ece8a530Spatrick << "\n";
203ece8a530Spatrick break;
204ece8a530Spatrick default:
205ece8a530Spatrick os << toString(*arg) << "\n";
206ece8a530Spatrick }
207ece8a530Spatrick }
208bb684c34Spatrick return std::string(data.str());
209ece8a530Spatrick }
210ece8a530Spatrick
211ece8a530Spatrick // Find a file by concatenating given paths. If a resulting path
212ece8a530Spatrick // starts with "=", the character is replaced with a --sysroot value.
findFile(StringRef path1,const Twine & path2)213*05edf1c1Srobert static std::optional<std::string> findFile(StringRef path1,
214*05edf1c1Srobert const Twine &path2) {
215ece8a530Spatrick SmallString<128> s;
216ece8a530Spatrick if (path1.startswith("="))
217ece8a530Spatrick path::append(s, config->sysroot, path1.substr(1), path2);
218ece8a530Spatrick else
219ece8a530Spatrick path::append(s, path1, path2);
220ece8a530Spatrick
221ece8a530Spatrick if (fs::exists(s))
222bb684c34Spatrick return std::string(s);
223*05edf1c1Srobert return std::nullopt;
224ece8a530Spatrick }
225ece8a530Spatrick
findFromSearchPaths(StringRef path)226*05edf1c1Srobert std::optional<std::string> elf::findFromSearchPaths(StringRef path) {
227ece8a530Spatrick for (StringRef dir : config->searchPaths)
228*05edf1c1Srobert if (std::optional<std::string> s = findFile(dir, path))
229ece8a530Spatrick return s;
230*05edf1c1Srobert return std::nullopt;
231ece8a530Spatrick }
232ece8a530Spatrick
23304601fb1Sgnezdo namespace {
23404601fb1Sgnezdo // Must be in sync with findMajMinShlib in clang/lib/Driver/Driver.cpp.
findMajMinShlib(StringRef dir,const Twine & libNameSo)235*05edf1c1Srobert std::optional<std::string> findMajMinShlib(StringRef dir, const Twine& libNameSo) {
236adae0cfdSpatrick // Handle OpenBSD-style maj/min shlib scheme
237adae0cfdSpatrick llvm::SmallString<128> Scratch;
23804601fb1Sgnezdo const StringRef LibName = (libNameSo + ".").toStringRef(Scratch);
239adae0cfdSpatrick int MaxMaj = -1, MaxMin = -1;
240adae0cfdSpatrick std::error_code EC;
24104601fb1Sgnezdo for (llvm::sys::fs::directory_iterator LI(dir, EC), LE;
242adae0cfdSpatrick LI != LE; LI = LI.increment(EC)) {
243adae0cfdSpatrick StringRef FilePath = LI->path();
24404601fb1Sgnezdo StringRef FileName = llvm::sys::path::filename(FilePath);
245adae0cfdSpatrick if (!(FileName.startswith(LibName)))
246adae0cfdSpatrick continue;
247adae0cfdSpatrick std::pair<StringRef, StringRef> MajMin =
248adae0cfdSpatrick FileName.substr(LibName.size()).split('.');
249adae0cfdSpatrick int Maj, Min;
250adae0cfdSpatrick if (MajMin.first.getAsInteger(10, Maj) || Maj < 0)
251adae0cfdSpatrick continue;
252adae0cfdSpatrick if (MajMin.second.getAsInteger(10, Min) || Min < 0)
253adae0cfdSpatrick continue;
254adae0cfdSpatrick if (Maj > MaxMaj)
255adae0cfdSpatrick MaxMaj = Maj, MaxMin = Min;
256adae0cfdSpatrick if (MaxMaj == Maj && Min > MaxMin)
257adae0cfdSpatrick MaxMin = Min;
258adae0cfdSpatrick }
259adae0cfdSpatrick if (MaxMaj >= 0)
260adae0cfdSpatrick return findFile(dir, LibName + Twine(MaxMaj) + "." + Twine(MaxMin));
261*05edf1c1Srobert return std::nullopt;
26204601fb1Sgnezdo }
26304601fb1Sgnezdo } // namespace
26404601fb1Sgnezdo
26504601fb1Sgnezdo // This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
26604601fb1Sgnezdo // search paths.
searchLibraryBaseName(StringRef name)267*05edf1c1Srobert std::optional<std::string> elf::searchLibraryBaseName(StringRef name) {
26804601fb1Sgnezdo for (StringRef dir : config->searchPaths) {
26904601fb1Sgnezdo if (!config->isStatic) {
270*05edf1c1Srobert if (std::optional<std::string> s = findFile(dir, "lib" + name + ".so"))
27104601fb1Sgnezdo return s;
272*05edf1c1Srobert if (std::optional<std::string> s = findMajMinShlib(dir, "lib" + name + ".so"))
27304601fb1Sgnezdo return s;
274adae0cfdSpatrick }
275*05edf1c1Srobert if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a"))
276ece8a530Spatrick return s;
277ece8a530Spatrick }
278*05edf1c1Srobert return std::nullopt;
279ece8a530Spatrick }
280ece8a530Spatrick
281ece8a530Spatrick // This is for -l<namespec>.
searchLibrary(StringRef name)282*05edf1c1Srobert std::optional<std::string> elf::searchLibrary(StringRef name) {
283a0747c9fSpatrick llvm::TimeTraceScope timeScope("Locate library", name);
284ece8a530Spatrick if (name.startswith(":"))
285ece8a530Spatrick return findFromSearchPaths(name.substr(1));
286ece8a530Spatrick return searchLibraryBaseName(name);
287ece8a530Spatrick }
288ece8a530Spatrick
289ece8a530Spatrick // If a linker/version script doesn't exist in the current directory, we also
290ece8a530Spatrick // look for the script in the '-L' search paths. This matches the behaviour of
291ece8a530Spatrick // '-T', --version-script=, and linker script INPUT() command in ld.bfd.
searchScript(StringRef name)292*05edf1c1Srobert std::optional<std::string> elf::searchScript(StringRef name) {
293ece8a530Spatrick if (fs::exists(name))
294ece8a530Spatrick return name.str();
295ece8a530Spatrick return findFromSearchPaths(name);
296ece8a530Spatrick }
297