1//===- ErrorHandler.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 "lld/Common/ErrorHandler.h"
10
11#include "llvm/Support/Parallel.h"
12
13#include "lld/Common/CommonLinkerContext.h"
14#include "llvm/ADT/Twine.h"
15#include "llvm/IR/DiagnosticInfo.h"
16#include "llvm/IR/DiagnosticPrinter.h"
17#include "llvm/Support/CrashRecoveryContext.h"
18#include "llvm/Support/ManagedStatic.h"
19#include "llvm/Support/Process.h"
20#include "llvm/Support/Program.h"
21#include "llvm/Support/raw_ostream.h"
22#include <regex>
23
24using namespace llvm;
25using namespace lld;
26
27static StringRef getSeparator(const Twine &msg) {
28  if (StringRef(msg.str()).contains('\n'))
29    return "\n";
30  return "";
31}
32
33ErrorHandler::~ErrorHandler() {
34  if (cleanupCallback)
35    cleanupCallback();
36}
37
38void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS,
39                              llvm::raw_ostream &stderrOS, bool exitEarly,
40                              bool disableOutput) {
41  this->stdoutOS = &stdoutOS;
42  this->stderrOS = &stderrOS;
43  stderrOS.enable_colors(stderrOS.has_colors());
44  this->exitEarly = exitEarly;
45  this->disableOutput = disableOutput;
46}
47
48void ErrorHandler::flushStreams() {
49  std::lock_guard<std::mutex> lock(mu);
50  outs().flush();
51  errs().flush();
52}
53
54ErrorHandler &lld::errorHandler() { return context().e; }
55
56void lld::error(const Twine &msg) { errorHandler().error(msg); }
57void lld::error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) {
58  errorHandler().error(msg, tag, args);
59}
60void lld::fatal(const Twine &msg) { errorHandler().fatal(msg); }
61void lld::log(const Twine &msg) { errorHandler().log(msg); }
62void lld::message(const Twine &msg, llvm::raw_ostream &s) {
63  errorHandler().message(msg, s);
64}
65void lld::warn(const Twine &msg) { errorHandler().warn(msg); }
66uint64_t lld::errorCount() { return errorHandler().errorCount; }
67
68raw_ostream &lld::outs() {
69  ErrorHandler &e = errorHandler();
70  return e.outs();
71}
72
73raw_ostream &lld::errs() {
74  ErrorHandler &e = errorHandler();
75  return e.errs();
76}
77
78raw_ostream &ErrorHandler::outs() {
79  if (disableOutput)
80    return llvm::nulls();
81  return stdoutOS ? *stdoutOS : llvm::outs();
82}
83
84raw_ostream &ErrorHandler::errs() {
85  if (disableOutput)
86    return llvm::nulls();
87  return stderrOS ? *stderrOS : llvm::errs();
88}
89
90void lld::exitLld(int val) {
91  if (hasContext()) {
92    ErrorHandler &e = errorHandler();
93    // Delete any temporary file, while keeping the memory mapping open.
94    if (e.outputBuffer)
95      e.outputBuffer->discard();
96  }
97
98  // Re-throw a possible signal or exception once/if it was caught by
99  // safeLldMain().
100  CrashRecoveryContext::throwIfCrash(val);
101
102  // Dealloc/destroy ManagedStatic variables before calling _exit().
103  // In an LTO build, allows us to get the output of -time-passes.
104  // Ensures that the thread pool for the parallel algorithms is stopped to
105  // avoid intermittent crashes on Windows when exiting.
106  if (!CrashRecoveryContext::GetCurrent())
107    llvm_shutdown();
108
109  if (hasContext())
110    lld::errorHandler().flushStreams();
111
112  // When running inside safeLldMain(), restore the control flow back to the
113  // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup,
114  // since we want to avoid further crashes on shutdown.
115  llvm::sys::Process::Exit(val, /*NoCleanup=*/true);
116}
117
118void lld::diagnosticHandler(const DiagnosticInfo &di) {
119  SmallString<128> s;
120  raw_svector_ostream os(s);
121  DiagnosticPrinterRawOStream dp(os);
122
123  // For an inline asm diagnostic, prepend the module name to get something like
124  // "$module <inline asm>:1:5: ".
125  if (auto *dism = dyn_cast<DiagnosticInfoSrcMgr>(&di))
126    if (dism->isInlineAsmDiag())
127      os << dism->getModuleName() << ' ';
128
129  di.print(dp);
130  switch (di.getSeverity()) {
131  case DS_Error:
132    error(s);
133    break;
134  case DS_Warning:
135    warn(s);
136    break;
137  case DS_Remark:
138  case DS_Note:
139    message(s);
140    break;
141  }
142}
143
144void lld::checkError(Error e) {
145  handleAllErrors(std::move(e),
146                  [&](ErrorInfoBase &eib) { error(eib.message()); });
147}
148
149// This is for --vs-diagnostics.
150//
151// Normally, lld's error message starts with argv[0]. Therefore, it usually
152// looks like this:
153//
154//   ld.lld: error: ...
155//
156// This error message style is unfortunately unfriendly to Visual Studio
157// IDE. VS interprets the first word of the first line as an error location
158// and make it clickable, thus "ld.lld" in the above message would become a
159// clickable text. When you click it, VS opens "ld.lld" executable file with
160// a binary editor.
161//
162// As a workaround, we print out an error location instead of "ld.lld" if
163// lld is running in VS diagnostics mode. As a result, error message will
164// look like this:
165//
166//   src/foo.c(35): error: ...
167//
168// This function returns an error location string. An error location is
169// extracted from an error message using regexps.
170std::string ErrorHandler::getLocation(const Twine &msg) {
171  if (!vsDiagnostics)
172    return std::string(logName);
173
174  static std::regex regexes[] = {
175      std::regex(
176          R"(^undefined (?:\S+ )?symbol:.*\n)"
177          R"(>>> referenced by .+\((\S+):(\d+)\))"),
178      std::regex(
179          R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"),
180      std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
181      std::regex(
182          R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
183      std::regex(
184          R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"),
185      std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"),
186      std::regex(
187          R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"),
188      std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
189      std::regex(R"((\S+):(\d+): unclosed quote)"),
190  };
191
192  std::string str = msg.str();
193  for (std::regex &re : regexes) {
194    std::smatch m;
195    if (!std::regex_search(str, m, re))
196      continue;
197
198    assert(m.size() == 2 || m.size() == 3);
199    if (m.size() == 2)
200      return m.str(1);
201    return m.str(1) + "(" + m.str(2) + ")";
202  }
203
204  return std::string(logName);
205}
206
207void ErrorHandler::reportDiagnostic(StringRef location, Colors c,
208                                    StringRef diagKind, const Twine &msg) {
209  SmallString<256> buf;
210  raw_svector_ostream os(buf);
211  os << sep << location << ": ";
212  if (!diagKind.empty()) {
213    if (lld::errs().colors_enabled()) {
214      os.enable_colors(true);
215      os << c << diagKind << ": " << Colors::RESET;
216    } else {
217      os << diagKind << ": ";
218    }
219  }
220  os << msg << '\n';
221  lld::errs() << buf;
222}
223
224void ErrorHandler::log(const Twine &msg) {
225  if (!verbose || disableOutput)
226    return;
227  std::lock_guard<std::mutex> lock(mu);
228  reportDiagnostic(logName, Colors::RESET, "", msg);
229}
230
231void ErrorHandler::message(const Twine &msg, llvm::raw_ostream &s) {
232  if (disableOutput)
233    return;
234  std::lock_guard<std::mutex> lock(mu);
235  s << msg << "\n";
236  s.flush();
237}
238
239void ErrorHandler::warn(const Twine &msg) {
240  if (fatalWarnings) {
241    error(msg);
242    return;
243  }
244
245  if (suppressWarnings)
246    return;
247
248  std::lock_guard<std::mutex> lock(mu);
249  reportDiagnostic(getLocation(msg), Colors::MAGENTA, "warning", msg);
250  sep = getSeparator(msg);
251}
252
253void ErrorHandler::error(const Twine &msg) {
254  // If Visual Studio-style error message mode is enabled,
255  // this particular error is printed out as two errors.
256  if (vsDiagnostics) {
257    static std::regex re(R"(^(duplicate symbol: .*))"
258                         R"((\n>>> defined at \S+:\d+.*\n>>>.*))"
259                         R"((\n>>> defined at \S+:\d+.*\n>>>.*))");
260    std::string str = msg.str();
261    std::smatch m;
262
263    if (std::regex_match(str, m, re)) {
264      error(m.str(1) + m.str(2));
265      error(m.str(1) + m.str(3));
266      return;
267    }
268  }
269
270  bool exit = false;
271  {
272    std::lock_guard<std::mutex> lock(mu);
273
274    if (errorLimit == 0 || errorCount < errorLimit) {
275      reportDiagnostic(getLocation(msg), Colors::RED, "error", msg);
276    } else if (errorCount == errorLimit) {
277      reportDiagnostic(logName, Colors::RED, "error", errorLimitExceededMsg);
278      exit = exitEarly;
279    }
280
281    sep = getSeparator(msg);
282    ++errorCount;
283  }
284
285  if (exit)
286    exitLld(1);
287}
288
289void ErrorHandler::error(const Twine &msg, ErrorTag tag,
290                         ArrayRef<StringRef> args) {
291  if (errorHandlingScript.empty()) {
292    error(msg);
293    return;
294  }
295  SmallVector<StringRef, 4> scriptArgs;
296  scriptArgs.push_back(errorHandlingScript);
297  switch (tag) {
298  case ErrorTag::LibNotFound:
299    scriptArgs.push_back("missing-lib");
300    break;
301  case ErrorTag::SymbolNotFound:
302    scriptArgs.push_back("undefined-symbol");
303    break;
304  }
305  scriptArgs.insert(scriptArgs.end(), args.begin(), args.end());
306  int res = llvm::sys::ExecuteAndWait(errorHandlingScript, scriptArgs);
307  if (res == 0) {
308    return error(msg);
309  } else {
310    // Temporarily disable error limit to make sure the two calls to error(...)
311    // only count as one.
312    uint64_t currentErrorLimit = errorLimit;
313    errorLimit = 0;
314    error(msg);
315    errorLimit = currentErrorLimit;
316    --errorCount;
317
318    switch (res) {
319    case -1:
320      error("error handling script '" + errorHandlingScript +
321            "' failed to execute");
322      break;
323    case -2:
324      error("error handling script '" + errorHandlingScript +
325            "' crashed or timeout");
326      break;
327    default:
328      error("error handling script '" + errorHandlingScript +
329            "' exited with code " + Twine(res));
330    }
331  }
332}
333
334void ErrorHandler::fatal(const Twine &msg) {
335  error(msg);
336  exitLld(1);
337}
338