summaryrefslogtreecommitdiffstats
path: root/include/haproxy/initcall.h
blob: dffec04d17a74437ac61c7b6976857944b84c5e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 * include/haproxy/initcall.h
 *
 * Initcall management.
 *
 * Copyright (C) 2018-2020 Willy Tarreau - w@1wt.eu
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef _HAPROXY_INITCALL_H
#define _HAPROXY_INITCALL_H

#include <haproxy/compiler.h>

/* List of known init stages. If others are added, please declare their
 * section at the end of the file below.
 */

/* The principle of the initcalls is to create optional sections in the target
 * program which are made of arrays of structures containing a function pointer
 * and 3 argument pointers. Then at boot time, these sections are scanned in a
 * well defined order to call in turn each of these functions with their
 * arguments. This allows to declare register callbacks in C files without
 * having to export lots of things nor to cross-reference functions. There are
 * several initialization stages defined so that certain guarantees are offered
 * (for example list heads might or might not be initialized, pools might or
 * might not have been created yet).
 *
 * On some very old platforms there is no convenient way to retrieve the start
 * or stop pointer for these sections so there is no reliable way to enumerate
 * the callbacks. When this is the case, as detected when USE_OBSOLETE_LINKER
 * is set, instead of using sections we exclusively use constructors whose name
 * is based on the current line number in the file to guarantee uniqueness.
 * When called, these constructors then add their callback to their respective
 * list. It works as well but slightly inflates the executable's size since
 * code has to be emitted just to register each of these callbacks.
 */

/*
 * Please keep those names short enough, they are used to generate section
 * names, Mac OS X accepts section names up to 16 characters, and we prefix
 * them with i_, so stage name can't be more than 14 characters.
 */
enum init_stage {
	STG_PREPARE = 0,      // preset variables, tables, list heads
	STG_LOCK,             // pre-initialize locks
	STG_REGISTER,         // register static lists (keywords etc)
	STG_ALLOC,            // allocate required structures
	STG_POOL,             // create pools
	STG_INIT,             // subsystems normal initialization
	STG_SIZE              // size of the stages array, must be last
};

/* This is the descriptor for an initcall */
struct initcall {
	void (*const fct)(void *arg1, void *arg2, void *arg3);
	void *arg1;
	void *arg2;
	void *arg3;
#if defined(USE_OBSOLETE_LINKER)
	void *next;
#endif
};


#if !defined(USE_OBSOLETE_LINKER)

#define HA_INIT_SECTION(s)  HA_SECTION("i_" # s)

/* Declare a static variable in the init section dedicated to stage <stg>,
 * with an element referencing function <function> and arguments <a1..a3>.
 * <linenum> is needed to deduplicate entries created from a same file. The
 * trick with (stg<STG_SIZE) consists in verifying that stg if a valid enum
 * value from the initcall set, and to emit a warning or error if it is not.
 * The function's type is cast so that it is technically possible to call a
 * function taking other argument types, provided they are all the same size
 * as a pointer (args are cast to (void*)). Do not use this macro directly,
 * use INITCALL{0..3}() instead.
 */
