12.3. Linker Plugin API Usage and Reference

12.3.1. Linker Wrapper

LinkerWrapper flow.

Plugins interacts with the linker using LinkerWrapper.

12.3.2. Plugin Abstract Data Types

12.3.2.1. Chunk

12.3.2.2. LinkerScriptRule

12.3.2.3. OutputSection

12.3.2.4. Section

12.3.2.5. Use

12.3.2.6. Symbol

12.3.2.7. INIFile

12.3.2.8. InputFile

12.3.2.9. PluginData

12.3.2.10. AutoTimer

12.3.2.11. Timer

12.3.2.12. RelocationHandler

12.3.3. LinkerPluginConfig

12.3.4. Utility Data Structures

12.3.4.1. JSON Objects

12.3.4.2. TarWriter

12.3.4.3. ThreadPool

12.3.5. Plugin Diagnostic Framework

Plugin diagnostic framework provides a uniform and consistent solution for reporting diagnostics from plugins. It is recommended to avoid custom solutions for printing diagnostics from a plugin.

First, let’s go over some of the key benefits of using the Plugin Diagnostic Framework. After that, we’ll see how to effectively use the framework.

The framework allows for creating and reporting diagnositcs on the fly. Diagnostics can be reported with different severities such as Note, Warning, Error and more. Each severity has a unique color, and diagnostics are prefixed with “PluginName:DiagnosticSeverity”.

The linker keeps track of diagnostics emitted from the diagnostic framework. This is required to maintain extensive logs for diagnostic purposes. The framework also adjusts diagnostic behavior based on linker command-line options that affect diagnostics. For example, if the --fatal-warnings option is enabled, then warnings will be converted to fatal errors. If the -Werror option is used, warnings are promoted to regular errors instead of fatals.

The framework is thread-safe. Thus, diagnostics can be reported safely from multiple threads simultaneously. This is crucial in situations where thread-safety is neessary. In such cases, C++ std::cout and std::cerr output streams cannot be used, as they are not thread-safe.

That’s enough about the theories. Now, let’s take a look at how to use the Plugin Diagnostic Framework.

The linker assigns a unique ID to each diagnostic template. A diagnostic template has a diagnostic severity and a diagnostic format string. Diagnostic template unique IDs are necessary for creating and reporting diagnostics. The code below demonstrates how to obtain a diagnostic ID for a diagnostic template.

// There are similar functions for other diagnostic severities.
// Linker is a LinkerWrapper object.
DiagnosticEntry::DiagIDType errorID = Linker->getErrorDiagID("Error diagnostic with two args: %0 and %1");
DiagnosticEntry::DiagIDType warnID = Linker->getWarningDiagID("Warning diagnostic with one arg: %0");

%0, %1 and so on in diagnostic format string are replaced by diagnostic arguments. Diagnostic arguments must be provided when reporting a diagnostic.

The below code demonstrates how to report a diagnostic using a diagnostic ID.

// arguments can be of any type. 'LinkerWrapper::reportDiag' is a
// variadic template function.
Linker->reportDiag(errorID, arg1, arg2);
Linker->reportDiag(warnID, arg1);

12.3.5.1. DiagnosticEntry

DiagnosticEntry packs complete diagnostic information, and can be used to pass diagnostics from one location to another. Many plugin framework APIs use DiagnosticEntry to return errors to the caller, where they can be properly handled.

// diag represents a complete diagnostic.
// diagID encodes diagnostic severity and diagnostic format string.
DiagnosticEntry diag(diagID, {arg1, arg2, ...});

12.3.5.2. eld::Expected<ReturnType>

Many plugin framework APIs return values of type eld::Expected<ReturnType>. At any given time, eld::Expected<ReturnType> holds either an expected value of type ReturnType or an unexpected value of type std::unique_ptr<eld::plugin::DiagnosticEntry>. Returning the error to the caller allows plugin authors to decide how to best handle a particular error for their use case. A typical usage pattern for this is.

eld::Expected<eld::plugin::INIFile> readFile = Linker->readINIFile(configPath);
if (!readFile) {
  if (readFile.error()->diagID() == eld::plugin::Diagnostic::errr_file_does_not_exit) {
    // handle this particular error
    // ...
  } else {
    // Or, simply report the error and return.
    Linker->reportDiagEntry(std::move(readFile.error()));
    return;
  }
}

eld::plugin::INIFile file = std::move(readFile.value());

12.3.5.3. Overriding Diagnostic Severity

Diagnostic severity can be overriden by using DiagnosticEntry subclasses. DiagnosticEntry has subclasses for each diagnostic severity level. Let’s explore how to use them to override diagnostic severity.

eld::Expected<eld::plugin::INIFile> readFile = Linker->readINIFile(configPath);
eld::plugin::INIFile file;
if (readFile)
  file = std::move(readFile.value());
else {
  // Let's report the error as a Note, and move on by using a default INI file.
  eld::plugin::NoteDiagnositcEntry noteDiag(readFile.error()->diagID(), readFile.error()->args());
  Linker->reportDiagEntry(noteDiag);
  file = DefaultINIFile();
}

12.3.5.4. Diagnostic Framework types API reference