mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-27 03:33:33 +08:00
elf: Testing infrastructure for ld.so DSO sorting (BZ #17645)
This is the first of a 2-part patch set that fixes slow DSO sorting behavior in the dynamic loader, as reported in BZ #17645. In order to facilitate such a large modification to the dynamic loader, this first patch implements a testing framework for validating shared object sorting behavior, to enable comparison between old/new sorting algorithms, and any later enhancements. This testing infrastructure consists of a Python script scripts/dso-ordering-test.py' which takes in a description language, consisting of strings that describe a set of link dependency relations between DSOs, and generates testcase programs and Makefile fragments to automatically test the described situation, for example: a->b->c->d # four objects linked one after another a->[bc]->d;b->c # a depends on b and c, which both depend on d, # b depends on c (b,c linked to object a in fixed order) a->b->c;{+a;%a;-a} # a, b, c serially dependent, main program uses # dlopen/dlsym/dlclose on object a a->b->c;{}!->[abc] # a, b, c serially dependent; multiple tests generated # to test all permutations of a, b, c ordering linked # to main program (Above is just a short description of what the script can do, more documentation is in the script comments.) Two files containing several new tests, elf/dso-sort-tests-[12].def are added, including test scenarios for BZ #15311 and Redhat issue #1162810 [1]. Due to the nature of dynamic loader tests, where the sorting behavior and test output occurs before/after main(), generating testcases to use support/test-driver.c does not suffice to control meaningful timeout for ld.so. Therefore a new utility program 'support/test-run-command', based on test-driver.c/support_test_main.c has been added. This does the same testcase control, but for a program specified through a command-line rather than at the source code level. This utility is used to run the dynamic loader testcases generated by dso-ordering-test.py. [1] https://bugzilla.redhat.com/show_bug.cgi?id=1162810 Signed-off-by: Chung-Lin Tang <cltang@codesourcery.com> Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
parent
0ff2d30dae
commit
e6fd79f379
15
elf/Makefile
15
elf/Makefile
@ -477,6 +477,21 @@ tests-special += $(objpfx)order-cmp.out $(objpfx)tst-array1-cmp.out \
|
||||
$(objpfx)tst-unused-dep-cmp.out
|
||||
endif
|
||||
|
||||
# DSO sorting tests:
|
||||
# The dso-ordering-test.py script generates testcase source files in $(objpfx),
|
||||
# creating a $(objpfx)<testcase-name>-dir for each testcase, and creates a
|
||||
# Makefile fragment to be included.
|
||||
define include_dsosort_tests
|
||||
$(objpfx)$(1).generated-makefile: $(1)
|
||||
$(PYTHON) $(..)scripts/dso-ordering-test.py \
|
||||
--description-file $$< --objpfx $(objpfx) --output-makefile $$@
|
||||
include $(objpfx)$(1).generated-makefile
|
||||
endef
|
||||
|
||||
# Generate from each testcase description file
|
||||
$(eval $(call include_dsosort_tests,dso-sort-tests-1.def))
|
||||
$(eval $(call include_dsosort_tests,dso-sort-tests-2.def))
|
||||
|
||||
check-abi: $(objpfx)check-abi-ld.out
|
||||
tests-special += $(objpfx)check-abi-ld.out
|
||||
update-abi: update-abi-ld
|
||||
|
66
elf/dso-sort-tests-1.def
Normal file
66
elf/dso-sort-tests-1.def
Normal file
@ -0,0 +1,66 @@
|
||||
# DSO sorting test descriptions.
|
||||
# This file is to be processed by ../scripts/dso-ordering-test.py, see usage
|
||||
# in elf/Makefile for how it is executed.
|
||||
|
||||
# We test both dynamic loader sorting algorithms
|
||||
tunable_option: glibc.rtld.dynamic_sort=1
|
||||
tunable_option: glibc.rtld.dynamic_sort=2
|
||||
|
||||
# Sequence of single dependencies with no cycles.
|
||||
tst-dso-ordering1: a->b->c
|
||||
output: c>b>a>{}<a<b<c
|
||||
|
||||
# Sequence including 2 dependent DSOs not at the end of the graph.
|
||||
tst-dso-ordering2: a->b->[cd]->e
|
||||
output: e>d>c>b>a>{}<a<b<c<d<e
|
||||
|
||||
# Complex order with 3 "layers" of full dependencies
|
||||
tst-dso-ordering3: a->[bc]->[def]->[gh]->i
|
||||
output: i>h>g>f>e>d>c>b>a>{}<a<b<c<d<e<f<g<h<i
|
||||
|
||||
# Sequence including 2 dependent DSOs at the end of the graph.
|
||||
# Additionally the same dependencies appear in two paths.
|
||||
tst-dso-ordering4: a->b->[de];a->c->d->e
|
||||
output: e>d>c>b>a>{}<a<b<c<d<e
|
||||
|
||||
# Test that b->c cross link is respected correctly
|
||||
tst-dso-ordering5: a!->[bc]->d;b->c
|
||||
output: d>c>b>a>{}<a<b<c<d
|
||||
|
||||
# First DSO fully dependent on 4 DSOs, with another DSO at the end of chain.
|
||||
tst-dso-ordering6: a->[bcde]->f
|
||||
output: f>e>d>c>b>a>{}<a<b<c<d<e<f
|
||||
|
||||
# Sequence including 2 dependent and 3 dependent DSOs, and one of the
|
||||
# dependent DSOs is dependent on an earlier DSO.
|
||||
tst-dso-ordering7: a->[bc];b->[cde];e->f
|
||||
output: f>e>d>c>b>a>{}<a<b<c<d<e<f
|
||||
|
||||
# Sequence where the DSO c is unerlinked and calls a function in DSO a which
|
||||
# is technically a cycle. The main executable depends on the first two DSOs.
|
||||
# Note: This test has unspecified behavior.
|
||||
tst-dso-ordering8: a->b->c=>a;{}->[ba]
|
||||
output: c>b>a>{}<a<b<c
|
||||
|
||||
# Generate the permutation of DT_NEEDED order between the main binary and
|
||||
# all 5 DSOs; all link orders should produce exact same init/fini ordering
|
||||
tst-dso-ordering9: a->b->c->d->e;{}!->[abcde]
|
||||
output: e>d>c>b>a>{}<a<b<c<d<e
|
||||
|
||||
# Test if init/fini ordering behavior is proper, despite main program with
|
||||
# an soname that may cause confusion
|
||||
tst-dso-ordering10: {}->a->b->c;soname({})=c
|
||||
output: b>a>{}<a<b
|
||||
|
||||
# Complex example from Bugzilla #15311, under-linked and with circular
|
||||
# relocation(dynamic) dependencies. While this is technically unspecified, the
|
||||
# presumed reasonable practical behavior is for the destructor order to respect
|
||||
# the static DT_NEEDED links (here this means the a->b->c->d order).
|
||||
# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based
|
||||
# dynamic_sort=2 algorithm does, although it is still arguable whether going
|
||||
# beyond spec to do this is the right thing to do.
|
||||
# The below expected outputs are what the two algorithms currently produce
|
||||
# respectively, for regression testing purposes.
|
||||
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
|
||||
xfail_output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
|
||||
output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
|
614
elf/dso-sort-tests-2.def
Normal file
614
elf/dso-sort-tests-2.def
Normal file
File diff suppressed because one or more lines are too long
1144
scripts/dso-ordering-test.py
Normal file
1144
scripts/dso-ordering-test.py
Normal file
File diff suppressed because it is too large
Load Diff
1
support/Depend
Normal file
1
support/Depend
Normal file
@ -0,0 +1 @@
|
||||
elf
|
@ -257,10 +257,16 @@ others-noinstall += shell-container echo-container true-container
|
||||
others += $(LINKS_DSO_PROGRAM)
|
||||
others-noinstall += $(LINKS_DSO_PROGRAM)
|
||||
|
||||
others += test-run-command
|
||||
others-static += test-run-command
|
||||
others-noinstall += test-run-command
|
||||
LDLIBS-test-run-command = $(libsupport)
|
||||
|
||||
$(objpfx)test-container : $(libsupport)
|
||||
$(objpfx)shell-container : $(libsupport)
|
||||
$(objpfx)echo-container : $(libsupport)
|
||||
$(objpfx)true-container : $(libsupport)
|
||||
$(objpfx)test-run-command : $(libsupport) $(common-objpfx)elf/static-stubs.o
|
||||
|
||||
tests = \
|
||||
README-testing \
|
||||
|
@ -228,6 +228,18 @@ run_test_function (int argc, char **argv, const struct test_config *config)
|
||||
while (wait_for_debugger)
|
||||
usleep (1000);
|
||||
|
||||
if (config->run_command_mode)
|
||||
{
|
||||
/* In run-command-mode, the child process executes the command line
|
||||
arguments as a new program. */
|
||||
char **argv_ = xmalloc (sizeof (char *) * argc);
|
||||
memcpy (argv_, &argv[1], sizeof (char *) * (argc - 1));
|
||||
argv_[argc - 1] = NULL;
|
||||
execv (argv_[0], argv_);
|
||||
printf ("error: should not return here\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (config->test_function != NULL)
|
||||
return config->test_function ();
|
||||
else if (config->test_function_argv != NULL)
|
||||
|
@ -116,7 +116,9 @@ main (int argc, char **argv)
|
||||
#if defined (TEST_FUNCTION) && defined (TEST_FUNCTON_ARGV)
|
||||
# error TEST_FUNCTION and TEST_FUNCTION_ARGV cannot be defined at the same time
|
||||
#endif
|
||||
#if defined (TEST_FUNCTION)
|
||||
#ifdef RUN_COMMAND_MODE
|
||||
test_config.run_command_mode = 1;
|
||||
#elif defined (TEST_FUNCTION)
|
||||
test_config.test_function = TEST_FUNCTION;
|
||||
#elif defined (TEST_FUNCTION_ARGV)
|
||||
test_config.test_function_argv = TEST_FUNCTION_ARGV;
|
||||
|
@ -36,6 +36,7 @@ struct test_config
|
||||
int expected_signal; /* If non-zero, expect termination by signal. */
|
||||
char no_mallopt; /* Boolean flag to disable mallopt. */
|
||||
char no_setvbuf; /* Boolean flag to disable setvbuf. */
|
||||
char run_command_mode; /* Boolean flag to indicate run-command-mode. */
|
||||
const char *optstring; /* Short command line options. */
|
||||
};
|
||||
|
||||
|
22
support/test-run-command.c
Normal file
22
support/test-run-command.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* Main program for test-run-command support utility.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This is basically a configuration of test-driver.c into a general
|
||||
command-line program runner. */
|
||||
#define RUN_COMMAND_MODE
|
||||
#include <test-driver.c>
|
Loading…
Reference in New Issue
Block a user