d8307d
commit e5d262effe3a87164308a3f37e61b32d0348692a
d8307d
Author: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
d8307d
Date:   Fri Nov 30 18:05:32 2018 -0200
d8307d
d8307d
    Fix _dl_profile_fixup data-dependency issue (Bug 23690)
d8307d
    
d8307d
    There is a data-dependency between the fields of struct l_reloc_result
d8307d
    and the field used as the initialization guard. Users of the guard
d8307d
    expect writes to the structure to be observable when they also observe
d8307d
    the guard initialized. The solution for this problem is to use an acquire
d8307d
    and release load and store to ensure previous writes to the structure are
d8307d
    observable if the guard is initialized.
d8307d
    
d8307d
    The previous implementation used DL_FIXUP_VALUE_ADDR (l_reloc_result->addr)
d8307d
    as the initialization guard, making it impossible for some architectures
d8307d
    to load and store it atomically, i.e. hppa and ia64, due to its larger size.
d8307d
    
d8307d
    This commit adds an unsigned int to l_reloc_result to be used as the new
d8307d
    initialization guard of the struct, making it possible to load and store
d8307d
    it atomically in all architectures. The fix ensures that the values
d8307d
    observed in l_reloc_result are consistent and do not lead to crashes.
d8307d
    The algorithm is documented in the code in elf/dl-runtime.c
d8307d
    (_dl_profile_fixup). Not all data races have been eliminated.
d8307d
    
d8307d
    Tested with build-many-glibcs and on powerpc, powerpc64, and powerpc64le.
d8307d
    
