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