blob: 7af34e9e31bd8e3106ff4947e91171a782c53cfa [file] [log] [blame] [edit]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_LD_DIAGNOSTICS_H_
#define LIB_LD_DIAGNOSTICS_H_
#include <lib/elfldltl/diagnostics.h>
#include <cassert>
#include <cstdarg>
#include <string_view>
#include <type_traits>
#include <utility>
namespace ld {
// This is declared in the OS-specific header (posix.h or zircon.h).
struct StartupData;
// This is the version of the elfldltl::DiagnosticsFlags API always used in the
// startup dynamic linker: allow warnings; keep going after errors. Before
// beginning relocation and before handing off control, the diagnostics
// object's error count will be checked to bail out safely after doing as much
// work as possible to report all the detailed errors that can be found.
struct StartupDiagnosticsFlags {
// Note these are not in the canonical order elfldltl::DiagnosticFlags uses.
// No two adjacent fields have the same type for [[no_unique_address]] rules.
[[no_unique_address]] std::false_type warnings_are_errors;
[[no_unique_address]] std::true_type multiple_errors;
[[no_unique_address]] std::false_type extra_checking;
};
// This provides the Report callable object for Diagnostics. It uses the
// printf engine to ultimately call StartupMessage with the captured
// StartupData reference. But it also holds a current module name that can
// be changed; when set, it's used as a prefix on error messages.
class DiagnosticsReport {
public:
constexpr explicit DiagnosticsReport(StartupData& startup) : startup_(startup) {}
constexpr std::string_view module() const { return module_; }
constexpr void set_module(std::string_view module) { module_ = module; }
constexpr void clear_module() { module_ = {}; }
template <typename... Args>
bool operator()(Args&&... args) const {
auto report = [&](auto... prefix) {
auto printf = [this](auto... args) { this->Printf(args...); };
auto report = elfldltl::PrintfDiagnosticsReport(printf, prefix...);
report(std::forward<Args>(args)...);
};
if (module_.empty()) {
report();
} else {
constexpr std::string_view kColon = ": ";
report(module_, kColon);
}
return true;
}
private:
void Printf(const char* format, ...) const;
void Printf(const char* format, va_list args) const;
StartupData& startup_;
std::string_view module_;
};
// This constructs the main Diagnostics object for the startup dynamic linker.
constexpr auto MakeDiagnostics(StartupData& startup) {
return elfldltl::Diagnostics{DiagnosticsReport(startup), StartupDiagnosticsFlags{}};
}
// Its type is captured so it can be used as an explicit non-template argument.
using Diagnostics = decltype(MakeDiagnostics(std::declval<StartupData&>()));
// This is an RAII type that exists to temporarily set the module name in the
// Diagnostics::report() object.
class ModuleDiagnostics {
public:
constexpr explicit ModuleDiagnostics(Diagnostics& diag, std::string_view name) : diag_(diag) {
assert(diag_.report().module().empty());
diag_.report().set_module(name);
}
~ModuleDiagnostics() { diag_.report().clear_module(); }
private:
Diagnostics& diag_;
};
// This is called before proceeding from loading to relocation / linking, and
// then again when proceeding from final cleanup to transferring control.
void CheckErrors(Diagnostics& diag);
} // namespace ld
#endif // LIB_LD_DIAGNOSTICS_H_