1. Linker Plugins
1.1. Introduction
Linker plugins make it possible to run user-defined code during the link process. Implemented as a C++ class, they are a programmatical way to add new functionalities and modify default linker behavior.
Plugins provide more control and flexibility over the output image layout as compared to the linker scripts. For complicated rules, linker scripts are often very cumbersome to write, sometimes desired rule matching behavior might even be impossible from just linker script. For example, a plugin can move around chunks from one output section to another. Linker plugins also allow the user to alter program layout dynamically which is not possible using linker script.
Plugins add custom behavior to the linker by defining behavior for the hooks that the linker runs in various stages of the link process. These hooks allows plugins to modify the default linker behavior, provide a fine-grained control over the output image layout, add new functionalities to the linker.
This guide will describe all that you need to know about linker plugins, and how to effectively write one yourself. This guide assumes that the reader has a basic understanding of linkers, linker relocations and linker scripts.
1.2. Elements of the link process
Before we move further, let’s first quickly go over some important elements of the link process. A solid understanding of these elements will help to better understand the link process, and linker’s capabilities and limitations. This in turn will help to effectively design and write plugins.
1.2.1. Symbols
Each relocatable object module, M, has a symbol table that contains information about the symbols that are defined and referenced by M. In the context of a linker, there are three different kinds of symbols:
Global symbols that are defined in module M and that can be referenced by other modules. These symbols correspond to nonstatic C functions and nonstatic global variables defined in M.
Global symbols that are referenced by module M but are defined by some other module. Such symbols are called externals and correspond to nonstatic C functions and global variables that are defined in other modules.
Local symbols that are defined and referenced exclusively by module M. These correspond to static C functions and static global variables. These symbols cannot be accessed by other modules.
It is important to note that the local linker symbols are different from the local program symbols such as non-static variables defined within a function. Non-static local program variables are managed at run-time on the stack, and thus, the linker is unaware of them.
eld::plugin::Symbol
is a handler for a linker symbol. It can be used to
inspect symbol properties, address and value in the plugin code.
1.2.1.1. Symbol resolution
Object files define and reference symbols. Symbol resolution refers to associating the correct symbol definition to each symbol reference.
1.2.2. Input Section
Each relocatable object module, M, has input sections. Input sections contains most of the object file information for the linking view: instructions, data, symbol table, relocation information and so on.
eld::plugin::Section
is a handler for an input section. It can be used in
the plugin code to inspect and discard an input section among other things.
1.2.3. Output Section
Output section contains one or more input sections. The output sections are in turn mapped to a segment. A segment contains one or more output sections, and is used for finally loading the program. Typically, output sections with same Alloc, Exec, and Write permissions are mapped to the same segment.
eld::plugin::OutputSection
is a handler for an output section. It can be used
to inspect an output section, and get it’s associated linker script rules.
1.2.4. Chunk
In ELD’s terminology, a section is made up of many sub-parts called as chunk or fragment. Plugins can move chunks from one output section to another. Linker scripts do not have this level of control over the output image layout.
eld::plugin::Chunk
is a handler for a chunk. It can be used to inspect the
properties, symbols and content of a chunk.
1.2.5. Relocation
Relocation is the process of connecting symbolic references with symbolic definitions. All symbolic references of the same symbol should be connected to the same definition. Object files contain relocation entries that describes how the relocations should be processed.
eld::plugin::Use
represent relocations. It can be used to inspect all about
a reloction – souce, target and symbolic information.
1.2.6. Section Mapping
Each input section is either discarded or is mapped to some output section.
Linker has some default mapping rules that defines which input section is to
be mapped to which output section. Traditionally, linkers allowed users to
add their own custom mapping rules using linker script SECTIONS
command.
Now, we have covered the basic elements of the link process. Let’s dive into the linker plugin functionality.
1.3. Plugin Interface Types
ELD has different plugin interfaces. Each plugin interface provides different functionalities. Each plugin interface provides hooks at specific points in the linker and designed for specific use cases. For example, SectionMatcher plugin interface type provide hooks to process input sections, whereas OutputSectionIterator provides hooks to process output sections. Currently, there are 6 plugin interfaces, more interfaces will be added based on new use-cases.
For each plugin interface, there is a specific plugin interface class. To create a plugin of a specific plugin interface, users have to create a class that inherits the corresponding plugin interface class. The user plugin class needs to provide implementation for the hooks by overriding virtual member functions.
A plugin can have functionalities of multiple plugin interfaces by inheriting from multiple plugin interface classes.
The 6 plugin interfaces along with their corresponding C++ class and header file are listed below.
Plugin interface |
Plugin interface class |
Header file |
---|---|---|
LinkerPlugin |
LinkerPlugin |
ELD/PluginAPI/LinkerPlugin.h |
SectionMatcher |
SectionMatcherPlugin |
ELD/PluginAPI/SectionMatcherPlugin.h |
SectionIterator |
SectionIteratorPlugin |
ELD/PluginAPI/SectionIteratorPlugin.h |
ControlFileSize |
ControlFileSizePlugin |
ELD/PluginAPI/ControlFileSizePlugin.h |
ControlMemorySize |
ControlMemorySizePlugin |
ELD/PluginAPI/ControlMemorySizePlugin.h |
OutputSectionIterator |
OutputSectionIteratorPlugin.h |
ELD/PluginAPI/OutputSectionIteratorPlugin.h |
1.5. Running a linker plugin
There are two ways to run linker plugins:
Adding a plugin invocation command in a linker script.
Specifying plugin configuration file using –plugin-config option.
These ways are also called plugin load specification, as they specify how to load a plugin by the linker.
1.5.1. Adding a plugin invocation command in a linker script.
<PluginInterfaceKeyword>("LibraryName", "PluginName" [, "PluginOption"])
For output section plugin types, ControlMemorySizePlugin
and
ControlFileSizePlugin
, users should add the plugin invocation command to the
output section description of the output section on which these plugins
should run.
For example:
SECTIONS {
output_section <PluginInterfaceKeyword>("LibraryName", "PluginName" [, "PluginOption"]) : {
*(.text)
}
}
Note
Only one output section plugin can be attached to an output section.
PluginInterfaceKeyword
Each plugin interface type has a corresponding linker script plugin interface keyword.
Plugin interface type
Linker script keyword
SectionMatcher
PLUGIN_SECTION_MATCHER
SectionIterator
PLUGIN_ITER_SECTIONS
ControlFileSize
PLUGIN_CONTROL_FILESZ
ControlMemorySize
PLUGIN_CONTROL_MEMSZ
OutputSectionIterator
PLUGIN_OUTPUT_SECTION_ITER
LinkerPlugin
LINKER_PLUGIN
LibraryName
Name of the dynamic library that contains the plugin for the linker to load.
Finds the library in the same search paths as if the library was passed as an input to the linker.
Uses the name of the library without the lib prefix on Linux and without the .so/.dll suffix on Linux/Windows, respectively
PluginName
Name of the plugin. The linker queries the dynamic library to provide an implementation of the plugin with the name PluginName for the specified interface type.
PluginOption
It can be used to pass an option to the plugin.
1.5.2. Specifying plugin configuration file using --plugin-config
option
--plugin-config
option takes a plugin configuration yaml file.
Plugin configuration file should contain a GlobalPlugins
list.
Elements of the GlobalPlugins
list defines which plugins should be
loaded. Each value of the list is an object containing all the information
required to load and initialize a specific plugin.
Plugin configuration file format should be as follows::
---
GlobalPlugins:
- Type: PluginInterfaceType
Name: PluginName
Library: LibraryName
Options: PluginOption
OutputSectionPlugins:
- OutputSection : OutputSectionName
Type : PluginInterfaceType
Name : PluginName
Library : LibraryName
Options : PluginOption
GlobalPlugins
list can specify any number of elements.
Options
member is optional.
Library
name should be specified without the lib prefix on Linux
and without the .so/.dll suffix on Linux/Windows
ControlMemorySizePlugin
and ControlFileSizePlugin
are output
section plugins. Therefore, in the plugin configuration file, they need to be
added to OutputSectionPlugins
list.
Note
Only one output section plugin can be attached to an output section.
1.5.3. Running plugins which inherits multiple plugin interface classes
Plugins can inherit from multiple plugin interface classes to have functionalities of multiple plugin interfaces. For such plugins, the functionalities of each plugin interface, from which the plugin inherits, need to be selectively enabled. This means:
In the case of a linker script, there should be a separate plugin invocation command for each plugin interface, from which the plugin inherits.
In the case of plugin configuration file, there should be an entry in the
GlobalPlugins
list for each plugin interface type, from which the plugin inherits.
1.6. User Plugin Workflow
The following steps describe how to develop a plugin:
Determine the appropriate plugin interfaces for your plugin.
A plugin type can be one of
LinkerPlugin
,SectionIterator
,SectionMatcher
,OutputSectionIterator
,ControlMemorySize
, andControlFileSize
.A plugin should ideally follow the unix-philosophy of doing one thing well. You should generally try to avoid inheriting from multiple plugin interfaces if possible. It generally leads to more complicated code, and a plugin that does more than one thing. But that being said, if you do think functionalities of multiple plugin interfaces will improve your plugin’s design and readability, then you should go ahead with inheriting from multiple plugins.
You can also create and chain multiple plugins for your task, similar in ideology to how unix utilities operate, where action of one plugin depends on the action and result of the previous one.
Create a C++ source file that will contain your plugin(s).
Plugins source file needs to include the headers of the plugin interface(s) that the plugin(s) inherits from. Plugin interfaces header files are contained in,
${HEXAGON_TOOLCHAIN}/Tools/include/ELD/PluginAPI
.
Create a C++ class that inherits from the appropriate plugin interface(s) for each plugin.
Override virtual functions.
Virtual functions are the means the linker works with the plugins. Each plugin interface provides hooks at various place in the linker codebase, the hooks functionalities are implemented by overriding virtual functions. There are also other virtual functions that do not implement functionalities for the hooks, but are required to work with the plugin infrastructure. Example of these virtual functions are:
Plugin::GetName
,Plugin::GetLastError
,Plugin::GetLastErrorAsString
etc.
Associate the plugins with unique names.
Build a shared library for the source file.
In a unix environment, shared library for the plugins can be created as:
# Compile the plugins source file clang++ -c -I${HEXAGON_TOOLCHAIN_ROOT}/Tools/include ${SOURCE_BASENAME}.cpp -fPIC -stdlib=libc++ # Link the plugin library with linker wrapper library, LW. clang++ -shared ./${SOURCE_BASENAME}.o -L${HEXAGON_TOOLCHAIN_ROOT}/Tools/lib -lLW -stdlib=libc++ -o lib${SOURCE_BASENAME}.so
Define
RegisterAll
function in C linkage.
RegisterAll
function goal is to register the plugins contained in the file. Registering a plugin here simply means to initialize a plugin object.Linker calls this function for each plugin library. This function should creates a plugin object for each plugin that the library contains.
Define a
getPlugin(const char *pluginName)
function in C linkage.
getPlugin(const char* plugin)
function goal is to return the requested plugin. Each plugin in the plugin source file should be uniquely identifiable by a name.Linker calls this function at the time of loading plugins to get a plugin object of plugin named
pluginName
.
Make the plugin report its API version
Linker verifies linker plugin compatibility. Linker will report an error and refuse to load a plugin if it determines that it is not compatible.
Linker plugin compatibility is determined using Linker API version. Plugin API version consists of major and minor version numbers. A plugin is compatible with the linker if plugin’s major API version is equal to the linker major API version and plugin’s minor API version is equal or lower to the linker’s minor API version. The major API version is incremented when a existing functionality in the API is changed in an incompatible way. Such changes are expected to be rare. The minor API version is incremented when new functionality is added to the Plugin API. Note that Plugin API versions are distinct from linker release version numbers. The current linker Plugin API version is reported by the
--version
command line option.For the versioning mechanism to work, each plugin must report its API version by exporting the
getPluginAPIVersion(unsigned int *Major, unsigned int *Minor)
function (with C linkage). For convenience, this function is defined in the header filePluginVersion.h
and including this file in one of the plugin’s C++ source files will automatically make the plugin report its correct API version.Starting with version 19.0, each plugin must report its API version. Linker will refuse to load a plugin that does not report its version.
(Optionally) Define a
getPluginConfig(const char *pluginName)
in C linkage to return the configuration object of the requested plugin.(Optionally) Define a
Cleanup
function in C linkage.
This function is called at the time unloading plugins. This function generally deletes any allocated memory that was required for the lifetime of the plugin library.
1.7. How the linker runs plugin
Linker performs the following operations to load, run and unload plugins.
Finds all the plugins, from linker script and plugins configuration file, that needs to be attached to the link process.
Loads all the specified plugin libraries.
To find plugin libraries,
LD_LIBRARY_PATH
environment variable is used on unix environment.Standard method for searching dynamic libraries is used in Windows.
Calls
RegisterAll
function from each plugin library. This function should ideally create the plugin objects for each plugin defined in the library.Calls
getPlugin(const char *pluginName)
function for each plugin.This function returns an appropriate plugin object for pluginName plugin. This object is used to run the plugin.
Calls
getPluginConfig(const char *pluginName)
function, if available, for each plugin. This function returns an appropriate plugin configuration object for pluginName plugin.Inspects the plugin to verify that the plugin load specification and plugin type have same plugin interface type.
Initializes each plugin at their Init hook point by calling
Plugin::Init(std::string Option)
virtual function. Option argument is optionally specified by the plugin user at the plugin load specification.Calls
Cleanup
function, if available, for each plugin library. This function should ideally delete any allocated memory that was required for the lifetime of the plugin library.Unload the plugins and the plugin libraries.
1.8. Tracing plugins
Plugin workflow can be traced using the option --trace=plugin
.
This option informs the linker to emit detailed diagnostics for all the plugins.
A sample trace output is shown below:
...
...
Note: Registration function found RegisterAll in Library libDiagOpt.so
Note: Plugin handler getPlugin found in Library libDiagOpt.so
Note: Cleanup function found Cleanup in Library libDiagOpt.so
Note: Plugin Config function getPluginConfig found in library libDiagOpt.so
Note: Registering all plugin handlers for plugin types
Note: Found plugin handler for plugin type DIAGRELOCATION in Library libDiagOpt.so
Note: Initializing Plugin libDiagOpt.so, requested by Plugin DIAGRELOCATION having Name DIAGOPT
...
...
Note: Plugin DIAGRELOCATION Destroyed
<-------------------------> DiagOpt::Destroy() AfterLayout<------------------------->
Note: Unloaded Library libDiagOpt.so.16
1.9. Link States

