A linkerset is a collection of unordered pointers that is bounded by a 'start' and 'stop. The 'stop' and 'start' symbols are automatically created by the Gnu Linker, and form an open-ended bounded by [start, stop). The memory referenced by the pointers in the [start, stop) range will refer to data generated by your program.
Linkersets can be used, for example, for the following:
o A list of initialization functions that correspond to modules that have been linked into the program.
o A list of finalization functions that must be executed at program termination. The contents will be based on what modules have been linked into the program.
o Pre-allocation memory requirements for various aspects of a system. During program startup, processing the linkerset would allocate the memory necessary.
o To automatically start a set of threads that is dependent on what is linked into the final program.
The operations supported by a linkerset are:
o Declaration (compile time).
A linkerset, and its name,must be declared before use. See
LINKERSET_DECLARE.
The declaration names the linkerset. The name of the
linkerset must have a corresponding type with '_t' as a
suffix. For example, if
LINKERSET_DECLARE(linkerset)
is used, there must be a C type named 'linkerset_t'.
Generally this data type is used to hold the actual data to
be grouped together in the linkerset. For example, a set of
functions used to start threads, or a collection of devices
that are to be supported.
NOTE: Linkersets are based on C type names. The result of
declaring multiple linkersets using same-name, yet
different C types, is undefined.
This would be possible in different C source files, where
the same type name is used, but those C types have different
structures. At link time, they contents of all same-named
linkersets will be collated into a single section.
o Add elements (compile time).
Data is added to a linkerset with LINKERSET_ADD_ITEM.
All data stored in a linkerset will be a pointer to the C
data structure implied via LINKERSET_DECLARE().
The elements added to the linkerset will be collated by the
linker. The linkerset will be contained in a specially-named
data section (the name is managed by this linkerset module,
and is not material to its use).
Only elements added to the linkerset that are actually linked
into the program will be present in the final executable.
This is made possible because the symbols are marked as
'weak'.
NOTE: A linkerset that has no data added, or for which
nothing is linked will have zero (0) elements at run
time.
o Iterate over elements in a linkerset (run time).
LINKERSET_ITERATE allows one to visit every element in the
linkerset.
o Determine number of elements in linkerset (run time).
While rarely needed outside of this header, it's possible to
determine the number of elements in a linkerset with
LINKERSET_SIZE().
o Sort elements (run time).
By definition a linkerset is unordered, but it can be brought
into an order of your choosing using LINKERSET_SORT(). The
pointers contained in the linkerset are sorted in-place.
Here's how a linkerset may look in memory:
header.h: struct example_t { unsigned v; };
a.c : example_t data_0; LINKERSET_ADD_ITEM(example, data_0);
b.c : example_t data_1; LINKERSET_ADD_ITEM(example, data_1);
memory layout .bss: data_0: <data_0 data> ... <more, unrelated, data> ... data_1: <data_1 data>
__start_example:
&data_1
&data_0
__stop_example
Notice that the ordering of the data in the linkerset -- [__start_example, __stop_example)) -- is not ordered in the same way as the data in the .bss section, nor as implied by the ordering of the C files.
This repository holds examples of using linkersets (sets of unordered data that can be produced by gcc / ld for arbitrary data. The initial examples that are included:
o Module initialization
Ensure that modules are initialized once, and in the proper order based on the DAG produced by modules importing other modules.
Execute 'make' in the directory, then run:
./example
o Simple
The simple example shoulds that data can be collected, and the values present in the final executable are dependent upon what is linked into the program. There are three configurations of the program:
make example circle point
./circle
./point
./example
The 'circle' and 'point' configurations only contain information about the corresponding shape in the linker set, but 'example' contains both -- controlled all at compile & link time.