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