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