Skip to content

Commit

Permalink
[ELF] Add context-aware diagnostic functions (#112319)
Browse files Browse the repository at this point in the history
The current diagnostic functions log/warn/error/fatal lack a context
argument and call the global `lld::errorHandler()`, which prevents
multiple lld instances in one process.

This patch introduces context-aware replacements:

* log => Log(ctx)
* warn => Warn(ctx)
* errorOrWarn => Err(ctx)
* error => ErrAlways(ctx)
* fatal => Fatal(ctx)

Example: `errorOrWarn(toString(f) + "xxx")` => `Err(ctx) << f << "xxx"`.
(`toString(f)` is shortened to `f` as a bonus and may access `ctx`
without accessing the global variable (see `Target.cpp`)).

`ctx.e = &context->e;` can be replaced with a non-global Errorhandler
when `ctx` becomes a local variable.

(For the ELF port, the long term goal is to eliminate `error`. Most can
be straightforwardly converted to `Err(ctx)`.)
  • Loading branch information
MaskRay authored Nov 6, 2024
1 parent f74aed7 commit 201d760
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 6 deletions.
18 changes: 18 additions & 0 deletions lld/Common/ErrorHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,21 @@ void ErrorHandler::fatal(const Twine &msg) {
error(msg);
exitLld(1);
}

SyncStream::~SyncStream() {
os.flush();
switch (level) {
case DiagLevel::Log:
e.log(buf);
break;
case DiagLevel::Warn:
e.warn(buf);
break;
case DiagLevel::Err:
e.error(buf);
break;
case DiagLevel::Fatal:
e.fatal(buf);
break;
}
}
35 changes: 35 additions & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,8 @@ struct Ctx {
LinkerScript *script;
std::unique_ptr<TargetInfo> target;

ErrorHandler *errHandler;

// These variables are initialized by Writer and should not be used before
// Writer is initialized.
uint8_t *bufferStart;
Expand Down Expand Up @@ -679,6 +681,39 @@ static inline void internalLinkerError(StringRef loc, const Twine &msg) {
llvm::getBugReportMsg());
}

struct ELFSyncStream : SyncStream {
Ctx &ctx;
ELFSyncStream(Ctx &ctx, DiagLevel level)
: SyncStream(*ctx.errHandler, level), ctx(ctx) {}
};

template <typename T>
std::enable_if_t<!std::is_pointer_v<std::remove_reference_t<T>>,
const ELFSyncStream &>
operator<<(const ELFSyncStream &s, T &&v) {
s.os << std::forward<T>(v);
return s;
}

// Report a log if --verbose is specified.
ELFSyncStream Log(Ctx &ctx);

// Report a warning. Upgraded to an error if --fatal-warnings is specified.
ELFSyncStream Warn(Ctx &ctx);

// Report an error that will suppress the output file generation. Downgraded to
// a warning if --noinhibit-exec is specified.
ELFSyncStream Err(Ctx &ctx);

// Report an error regardless of --noinhibit-exec.
ELFSyncStream ErrAlways(Ctx &ctx);

// Report a fatal error that exits immediately. This should generally be avoided
// in favor of Err.
ELFSyncStream Fatal(Ctx &ctx);

uint64_t errCount(Ctx &ctx);

} // namespace lld::elf

#endif
23 changes: 18 additions & 5 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ void elf::errorOrWarn(const Twine &msg) {
error(msg);
}

ELFSyncStream elf::Log(Ctx &ctx) { return {ctx, DiagLevel::Log}; }
ELFSyncStream elf::Warn(Ctx &ctx) { return {ctx, DiagLevel::Warn}; }
ELFSyncStream elf::Err(Ctx &ctx) {
return {ctx, ctx.arg.noinhibitExec ? DiagLevel::Warn : DiagLevel::Err};
}
ELFSyncStream elf::ErrAlways(Ctx &ctx) { return {ctx, DiagLevel::Err}; }
ELFSyncStream elf::Fatal(Ctx &ctx) { return {ctx, DiagLevel::Fatal}; }
uint64_t elf::errCount(Ctx &ctx) { return ctx.errHandler->errorCount; }

Ctx::Ctx() : driver(*this) {}