The link process has different run states, also known as link states. The actions that a plugin can perform at any given time depend on the current link state. Many actions are only meaningful for specific link states. Understanding what happens in each link state will help determine which actions can be performed in each state. Therefore, a thorough understanding of linker and link states is crucial for writing a linker plugin. Different link states are described below:
1.9.1. Initializing
The linker begins with the Initializing link state. In this state, the linker reads input files, initializes standard sections and configurations, and read relocations. Not much of interest happens during this state.
Let’s move on to the more happening states.
1.9.2. BeforeLayout
After the Initializing state, the linker enters the BeforeLayout link state. Here, it performs section rule matching, garbage collection, updates input sections with attributes (such as KEEP) present in the linker script, and updates the linker cache-file, among other things.
1.9.3. CreatingSections
After the BeforeLayout state, the linker enters the CreatingSections link state. Here, it transfers the contents (chunks) of input sections into output sections and finalizes the layout and symbols.
Note that the section rule-matching is already complete before the CreatingSections state, therefore, plugins cannot alter the section rule-matching in CreatingSections state or any subsequent state.
1.9.4. AfterLayout
After the BeforeLayout state, the linker enters Afterlayout link state. Here, it finalizes the relocations and writes the output object file.
In this link state, a plugin can compute output image layout checksum using
eld::plugin::LinkerWrapper::getImageLayoutChecksum
. A plugin cannot perform
any action that tries to modify the image layout now.
1.10. Deep Dive into the Plugin Interfaces
1.10.1. Plugin class
This is the base plugin class for all the plugin interface classes. It provides common functionalities to all the plugin interfaces. This class can not be directly used by a user.
Warning
doxygenclass: Cannot find class “eld::plugin::Plugin” in doxygen xml output for project “ELD” from directory: /local/mnt/workspace/partaror/eld/formal/obj/tools/eld/docs/userguide/api_docs/xml
Please note, that even though the virtual functions – Plugin::Init
,
Plugin::Run
and Plugin::Destroy
are available in
all the plugin interfaces, they map to different hooks in the linker for
different plugin interfaces. For example, Run
hook of
SectionIteratorPlugin`
is not equivalent to the Run
hook of
OutputSectionIteratorPlugin
even though in both cases, the hooks
functionality is defined by overriding the Run
virtual function.
For the base Plugin
, these functions are not mapped to any hook in
the linker.
1.10.2. Linker Plugin Interface
LinkerPlugin
interface is the simplest plugin interface type that can
be used to create plugins. It provides three hooks: Init
, Run
and Destroy
in the linker. To define behavior of these hooks, the
functions, Plugin::Init
, Plugin::Run
and
Plugin::Destroy
must be overridden.
It is called into action after linker performs garbage-collection of
input sections and symbols, and before merging input sections.
Merging input sections means merging all the input sections that are
mapped to the same input section description. Link state
throughout the plugin run is eld::plugin::LinkerWrapper::BeforeLayout
.

Linker script keyword for LinkerPlugin
interface is LINKER_PLUGIN
. Therefore,
to run a LinkerPlugin
using a linker script, add the following line to the linker script:
LINKER_PLUGIN("LibraryName", "PluginName" [, "PluginOption"])
1.10.3. Section Matcher Interface
SectionMatcherPlugin
interface iterates input sections. It provides
four hooks: Init
, ProcessSection
, Run
and
Destroy
. To define the behavior of these hooks, the functions
Plugin::Init
, SectionMatcher::ProcessSection
and Plugin::Destroy
must be overridden.
ProcessSection(Section S)
callback hook function is called for each
input section.
SectionMatcherPlugin
is called into action after the linker reads input
files and performs section rule-matching, but before
garbage collection. As a consequence, garbage-collection information
cannot be accessed during
SectionMatcherPlugin
run. Link state throughout the plugin run is
eld::plugin::LinkerWrapper::BeforeLayout
.

UserPlugin::Init
is called before UserPlugin::ProcessSection
,
and UserPlugin::Run
and UserPlugin::Destroy
are called
subsequently after processing input sections, as described in the figure above.
Linker script keyword for SectionMatcherPlugin
interface is
PLUGIN_SECTION_MATCHER
. Therefore, to run a
SectionMatcherPlugin
plugin using a linker script, add the following
line to the linker script:
PLUGIN_SECTION_MATCHER("LibraryName", "PluginName" [, "PluginOption"])
1.10.4. Section Iterator Interface
SectionIteratorPlugin
interface iterates over input sections.
It provides four hooks: Init
, ProcessSection
, Run
and Destroy
. To define the behavior of these hooks, the functions
Plugin::Init
, Plugin::ProcessSection
,
SectionIterator::ProcessSection
and Plugin::Destroy
must be overridden.
ProcessSection(Section S)
callback hook function is not called for
garbage collected input sections. However, it is called for discarded
input sections.
SectionIteratorPlugin
is called into action after the linker performs
section rule-matching and garbage collection. As a consequence, section rule
matching and garbage collection information is accessible during
SectionIteratorPlugin
run. Link state throughout the plugin run is
eld::plugin::LinkerWrapper::BeforeLayout
.

UserPlugin::Init
is called before ProcessSection
,
and UserPlugin::Run
and UserPlugin::Destroy
are called
after ProcessSection
, as described in the figure above.
Linker script keyword for SectionIteratorPlugin
interface is
PLUGIN_ITER_SECTIONS
. Therefore, to run a
SectionIteratorPlugin
plugin using a linker script, add the following
line to the linker script:
PLUGIN_ITER_SECTIONS("LibraryName", "PluginName" [, "PluginOption"])
1.10.5. Output Section Iterator Interface
outputSectionIteratorPlugin
interface iterates over all the
output sections. It provides four hooks: Init
,
ProcessOutputSection
, Run
and Destroy
.
To define the behavior of these hooks, the functions Plugin::Init
,
Plugin::ProcessSection
,
OutputSectionIteratorPlugin::ProcessOutputSection
and
Plugin::Destroy
must be overridden. OutputSectionIteratorPlugin
is called at different link states:
BeforeLayout
CreatingSections
AfterLayout
All the plugin hooks are called for each of the link states. That means,
each of Init
, Run
and Destory
are called 3 times over the complete plugin run. And
ProcessOutputSection
is called three times for each
output section over the complete plugin run.
The link state can be queried via LinkerWrapper::getState
member function.
Linker script keyword for OutputSectionIteratorPlugin
interface is
PLUGIN_OUTPUT_SECTION_ITER
. Therefore, to run a
SectionIteratorPlugin
using a linker script, add the following line
to the linker script:
PLUGIN_OUTPUT_SECTION_ITER("LibraryName", "PluginName" [, "PluginOption"])
Different phases of OutputSectionIteratorPlugin
are described below:
1.10.5.1. BeforeLayout – Phase 1

This phase is called into action after the linker has performed section rule-matching and before the linker has performed section merging.
1.10.5.2. CreatingSections – Phase 2

This phase is called into action after the linker has performed section merging.
1.10.5.3. AfterLayout – Phase 3

This phase is called into action after the linker has finalized the image layout and before the linker has written the output object file.
1.10.6. ControlMemorySizePlugin
This interface allows the plugin to add input sections to the output image. These newly added input sections are generally, but not necessary, based on some already existing output section.
ControlMemorySizePlugin
is an output section plugin. Plugins of this
type has to explicitly mark each output section that it needs to run on. Since
this is an output section plugin, init
and destroy
functions are also
called for each output section the plugin runs on. This plugin is called in the
LinkerWrapper::CreatingSections step of the linker.

init
, AddBlocks
, Run
, GetBlocks
and Destroy
functions are called for each output section.
Brief description of ControlMemorySizePlugin
hooks:
1.10.6.1. init
This hook initializes the plugin for the output section.
1.10.6.2. AddBlocks
This hook gives access to the current output section to the plugin.
Block
parameter of the hook function contains the current
output section. It is generally used in the creation of the new block
which linker can use as an additional input section.
1.10.6.3. Run
This hook is generally used to process the current output section, and create any new sections that linker should use as additional input sections.
1.10.6.4. GetBlocks
This hook returns a vector of Block
. These blocks are used by the
linker as additional input sections.
1.10.6.5. Destroy
This hook is used to perform any required clean-up.
Linker script keyword for ControlMemorySize interface is
PLUGIN_CONTROL_MEMSZ
. Therefore, to run a SectionIterator plugin
on an output section, .out
using a linker script, add the following
line to the output section description of .out
in the linker script:
SECTIONS {
.out PLUGIN_CONTROL_MEMSZ("LibraryName", "PluginName" [, "PluginOption"]) : { *(.out) }
}