4 /** @page ifdef_harmful #ifdef considered harmful
6 * Overuse of @c #ifdef has long been a problem in Etherboot.
7 * Etherboot provides a rich array of features, but all these features
8 * take up valuable space in a ROM image. The traditional solution to
9 * this problem has been for each feature to have its own @c #ifdef
10 * option, allowing the feature to be compiled in only if desired.
12 * The problem with this is that it becomes impossible to compile, let
13 * alone test, all possible versions of Etherboot. Code that is not
14 * typically used tends to suffer from bit-rot over time. It becomes
15 * extremely difficult to predict which combinations of compile-time
16 * options will result in code that can even compile and link
19 * To solve this problem, we have adopted a new approach from
20 * Etherboot 5.5 onwards. @c #ifdef is now "considered harmful", and
21 * its use should be minimised. Separate features should be
22 * implemented in separate @c .c files, and should \b always be
23 * compiled (i.e. they should \b not be guarded with a @c #ifdef @c
24 * MY_PET_FEATURE statement). By making (almost) all code always
25 * compile, we avoid the problem of bit-rot in rarely-used code.
27 * The file config.h, in combination with the @c make command line,
28 * specifies the objects that will be included in any particular build
29 * of Etherboot. For example, suppose that config.h includes the line
33 * #define CONSOLE_SERIAL
34 * #define DOWNLOAD_PROTO_TFTP
38 * When a particular Etherboot image (e.g. @c bin/rtl8139.zdsk) is
39 * built, the options specified in config.h are used to drag in the
40 * relevant objects at link-time. For the above example, serial.o and
41 * tftp.o would be linked in.
43 * There remains one problem to solve: how do these objects get used?
44 * Traditionally, we had code such as
48 * #ifdef CONSOLE_SERIAL
54 * in main.c, but this reintroduces @c #ifdef and so is a Bad Idea.
55 * We cannot simply remove the @c #ifdef and make it
63 * because then serial.o would end up always being linked in.
65 * The solution is to use @link tables.h linker tables @endlink.
73 * Read @ref ifdef_harmful first for some background on the motivation
74 * for using linker tables.
76 * This file provides macros for dealing with linker-generated tables
77 * of fixed-size symbols. We make fairly extensive use of these in
78 * order to avoid @c #ifdef spaghetti and/or linker symbol pollution.
79 * For example, instead of having code such as
83 * #ifdef CONSOLE_SERIAL
89 * we make serial.c generate an entry in the initialisation function
90 * table, and then have a function call_init_fns() that simply calls
91 * all functions present in this table. If and only if serial.o gets
92 * linked in, then its initialisation function will be called. We
93 * avoid linker symbol pollution (i.e. always dragging in serial.o
94 * just because of a call to serial_init()) and we also avoid @c
95 * #ifdef spaghetti (having to conditionalise every reference to
96 * functions in serial.c).
98 * The linker script takes care of assembling the tables for us. All
99 * our table sections have names of the format @c .tbl.NAME.NN where
100 * @c NAME designates the data structure stored in the table (e.g. @c
101 * init_fns) and @c NN is a two-digit decimal number used to impose an
102 * ordering upon the tables if required. @c NN=00 is reserved for the
103 * symbol indicating "table start", and @c NN=99 is reserved for the
104 * symbol indicating "table end".
106 * As an example, suppose that we want to create a "frobnicator"
107 * feature framework, and allow for several independent modules to
108 * provide frobnicating services. Then we would create a frob.h
109 * header file containing e.g.
113 * struct frobnicator {
114 * const char *name; // Name of the frobnicator
115 * void ( *frob ) ( void ); // The frobnicating function itself
118 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
120 * #define __frobnicator __table_entry ( FROBNICATORS, 01 )
124 * Any module providing frobnicating services would look something
131 * static void my_frob ( void ) {
132 * // Do my frobnicating
136 * struct frob my_frobnicator __frobnicator = {
143 * The central frobnicator code (frob.c) would use the frobnicating
150 * // Call all linked-in frobnicators
151 * void frob_all ( void ) {
154 * for_each_table ( frob, FROBNICATORS ) {
155 * printf ( "Calling frobnicator \"%s\"\n", frob->name );
162 * See init.h and init.c for a real-life example.
167 #define __attribute__( x )
171 * Declare a linker table
175 * @ret table Linker table
177 #define __table( type, name ) ( type, name )
180 * Get linker table data type
182 * @v table Linker table
183 * @ret type Data type
185 #define __table_type( table ) __table_extract_type table
186 #define __table_extract_type( type, name ) type
189 * Get linker table name
191 * @v table Linker table
192 * @ret name Table name
194 #define __table_name( table ) __table_extract_name table
195 #define __table_extract_name( type, name ) name
198 * Get linker table section name
200 * @v table Linker table
201 * @v idx Sub-table index
202 * @ret section Section name
204 #define __table_section( table, idx ) \
205 ".tbl." __table_name ( table ) "." __table_str ( idx )
206 #define __table_str( x ) #x
209 * Get linker table alignment
211 * @v table Linker table
212 * @ret align Alignment
214 #define __table_alignment( table ) __alignof__ ( __table_type ( table ) )
217 * Declare a linker table entry
219 * @v table Linker table
220 * @v idx Sub-table index
226 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
228 * #define __frobnicator __table_entry ( FROBNICATORS, 01 )
230 * struct frobnicator my_frob __frobnicator = {
236 #define __table_entry( table, idx ) \
237 __attribute__ (( __section__ ( __table_section ( table, idx ) ) \
238 __aligned__ ( __table_alignment ( table ) ) ))
241 * Get start of linker table
243 * @v table Linker table
244 * @ret start Start of linker table
250 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
252 * struct frobnicator *frobs = table_start ( FROBNICATORS );
256 #define table_start( table ) ( { \
257 static __table_type ( table ) __table_start[0] \
258 __table_entry ( table, 00 ); \
262 * Get end of linker table
264 * @v table Linker table
265 * @ret end End of linker table
271 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
273 * struct frobnicator *frobs_end = table_end ( FROBNICATORS );
277 #define table_end( table ) ( { \
278 static __table_type ( table ) __table_end[0] \
279 __table_entry ( table, 99 ); \
283 * Get number of entries in linker table
285 * @v table Linker table
286 * @ret num_entries Number of entries in linker table
292 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
294 * unsigned int num_frobs = table_num_entries ( FROBNICATORS );
299 #define table_num_entries( table ) \
300 ( ( unsigned int ) ( table_end ( table ) - \
301 table_start ( table ) ) )
304 * Iterate through all entries within a linker table
306 * @v pointer Entry pointer
307 * @v table Linker table
313 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
315 * struct frobnicator *frob;
317 * for_each_table_entry ( frob, FROBNICATORS ) {
324 #define for_each_table_entry( pointer, table ) \
325 for ( pointer = table_start ( table ) ; \
326 pointer < table_end ( table ) ; \
330 * Iterate through all entries within a linker table in reverse order
332 * @v pointer Entry pointer
333 * @v table Linker table
339 * #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
341 * struct frobnicator *frob;
343 * for_each_table_entry_reverse ( frob, FROBNICATORS ) {
350 #define for_each_table_entry_reverse( pointer, table ) \
351 for ( pointer = ( table_end ( table ) - 1 ) ; \
352 pointer >= table_start ( table ) ; \
355 #endif /* _GPXE_TABLES_H */