diff --git a/Makefile b/Makefile index 089353ba9..26dfcb45a 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ # USE_DEVICEATLAS : enable DeviceAtlas api. # USE_51DEGREES : enable third party device detection library from 51Degrees # USE_SYSTEMD : enable sd_notify() support. +# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init # # Options can be forced by specifying "USE_xxx=1" or can be disabled by using # "USE_xxx=" (empty string). @@ -279,7 +280,8 @@ use_opts = USE_EPOLL USE_KQUEUE USE_MY_EPOLL USE_MY_SPLICE USE_NETFILTER \ USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_VSYSCALL \ USE_GETADDRINFO USE_OPENSSL USE_LUA USE_FUTEX USE_ACCEPT4 \ USE_MY_ACCEPT4 USE_ZLIB USE_SLZ USE_CPU_AFFINITY USE_TFO USE_NS \ - USE_DL USE_RT USE_DEVICEATLAS USE_51DEGREES USE_SYSTEMD + USE_DL USE_RT USE_DEVICEATLAS USE_51DEGREES USE_SYSTEMD \ + USE_OBSOLETE_LINKER #### Target system options # Depending on the target platform, some options are set, as well as some @@ -379,7 +381,7 @@ endif # AIX 5.1 only ifeq ($(TARGET),aix51) set_target_defaults = $(call default_opts, \ - USE_POLL USE_LIBCRYPT) + USE_POLL USE_LIBCRYPT USE_OBSOLETE_LINKER) TARGET_CFLAGS = -Dss_family=__ss_family DEBUG_CFLAGS = endif @@ -387,14 +389,15 @@ endif # AIX 5.2 and above ifeq ($(TARGET),aix52) set_target_defaults = $(call default_opts, \ - USE_POLL USE_LIBCRYPT) + USE_POLL USE_LIBCRYPT USE_OBSOLETE_LINKER) TARGET_CFLAGS = -D_MSGQSUPPORT DEBUG_CFLAGS = endif # Cygwin ifeq ($(TARGET),cygwin) - set_target_defaults = $(call default_opts,USE_POLL USE_TPROXY) + set_target_defaults = $(call default_opts, \ + USE_POLL USE_TPROXY USE_OBSOLETE_LINKER) # Cygwin adds IPv6 support only in version 1.7 (in beta right now). TARGET_CFLAGS = $(if $(filter 1.5.%, $(shell uname -r)), -DUSE_IPV6 -DAF_INET6=23 -DINET6_ADDRSTRLEN=46, ) endif diff --git a/include/common/initcall.h b/include/common/initcall.h index 9e7059262..cf3febb68 100644 --- a/include/common/initcall.h +++ b/include/common/initcall.h @@ -32,6 +32,27 @@ /* 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 @@ -53,8 +74,14 @@ struct initcall { void *arg1; void *arg2; void *arg3; +#if defined(USE_OBSOLETE_LINKER) + void *next; +#endif }; + +#if !defined(USE_OBSOLETE_LINKER) + #ifdef __APPLE__ #define HA_SECTION(s) __section__("__DATA, i_" # s) #else @@ -72,7 +99,7 @@ struct initcall { * use INITCALL{0..3}() instead. */ #define __DECLARE_INITCALL(stg, linenum, function, a1, a2, a3) \ - static const struct initcall *__initcb_##linenum \ + static const struct initcall *__initcb_##linenum \ __attribute__((__used__,HA_SECTION(stg))) = \ (stg < STG_SIZE) ? &(const struct initcall) { \ .fct = (void (*)(void *,void *,void *))function, \ @@ -81,6 +108,36 @@ struct initcall { .arg3 = (void *)(a3), \ } : NULL +#else // USE_OBSOLETE_LINKER + +/* Declare a static constructor function to register a static descriptor for + * stage , with an element referencing function and arguments + * . is needed to deduplicate entries created from a same + * file. The trick with (stg to an integer before calling * __DECLARE_INITCALL(). Do not use this macro directly, use INITCALL{0..3}() * instead. @@ -112,12 +169,21 @@ struct initcall { #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 . */ #define FOREACH_INITCALL(p,stg) \ for ((p) = &(__start_init_##stg); (p) < &(__stop_init_##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 . 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 @@ -143,9 +209,22 @@ DECLARE_INIT_SECTION(STG_POOL); DECLARE_INIT_SECTION(STG_REGISTER); 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 . The test on is only there to * ensure it is a valid initcall stage. */ @@ -158,6 +237,22 @@ DECLARE_INIT_SECTION(STG_INIT); (*ptr)->fct((*ptr)->arg1, (*ptr)->arg2, (*ptr)->arg3); \ } while (0) +#else // USE_OBSOLETE_LINKER + +/* Run the initcalls for stage . The test on 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 /* _COMMON_INIT_H */ /* diff --git a/src/haproxy.c b/src/haproxy.c index 30abbd73d..f0cb2baae 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -124,6 +124,9 @@ #include #endif +/* array of init calls for older platforms */ +DECLARE_INIT_STAGES; + /* list of config files */ static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles); int pid; /* current process id */