void Ctx::reset() {
Expand All @@ -101,6 +110,8 @@ void Ctx::reset() {
script = nullptr;
target.reset();

errHandler = nullptr;

bufferStart = nullptr;
mainPart = nullptr;
tlsPhdr = nullptr;
Expand Down Expand Up @@ -169,6 +180,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
Ctx &ctx = elf::ctx;
LinkerScript script(ctx);
ctx.script = &script;
ctx.errHandler = &context->e;
ctx.symAux.emplace_back();
ctx.symtab = std::make_unique<SymbolTable>(ctx);

Expand All @@ -179,7 +191,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,

ctx.driver.linkerMain(args);

return errorCount() == 0;
return errCount(ctx) == 0;
}
} // namespace elf
} // namespace lld
Expand Down Expand Up @@ -366,7 +378,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
files.push_back(createObjFile(ctx, mbref, "", inLib));
break;
default:
error(path + ": unknown file type");
ErrAlways(ctx) << path << ": unknown file type";
}
}

Expand Down Expand Up @@ -2780,8 +2792,9 @@ static void readSecurityNotes(Ctx &ctx) {
if (ctx.arg.zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
if (ctx.arg.zBtiReport == "none")
warn(toString(f) + ": -z force-bti: file does not have "
"GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
Warn(ctx) << f
<< ": -z force-bti: file does not have "
"GNU_PROPERTY_AARCH64_FEATURE_1_BTI property";
} else if (ctx.arg.zForceIbt &&
!(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
if (ctx.arg.zCetReport == "none")
Expand Down Expand Up @@ -3048,7 +3061,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
oldFilenames.insert(f->getName());
for (InputFile *newFile : newInputFiles)
if (!oldFilenames.contains(newFile->getName()))
errorOrWarn("input file '" + newFile->getName() + "' added after LTO");
Err(ctx) << "input file '" << newFile->getName() << "' added after LTO";
}

// Handle --exclude-libs again because lto.tmp may reference additional
Expand Down
6 changes: 6 additions & 0 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ std::string lld::toString(const InputFile *f) {
return std::string(f->toStringCache);
}

const ELFSyncStream &elf::operator<<(const ELFSyncStream &s,
const InputFile *f) {
s << toString(f);
return s;
}

static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
unsigned char size;
unsigned char endian;
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ ELFFileBase *createObjFile(Ctx &, MemoryBufferRef mb,

std::string replaceThinLTOSuffix(Ctx &, StringRef path);

const ELFSyncStream &operator<<(const ELFSyncStream &, const InputFile *);

} // namespace elf
} // namespace lld

Expand Down
9 changes: 9 additions & 0 deletions lld/ELF/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ std::string lld::toString(RelType type) {
return std::string(s);
}

const ELFSyncStream &elf::operator<<(const ELFSyncStream &s, RelType type) {
StringRef buf = getELFRelocationTypeName(s.ctx.arg.emachine, type);
if (buf == "Unknown")
s << "Unknown (" << type << ')';
else
s << buf;
return s;
}

void elf::setTarget(Ctx &ctx) {
switch (ctx.arg.emachine) {
case EM_386:
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ inline uint64_t overwriteULEB128(uint8_t *bufLoc, uint64_t val) {
*bufLoc = val;
return val;
}

const ELFSyncStream &operator<<(const ELFSyncStream &, RelType);
} // namespace elf
} // namespace lld

Expand Down
17 changes: 16 additions & 1 deletion lld/include/lld/Common/ErrorHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>

namespace llvm {
class DiagnosticInfo;
class raw_ostream;
}

namespace lld {
Expand Down Expand Up @@ -152,6 +152,21 @@ void message(const Twine &msg, llvm::raw_ostream &s = outs());
void warn(const Twine &msg);
uint64_t errorCount();

enum class DiagLevel { Log, Warn, Err, Fatal };

// A class that synchronizes thread writing to the same stream similar
// std::osyncstream.
class SyncStream {
ErrorHandler &e;
DiagLevel level;
std::string buf;

public:
mutable llvm::raw_string_ostream os{buf};
SyncStream(ErrorHandler &e, DiagLevel level) : e(e), level(level) {}
~SyncStream();
};

[[noreturn]] void exitLld(int val);

void diagnosticHandler(const llvm::DiagnosticInfo &di);
Expand Down

0 comments on commit 201d760

Please sign in to comment.