d8307d
            [BZ #23690]
d8307d
            * elf/dl-runtime.c (_dl_profile_fixup): Guarantee memory
d8307d
            modification order when accessing reloc_result->addr.
d8307d
            * include/link.h (reloc_result): Add field init.
d8307d
            * nptl/Makefile (tests): Add tst-audit-threads.
d8307d
            (modules-names): Add tst-audit-threads-mod1 and
d8307d
            tst-audit-threads-mod2.
d8307d
            Add rules to build tst-audit-threads.
d8307d
            * nptl/tst-audit-threads-mod1.c: New file.
d8307d
            * nptl/tst-audit-threads-mod2.c: Likewise.
d8307d
            * nptl/tst-audit-threads.c: Likewise.
d8307d
            * nptl/tst-audit-threads.h: Likewise.
d8307d
    
d8307d
    Signed-off-by: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
d8307d
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
d8307d
d8307d
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
d8307d
index 63bbc89776..3d2f4a7a76 100644
d8307d
--- a/elf/dl-runtime.c
d8307d
+++ b/elf/dl-runtime.c
d8307d
@@ -183,10 +183,36 @@ _dl_profile_fixup (
d8307d
   /* This is the address in the array where we store the result of previous
d8307d
      relocations.  */
d8307d
   struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
d8307d
-  DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr;
d8307d
 
d8307d
-  DL_FIXUP_VALUE_TYPE value = *resultp;
d8307d
-  if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0)
d8307d
+ /* CONCURRENCY NOTES:
d8307d
+
d8307d
+  Multiple threads may be calling the same PLT sequence and with
d8307d
+  LD_AUDIT enabled they will be calling into _dl_profile_fixup to
d8307d
+  update the reloc_result with the result of the lazy resolution.
d8307d
+  The reloc_result guard variable is reloc_init, and we use
d8307d
+  acquire/release loads and store to it to ensure that the results of
d8307d
+  the structure are consistent with the loaded value of the guard.
d8307d
+  This does not fix all of the data races that occur when two or more
d8307d
+  threads read reloc_result->reloc_init with a value of zero and read
d8307d
+  and write to that reloc_result concurrently.  The expectation is
d8307d
+  generally that while this is a data race it works because the
d8307d
+  threads write the same values.  Until the data races are fixed
d8307d
+  there is a potential for problems to arise from these data races.
d8307d
+  The reloc result updates should happen in parallel but there should
d8307d
+  be an atomic RMW which does the final update to the real result
d8307d
+  entry (see bug 23790).
d8307d
+
d8307d
+  The following code uses reloc_result->init set to 0 to indicate if it is
d8307d
+  the first time this object is being relocated, otherwise 1 which
d8307d
+  indicates the object has already been relocated.
d8307d
+
d8307d
+  Reading/Writing from/to reloc_result->reloc_init must not happen
d8307d
+  before previous writes to reloc_result complete as they could
d8307d
+  end-up with an incomplete struct.  */
d8307d
+  DL_FIXUP_VALUE_TYPE value;
d8307d
+  unsigned int init = atomic_load_acquire (&reloc_result->init);
d8307d
+
d8307d
+  if (init == 0)
d8307d
     {
d8307d
       /* This is the first time we have to relocate this object.  */
d8307d
       const ElfW(Sym) *const symtab
d8307d
@@ -346,19 +372,31 @@ _dl_profile_fixup (
d8307d
 
d8307d
       /* Store the result for later runs.  */
d8307d
       if (__glibc_likely (! GLRO(dl_bind_not)))
d8307d
-	*resultp = value;
d8307d
+	{
d8307d
+	  reloc_result->addr = value;
d8307d
+	  /* Guarantee all previous writes complete before
d8307d
+	     init is updated.  See CONCURRENCY NOTES earlier  */
d8307d
+	  atomic_store_release (&reloc_result->init, 1);
d8307d
+	}
d8307d
+      init = 1;
d8307d
     }
d8307d
+  else
d8307d
+    value = reloc_result->addr;
d8307d
 
d8307d
   /* By default we do not call the pltexit function.  */
d8307d
   long int framesize = -1;
d8307d
 
d8307d
+
d8307d
 #ifdef SHARED
d8307d
   /* Auditing checkpoint: report the PLT entering and allow the
d8307d
      auditors to change the value.  */
d8307d
-  if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0
d8307d
+  if (GLRO(dl_naudit) > 0
d8307d
       /* Don't do anything if no auditor wants to intercept this call.  */
d8307d
       && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0)
d8307d
     {
d8307d
+      /* Sanity check:  DL_FIXUP_VALUE_CODE_ADDR (value) should have been
d8307d
+	 initialized earlier in this function or in another thread.  */
d8307d
+      assert (DL_FIXUP_VALUE_CODE_ADDR (value) != 0);
d8307d
       ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
d8307d
 						l_info[DT_SYMTAB])
d8307d
 			   + reloc_result->boundndx);
d8307d
diff --git a/include/link.h b/include/link.h
d8307d
index 5924594548..83b1c34b7b 100644
d8307d
--- a/include/link.h
d8307d
+++ b/include/link.h
d8307d
@@ -216,6 +216,10 @@ struct link_map
d8307d
       unsigned int boundndx;
d8307d
       uint32_t enterexit;
d8307d
       unsigned int flags;
d8307d
+      /* CONCURRENCY NOTE: This is used to guard the concurrent initialization
d8307d
+	 of the relocation result across multiple threads.  See the more
d8307d
+	 detailed notes in elf/dl-runtime.c.  */
d8307d
+      unsigned int init;
d8307d
     } *l_reloc_result;
d8307d
 
d8307d
     /* Pointer to the version information if available.  */
d8307d
diff --git a/nptl/Makefile b/nptl/Makefile
d8307d
index 982e43adfa..98b0aa01c7 100644
d8307d
--- a/nptl/Makefile
d8307d
+++ b/nptl/Makefile
d8307d
@@ -382,7 +382,8 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
d8307d
 	 tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4 \
d8307d
 	 tst-oncex3 tst-oncex4
d8307d
 ifeq ($(build-shared),yes)
d8307d
-tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder
d8307d
+tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder \
d8307d
+	 tst-audit-threads
d8307d
 tests-internal += tst-tls3 tst-tls3-malloc tst-tls5 tst-stackguard1
d8307d
 tests-nolibpthread += tst-fini1
d8307d
 ifeq ($(have-z-execstack),yes)
d8307d
@@ -394,7 +395,8 @@ modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
d8307d
 		tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
d8307d
 		tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
d8307d
 		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
