blob: 63cd1c5e8289efddb2942477c81a577e679eca53 [file] [log] [blame]
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if !defined(_LINKERSET_H_)
#define _LINKERSET_H_
#include <assert.h>
#include <stdlib.h>
#include "stdmacro.h"
/* LINKERSET
*
* A linker set is a pair of special symbols, created by the linker,
* which form an open ended range, like [start, stop).
*
* The space between 'beg' and 'end' will be filled with pointers to
* descriptors created by the macros in this file.
*
* In basic terms a linker set allows the programmer to determine
* the size of a section of data, without resorting to linker
* scripts. All data stored in the linker sets implemented in this
* file must be pointers.
*
* To use a linker set, you need to do the following:
*
* o Name your linker set
*
* You must decide on a C identifier to use as a name for your
* linker set.
*
* This name is passed to the '_name' argument of the linker set
* macros.
*
* o Create linker set data type
*
* Each linker set must have an associated 'typedef' which is the
* name of the linker set with a '_t' suffix.
*
* Generally this data type is used to hole the data you want to
* group together in the linker set. For example, a set of
* functions used to start threads, or a set of devices that are
* to be supported.
*
* This name is generated by the linker set macros below.
*
* o Define the data
*
* Data is inserted into the linker set using LINKERSET_ADD_ITEM.
*
* o Declare the linker set
*
* Somewhere prior to iterating over the linker set, the set must
* be declared. This is accomplished with the 'LINKERSET_DECLARE'
* macro.
*
* The '_name' argument is the name of the linker set, as
* described above.
*
* o Iterate over the linker set
*
* To process the elements in a linker set, use the
* 'LINKERSET_ITERATE' macro.
*
* NOTES:
*
* It is possible to have a linker set that has no elements; in
* this case, both the symbol denoting the beginning & ending of
* the set will be NULL.
*
* There is no ordering on linker sets. If you want ordering, you
* must enforce it yourself.
*
* Linker sets are based on type names, thus the result of
* declaring linker sets using same-name, yet different, types is
* undefined.
*
* Here's how a linker set 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>
* data_0:
* <data_0 data>
* <more data>
* data_1:
* <data_1 data>
*
* __start_example:
* &data_1
* &data_0
* __stop_example
*
* Notice that the ordering of the data in the linker set pointer
* region -- [__start_example, __stop_example)) -- is not ordered in
* the same way as the .bss section, nor the order implied by the
* ordering of the C files.
*
* Both the linker set and the associated data are regular R/W data.
*/
#define LINKERSET_START(_name) \
XCONCAT(&__start_, _name)
#define LINKERSET_STOP(_name) \
XCONCAT(&__stop_, _name)
/* LINKERSET_DECLARE: Enable access to a linker set in a source file.
*
* The __start_<name> & __stop_<name> symbols are generated
* automatically by the linker, but they are not marked as global.
* Without otherwise being marked as global, the symbols will not be
* accessible to the code generated by these macros.
*
* This automatic symbol generation is a feature of C++ code
* generation and applies only to sections which are named with names
* that are C identifiers; '.section'-style names will not cause this
* symbol generation feature to occur.
*/
#define LINKERSET_DECLARE(_name) \
extern XCONCAT(_name, _t) WEAK *XCONCAT(__start_, _name); \
extern XCONCAT(_name, _t) WEAK *XCONCAT(__stop_, _name); \
__asm__(".global __start_" XSTRING(_name)); \
__asm__(".global __stop_" XSTRING(_name))
/* LINKERSET_ADD_ITEM: Add an item to a linker set.
*
* _name : The name of the linker set, as described above.
*
* _desc_name: The name of the variable which should be referenced by
* the linker set entry. This variable should be typed
* as the 'linker set data type', as described above.
* Generally this variable is statically initialized.
*/
#define LINKERSET_ADD_ITEM(_name, _desc_name) \
static void const * XCONCAT(__, XCONCAT(_name, \
XCONCAT(_ptr_, \
_desc_name))) \
__attribute__((section(XSTRING(_name)),used)) = &_desc_name
/* LINKERSET_ITERATE: Iterate over an entire linker set.
*
* _name: The name of the linker set, as described above.
*
* _var : Used to create a local variable which can be used to access
* the data associated with each linker set member. The
* created variable will be typed as a 'pointer to linker set
* data type', as described above.
*
* _body: Custom code that you want to be executed for each element
* in the linker set. You may use the '_var' argument to
* access fields of the data type.
*/
#define LINKERSET_ITERATE(_name, _var, _body) \
do { \
XCONCAT(_name, _t) **_beg = LINKERSET_START(_name); \
XCONCAT(_name, _t) **_end = LINKERSET_STOP(_name); \
while (_beg < _end) { \
XCONCAT(_name, _t) *_var = *_beg; \
_body; \
++_beg; \
} \
} while (0)
/* LINKERSET_SIZE: Produces the number of elements in a linker set.
*
* _name: The name of the linker set, as described above.
* _type: The desired type of the result.
*
* Note: This macro creates a result that is ptrdiff_t, and it may
* need further casting to meet size requirements. Be sure to
* test code using this macro on a machine where:
*
* sizeof(size_t) != sizeof(ptrdiff_t)
* sizeof(size_t) == sizeof(ptrdiff_t)
*/
#define LINKERSET_SIZE_PTRDIFF(_name) \
(LINKERSET_STOP(_name) - LINKERSET_START(_name))
#define LINKERSET_SIZE(_name, _type) \
(_type)LINKERSET_SIZE_PTRDIFF(_name)
/* LINKERSET_SORT: Sort a linker set using qsort().
*
* _name : The name of the linker set, as described above.
* _compare: A function which will be used as the compare function by
* qsort(). Each argument passed to '_compare' will be of
* type 'pointer to pointer to linker set data type'.
*/
#define LINKERSET_SORT(_name, _compare) \
do { \
const size_t n = LINKERSET_SIZE(_name, size_t); \
assert((ptrdiff_t)n == /* Truncation? */ \
LINKERSET_SIZE_PTRDIFF(_name)); \
qsort(LINKERSET_START(_name), n, \
sizeof(LINKERSET_START(_name)), _compare); \
} while (0)
#endif