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 | #include "Config.h" |
10 | #include "Driver.h" |
11 | #include "InputFiles.h" |
12 | #include "ObjC.h" |
13 | #include "Target.h" |
14 | |
15 | #include "lld/Common/Args.h" |
16 | #include "lld/Common/CommonLinkerContext.h" |
17 | #include "lld/Common/Reproduce.h" |
18 | #include "llvm/ADT/CachedHashString.h" |
19 | #include "llvm/ADT/DenseMap.h" |
20 | #include "llvm/LTO/LTO.h" |
21 | #include "llvm/Option/Arg.h" |
22 | #include "llvm/Option/ArgList.h" |
23 | #include "llvm/Option/Option.h" |
24 | #include "llvm/Support/CommandLine.h" |
25 | #include "llvm/Support/FileSystem.h" |
26 | #include "llvm/Support/Path.h" |
27 | #include "llvm/TextAPI/InterfaceFile.h" |
28 | #include "llvm/TextAPI/TextAPIReader.h" |
29 | |
30 | using namespace llvm; |
31 | using namespace llvm::MachO; |
32 | using namespace llvm::opt; |
33 | using namespace llvm::sys; |
34 | using namespace lld; |
35 | using namespace lld::macho; |
36 | |
37 | // Create prefix string literals used in Options.td |
38 | #define PREFIX(NAME, VALUE) \ |
39 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
40 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
41 | std::size(NAME##_init) - 1); |
42 | #include "Options.inc" |
43 | #undef PREFIX |
44 | |
45 | // Create table mapping all options defined in Options.td |
46 | static constexpr OptTable::Info optInfo[] = { |
47 | #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \ |
48 | VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \ |
49 | VALUES) \ |
50 | {PREFIX, \ |
51 | NAME, \ |
52 | HELPTEXT, \ |
53 | HELPTEXTSFORVARIANTS, \ |
54 | METAVAR, \ |
55 | OPT_##ID, \ |
56 | opt::Option::KIND##Class, \ |
57 | PARAM, \ |
58 | FLAGS, \ |
59 | VISIBILITY, \ |
60 | OPT_##GROUP, \ |
61 | OPT_##ALIAS, \ |
62 | ALIASARGS, \ |
63 | VALUES}, |
64 | #include "Options.inc" |
65 | #undef OPTION |
66 | }; |
67 | |
68 | MachOOptTable::MachOOptTable() : GenericOptTable(optInfo) {} |
69 | |
70 | // Set color diagnostics according to --color-diagnostics={auto,always,never} |
71 | // or --no-color-diagnostics flags. |
72 | static void handleColorDiagnostics(InputArgList &args) { |
73 | const Arg *arg = |
74 | args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, |
75 | OPT_no_color_diagnostics); |
76 | if (!arg) |
77 | return; |
78 | if (arg->getOption().getID() == OPT_color_diagnostics) { |
79 | lld::errs().enable_colors(enable: true); |
80 | } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { |
81 | lld::errs().enable_colors(enable: false); |
82 | } else { |
83 | StringRef s = arg->getValue(); |
84 | if (s == "always" ) |
85 | lld::errs().enable_colors(enable: true); |
86 | else if (s == "never" ) |
87 | lld::errs().enable_colors(enable: false); |
88 | else if (s != "auto" ) |
89 | error(msg: "unknown option: --color-diagnostics=" + s); |
90 | } |
91 | } |
92 | |
93 | InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) { |
94 | // Make InputArgList from string vectors. |
95 | unsigned missingIndex; |
96 | unsigned missingCount; |
97 | SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size()); |
98 | |
99 | // Expand response files (arguments in the form of @<filename>) |
100 | // and then parse the argument again. |
101 | cl::ExpandResponseFiles(Saver&: saver(), Tokenizer: cl::TokenizeGNUCommandLine, Argv&: vec); |
102 | InputArgList args = ParseArgs(Args: vec, MissingArgIndex&: missingIndex, MissingArgCount&: missingCount); |
103 | |
104 | // Handle -fatal_warnings early since it converts missing argument warnings |
105 | // to errors. |
106 | errorHandler().fatalWarnings = args.hasArg(OPT_fatal_warnings); |
107 | errorHandler().suppressWarnings = args.hasArg(OPT_w); |
108 | |
109 | if (missingCount) |
110 | error(msg: Twine(args.getArgString(Index: missingIndex)) + ": missing argument" ); |
111 | |
112 | handleColorDiagnostics(args); |
113 | |
114 | for (const Arg *arg : args.filtered(OPT_UNKNOWN)) { |
115 | std::string nearest; |
116 | if (findNearest(arg->getAsString(args), nearest) > 1) |
117 | error("unknown argument '" + arg->getAsString(args) + "'" ); |
118 | else |
119 | error("unknown argument '" + arg->getAsString(args) + |
120 | "', did you mean '" + nearest + "'" ); |
121 | } |
122 | return args; |
123 | } |
124 | |
125 | void MachOOptTable::printHelp(const char *argv0, bool showHidden) const { |
126 | OptTable::printHelp(OS&: lld::outs(), |
127 | Usage: (std::string(argv0) + " [options] file..." ).c_str(), |
128 | Title: "LLVM Linker" , ShowHidden: showHidden); |
129 | lld::outs() << "\n" ; |
130 | } |
131 | |
132 | static std::string rewritePath(StringRef s) { |
133 | if (fs::exists(Path: s)) |
134 | return relativeToRoot(path: s); |
135 | return std::string(s); |
136 | } |
137 | |
138 | static std::string rewriteInputPath(StringRef s) { |
139 | // Don't bother rewriting "absolute" paths that are actually under the |
140 | // syslibroot; simply rewriting the syslibroot is sufficient. |
141 | if (rerootPath(path: s) == s && fs::exists(Path: s)) |
142 | return relativeToRoot(path: s); |
143 | return std::string(s); |
144 | } |
145 | |
146 | // Reconstructs command line arguments so that so that you can re-run |
147 | // the same command with the same inputs. This is for --reproduce. |
148 | std::string macho::createResponseFile(const InputArgList &args) { |
149 | SmallString<0> data; |
150 | raw_svector_ostream os(data); |
151 | |
152 | // Copy the command line to the output while rewriting paths. |
153 | for (const Arg *arg : args) { |
154 | switch (arg->getOption().getID()) { |
155 | case OPT_reproduce: |
156 | break; |
157 | case OPT_INPUT: |
158 | os << quote(s: rewriteInputPath(s: arg->getValue())) << "\n" ; |
159 | break; |
160 | case OPT_o: |
161 | os << "-o " << quote(s: path::filename(path: arg->getValue())) << "\n" ; |
162 | break; |
163 | case OPT_filelist: |
164 | if (std::optional<MemoryBufferRef> buffer = readFile(path: arg->getValue())) |
165 | for (StringRef path : args::getLines(mb: *buffer)) |
166 | os << quote(s: rewriteInputPath(s: path)) << "\n" ; |
167 | break; |
168 | case OPT_force_load: |
169 | case OPT_weak_library: |
170 | case OPT_load_hidden: |
171 | os << arg->getSpelling() << " " |
172 | << quote(s: rewriteInputPath(s: arg->getValue())) << "\n" ; |
173 | break; |
174 | case OPT_F: |
175 | case OPT_L: |
176 | case OPT_bundle_loader: |
177 | case OPT_exported_symbols_list: |
178 | case OPT_order_file: |
179 | case OPT_syslibroot: |
180 | case OPT_unexported_symbols_list: |
181 | os << arg->getSpelling() << " " << quote(s: rewritePath(s: arg->getValue())) |
182 | << "\n" ; |
183 | break; |
184 | case OPT_sectcreate: |
185 | os << arg->getSpelling() << " " << quote(s: arg->getValue(N: 0)) << " " |
186 | << quote(s: arg->getValue(N: 1)) << " " |
187 | << quote(s: rewritePath(s: arg->getValue(N: 2))) << "\n" ; |
188 | break; |
189 | default: |
190 | os << toString(arg: *arg) << "\n" ; |
191 | } |
192 | } |
193 | return std::string(data); |
194 | } |
195 | |
196 | static void searchedDylib(const Twine &path, bool found) { |
197 | if (config->printDylibSearch) |
198 | message(msg: "searched " + path + (found ? ", found " : ", not found" )); |
199 | if (!found) |
200 | depTracker->logFileNotFound(path); |
201 | } |
202 | |
203 | std::optional<StringRef> macho::resolveDylibPath(StringRef dylibPath) { |
204 | // TODO: if a tbd and dylib are both present, we should check to make sure |
205 | // they are consistent. |
206 | SmallString<261> tbdPath = dylibPath; |
207 | path::replace_extension(path&: tbdPath, extension: ".tbd" ); |
208 | bool tbdExists = fs::exists(Path: tbdPath); |
209 | searchedDylib(path: tbdPath, found: tbdExists); |
210 | if (tbdExists) |
211 | return saver().save(S: tbdPath.str()); |
212 | |
213 | bool dylibExists = fs::exists(Path: dylibPath); |
214 | searchedDylib(path: dylibPath, found: dylibExists); |
215 | if (dylibExists) |
216 | return saver().save(S: dylibPath); |
217 | return {}; |
218 | } |
219 | |
220 | // It's not uncommon to have multiple attempts to load a single dylib, |
221 | // especially if it's a commonly re-exported core library. |
222 | static DenseMap<CachedHashStringRef, DylibFile *> loadedDylibs; |
223 | |
224 | DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, |
225 | bool isBundleLoader, bool explicitlyLinked) { |
226 | CachedHashStringRef path(mbref.getBufferIdentifier()); |
227 | DylibFile *&file = loadedDylibs[path]; |
228 | if (file) { |
229 | if (explicitlyLinked) |
230 | file->setExplicitlyLinked(); |
231 | return file; |
232 | } |
233 | |
234 | DylibFile *newFile; |
235 | file_magic magic = identify_magic(magic: mbref.getBuffer()); |
236 | if (magic == file_magic::tapi_file) { |
237 | Expected<std::unique_ptr<InterfaceFile>> result = TextAPIReader::get(InputBuffer: mbref); |
238 | if (!result) { |
239 | error(msg: "could not load TAPI file at " + mbref.getBufferIdentifier() + |
240 | ": " + toString(E: result.takeError())); |
241 | return nullptr; |
242 | } |
243 | file = |
244 | make<DylibFile>(args&: **result, args&: umbrella, args&: isBundleLoader, args&: explicitlyLinked); |
245 | |
246 | // parseReexports() can recursively call loadDylib(). That's fine since |
247 | // we wrote the DylibFile we just loaded to the loadDylib cache via the |
248 | // `file` reference. But the recursive load can grow loadDylibs, so the |
249 | // `file` reference might become invalid after parseReexports() -- so copy |
250 | // the pointer it refers to before continuing. |
251 | newFile = file; |
252 | if (newFile->exportingFile) |
253 | newFile->parseReexports(interface: **result); |
254 | } else { |
255 | assert(magic == file_magic::macho_dynamically_linked_shared_lib || |
256 | magic == file_magic::macho_dynamically_linked_shared_lib_stub || |
257 | magic == file_magic::macho_executable || |
258 | magic == file_magic::macho_bundle); |
259 | file = make<DylibFile>(args&: mbref, args&: umbrella, args&: isBundleLoader, args&: explicitlyLinked); |
260 | |
261 | // parseLoadCommands() can also recursively call loadDylib(). See comment |
262 | // in previous block for why this means we must copy `file` here. |
263 | newFile = file; |
264 | if (newFile->exportingFile) |
265 | newFile->parseLoadCommands(mb: mbref); |
266 | } |
267 | return newFile; |
268 | } |
269 | |
270 | void macho::resetLoadedDylibs() { loadedDylibs.clear(); } |
271 | |
272 | std::optional<StringRef> |
273 | macho::findPathCombination(const Twine &name, |
274 | const std::vector<StringRef> &roots, |
275 | ArrayRef<StringRef> extensions) { |
276 | SmallString<261> base; |
277 | for (StringRef dir : roots) { |
278 | base = dir; |
279 | path::append(path&: base, a: name); |
280 | for (StringRef ext : extensions) { |
281 | Twine location = base + ext; |
282 | bool exists = fs::exists(Path: location); |
283 | searchedDylib(path: location, found: exists); |
284 | if (exists) |
285 | return saver().save(S: location.str()); |
286 | } |
287 | } |
288 | return {}; |
289 | } |
290 | |
291 | StringRef macho::rerootPath(StringRef path) { |
292 | if (!path::is_absolute(path, style: path::Style::posix) || path.ends_with(Suffix: ".o" )) |
293 | return path; |
294 | |
295 | if (std::optional<StringRef> rerootedPath = |
296 | findPathCombination(path, config->systemLibraryRoots)) |
297 | return *rerootedPath; |
298 | |
299 | return path; |
300 | } |
301 | |
302 | uint32_t macho::getModTime(StringRef path) { |
303 | if (config->zeroModTime) |
304 | return 0; |
305 | |
306 | fs::file_status stat; |
307 | if (!fs::status(path, result&: stat)) |
308 | if (fs::exists(status: stat)) |
309 | return toTimeT(TP: stat.getLastModificationTime()); |
310 | |
311 | warn(msg: "failed to get modification time of " + path); |
312 | return 0; |
313 | } |
314 | |
315 | void macho::printArchiveMemberLoad(StringRef reason, const InputFile *f) { |
316 | if (config->printEachFile) |
317 | message(msg: toString(file: f)); |
318 | if (config->printWhyLoad) |
319 | message(msg: reason + " forced load of " + toString(file: f)); |
320 | } |
321 | |
322 | macho::DependencyTracker::DependencyTracker(StringRef path) |
323 | : path(path), active(!path.empty()) { |
324 | if (active && fs::exists(Path: path) && !fs::can_write(Path: path)) { |
325 | warn(msg: "Ignoring dependency_info option since specified path is not " |
326 | "writeable." ); |
327 | active = false; |
328 | } |
329 | } |
330 | |
331 | void macho::DependencyTracker::write(StringRef version, |
332 | const SetVector<InputFile *> &inputs, |
333 | StringRef output) { |
334 | if (!active) |
335 | return; |
336 | |
337 | std::error_code ec; |
338 | raw_fd_ostream os(path, ec, fs::OF_None); |
339 | if (ec) { |
340 | warn(msg: "Error writing dependency info to file" ); |
341 | return; |
342 | } |
343 | |
344 | auto addDep = [&os](DepOpCode opcode, const StringRef &path) { |
345 | // XXX: Even though DepOpCode's underlying type is uint8_t, |
346 | // this cast is still needed because Clang older than 10.x has a bug, |
347 | // where it doesn't know to cast the enum to its underlying type. |
348 | // Hence `<< DepOpCode` is ambiguous to it. |
349 | os << static_cast<uint8_t>(opcode); |
350 | os << path; |
351 | os << '\0'; |
352 | }; |
353 | |
354 | addDep(DepOpCode::Version, version); |
355 | |
356 | // Sort the input by its names. |
357 | std::vector<StringRef> inputNames; |
358 | inputNames.reserve(n: inputs.size()); |
359 | for (InputFile *f : inputs) |
360 | inputNames.push_back(x: f->getName()); |
361 | llvm::sort(C&: inputNames); |
362 | |
363 | for (const StringRef &in : inputNames) |
364 | addDep(DepOpCode::Input, in); |
365 | |
366 | for (const std::string &f : notFounds) |
367 | addDep(DepOpCode::NotFound, f); |
368 | |
369 | addDep(DepOpCode::Output, output); |
370 | } |
371 | |