BUILD: re-implement an initcall variant without using executable sections

The current initcall implementation relies on dedicated sections (one
section per init stage) to store the initcall descriptors. Then upon
startup, these sections are scanned from beginning to end and all items
found there are called in sequence.

On platforms like AIX or Cygwin it seems difficult to figure the
beginning and end of sections as the linker doesn't seem to provide
the corresponding symbols. In order to replace this, this patch
simply implements an array of single linked (one per init stage)
which are fed using constructors for each register call. These
constructors are declared static, with a name depending on their
line number in the file, in order to avoid name clashes. The final
effect is the same, except that the method is slightly more expensive
in that it explicitly produces code to register these initcalls :

$ size  haproxy.sections haproxy.constructor
   text    data     bss     dec     hex filename
4060312  249176 1457652 5767140  57ffe4 haproxy.sections
4062862  260408 1457652 5780922  5835ba haproxy.constructor

This mechanism is enabled as an alternative to the default one when
build option USE_OBSOLETE_LINKER is set. This option is currently
enabled by default only on AIX and Cygwin, and may be attempted for
any target which fails to build complaining about missing symbols
__start_init_* and/or __stop_init_*.

Once confirmed as a reliable fix, this will likely have to be backported
to 1.9 where AIX and Cygwin do not build anymore.
diff --git a/Makefile b/Makefile
index 089353b..26dfcb4 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_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 @@
 # 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 @@
 # 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 9e70592..cf3febb 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 @@
 	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 @@
  * 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 @@
 		.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.
@@ -112,12 +169,21 @@
 #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_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 <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
@@ -143,9 +209,22 @@
 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 <stg>. The test on <stg> is only there to
  * ensure it is a valid initcall stage.
  */
@@ -158,6 +237,22 @@
 			(*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 /* _COMMON_INIT_H */
 
 /*
diff --git a/src/haproxy.c b/src/haproxy.c
index 30abbd7..f0cb2ba 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -124,6 +124,9 @@
 #include <proto/ssl_sock.h>
 #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 */