| /* 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 |