From patchwork Thu Oct 18 02:00:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tulio Magno Quites Machado Filho X-Patchwork-Id: 29783 X-Patchwork-Delegate: carlos@redhat.com Received: (qmail 97593 invoked by alias); 18 Oct 2018 02:01:25 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 97432 invoked by uid 89); 18 Oct 2018 02:01:25 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-21.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_MANYTO, KAM_SHORT, KAM_STOCKGEN, KHOP_DYNAMIC, RCVD_IN_DNSWL_LOW, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.2 spammy=auditor, observe, Writing, auditing X-HELO: mx0a-001b2d01.pphosted.com From: Tulio Magno Quites Machado Filho To: "Carlos O'Donell" , Florian Weimer , libc-alpha@sourceware.org, John David Anglin , Adhemerval Zanella , Joseph Myers , Florian Weimer Subject: [PATCHv4] Protect _dl_profile_fixup data-dependency order [BZ #23690] Date: Wed, 17 Oct 2018 23:00:56 -0300 In-Reply-To: <54ac395f-7415-ce4b-7204-d3c3272ad88b@redhat.com> References: <54ac395f-7415-ce4b-7204-d3c3272ad88b@redhat.com> x-cbid: 18101802-0016-0000-0000-000009445661 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00009894; HX=3.00000242; KW=3.00000007; PH=3.00000004; SC=3.00000268; SDB=6.01104234; UDB=6.00571633; IPR=6.00884287; MB=3.00023800; MTD=3.00000008; XFM=3.00000015; UTC=2018-10-18 02:01:17 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 18101802-0017-0000-0000-000040BE83D3 Message-Id: <20181018020056.13549-1-tuliom@linux.ibm.com> Carlos O'Donell writes: > On 10/10/18 10:57 PM, Tulio Magno Quites Machado Filho wrote: > > We will need a v4. Please review (1), (2) and (3) carefully, feel free to > ignore (4). > > (1) I added a bunch of comments. > > Comments added inline. Ack. > (2) -Wl,-z,now worries. > > Added some things for you to check. Fixed. > (3) Fence-to-fence sync. > > For fence-to-fence synchronization to work we need an acquire and release > fence, and we have that. > > We are missing the atomic read and write of the guard. Please review below. > Florian mentioned this in his review. He is correct. > > And all the problems are back again because you can't do atomic loads of > the large guards because they are actually the function descriptor structures. > However, this is just laziness, we used the addr because it was convenient. > It is no longer convenient. Just add a 'init' field to reloc_result and use > that as the guard to synchronize the threads against for initialization of > the results. This should solve the reloc_result problem (ignorning the issues > hppa and ia64 have with the fdesc updates across multiple threads in _dl_fixup). Ack. > (4) Review of elf_machine_fixup_plt, and DL_FIXUP_MAKE_VALUE. > > I reviewed the uses of elf_machine_fixup_plt, and DL_FIXUP_MAKE_VALUE to > see if there was any other case of this problem, particularly where there > might be a case where a write happens on one thread that might not be > seen in another. > > I also looked at _dl_relocate_object and the initialization of all > l_reloc_result via calloc, and that is also covered because the > atomic_thread_fence_acquire ensures any secondary thread sees the > initialization. Related to this: I'm skeptical about the usage of l->l_relocated in _dl_relocate_object. But I was not able to reproduce a failure there yet. >> diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c >> index 63bbc89776..c1ba372bd7 100644 >> --- a/elf/dl-runtime.c >> +++ b/elf/dl-runtime.c >> @@ -183,9 +183,18 @@ _dl_profile_fixup ( >> /* This is the address in the array where we store the result of previous >> relocations. */ >> struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index]; >> - DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr; >> >> + /* CONCURRENCY NOTES: >> + > > Suggest adding: > > Multiple threads may be calling the same PLT sequence and with LD_AUDIT enabled > they will be calling into _dl_profile_fixup to update the reloc_result with the > result of the lazy resolution. The reloc_result guard variable is addr, and we > use relaxed MO loads and store to it along with an atomic_thread_acquire and > atomic_thread_release fence to ensure that the results of the structure are > consistent with the loaded value of the guard. I added this comment with small changes based on your suggestion for reloc_result.init. >> + The following code uses DL_FIXUP_VALUE_CODE_ADDR to access a potential >> + member of reloc_result->addr to indicate if it is the first time this >> + object is being relocated. >> + Reading/Writing from/to reloc_result->addr must not happen before previous >> + writes to reloc_result complete as they could end-up with an incomplete >> + struct. */ > > OK. After adding your previous comment and the changes to use reloc_result.init, this comment is obsolete. I removed it. >> DL_FIXUP_VALUE_TYPE value = *resultp; > > Not OK. This is a guard. You read it here, and write to it below. > That's a data race. Both need to be atomic accesses with any MO you want. Agreed. > On hppa this will require a new enough compile to get a 64-bit atomic load. > On ia64 I don't know if there is a usable 128-bit atomic. > > The key problem here is that addr is being overloaded as a guard here because > it was convenient. It's non-zero when the symbol is initialized, otherwhise it's > zero when it's not. However, for arches with function descriptors you've found > out that using it is causing problems because it's too big for traditional atomic > operations. > > What you really need is a new "init" field in reloc_result, make it a word, > and then use word-sized atomics on that with relaxed MO, and keep the fences. Ack. Fixed. >> + *resultp = value; > > Not OK, see above, this needs to be an atomic relaxed-MO store to 'init' > or something smaller than value. > > You need a guard small enough that arches will have an atomic load/store > to the size. Ack. Fixed. >> extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \ >> tst-cleanup4aux.o tst-cleanupx4aux.o >> test-extras += tst-cleanup4aux tst-cleanupx4aux >> @@ -709,6 +711,10 @@ endif >> >> $(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so >> >> +$(objpfx)tst-audit-threads: $(objpfx)tst-audit-threads-mod2.so >> +$(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so >> +tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so > > Do we need to add -Wl,-z,lazy? > > Users might have -Wl,-z,now as the default for their build? > > With BIND_NOW the test doesn't test what we want. Indeed. Fixed. >> diff --git a/nptl/tst-audit-threads-mod1.c b/nptl/tst-audit-threads-mod1.c >> +#include >> +#include >> +#include >> +#include >> +#include >> + > > Suggest: > > /* We must use a dummy LD_AUDIT module to force the dynamic loader to > *not* update the real PLT, and instead use a cached value for the > lazy resolution result. It is the update of that cached value that > we are testing for correctness by doing this. */ Fixed. >> +volatile int count = 0; >> + >> +unsigned int >> +la_version (unsigned int ver) >> +{ >> + return 1; >> +} >> + >> +unsigned int >> +la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) >> +{ >> + return LA_FLG_BINDTO | LA_FLG_BINDFROM; >> +} > > I'm worried binutils will optimize away the PLT entries and this test will > pass without failing but the lazy resolution will not be tested. > > Can we just *count* the number of PLT resolutions and see if they match? With so many threads calling these functions, la_symbind is called many times. As the intention was to avoid optimizing away the PLT entries, I added another validation: it tests if it's always increasing and that it's never higher than the expected value. >> diff --git a/nptl/tst-audit-threads-mod2.c b/nptl/tst-audit-threads-mod2.c > > Suggest: > > /* Define all the retNumN functions in a library. */ > > Just to be clear that this must be distinct from the executable. Fixed. >> diff --git a/nptl/tst-audit-threads.c b/nptl/tst-audit-threads.c > > Suggest: > > /* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a > library with a huge number of functions in order to validate lazy symbol > binding with an audit library. We use one thread per CPU to test that > concurrent lazy resolution does not have any defects which would cause > the process to fail. We use an LD_AUDIT library to force the testing of > the relocation resolution caching code in the dynamic loader i.e. > _dl_runtime_profile and _dl_profile_fixup. */ Fixed. >> +/* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a >> + library with a huge number of functions in order to validate lazy symbol >> + binding with an audit library. */ >> + >> +#include >> +#include >> +#include >> +#include >> + >> +static int do_test (void); >> + >> +/* This test usually takes less than 3s to run. However, there are cases that >> + take up to 30s. */ >> +#define TIMEOUT 60 >> +#define TEST_FUNCTION do_test () >> +#include "../test-skeleton.c" >> + > > Suggest: > > /* Declare the functions we are going to call. */ Fixed. >> +void >> +call_all_ret_nums (void) >> +{ > > Suggest: > > /* Call each function one at a time from all threads. */ Fixed. >> diff --git a/nptl/tst-audit-threads.h b/nptl/tst-audit-threads.h > Suggest adding: > > /* We use this helper to create a large number of functions, all of > which will be resolved lazily and thus have their PLT updated. > This is done to provide enough functions that we can statistically > observe a thread vs. PLT resolution failure if one exists. */ Fixed. Changes since v3: - Improved comments. - Started to use -Wl,-z,now. - Added field init to l_reloc_result to be used as a guard. Changes since v2: - Fixed coding style in nptl/tst-audit-threads-mod1.c. - Replaced pthreads.h functions with respective support/xthread.h ones. - Replaced malloc() with xcalloc() in nptl/tst-audit-threads.c. - Removed bzero(). - Reduced the amount of functions to 7k in order to fit the relocation limit of some architectures, e.g. m68k, mips. - Fixed issues in nptl/Makefile. Changes since v1: - Fixed the coding style issues. - Replaced atomic loads/store with memory fences. - Added a test. ---- 8< ---- The field reloc_result->addr is used to indicate if the rest of the fields of reloc_result have already been written, creating a data-dependency order. Reading reloc_result->addr to the variable value requires to complete before reading the rest of the fields of reloc_result. Likewise, the writes to the other fields of the reloc_result must complete before reloc_result-addr is updated. Tested with build-many-glibcs. 2018-10-17 Tulio Magno Quites Machado Filho [BZ #23690] * elf/dl-runtime.c (_dl_profile_fixup): Guarantee memory modification order when accessing reloc_result->addr. * include/link.h (reloc_result): Add field init. * nptl/Makefile (tests): Add tst-audit-threads. (modules-names): Add tst-audit-threads-mod1 and tst-audit-threads-mod2. Add rules to build tst-audit-threads. * nptl/tst-audit-threads-mod1.c: New file. * nptl/tst-audit-threads-mod2.c: Likewise. * nptl/tst-audit-threads.c: Likewise. * nptl/tst-audit-threads.h: Likewise. Signed-off-by: Tulio Magno Quites Machado Filho --- elf/dl-runtime.c | 30 +++++++++++-- include/link.h | 2 + nptl/Makefile | 14 ++++++- nptl/tst-audit-threads-mod1.c | 74 +++++++++++++++++++++++++++++++++ nptl/tst-audit-threads-mod2.c | 22 ++++++++++ nptl/tst-audit-threads.c | 97 +++++++++++++++++++++++++++++++++++++++++++ nptl/tst-audit-threads.h | 89 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 nptl/tst-audit-threads-mod1.c create mode 100644 nptl/tst-audit-threads-mod2.c create mode 100644 nptl/tst-audit-threads.c create mode 100644 nptl/tst-audit-threads.h diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index 63bbc89776..a760a00a62 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -183,10 +183,22 @@ _dl_profile_fixup ( /* This is the address in the array where we store the result of previous relocations. */ struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index]; + + /* CONCURRENCY NOTES: + + Multiple threads may be calling the same PLT sequence and with LD_AUDIT + enabled they will be calling into _dl_profile_fixup to update the + reloc_result with the result of the lazy resolution. The reloc_result + guard variable is init, and we use relaxed MO loads and stores to it + along with an atomic_thread_acquire and atomic_thread_release fence to + ensure that the results of the structure are consistent with the + loaded value of the guard. */ DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr; + DL_FIXUP_VALUE_TYPE value; + unsigned int init = atomic_load_relaxed (&reloc_result->init); + atomic_thread_fence_acquire (); - DL_FIXUP_VALUE_TYPE value = *resultp; - if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0) + if (init == 0) { /* This is the first time we have to relocate this object. */ const ElfW(Sym) *const symtab @@ -346,16 +358,26 @@ _dl_profile_fixup ( /* Store the result for later runs. */ if (__glibc_likely (! GLRO(dl_bind_not))) - *resultp = value; + { + *resultp = value; + atomic_thread_fence_release (); + /* Guarantee all previous writes complete before + init is updated. See CONCURRENCY NOTES earlier */ + atomic_store_relaxed (&reloc_result->init, 1); + } + init = 1; } + else + value = *resultp; /* By default we do not call the pltexit function. */ long int framesize = -1; + #ifdef SHARED /* Auditing checkpoint: report the PLT entering and allow the auditors to change the value. */ - if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0 + if (init != 0 && GLRO(dl_naudit) > 0 /* Don't do anything if no auditor wants to intercept this call. */ && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0) { diff --git a/include/link.h b/include/link.h index 5924594548..1d13d02637 100644 --- a/include/link.h +++ b/include/link.h @@ -216,6 +216,8 @@ struct link_map unsigned int boundndx; uint32_t enterexit; unsigned int flags; + /* Indicates if reloc_result fields have been initialized. */ + unsigned int init; } *l_reloc_result; /* Pointer to the version information if available. */ diff --git a/nptl/Makefile b/nptl/Makefile index be8066524c..9862ef53fc 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -382,7 +382,8 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \ tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4 \ tst-oncex3 tst-oncex4 ifeq ($(build-shared),yes) -tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder +tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder \ + tst-audit-threads tests-internal += tst-tls3 tst-tls3-malloc tst-tls5 tst-stackguard1 tests-nolibpthread += tst-fini1 ifeq ($(have-z-execstack),yes) @@ -394,7 +395,8 @@ modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \ tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \ tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \ tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \ - tst-join7mod tst-compat-forwarder-mod + tst-join7mod tst-compat-forwarder-mod tst-audit-threads-mod1 \ + tst-audit-threads-mod2 extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \ tst-cleanup4aux.o tst-cleanupx4aux.o test-extras += tst-cleanup4aux tst-cleanupx4aux @@ -709,6 +711,14 @@ endif $(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so +# Protect against a build using -Wl,-z,now. +LDFLAGS-tst-audit-threads-mod1.so = -Wl,-z,lazy +LDFLAGS-tst-audit-threads-mod2.so = -Wl,-z,lazy +LDFLAGS-tst-audit-threads = -Wl,-z,lazy +$(objpfx)tst-audit-threads: $(objpfx)tst-audit-threads-mod2.so +$(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so +tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so + # The tests here better do not run in parallel ifneq ($(filter %tests,$(MAKECMDGOALS)),) .NOTPARALLEL: diff --git a/nptl/tst-audit-threads-mod1.c b/nptl/tst-audit-threads-mod1.c new file mode 100644 index 0000000000..6fa0c0c6c4 --- /dev/null +++ b/nptl/tst-audit-threads-mod1.c @@ -0,0 +1,74 @@ +/* Dummy audit library for test-audit-threads. + + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +/* We must use a dummy LD_AUDIT module to force the dynamic loader to + *not* update the real PLT, and instead use a cached value for the + lazy resolution result. It is the update of that cached value that + we are testing for correctness by doing this. */ + +/* Library to be audited. */ +#define LIB "tst-audit-threads-mod2.so" +/* CALLNUM is the number of retNum functions. */ +#define CALLNUM 7999 + +#define CONCATX(a, b) __CONCAT (a, b) + +static int previous = 0; + +unsigned int +la_version (unsigned int ver) +{ + return 1; +} + +unsigned int +la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) +{ + return LA_FLG_BINDTO | LA_FLG_BINDFROM; +} + +uintptr_t +CONCATX(la_symbind, __ELF_NATIVE_CLASS) (ElfW(Sym) *sym, + unsigned int ndx, + uintptr_t *refcook, + uintptr_t *defcook, + unsigned int *flags, + const char *symname) +{ + const char * retnum = "retNum"; + char * num = strstr (symname, retnum); + int n; + /* Validate if the symbols are getting called in the correct order. + This code is here just to guarantee Binutils will not optimize out this + function. */ + if (num != NULL) + { + n = atoi (num); + assert (n >= previous); + assert (n <= CALLNUM); + previous = n; + } + return sym->st_value; +} diff --git a/nptl/tst-audit-threads-mod2.c b/nptl/tst-audit-threads-mod2.c new file mode 100644 index 0000000000..f9817dd3dc --- /dev/null +++ b/nptl/tst-audit-threads-mod2.c @@ -0,0 +1,22 @@ +/* Shared object with a huge number of functions for test-audit-threads. + + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* Define all the retNumN functions in a library. */ +#define definenum +#include "tst-audit-threads.h" diff --git a/nptl/tst-audit-threads.c b/nptl/tst-audit-threads.c new file mode 100644 index 0000000000..e4bf433bd8 --- /dev/null +++ b/nptl/tst-audit-threads.c @@ -0,0 +1,97 @@ +/* Test multi-threading using LD_AUDIT. + + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a + library with a huge number of functions in order to validate lazy symbol + binding with an audit library. We use one thread per CPU to test that + concurrent lazy resolution does not have any defects which would cause + the process to fail. We use an LD_AUDIT library to force the testing of + the relocation resolution caching code in the dynamic loader i.e. + _dl_runtime_profile and _dl_profile_fixup. */ + +#include +#include +#include +#include + +static int do_test (void); + +/* This test usually takes less than 3s to run. However, there are cases that + take up to 30s. */ +#define TIMEOUT 60 +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +/* Declare the functions we are going to call. */ +#define externnum +#include "tst-audit-threads.h" +#undef externnum + +int num_threads; +pthread_barrier_t barrier; + +void +sync_all (int num) +{ + pthread_barrier_wait (&barrier); +} + +void +call_all_ret_nums (void) +{ + /* Call each function one at a time from all threads. */ +#define callnum +#include "tst-audit-threads.h" +#undef callnum +} + +void * +thread_main (void *unused) +{ + call_all_ret_nums (); + return NULL; +} + +#define STR2(X) #X +#define STR(X) STR2(X) + +static int +do_test (void) +{ + int i; + pthread_t *threads; + + num_threads = get_nprocs (); + if (num_threads <= 1) + num_threads = 2; + + /* Used to synchronize all the threads after calling each retNumN. */ + xpthread_barrier_init (&barrier, NULL, num_threads); + + threads = (pthread_t *) xcalloc (num_threads, sizeof(pthread_t)); + for (i = 0; i < num_threads; i++) + threads[i] = xpthread_create(NULL, thread_main, NULL); + + for (i = 0; i < num_threads; i++) + xpthread_join(threads[i]); + + free (threads); + + return 0; +} diff --git a/nptl/tst-audit-threads.h b/nptl/tst-audit-threads.h new file mode 100644 index 0000000000..491d0dcbf0 --- /dev/null +++ b/nptl/tst-audit-threads.h @@ -0,0 +1,89 @@ +/* Helper header for test-audit-threads. + + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* We use this helper to create a large number of functions, all of + which will be resolved lazily and thus have their PLT updated. + This is done to provide enough functions that we can statistically + observe a thread vs. PLT resolution failure if one exists. */ + +#define CONCAT(a, b) a ## b +#define NUM(x, y) CONCAT (x, y) + +#define FUNC10(x) \ + FUNC (NUM (x, 0)); \ + FUNC (NUM (x, 1)); \ + FUNC (NUM (x, 2)); \ + FUNC (NUM (x, 3)); \ + FUNC (NUM (x, 4)); \ + FUNC (NUM (x, 5)); \ + FUNC (NUM (x, 6)); \ + FUNC (NUM (x, 7)); \ + FUNC (NUM (x, 8)); \ + FUNC (NUM (x, 9)) + +#define FUNC100(x) \ + FUNC10 (NUM (x, 0)); \ + FUNC10 (NUM (x, 1)); \ + FUNC10 (NUM (x, 2)); \ + FUNC10 (NUM (x, 3)); \ + FUNC10 (NUM (x, 4)); \ + FUNC10 (NUM (x, 5)); \ + FUNC10 (NUM (x, 6)); \ + FUNC10 (NUM (x, 7)); \ + FUNC10 (NUM (x, 8)); \ + FUNC10 (NUM (x, 9)) + +#define FUNC1000(x) \ + FUNC100 (NUM (x, 0)); \ + FUNC100 (NUM (x, 1)); \ + FUNC100 (NUM (x, 2)); \ + FUNC100 (NUM (x, 3)); \ + FUNC100 (NUM (x, 4)); \ + FUNC100 (NUM (x, 5)); \ + FUNC100 (NUM (x, 6)); \ + FUNC100 (NUM (x, 7)); \ + FUNC100 (NUM (x, 8)); \ + FUNC100 (NUM (x, 9)) + +#define FUNC7000() \ + FUNC1000 (1); \ + FUNC1000 (2); \ + FUNC1000 (3); \ + FUNC1000 (4); \ + FUNC1000 (5); \ + FUNC1000 (6); \ + FUNC1000 (7); + +#ifdef FUNC +# undef FUNC +#endif + +#ifdef externnum +# define FUNC(x) extern int CONCAT (retNum, x) (void) +#endif + +#ifdef definenum +# define FUNC(x) int CONCAT (retNum, x) (void) { return x; } +#endif + +#ifdef callnum +# define FUNC(x) CONCAT (retNum, x) (); sync_all (x) +#endif + +FUNC7000 ();