d8307d
-		tst-join7mod tst-compat-forwarder-mod
d8307d
+		tst-join7mod tst-compat-forwarder-mod tst-audit-threads-mod1 \
d8307d
+		tst-audit-threads-mod2
d8307d
 extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
d8307d
 		   tst-cleanup4aux.o tst-cleanupx4aux.o
d8307d
 test-extras += tst-cleanup4aux tst-cleanupx4aux
d8307d
@@ -712,6 +714,14 @@ $(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so
d8307d
 
d8307d
 tst-mutex10-ENV = GLIBC_TUNABLES=glibc.elision.enable=1
d8307d
 
d8307d
+# Protect against a build using -Wl,-z,now.
d8307d
+LDFLAGS-tst-audit-threads-mod1.so = -Wl,-z,lazy
d8307d
+LDFLAGS-tst-audit-threads-mod2.so = -Wl,-z,lazy
d8307d
+LDFLAGS-tst-audit-threads = -Wl,-z,lazy
d8307d
+$(objpfx)tst-audit-threads: $(objpfx)tst-audit-threads-mod2.so
d8307d
+$(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so
d8307d
+tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so
d8307d
+
d8307d
 # The tests here better do not run in parallel
d8307d
 ifneq ($(filter %tests,$(MAKECMDGOALS)),)
d8307d
 .NOTPARALLEL:
d8307d
diff --git a/nptl/tst-audit-threads-mod1.c b/nptl/tst-audit-threads-mod1.c
d8307d
new file mode 100644
d8307d
index 0000000000..615d5ee512
d8307d
--- /dev/null
d8307d
+++ b/nptl/tst-audit-threads-mod1.c
d8307d
@@ -0,0 +1,74 @@
d8307d
+/* Dummy audit library for test-audit-threads.
d8307d
+
d8307d
+   Copyright (C) 2018 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <elf.h>
d8307d
+#include <link.h>
d8307d
+#include <stdio.h>
d8307d
+#include <assert.h>
d8307d
+#include <string.h>
d8307d
+
d8307d
+/* We must use a dummy LD_AUDIT module to force the dynamic loader to
d8307d
+   *not* update the real PLT, and instead use a cached value for the
d8307d
+   lazy resolution result.  It is the update of that cached value that
d8307d
+   we are testing for correctness by doing this.  */
d8307d
+
d8307d
+/* Library to be audited.  */
d8307d
+#define LIB "tst-audit-threads-mod2.so"
d8307d
+/* CALLNUM is the number of retNum functions.  */
d8307d
+#define CALLNUM 7999
d8307d
+
d8307d
+#define CONCATX(a, b) __CONCAT (a, b)
d8307d
+
d8307d
+static int previous = 0;
d8307d
+
d8307d
+unsigned int
d8307d
+la_version (unsigned int ver)
d8307d
+{
d8307d
+  return 1;
d8307d
+}
d8307d
+
d8307d
+unsigned int
d8307d
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
d8307d
+{
d8307d
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
d8307d
+}
d8307d
+
d8307d
+uintptr_t
d8307d
+CONCATX(la_symbind, __ELF_NATIVE_CLASS) (ElfW(Sym) *sym,
d8307d
+					unsigned int ndx,
d8307d
+					uintptr_t *refcook,
d8307d
+					uintptr_t *defcook,
d8307d
+					unsigned int *flags,
d8307d
+					const char *symname)
d8307d
+{
d8307d
+  const char * retnum = "retNum";
d8307d
+  char * num = strstr (symname, retnum);
d8307d
+  int n;
d8307d
+  /* Validate if the symbols are getting called in the correct order.
d8307d
+     This code is here to verify binutils does not optimize out the PLT
d8307d
+     entries that require the symbol binding.  */
d8307d
+  if (num != NULL)
d8307d
+    {
d8307d
+      n = atoi (num);
d8307d
+      assert (n >= previous);
d8307d
+      assert (n <= CALLNUM);
d8307d
+      previous = n;
d8307d
+    }
d8307d
+  return sym->st_value;
d8307d
+}
d8307d
diff --git a/nptl/tst-audit-threads-mod2.c b/nptl/tst-audit-threads-mod2.c
d8307d
new file mode 100644
d8307d
index 0000000000..f9817dd3dc
d8307d
--- /dev/null
d8307d
+++ b/nptl/tst-audit-threads-mod2.c
d8307d
@@ -0,0 +1,22 @@
d8307d
+/* Shared object with a huge number of functions for test-audit-threads.
d8307d
+
d8307d
+   Copyright (C) 2018 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* Define all the retNumN functions in a library.  */
d8307d
+#define definenum
d8307d
+#include "tst-audit-threads.h"
d8307d
diff --git a/nptl/tst-audit-threads.c b/nptl/tst-audit-threads.c
d8307d
new file mode 100644
d8307d
index 0000000000..e4bf433bd8
d8307d
--- /dev/null
d8307d
+++ b/nptl/tst-audit-threads.c
d8307d
@@ -0,0 +1,97 @@
d8307d
+/* Test multi-threading using LD_AUDIT.
d8307d
+
d8307d
+   Copyright (C) 2018 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a
d8307d
+   library with a huge number of functions in order to validate lazy symbol
d8307d
+   binding with an audit library.  We use one thread per CPU to test that
d8307d
+   concurrent lazy resolution does not have any defects which would cause
d8307d
+   the process to fail.  We use an LD_AUDIT library to force the testing of
d8307d
+   the relocation resolution caching code in the dynamic loader i.e.
d8307d
+   _dl_runtime_profile and _dl_profile_fixup.  */
d8307d
+
d8307d
+#include <support/xthread.h>
d8307d
+#include <strings.h>
d8307d
+#include <stdlib.h>
d8307d
+#include <sys/sysinfo.h>
d8307d
+
d8307d
+static int do_test (void);
d8307d
+
d8307d
+/* This test usually takes less than 3s to run.  However, there are cases that
d8307d
+   take up to 30s.  */
d8307d
+#define TIMEOUT 60
d8307d
+#define TEST_FUNCTION do_test ()
d8307d
+#include "../test-skeleton.c"
d8307d
+
d8307d
+/* Declare the functions we are going to call.  */
d8307d
+#define externnum
d8307d
+#include "tst-audit-threads.h"
d8307d
+#undef externnum
d8307d
+
d8307d
+int num_threads;
d8307d
+pthread_barrier_t barrier;
d8307d
+
d8307d
+void
d8307d
+sync_all (int num)
d8307d
+{
d8307d
+  pthread_barrier_wait (&barrier);
d8307d
+}
d8307d
+
d8307d
+void
d8307d
+call_all_ret_nums (void)
d8307d
+{
d8307d
+  /* Call each function one at a time from all threads.  */
d8307d
+#define callnum
d8307d
+#include "tst-audit-threads.h"
d8307d
+#undef callnum
d8307d
+}
d8307d
+
d8307d
+void *
d8307d
+thread_main (void *unused)
d8307d
+{
d8307d
+  call_all_ret_nums ();
d8307d
+  return NULL;
d8307d
+}
d8307d
+
d8307d
+#define STR2(X) #X
d8307d
+#define STR(X) STR2(X)
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  int i;
d8307d
+  pthread_t *threads;
d8307d
+
d8307d
+  num_threads = get_nprocs ();
d8307d
+  if (num_threads <= 1)
d8307d
+    num_threads = 2;
d8307d
+
d8307d
+  /* Used to synchronize all the threads after calling each retNumN.  */
d8307d
+  xpthread_barrier_init (&barrier, NULL, num_threads);
d8307d
+
d8307d
+  threads = (pthread_t *) xcalloc (num_threads, sizeof(pthread_t));
d8307d
+  for (i = 0; i < num_threads; i++)
d8307d
+    threads[i] = xpthread_create(NULL, thread_main, NULL);
d8307d
+
d8307d
+  for (i = 0; i < num_threads; i++)
d8307d
+    xpthread_join(threads[i]);
d8307d
+
d8307d
+  free (threads);
d8307d
+
d8307d
+  return 0;
d8307d
+}
d8307d
diff --git a/nptl/tst-audit-threads.h b/nptl/tst-audit-threads.h
d8307d
new file mode 100644
d8307d
index 0000000000..1c9ecc08df
d8307d
--- /dev/null
d8307d
+++ b/nptl/tst-audit-threads.h
d8307d
@@ -0,0 +1,92 @@
d8307d
+/* Helper header for test-audit-threads.
d8307d
+
d8307d
+   Copyright (C) 2018 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* We use this helper to create a large number of functions, all of
d8307d
+   which will be resolved lazily and thus have their PLT updated.
d8307d
+   This is done to provide enough functions that we can statistically
d8307d
+   observe a thread vs. PLT resolution failure if one exists.  */
d8307d
+
d8307d
+#define CONCAT(a, b) a ## b
d8307d
+#define NUM(x, y) CONCAT (x, y)
d8307d
+
d8307d
+#define FUNC10(x)	\
d8307d
+  FUNC (NUM (x, 0));	\
d8307d
+  FUNC (NUM (x, 1));	\
d8307d
+  FUNC (NUM (x, 2));	\
d8307d
+  FUNC (NUM (x, 3));	\
d8307d
+  FUNC (NUM (x, 4));	\
d8307d
+  FUNC (NUM (x, 5));	\
d8307d
+  FUNC (NUM (x, 6));	\
d8307d
+  FUNC (NUM (x, 7));	\
d8307d
+  FUNC (NUM (x, 8));	\
d8307d
+  FUNC (NUM (x, 9))
d8307d
+
d8307d
+#define FUNC100(x)	\
d8307d
+  FUNC10 (NUM (x, 0));	\
d8307d
+  FUNC10 (NUM (x, 1));	\
d8307d
+  FUNC10 (NUM (x, 2));	\
d8307d
+  FUNC10 (NUM (x, 3));	\
d8307d
+  FUNC10 (NUM (x, 4));	\
d8307d
+  FUNC10 (NUM (x, 5));	\
d8307d
+  FUNC10 (NUM (x, 6));	\
d8307d
+  FUNC10 (NUM (x, 7));	\
d8307d
+  FUNC10 (NUM (x, 8));	\
d8307d
+  FUNC10 (NUM (x, 9))
d8307d
+
d8307d
+#define FUNC1000(x)		\
d8307d
+  FUNC100 (NUM (x, 0));		\
d8307d
+  FUNC100 (NUM (x, 1));		\
d8307d
+  FUNC100 (NUM (x, 2));		\
d8307d
+  FUNC100 (NUM (x, 3));		\
d8307d
+  FUNC100 (NUM (x, 4));		\
d8307d
+  FUNC100 (NUM (x, 5));		\
d8307d
+  FUNC100 (NUM (x, 6));		\
d8307d
+  FUNC100 (NUM (x, 7));		\
d8307d
+  FUNC100 (NUM (x, 8));		\
d8307d
+  FUNC100 (NUM (x, 9))
d8307d
+
d8307d
+#define FUNC7000()	\
d8307d
+  FUNC1000 (1);		\
d8307d
+  FUNC1000 (2);		\
d8307d
+  FUNC1000 (3);		\
d8307d
+  FUNC1000 (4);		\
d8307d
+  FUNC1000 (5);		\
d8307d
+  FUNC1000 (6);		\
d8307d
+  FUNC1000 (7);
d8307d
+
d8307d
+#ifdef FUNC
d8307d
+# undef FUNC
d8307d
+#endif
d8307d
+
d8307d
+#ifdef externnum
d8307d
+# define FUNC(x) extern int CONCAT (retNum, x) (void)
d8307d
+#endif
d8307d
+
d8307d
+#ifdef definenum
d8307d
+# define FUNC(x) int CONCAT (retNum, x) (void) { return x; }
d8307d
+#endif
d8307d
+
d8307d
+#ifdef callnum
d8307d
+# define FUNC(x) CONCAT (retNum, x) (); sync_all (x)
d8307d
+#endif
d8307d
+
d8307d
+/* A value of 7000 functions is chosen as an arbitrarily large
d8307d
+   number of functions that will allow us enough attempts to
d8307d
+   verify lazy resolution operation.  */
d8307d
+FUNC7000 ();