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