[RTEMS Project] #2408: Linker set based initialization

RTEMS trac trac at rtems.org
Wed Sep 2 12:52:05 UTC 2015


#2408: Linker set based initialization
-----------------------------+------------------
 Reporter:  sebastian.huber  |      Owner:
     Type:  enhancement      |     Status:  new
 Priority:  normal           |  Milestone:  4.12
Component:  General          |    Version:  4.10
 Severity:  normal           |   Keywords:
-----------------------------+------------------
 Linker sets are used for example in Linux, FreeBSD (they are used in the
 RTEMS port of the FreeBSD network stack, e.g. libbsd), eCos and for global
 C++ constructors.  They provide a space efficient and flexible means to
 initialize modules.  A linker set consists of

 * dedicated input sections for the linker (e.g. `.ctors` and `.ctors.*` in
 the case of global constructors),
 * a begin marker (e.g. provided by `crtbegin.o`, and
 * an end marker (e.g. provided by `ctrend.o`).

 A module may place a certain data item into the dedicated input section.
 The linker will collect all such data items in this section and creates a
 begin and end marker.  The initialization code can then use the begin and
 end markers to find all the collected data items (e.g. function pointers).

 Lets look how this works using a simple example.  For this we need three
 files `myset.h`,
 {{{
 #!c
 #ifndef MYSET_H
 #define MYSET_H

 /* The linker set items */
 typedef struct {
   void (*func)(void);
 } item;

 /*
  * Macro to create a linker set item.  The first parameter is
  * the designator of the item.  It must be unique within the
  * module scope.  The second parameter is the desired function.
  */
 #define MYSET_ITEM(i, f) \
     __attribute__((used)) \
     __attribute__((section(".rtemsroset.myset.content"))) \
     static item i = { f }

 #endif /* MYSET_H */
 }}}
 `module.c`
 {{{
 #!c
 #include "myset.h"
 #include <stdio.h>

 /*
  * Some global function that needs a module specific
  * intialization done by f().
  */
 void
 g(void)
 {

         printf("g()\n");
 }

 /* The module constructor */
 static void
 f(void)
 {

         printf("f()\n");
 }

 /*
  * This registers the module constructor f()
  * in the linker set "myset".
  */
 MYSET_ITEM(i, &f);
 }}}
 and `init.c`.
 {{{
 #!c
 #include "myset.h"
 #include <stddef.h>

 /* Should be in a proper header file */
 void g(void);

 /* Define the start marker */
 __attribute__((used))
 __attribute__((section(".rtemsroset.myset.begin")))
 static volatile const item begin[0];

 /* Define the end marker */
 __attribute__((used))
 __attribute__((section(".rtemsroset.myset.end")))
 static volatile const item end[0];

 int main(void)
 {
         size_t n = &end[0] - &begin[0];
         size_t i;

         /* Call all functions of the linker set */
         for (i = 0; i < n; ++i) {
                 (*begin[i].func)();
         }

         /*
          * This will pull in the module.c and register its item in the
          * linker set "myset".  So g() can rely on f() being called first.
          */
         g();

         return (0);
 }
 }}}
 In the linker command file of the GNU linker we need the following
 statement.
 {{{
 .rtemsroset : {
         KEEP (*(SORT(.rtemsroset.*)))
 }}}
 The `KEEP()` ensures that a garbage collection by the linker will not
 discard the content of this section.  This would be normally the case
 since the linker set items are not referenced directly.  The `SORT()`
 directive sorts the input sections lexicographically.  Please note the
 lexicographical order of the `.begin`, `.content` and `.end` section name
 parts in the previous example which ensures that the position of the begin
 and end markers are right.  The interesting part of linker map file of the
 previous example may look like this.
 {{{
 .rtemsroset     0x0000000001001990        0x4 load address
 0x000000000002268c
  *(SORT(.rtemsroset.*))
  .rtemsroset.myset.begin
                 0x0000000001001990        0x0 init.o
  .rtemsroset.myset.content
                 0x0000000001001990        0x4 module.o
  .rtemsroset.myset.end
                 0x0000000001001994        0x0 init.o
 }}}

 So what is the benefit of using linker sets to initialize modules?
 Currently in RTEMS all available managers (semaphore, message queue,
 barrier, etc.) are initialized since the initialization code doesn't know
 what is actually used by the application.  With the linker set approach we
 need to initialize only those managers that are used by the application.
 In case an application uses message queues, then it must call
 `rtems_message_queue_create()`.  In the module implementing this function
 we can place a linker set item and register the message queue handler
 constructor.  Otherwise, in case the application doesn't use message
 queues, then there will be no reference to the
 `rtems_message_queue_create()` function and the constructor is not
 registered, thus nothing of the message queue handler will be in the final
 executable.

--
Ticket URL: <http://devel.rtems.org/ticket/2408>
RTEMS Project <http://www.rtems.org/>
RTEMS Project


More information about the bugs mailing list