2. Linker Plugin Examples
This section introduces and explains plugin framework APIs in a hands-on manner. The goal of these examples is to demonstrate how to use plugin framework APIs, rather than to present practical plugin use cases. Each plugin example introduces some new plugin framework APIs while keeping the example short and simple.
Each example will include the plugin, instructions on how to build and run it, and an analysis of the plugin’s run. To build and run the plugin, you will need access to the Hexagon toolchain. In the snippets below, the substitution ${HEXAGON} refers to the Hexagon toolchain installation path. Let’s get started.
2.1. Exclude symbols from the output image symbol table
This example plugin describes how to exclude symbols from the output image symbol table. Note that the plugin does not completely remove symbols from the link process, it only excludes symbols from the output image symbol table.
ExcludeSymbols plugin with self-contained documentation.
#include "ELD/PluginAPI/PluginVersion.h"
#include "ELD/PluginAPI/SectionIteratorPlugin.h"
#include <string>
#include <vector>
class ExcludeSymbols : public eld::plugin::SectionIteratorPlugin {
public:
ExcludeSymbols() : eld::plugin::SectionIteratorPlugin("ExcludeSymbols") {}
// 'Init' callback hook can be used for initialization and preparations.
void Init(std::string Options) override {
m_SymbolsToRemove = {"foo", "fooagain", "bar", "baragain"};
}
// 'processSection' callback hook is called for each input section that is not
// garbage-collected.
void processSection(eld::plugin::Section O) override {}
// 'Run' callback hook is called after 'processSection' callback hook calls.
// It is called once for each section iterator plugin run.
eld::plugin::Plugin::Status Run(bool trace) override {
for (auto symName : m_SymbolsToRemove) {
eld::plugin::Symbol S = Linker->getSymbol(symName);
if (S)
Linker->removeSymbolTableEntry(S);
}
return eld::plugin::Plugin::Status::SUCCESS;
}
// 'Destroy' callback hook can be used for finalization and clean-up tasks.
// It is called once for each section iterator plugin run.
void Destroy() override {}
uint32_t GetLastError() override { return 0; }
std::string GetLastErrorAsString() override { return "Success"; }
std::string GetName() override { return "ExcludeSymbols"; }
private:
std::vector<std::string> m_SymbolsToRemove;
};
eld::plugin::Plugin *ThisPlugin = nullptr;
extern "C" {
bool RegisterAll() {
ThisPlugin = new ExcludeSymbols{};
return true;
}
eld::plugin::Plugin *getPlugin(const char *pluginName) { return ThisPlugin; }
void Cleanup() {
if (!ThisPlugin)
return;
delete ThisPlugin;
ThisPlugin = nullptr;
}
}
ExcludeSymbols plugin removes the symbols ‘foo’, ‘fooagain’, ‘bar’, and ‘baragain’, if they exist, from the output image symbol table.
To build the plugin, run the following command:
clang++-14 -o libExcludeSymbols.so ExcludeSymbols.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include -L${HEXAGON}/lib -lLW
Now, let’s see the effect of this plugin on a sample program.
1.c
int foo() { return 1; }
int fooagain() { return 2; }
int abc() { return 3; }
int bar = 3;
int baragain = 4;
int def = 5;
int main() { return 0; }
1.linker.script
PLUGIN_ITER_SECTIONS("ExcludeSymbols", "ExcludeSymbols");
Now, let’s build 1.c with the ExcludeSymbols plugin enabled.
export LD_LIBRARY_PATH="${HEXAGON}/lib:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.o 1.c -c -ffunction-sections -fdata-sections
hexagon-link -o 1.elf 1.o -T 1.linker.script
We can list the symbols present in an object file using hexagon-readelf.
hexagon-readelf -s 1.elf
You can observe in the symbol table output that ‘foo’, ‘fooagain’, ‘bar’, and ‘baragain’ symbols has been removed from the symbol table as directed by the plugin.
2.2. Add Linker Script Rules
This example plugin describes how to add and modify linker script rules. By doing so, you can perform section rule-matching at a more granular level than what is possible through linker scripts.
LinkerScriptRule
is similar to an input section description in that
it has a corresponding output section. However, unlike an input section
description, it does not match input sections by pattern matching.
Instead, a plugin must manually match chunks to a LinkerScriptRule
object. This generally involves moving chunks from one
LinkerScriptRule
object to another. It is important to remove a chunk
from the old LinkerScriptRule
object once it has been added to a
new one. It is an undefined behavior for a chunk to be part of multiple
LinkerScriptRule
objects.
Also, you must only move chunks from one LinkerScriptRule
object to
another in the CreatingSections link state. It is an undefined behavior to
move chunks in other link states.
The AddRule plugin moves chunks from foo
and bar
output
sections to the var
output section. Let’s see how to create this
plugin.
AddRule plugin with self-contained documentation.
#include "ELD/PluginAPI/OutputSectionIteratorPlugin.h"
#include "ELD/PluginAPI/PluginVersion.h"
class AddRule : public eld::plugin::OutputSectionIteratorPlugin {
public:
AddRule() : eld::plugin::OutputSectionIteratorPlugin("AddRule") {}
// 'Init' callback hook can be used for initialization and preparations.
// This plugin does not need any initialization or preparation.
void Init(std::string cfg) override {}
// 'processOutputSection' callback hook is called once for each output
// section. In this function, the plugin stores 'var, 'foo' and 'bar' output
// sections in member variables.
void processOutputSection(eld::plugin::OutputSection O) override {
// OutputSectionIterator plugin essentially runs three times.
// It is run once for each of the following three link states: BeforeLayout,
// CreatingSections and AfterLayout.
// We are only interested in one link state, CreatingSections, as chunks can
// only be moved from one LinkerScriptRule to another in the
// CreatingSections link state. Thus, we simply return for the other link
// states. We will do this for each callback hook function.
if (Linker->getState() != eld::plugin::LinkerWrapper::State::CreatingSections)
return;
if (O.getName() == "var") {
m_Var = O;
} else if (O.getName() == "foo") {
m_Foo = O;
} else if (O.getName() == "bar") {
m_Bar = O;
}
}
// 'Run' callback hook is called after all the 'processSection' callback hook
// calls.
eld::plugin::Plugin::Status Run(bool trace) override {
if (Linker->getState() != eld::plugin::LinkerWrapper::State::CreatingSections)
return eld::plugin::Plugin::Status::SUCCESS;
auto lastRule = m_Var.getLinkerScriptRules().back();
// Create a new rule for m_Var output section.
// Annotation is used to name the linker sript rule, and is useful
// for diagnostic purposes.
eld::plugin::LinkerScriptRule newRule = Linker->createLinkerScriptRule(
m_Var, /*Annotation=*/"Move foo and bar chunks to var");
// Insert the newly created linker script rule in the m_Var output section.
// We can also insert the newly created rule before some already existing
// rule using LinkerWrapper::insertBeforeRule API.
Linker->insertAfterRule(m_Var, lastRule, newRule);
moveChunks(m_Foo, newRule);
moveChunks(m_Bar, newRule);
return eld::plugin::Plugin::Status::SUCCESS;
}
// 'Destroy' callback hook can be used for finalization and clean-up tasks.
// It is called once for each section iterator plugin run.
void Destroy() override {}
uint32_t GetLastError() override { return 0; }
std::string GetLastErrorAsString() override { return "Success"; }
std::string GetName() override { return "AddRule"; }
private:
void moveChunks(eld::plugin::LinkerScriptRule oldRule,
eld::plugin::LinkerScriptRule newRule) {
for (eld::plugin::Chunk C : oldRule.getChunks()) {
// It is crucial to maintain that no two LinkerScriptRule objects contain
// the same chunk. It is an undefined behavior for a chunk to be contained
// by multiple linker script rules.
Linker->addChunk(newRule, C);
Linker->removeChunk(oldRule, C);
}
}
void moveChunks(eld::plugin::OutputSection oldSection,
eld::plugin::LinkerScriptRule newRule) {
for (eld::plugin::LinkerScriptRule rule : oldSection.getLinkerScriptRules())
moveChunks(rule, newRule);
}
private:
eld::plugin::OutputSection m_Var = eld::plugin::OutputSection(nullptr);
eld::plugin::OutputSection m_Foo = eld::plugin::OutputSection(nullptr);
eld::plugin::OutputSection m_Bar = eld::plugin::OutputSection(nullptr);
};
eld::plugin::Plugin *ThisPlugin = nullptr;
extern "C" {
// RegisterAll should initialize all the plugins that a plugin library aims
// to provide. Linker calls this function before running any plugins provided
// by the library.
bool RegisterAll() {
ThisPlugin = new AddRule{};
return true;
}
// Linker calls this function to request an instance of the plugin
// with the plugin name pluginName. pluginName is provided in the plugin
// invocation command.
eld::plugin::Plugin *getPlugin(const char *pluginName) { return ThisPlugin; }
// Cleanup should free all the resources owned by a plugin library.
// Linker calls this function after all runs of the plugins provided
// by the library have completed.
void Cleanup() {
if (!ThisPlugin)
return;
delete ThisPlugin;
ThisPlugin = nullptr;
}
}
To build the plugin, run the following command:
clang++-14 -o libAddRule.so AddRule.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include -L${HEXAGON}/lib -lLW
Now, let’s see the effect of this plugin on a sample program.
1.c
int foo() { return 1; }
int bar() { return 2; }
int baz() { return 3; }
int int_var = 1;
long long_var = 2;
double double_var = 3;
int main() {
return 0;
}
1.linker.script
SECTIONS {
foo : { *(.text.foo) }
bar : { *(.text.bar) }
baz : { *(.text.baz) }
var : { *(*var) }
}
PLUGIN_OUTPUT_SECTION_ITER("AddRule", "AddRule");
Now, let’s build 1.c with the AddRule plugin enabled.
export LD_LIBRARY_PATH="${HEXAGON}/lib:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.o 1.c -c -ffunction-sections -fdata-sections
hexagon-link -o 1.elf 1.o -T 1.linker.script -Map 1.map.txt
We can see the symbol to section mapping using hexagon-readelf.
hexagon-readelf -Ss 1.elf
readelf output:
There are 8 section headers, starting at offset 0x12a0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] baz PROGBITS 00000000 001000 00000c 00 AX 0 0 16
[ 2] .text.main PROGBITS 00000010 001010 000014 00 AX 0 0 16
[ 3] var PROGBITS 00000030 001030 00002c 00 WAXp 0 0 16
[ 4] .comment PROGBITS 00000000 00105c 0000d2 01 MS 0 0 1
[ 5] .shstrtab STRTAB 00000000 00112e 000037 00 0 0 1
[ 6] .symtab SYMTAB 00000000 001168 0000e0 10 7 6 4
[ 7] .strtab STRTAB 00000000 001248 000054 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
R (retain), p (processor specific)
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1 baz
2: 00000000 0 SECTION LOCAL DEFAULT 4 .comment
3: 00000010 0 SECTION LOCAL DEFAULT 2 .text.main
4: 00000030 0 SECTION LOCAL DEFAULT 3 var
5: 00000000 0 FILE LOCAL DEFAULT ABS 1.c
6: 00000000 12 FUNC GLOBAL DEFAULT 1 baz
7: 00000010 20 FUNC GLOBAL DEFAULT 2 main
8: 00000030 4 OBJECT GLOBAL DEFAULT 3 int_var
9: 00000034 4 OBJECT GLOBAL DEFAULT 3 long_var
10: 00000038 8 OBJECT GLOBAL DEFAULT 3 double_var
11: 00000040 12 FUNC GLOBAL DEFAULT 3 foo
12: 00000050 12 FUNC GLOBAL DEFAULT 3 bar
13: 00000061 0 NOTYPE GLOBAL DEFAULT ABS __end
You can observe in the readelf output that the section var
contains
the foo
and the bar
symbols, which is exactly what we expected
from the plugin.
2.3. Reading INI Files Functionality
The plugin framework provides built-in support for the input/output operations of INI files. Linker plugins, like any other tool, may require external options and configurations. Traditionally, linker plugins use INI files for this purpose. This plugin demo demonstrates how to use INI files to provide options and configurations to a plugin. You can also use JSON, YAML, plain text, or any other format you prefer. However, for consistency with other linker plugins, I suggest using INI files for the plugin options and configurations.
The demo will feature the PrintSectionsInfo plugin. This plugin will print section information for all input sections that match any of the section name patterns specified in the plugin configuration file. The plugin will expect the configuration file to in INI format.
PrintSectionsInfo plugin with self-contained documentation.
To build the plugin, run:
clang++-14 -o libPrintSectionsInfo.so PrintSectionsInfo.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include \
-L${HEXAGON}/lib -lLW
Now, let’s see the effect of this plugin on a sample program.
1.c
int i[5] = {1, 2, 3, 4, 5};
int arr[100];
int foo() { return 1; }
int bar() { return 2; }
int main() { return 0; }
1.linker.script
# PluginConf.ini is the plugin configuration file name.
PLUGIN_SECTION_MATCHER("PrintSectionsInfo", "PrintSectionsInfo", "PluginConf.ini");
PluginConf.ini
[sections]
*i=1
*arr=1
*foo=0
*bar=1
Now, let’s build 1.c with PrintSectionsInfo plugin enabled.
export LD_LIBRARY_PATH="${HEXAGON}/lib:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.o 1.c -c -ffunction-sections -fdata-sections
hexagon-link -o 1.elf 1.o -T 1.linker.script
The plugin gives the below output:
.text.bar
Input file: 1.o
Section index: 4
Section alignment: 16
.data.i
Input file: 1.o
Section index: 6
Section alignment: 8
COMMON.arr
Input file: CommonSymbols
Section index: 0
Section alignment: 8
As expected, the plugin prints the section information of the sections whose names match a pattern specified in the plugin configuration file, PluginConf.ini.
2.4. Modify Relocations
The ModifyRelocations example plugin demonstrates how to inspect and modify
relocations. This plugin modifies the relocation symbol from
HelloWorld
to HelloQualcomm
. Additionally, it prints the
relocation source section and symbol for each relocation it iterates over.
To inspect and modify relocations, ModifyRelocations uses
LinkerPluginConfig
. LinkerPluginConfig
provides a callback
hook for relocations. This callback hook function is called for each
relocation that is of a registered relocation type. Relocation types can be
registered using LinkerWrapper::registerReloc
.
ModifyRelocations plugin with self-contained documentation.
#include "ELD/PluginAPI/PluginVersion.h"
#include "ELD/PluginAPI/SectionIteratorPlugin.h"
#include <iostream>
class ModifyRelocations : public eld::plugin::SectionIteratorPlugin {
public:
ModifyRelocations() : SectionIteratorPlugin("ModifyRelocations") {}
// 'Init' callback hook can be used for initialization and preparations.
// This plugin does not need any initialization or preparation.
void Init(std::string cfg) override {}
// 'processSection' callback hook of SectionIteratorPlugin is called for
// each non-garbage collected section.
void processSection(eld::plugin::Section S) override {}
// 'Run' callback hook is called after all the 'processSection' callback hook
// calls. It is called once for each section iterator plugin run.
// This plugin does not need to run anything.
eld::plugin::Plugin::Status Run(bool trace) override {
return eld::plugin::Plugin::Status::SUCCESS;
}
// 'Destroy' callback hook can be used for finalization and clean-up tasks.
// It is called once for each section iterator plugin run.
// This plugin does not need any finalization and clean-up.
void Destroy() override {}
uint32_t GetLastError() override { return 0; }
std::string GetLastErrorAsString() override { return "Success"; }
std::string GetName() override { return "ModifyRelocations"; }
eld::plugin::LinkerWrapper *getLinker() { return Linker; }
};
// LinkerPluginConfig allows to inspect and modify relocations.
class ModifyRelocationsPluginConfig : public eld::plugin::LinkerPluginConfig {
public:
ModifyRelocationsPluginConfig(ModifyRelocations *P)
: LinkerPluginConfig(P), P(P) {}
void Init() override {
// Register R_HEX_B22_PCREL relocation type.
// Linker will call RelocCallBack callback hook function on each relocation
// that is of a registered relocation type.
std::string b22pcrel = "R_HEX_B22_PCREL";
uint32_t relocationType =
P->getLinker()->getRelocationHandler().getRelocationType(b22pcrel);
P->getLinker()->registerReloc(relocationType);
}
// Relocation callback hook function.
void RelocCallBack(eld::plugin::Use U) override {
// Print relocation source section name and symbol names.
std::string sourceSectionName = U.getSourceChunk().getName();
std::cout << "Relocation callback. Source section: " << sourceSectionName
<< ", symbol: " << U.getName() << "\n";
// Change relocation symbol from HelloWorld to HelloQualcomm.
if (U.getSymbol().getName() == "HelloWorld") {
eld::Expected<eld::plugin::Symbol> expHelloQualcommSymbol =
P->getLinker()->getSymbol("HelloQualcomm");
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(
P->getLinker(), expHelloQualcommSymbol);
eld::plugin::Symbol helloQualcommSymbol =
std::move(expHelloQualcommSymbol.value());
U.resetSymbol(helloQualcommSymbol);
}
}
private:
ModifyRelocations *P;
};
eld::plugin::Plugin *ThisPlugin = nullptr;
eld::plugin::LinkerPluginConfig *ThisPluginConfig = nullptr;
extern "C" {
// RegisterAll should initialize all the plugins and plugin configs that a
// plugin library aims to provide. Linker calls this function before running
// any plugins provided by the library.
bool DLL_A_EXPORT RegisterAll() {
ThisPlugin = new ModifyRelocations();
ThisPluginConfig = new ModifyRelocationsPluginConfig(
dynamic_cast<ModifyRelocations *>(ThisPlugin));
return true;
}
// Linker calls this function to request an instance of the plugin
// with the plugin name pluginName. pluginName is provided in the plugin
// invocation command.
eld::plugin::Plugin *getPlugin(const char *pluginName) { return ThisPlugin; }
// Linker calls this function to request an instance of the plugin
// configuration for the plugin with the plugin name pluginName.
// pluginName is provided in the plugin invocation command.
eld::plugin::LinkerPluginConfig DLL_A_EXPORT *getPluginConfig(const char *pluginName) {
return ThisPluginConfig;
}
// Cleanup should free all the resources owned by a plugin library.
// Linker calls this function after all runs of the plugins provided
// by the library have completed.
void DLL_A_EXPORT Cleanup() {
if (ThisPlugin)
delete ThisPlugin;
if (ThisPluginConfig)
delete ThisPluginConfig;
}
}
To build the plugin, run:
clang++-14 -o libModifyRelocations.so ModifyRelocations.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include -L${HEXAGON}/lib -lLW
Now, let’s see the effect of this plugin on a sample program.
1.c
#include <stdio.h>
const char *HelloWorld() {
return "Hello World!";
}
const char *HelloQualcomm() {
return "Hello Qualcomm!";
}
int main() {
printf("%s\n", HelloWorld());
}
1.linker.script
PLUGIN_ITER_SECTIONS("ModifyRelocations", "ModifyRelocations");
Now, let’s build 1.c with ModifyRelocations plugin enabled.
export LD_LIBRARY_PATH="/local/mnt/workspace/partaror/llvm-project-formal/obj/bin:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.elf 1.c -ffunction-sections -fdata-sections -Wl,-T,1.linker.script
Running the above commands gives the below output:
Relocation callback. Source section: .text, symbol: .start
...
...
Relocation callback. Source section: .text.main, symbol: HelloWorld
Relocation callback. Source section: .text.main, symbol: printf
...
This output is emitted by the plugin when it’s iterating over relocations of registered types.
Now, let’s try running the generated binary image, 1.elf:
$ hexagon-sim ./1.elf
hexagon-sim INFO: The rev_id used in the simulation is 0x00008d68 (v68n_1024)
Hello Qualcomm!
Done!
T0: Insns=5479 Packets=2946
T1: Insns=0 Packets=0
T2: Insns=0 Packets=0
T3: Insns=0 Packets=0
T4: Insns=0 Packets=0
T5: Insns=0 Packets=0
Total: Insns=5479 Pcycles=8841
Hello World! has been replaced by Hello Qualcomm!. It’s the result of
plugin changing the relocation symbol from HelloWorld
to
HelloQualcomm
for all R_HEX_B22_PCREL
relocations.
2.5. Section Rule-Matching
This example plugin describes how to modify section rule-matching using a
plugin. A plugin can create section overrides using the
LinkerWrapper::setOutputSection
API. Section overrides created by a
plugin override linker script section rule-matching. Plugins must call
LinkerWrapper::finishAssignOutputSections
after all section
overrides have been created. LinkerWrapper::finishAssignOutputSections
brings the section override change into effect.
Section overrides must only be used in the BeforeLayout link state. After the BeforeLayout state, chunks from input sections get merged into output sections, making section overrides meaningless.
The ChangeOutputSection plugin sets the output section of .text.foo
to bar
. That’s it. Let’s see how to create this plugin.
ChangeOutputSection plugin with self-contained documentation.
#include "ELD/PluginAPI/PluginVersion.h"
#include "ELD/PluginAPI/SectionIteratorPlugin.h"
#include <iostream>
class ChangeOutputSection : public eld::plugin::SectionIteratorPlugin {
public:
ChangeOutputSection() : eld::plugin::SectionIteratorPlugin("ChangeOutputSection") {}
// 'Init' callback hook can be used for initialization and preparations.
// This plugin does not need any initialization or preparation.
void Init(std::string cfg) override {}
// 'processSection' callback hook of SectionIteratorPlugin is called for
// each non-garbage collected section.
void processSection(eld::plugin::Section S) override {
if (S.matchPattern("*foo")) {
// Changes the output section of the section S to
// bar. LinkerWrapper::setOutputSection must only be
// called in BeforeLayout link state. Section overrides created
// after BeforeLayout link state do not work and can result in
// undefined behavior.
//
// Annotation is useful for diagnostic purposes. Later, we will see where
// to find these annotations.
Linker->setOutputSection(
S, "bar",
/*Annotation=*/"Setting output section of '.text.foo' to 'bar'");
}
}
// 'Run' callback hook is called after all the 'processSection' callback hook
// calls. It is called once for each section iterator plugin run.
eld::plugin::Plugin::Status Run(bool trace) override {
return eld::plugin::Plugin::Status::SUCCESS;
}
// 'Destroy' callback hook can be used for finalization and clean-up tasks.
// It is called once for each section iterator plugin run.
void Destroy() override {
// LinkerWrapper::finishAssignOutputSections must be called
// after all section overrides have been created by the plugin.
// It brings the created section overrides into effect.
Linker->finishAssignOutputSections();
}
uint32_t GetLastError() override { return 0; }
std::string GetLastErrorAsString() override { return "Success"; }
std::string GetName() override { return "ChangeOutputSection"; }
};
eld::plugin::Plugin *ThisPlugin = nullptr;
extern "C" {
// RegisterAll should initialize all the plugins that a plugin library aims
// to provide. Linker calls this function before running any plugins provided
// by the library.
bool RegisterAll() {
ThisPlugin = new ChangeOutputSection{};
return true;
}
// Linker calls this function to request an instance of the plugin
// with the plugin name pluginName. pluginName is provided in the plugin
// invocation command.
eld::plugin::Plugin *getPlugin(const char *pluginName) { return ThisPlugin; }
// Cleanup should free all the resources owned by a plugin library.
// Linker calls this function after all runs of the plugins provided
// by the library have completed.
void Cleanup() {
if (!ThisPlugin)
return;
delete ThisPlugin;
ThisPlugin = nullptr;
}
}
To build the plugin, run the following command:
clang++-14 -o libChangeOutputSection.so ChangeOutputSection.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include -L${HEXAGON}/lib -lLW
1.c
int foo() { return 1; }
int bar() { return 2; }
int main() { return 0; }
1.linker.script
PLUGIN_ITER_SECTIONS("ChangeOutputSection", "ChangeOutputSection");
SECTIONS {
foo : { *(.text.foo) }
bar : { *(.text.bar) }
text : { *(.text*) }
}
Now, let’s build 1.c with the ChangeOutputSection plugin enabled.
export LD_LIBRARY_PATH="${HEXAGON}/lib:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.o 1.c -c -ffunction-sections -fdata-sections
hexagon-link -o 1.elf 1.o -T 1.linker.script -Map 1.map.txt
Now, let’s see the output section of foo
.
hexagon-readelf -Ss 1.elf
hexagon-readelf output:
There are 7 section headers, starting at offset 0x1200:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] bar PROGBITS 00000000 001000 00001c 00 AX 0 0 16
[ 2] text PROGBITS 00000020 001020 000014 00 AX 0 0 16
[ 3] .comment PROGBITS 00000000 001034 0000d2 01 MS 0 0 1
[ 4] .shstrtab STRTAB 00000000 001106 00002d 00 0 0 1
[ 5] .symtab SYMTAB 00000000 001134 000090 10 6 5 4
[ 6] .strtab STRTAB 00000000 0011c4 00002a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
R (retain), p (processor specific)
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1 bar
2: 00000000 0 SECTION LOCAL DEFAULT 3 .comment
3: 00000020 0 SECTION LOCAL DEFAULT 2 text
4: 00000000 0 FILE LOCAL DEFAULT ABS 1.c
5: 00000000 12 FUNC GLOBAL DEFAULT 1 bar
6: 00000010 12 FUNC GLOBAL DEFAULT 1 foo
7: 00000020 20 FUNC GLOBAL DEFAULT 2 main
8: 00000039 0 NOTYPE GLOBAL DEFAULT ABS __end
You can observe from the readelf output that the bar
section contains
the foo
symbol. This means the output section of .text.foo
was
indeed changed to bar
.
That’s all well and good, but how do we see the nice annotation we added when
calling LinkerWrapper::setOutputSection
? All plugin actions, along
with their annotations, are recorded in the map file. The text map file can be
used to quickly view plugin actions and the annotations.
To see all the plugin action information, you can either view the text map file and find the “Detailed Plugin information” component, or run the command below to directly view the information from the text map file.
less +/"Detailed Plugin information" 1.map.txt
Output
# Detailed Plugin information
# Plugin #0 ChangeOutputSection
# Modification #1 {C, Comment : Setting output section of '.text.foo' to 'bar'}
# Section :.text.foo 1.o
# Original Rule : *(.text.foo) #Rule 1, 1.linker.script
# Modified Rule : *(bar) #Rule 4, Internal-LinkerScript (Implicit rule inserted by Linker)
2.6. Change Symbol Value to Another Symbol
The ChangeSymbolValue plugin demonstrates how to change the value of a
symbol to another symbol. Specifically, it changes the value of the
HelloWorld
symbol to the value of the HelloQualcomm
symbol.
It also attempts (unsuccessfully) to change the value of the
HelloWorldAgain
symbol to the value of the HelloQualcommAgain
symbol. We will see why this happens.
Now, let’s see how to create the ChangeSymbolValue plugin.
The ChangeSymbolValue plugin with self-contained documentation.
#include "ELD/PluginAPI/OutputSectionIteratorPlugin.h"
#include "ELD/PluginAPI/PluginVersion.h"
#include <iostream>
class ChangeSymbolValue : public eld::plugin::OutputSectionIteratorPlugin {
public:
ChangeSymbolValue()
: eld::plugin::OutputSectionIteratorPlugin("ChangeSymbolValue") {}
// 'Init' callback hook can be used for initialization and preparations.
// This plugin does not need any initialization or preparation.
void Init(std::string cfg) override {}
// 'processOutputSection' callback hook is called once for each output
// section.
void processOutputSection(eld::plugin::OutputSection O) override {}
// 'Run' callback hook is called after all the 'processSection' callback hook
// calls.
eld::plugin::Plugin::Status Run(bool trace) override {
if (Linker->getState() != eld::plugin::LinkerWrapper::State::AfterLayout)
return eld::plugin::Plugin::Status::SUCCESS;
// Try to reset the 'HelloWorld' symbol value to the value of
// 'HelloQualcomm' symbol.
eld::plugin::Symbol helloWorldSymbol = Linker->getSymbol("HelloWorld");
eld::plugin::Symbol helloQualcommSymbol = Linker->getSymbol("HelloQualcomm");
bool resetSym =
Linker->resetSymbol(helloWorldSymbol, helloQualcommSymbol.getChunk());
if (resetSym)
std::cout
<< "'HelloWorld' symbol value has been successfully reset to the "
<< "value of 'HelloQualcomm' symbol.\n";
else
std::cout << "Symbol value resetting failed for 'HelloWorld'.\n";
// Try to reset the 'HelloWorldAgain' symbol value to the value of
// 'HelloQualcommAgain' symbol.
eld::plugin::Symbol helloWorldAgainSymbol = Linker->getSymbol("HelloWorldAgain");
eld::plugin::Symbol helloQualcommAgainSymbol =
Linker->getSymbol("HelloQualcommAgain");
resetSym = Linker->resetSymbol(helloWorldAgainSymbol,
helloQualcommAgainSymbol.getChunk());
if (resetSym)
std::cout << "'HelloWorldAgain' symbol value has been successfully reset "
<< "to the "
<< "value of 'HelloQualcommAgain' symbol.\n";
else
std::cout << "Symbol value resetting failed for 'HelloWorldAgain'.\n";
return eld::plugin::Plugin::Status::SUCCESS;
}
void Destroy() override {}
uint32_t GetLastError() override { return 0; }
std::string GetLastErrorAsString() override { return "Success"; }
std::string GetName() override { return "ChangeSymbol"; }
};
eld::plugin::Plugin *ThisPlugin = nullptr;
extern "C" {
// RegisterAll should initialize all the plugins that a plugin library aims
// to provide. Linker calls this function before running any plugins provided
// by the library.
bool RegisterAll() {
ThisPlugin = new ChangeSymbolValue{};
return true;
}
// Linker calls this function to request an instance of the plugin
// with the plugin name pluginName. pluginName is provided in the plugin
// invocation command.
eld::plugin::Plugin *getPlugin(const char *pluginName) { return ThisPlugin; }
// Cleanup should free all the resources owned by a plugin library.
// Linker calls this function after all runs of the plugins provided
// by the library have completed.
void Cleanup() {
if (!ThisPlugin)
return;
delete ThisPlugin;
ThisPlugin = nullptr;
}
}
To build the plugin, run:
clang++-14 -o libChangeSymbolValue.so ChangeSymbolValue.cpp -std=c++17 -stdlib=libc++ -fPIC -shared \
-I${HEXAGON}/include -L${HEXAGON}/lib -lLW
Now, let’s see the effect of this plugin on a sample program.
1.c
#include <stdio.h>
const char *HelloWorld;
const char *HelloQualcomm = "Hello Qualcomm!";
const char *HelloWorldAgain = "Hello again World!";
const char *HelloQualcommAgain = "Hello again Qualcomm!";
int main() {
printf("%s\n", HelloWorld);
printf("%s\n", HelloWorldAgain);
}
1.linker.script
PLUGIN_OUTPUT_SECTION_ITER("ChangeSymbolValue", "ChangeSymbolValue");
Now, let’s build 1.c with ChangeSymbolValue plugin enabled.
export LD_LIBRARY_PATH="${HEXAGON}/lib:${LD_LIBRARY_PATH}"
hexagon-clang -o 1.elf 1.c -ffunction-sections -fdata-sections -Wl,-T,1.linker.script
Running the above commands produces the following output:
'HelloWorld' symbol value has been successfully reset to the value of 'HelloQualcomm' symbol.
Symbol value resetting failed for 'HelloWorldAgain'.
The above output is printed by the plugin. It indicates whether the resetting
of symbol values was successful or not. So, why was the resetting of
‘HelloWorldAgain’ not successful? LinkerWrapper::resetSymbol
can only
resets the value of the symbols that do not already have a value.
LinkerWrapper::resetSymbol
silently fails and returns false if the
symbol requested to be reset already has a value.
Now, let’s run the generated 1.elf binary:
$ hexagon-sim ./1.elf
hexagon-sim INFO: The rev_id used in the simulation is 0x00008d68 (v68n_1024)
Hello Qualcomm!
Hello again World!
Done!
T0: Insns=6810 Packets=3558
T1: Insns=0 Packets=0
T2: Insns=0 Packets=0
T3: Insns=0 Packets=0
T4: Insns=0 Packets=0
T5: Insns=0 Packets=0
Total: Insns=6810 Pcycles=10677
As expected, the output first prints, ‘Hello Qualcomm!’, as the value of the
HelloWorld`
symbol successfully got reset to the value of
HelloQualcomm
. The output then prints, ‘Hello again World!’, as the
resetting of HelloWorldAgain`
to HelloQualcommAgain`
was
unsuccessful.