#define __DECLARE_INITCALL(stg, linenum, function, a1, a2, a3)     \
        HA_GLOBL(__start_i_##stg );                                \
        HA_GLOBL(__stop_i_##stg );                                 \
	static const struct initcall *__initcb_##linenum           \
	    __attribute__((__used__)) HA_INIT_SECTION(stg) =	   \
	        (stg < STG_SIZE) ? &(const struct initcall) {      \
		.fct = (void (*)(void *,void *,void *))function,   \
		.arg1 = (void *)(a1),                              \
		.arg2 = (void *)(a2),                              \
		.arg3 = (void *)(a3),                              \
	} : NULL


#else // USE_OBSOLETE_LINKER

/* Declare a static constructor function to register a static descriptor for
 * stage <stg>, with an element referencing function <function> and arguments
 * <a1..a3>. <linenum> is needed to deduplicate entries created from a same
 * file. The trick with (stg<STG_SIZE) consists in verifying that stg if a
 * valid enum value from the initcall set, and to emit a warning or error if
 * it is not.
 * The function's type is cast so that it is technically possible to call a
 * function taking other argument types, provided they are all the same size
 * as a pointer (args are cast to (void*)). Do not use this macro directly,
 * use INITCALL{0..3}() instead.
 */
#define __DECLARE_INITCALL(stg, linenum, function, a1, a2, a3)     \
__attribute__((constructor)) static void __initcb_##linenum()      \
{                                                                  \
	static struct initcall entry = {                           \
		.fct  = (void (*)(void *,void *,void *))function,  \
		.arg1 = (void *)(a1),                              \
		.arg2 = (void *)(a2),                              \
		.arg3 = (void *)(a3),                              \
	};                                                         \
	if (stg < STG_SIZE) {                                      \
		entry.next = __initstg[stg];                       \
		__initstg[stg] = &entry;                           \
	};                                                         \
}

#endif // USE_OBSOLETE_LINKER

/* This is used to resolve <linenum> to an integer before calling
 * __DECLARE_INITCALL(). Do not use this macro directly, use INITCALL{0..3}()
 * instead.
 */
#define _DECLARE_INITCALL(...) \
	__DECLARE_INITCALL(__VA_ARGS__)

/* This requires that function <function> is called with pointer argument
 * <argument> during init stage <stage> which must be one of init_stage.
 */
#define INITCALL0(stage, function)                                     \
	_DECLARE_INITCALL(stage, __LINE__, function, 0, 0, 0)

/* This requires that function <function> is called with pointer argument
 * <argument> during init stage <stage> which must be one of init_stage.
 */
#define INITCALL1(stage, function, arg1)                               \
	_DECLARE_INITCALL(stage, __LINE__, function, arg1, 0, 0)

/* This requires that function <function> is called with pointer arguments
 * <arg1..2> during init stage <stage> which must be one of init_stage.
 */
#define INITCALL2(stage, function, arg1, arg2)                         \
	_DECLARE_INITCALL(stage, __LINE__, function, arg1, arg2, 0)

/* This requires that function <function> is called with pointer arguments
 * <arg1..3> during init stage <stage> which must be one of init_stage.
 */
#define INITCALL3(stage, function, arg1, arg2, arg3)                   \
	_DECLARE_INITCALL(stage, __LINE__, function, arg1, arg2, arg3)

#if !defined(USE_OBSOLETE_LINKER)
/* Iterate pointer p (of type initcall**) over all registered calls at
 * stage <stg>.
 */
#define FOREACH_INITCALL(p,stg)                                               \
	for ((p) = &(__start_i_##stg); (p) < &(__stop_i_##stg); (p)++)

#else // USE_OBSOLETE_LINKER

#define FOREACH_INITCALL(p,stg)                                               \
	for ((p) = __initstg[stg]; (p); (p) = (p)->next)
#endif // USE_OBSOLETE_LINKER


#if !defined(USE_OBSOLETE_LINKER)
/* Declare a section for stage <stg>. The start and stop pointers are set by
 * the linker itself, which is why they're declared extern here. The weak
 * attribute is used so that we declare them ourselves if the section is
 * empty. The corresponding sections must contain exclusively pointers to
 * make sure each location may safely be visited by incrementing a pointer.
 */
#define DECLARE_INIT_SECTION(stg)                                                   \
	extern __attribute__((__weak__)) const struct initcall *__start_i_##stg HA_SECTION_START("i_" # stg); \
	extern __attribute__((__weak__)) const struct initcall *__stop_i_##stg  HA_SECTION_STOP("i_" # stg)

/* Declare all initcall sections here */
DECLARE_INIT_SECTION(STG_PREPARE);
DECLARE_INIT_SECTION(STG_LOCK);
DECLARE_INIT_SECTION(STG_REGISTER);
DECLARE_INIT_SECTION(STG_ALLOC);
DECLARE_INIT_SECTION(STG_POOL);
DECLARE_INIT_SECTION(STG_INIT);

// for use in the main haproxy.c file
#define DECLARE_INIT_STAGES asm("")

/* not needed anymore */
#undef DECLARE_INIT_SECTION

#else // USE_OBSOLETE_LINKER

extern struct initcall *__initstg[STG_SIZE];

// for use in the main haproxy.c file
#define DECLARE_INIT_STAGES struct initcall *__initstg[STG_SIZE]

#endif // USE_OBSOLETE_LINKER

#if !defined(USE_OBSOLETE_LINKER)
/* Run the initcalls for stage <stg>. The test on <stg> is only there to
 * ensure it is a valid initcall stage.
 */
#define RUN_INITCALLS(stg)                                                     \
	do {                                                                   \
		const struct initcall **ptr;                                   \
		if (stg >= STG_SIZE)                                           \
			break;                                                 \
		FOREACH_INITCALL(ptr, stg)                                     \
			(*ptr)->fct((*ptr)->arg1, (*ptr)->arg2, (*ptr)->arg3); \
	} while (0)

#else // USE_OBSOLETE_LINKER

/* Run the initcalls for stage <stg>. The test on <stg> is only there to
 * ensure it is a valid initcall stage.
 */
#define RUN_INITCALLS(stg)                                                     \
	do {                                                                   \
		const struct initcall *ptr;                                    \
		if (stg >= STG_SIZE)                                           \
			break;                                                 \
		FOREACH_INITCALL(ptr, stg)                                     \
			(ptr)->fct((ptr)->arg1, (ptr)->arg2, (ptr)->arg3);     \
	} while (0)

#endif // USE_OBSOLETE_LINKER

#endif /* _HAPROXY_INITCALL_H */

/*
 * Local variables:
 *  c-indent-level: 8
 *  c-basic-offset: 8
 * End:
 */