unofficial mirror of libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Carlos O'Donell via Libc-alpha <libc-alpha@sourceware.org>
To: cltang@codesourcery.com, Andreas Schwab <schwab@linux-m68k.org>
Cc: Catherine Moore <clm@codesourcery.com>,
	GNU C Library <libc-alpha@sourceware.org>
Subject: Re: [PATCH v2.1 1/2] BZ #17645, fix slow DSO sorting behavior in dynamic loader
Date: Thu, 18 Jun 2020 17:30:35 -0400	[thread overview]
Message-ID: <456fbddc-7915-19da-3ffe-e58b86ae7980@redhat.com> (raw)
In-Reply-To: <e7dfb605-ce00-9ce2-6b1c-39b1430999c3@mentor.com>

On 5/20/20 10:38 AM, Chung-Lin Tang wrote:
> On 2020/5/19 10:50 PM, Andreas Schwab wrote:
>> On Mai 19 2020, Chung-Lin Tang wrote:
>>
>>> Do you mean that an include target is automatically re-made?
>>
>> Yes.  The glibc makefiles already make use of that feature extensively.
>>
>> Andreas.
> 
> Thanks for the tip. I've updated that part of the patch (now dubbed v2.1),
> this time using a make rule for the Makefile fragment, and a small fix to
> the Python script to use os.makedirs instead of os.mkdir for subdirectory
> creation (the make rule change revealed a race bug there)

I'm starting my reivew from the v2.1 version. Thank you for all of this work.

No regressions on x86_64 and i686.

Please review comments.

Tested-by: Carlos O'Donell <carlos@redhat.com>

> diff --git a/elf/Makefile b/elf/Makefile
> index 77da61ae64..9dc65c10f9 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -420,6 +420,33 @@ 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.

OK.

> +define include_dsosort_tests
> +$(objpfx)$(1).tmp-makefile: $(1)
> +	$(PYTHON) $(..)scripts/dso-ordering-test.py \
> +	--description-file $$< --objpfx $(objpfx) --output-makefile $$@
> +include $(objpfx)$(1).tmp-makefile

OK.

> +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))

OK.

> +
> +# This function can be used to define a single DSO sorting test, completely
> +# here in the Makefile, for example:
> +#   $(eval $(call single_dsosort_test,testcase-name,'a->b->c','c>b>a>{}<a<b<c'))
> +# Currently all tests are defined in description files, and this function is
> +# not utilized, but kept here for possible conveniences.
> +define single_dsosort_test
> +$(objpfx)$(1).tmp-makefile:
> +	$(PYTHON) $(..)scripts/dso-ordering-test.py --objpfx $(objpfx) \
> +	$(2) $(1) $(3) --output-makefile $$@
> +include $(objpfx)$(1).tmp-makefile
> +endef

OK.

> +
>  check-abi: $(objpfx)check-abi-ld.out
>  tests-special += $(objpfx)check-abi-ld.out
>  update-abi: update-abi-ld
> diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
> new file mode 100644
> index 0000000000..d94c6eda87
> --- /dev/null
> +++ b/elf/dso-sort-tests-1.def
> @@ -0,0 +1,30 @@

We should have some comments explaing what each test does.

# 1. Sequence of signle dependencies with no cycles.

> +tst-dso-ordering1: a->b->c
> +output: c>b>a>{}<a<b<c
> +

# 2. 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

OK.

> +

# 3. Complex sequence including 2 and 3 dependent DSOs.

> +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
> +


# 4. 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

OK. Nicely sorted.

> +

# 5. Sequence including 2 dependent DSOs, and one of the
#    dependent DSOs is dependent on an earlier DSO.

> +tst-dso-ordering5: a->[bc]->d;b->c
> +output: d>c>b>a>{}<a<b<c<d
> +


# 6. Sequence including 4 dependent DSOs.

> +tst-dso-ordering6: a->[bcde]->f
> +output: f>e>d>c>b>a>{}<a<b<c<d<e<f
> +

# 7. 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
> +

# 8. 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 behaviour.

> +tst-dso-ordering8: a->b->c=>a;{}->[ba]
> +output: c>b>a>{}<a<b<c

OK. The call reference is ignored, and the DT_NEEDED order
is used in the sort, and this is what is expeted by users.
It is expected that if the main program depends on b and a
that they will be included in the program.

> +

# 9. Generate the permutation of DT_NEEDED order between
#    the main binary and all 5 DSOs and it should have no
#    impact on sorting.

> +tst-dso-ordering9: a->b->c->d->e;{}!->[abcde]
> +output: e>d>c>b>a>{}<a<b<c<d<e
> +

OK.

> +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

The test case us underlinked and the right solution for a program
doing this would be to fix all the underlinking. However, we keep
the test case here to ensure validity of the pre/post sorting for
underlinked unspecified cases.

- DSO d is underlinked and has call references to fn_a in a
  and fn_b in b and fn_g in g, but is not linked to a or b.
- DSO c is underlinked and has a call reference to fn_a in a,
  fn_f in f,  but is not linked to a or f.
- DSO b is underlinked and has a call reference to fn_e in e
  but is not linked to e.
- DSO g is underlinked and has a call reference to fn_c in c
  but is not linked to c.
- DSO f is underlinked and has a call reference to fn_b in b
  but is not linked to b.

dlopen(a)
 a DT_NEEDED b
 b DT_NEEDED c, reloc against fn_e, fn_g
 c DT_NEEDED d, reloc against fn_a, fn_f
 d DT_NEEDED NULL, reloc against fn_a, fn_b, fn_g
dlopen(e)
 reloc against fn_a
dlopen(f)
 reloc against fn_b
dlopen(g)
dlopen(d)
dlsym(d,fn_d)
fn_d()
 callref fn_b
   callref fn_e
     callref fn_a
 callref fn_a
 callref fn_g
   callref fn_c
     callref fn_a
     callref fn_f
       callref fn_b
         callref fn_e
           callref fn_a
dlclose(d)
dlclose(g)
dlclose(f)
dlclose(e)
dlclose(a)

Question: Why doesn't +d[] run a constructor for d?
Answer: Because the constructor ran early when a was loaded.

{+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[

At this point we have closed all the direct open counts on the libraries.
Closing a e.g. -a[ will start the final unload of all the other loaded libraries.

> +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];}

We need a comment explaining the two sorting orders and why we end up at each one.

> diff --git a/elf/dso-sort-tests-2.def b/elf/dso-sort-tests-2.def
> new file mode 100644
> index 0000000000..39c6acbaf2
> --- /dev/null
> +++ b/elf/dso-sort-tests-2.def
> @@ -0,0 +1,605 @@
> +tst-redhat-1162810:
> +{}->A101
> +{}->*
> +A101->(B101 B163 B122 B181)
> +A102->(B102 B140 B199 B158)
> +A103->(B103 B117 B176 B135)
> +A104->(B104 B194 B153 B112)
> +A105->(B105 B171 B130 B189)
> +A106->(B106 B148 B107 B166)
> +A107->(B107 B125 B184 B143)
> +A108->(B108 B102 B161 B120)
> +A109->(B109 B179 B138 B197)
> +A110->(B110 B156 B115 B174)
> +A111->(B111 B133 B192 B151)
> +A112->(B112 B110 B169 B128)
> +A113->(B113 B187 B146 B105)
> +A114->(B114 B164 B123 B182)
> +A115->(B115 B141 B200 B159)
> +A116->(B116 B118 B177 B136)
> +A117->(B117 B195 B154 B113)
> +A118->(B118 B172 B131 B190)
> +A119->(B119 B149 B108 B167)
> +A120->(B120 B126 B185 B144)
> +A121->(B121 B103 B162)
> +A122->(B122 B180 B139 B198)
> +A123->(B123 B157 B116 B175)
> +A124->(B124 B134 B193 B152)
> +A125->(B125 B111 B170 B129)
> +A126->(B126 B188 B147 B106)
> +A127->(B127 B165 B124 B183)
> +A128->(B128 B142 B101 B160)
> +A129->(B129 B119 B178 B137)
> +A130->(B130 B196 B155 B114)
> +A131->(B131 B173 B132 B191)
> +A132->(B132 B150 B109 B168)
> +A133->(B133 B127 B186 B145)
> +A134->(B134 B104 B163 B122)
> +A135->(B135 B181 B140 B199)
> +A136->(B136 B158 B117 B176)
> +A137->(B137 B135 B194 B153)
> +A138->(B138 B112 B171 B130)
> +A139->(B139 B189 B148 B107)
> +A140->(B140 B166 B125 B184)
> +A141->(B141 B143 B102 B161)
> +A142->(B142 B120 B179 B138)
> +A143->(B143 B197 B156 B115)
> +A144->(B144 B174 B133 B192)
> +A145->(B145 B151 B110 B169)
> +A146->(B146 B128 B187)
> +A147->(B147 B105 B164 B123)
> +A148->(B148 B182 B141 B200)
> +A149->(B149 B159 B118 B177)
> +A150->(B150 B136 B195 B154)
> +A151->(B151 B113 B172 B131)
> +A152->(B152 B190 B149 B108)
> +A153->(B153 B167 B126 B185)
> +A154->(B154 B144 B103 B162)
> +A155->(B155 B121 B180 B139)
> +A156->(B156 B198 B157 B116)
> +A157->(B157 B175 B134 B193)
> +A158->(B158 B152 B111 B170)
> +A159->(B159 B129 B188 B147)
> +A160->(B160 B106 B165 B124)
> +A161->(B161 B183 B142 B101)
> +A162->(B162 B160 B119 B178)
> +A163->(B163 B137 B196 B155)
> +A164->(B164 B114 B173 B132)
> +A165->(B165 B191 B150 B109)
> +A166->(B166 B168 B127 B186)
> +A167->(B167 B145 B104 B163)
> +A168->(B168 B122 B181 B140)
> +A169->(B169 B199 B158 B117)
> +A170->(B170 B176 B135 B194)
> +A171->(B171 B153 B112)
> +A172->(B172 B130 B189 B148)
> +A173->(B173 B107 B166 B125)
> +A174->(B174 B184 B143 B102)
> +A175->(B175 B161 B120 B179)
> +A176->(B176 B138 B197 B156)
> +A177->(B177 B115 B174 B133)
> +A178->(B178 B192 B151 B110)
> +A179->(B179 B169 B128 B187)
> +A180->(B180 B146 B105 B164)
> +A181->(B181 B123 B182 B141)
> +A182->(B182 B200 B159 B118)
> +A183->(B183 B177 B136 B195)
> +A184->(B184 B154 B113 B172)
> +A185->(B185 B131 B190 B149)
> +A186->(B186 B108 B167 B126)
> +A187->(B187 B185 B144 B103)
> +A188->(B188 B162 B121 B180)
> +A189->(B189 B139 B198 B157)
> +A190->(B190 B116 B175 B134)
> +A191->(B191 B193 B152 B111)
> +A192->(B192 B170 B129 B188)
> +A193->(B193 B147 B106 B165)
> +A194->(B194 B124 B183 B142)
> +A195->(B195 B101 B160 B119)
> +A196->(B196 B178 B137)
> +A197->(B197 B155 B114 B173)
> +A198->(B198 B132 B191 B150)
> +A199->(B199 B109 B168 B127)
> +A200->(B200 B186 B145 B104)
> +B101->(C101 C164 C123 C182)
> +B102->(C102 C141 C200 C159)
> +B103->(C103 C118 C177 C136)
> +B104->(C104 C195 C154 C113)
> +B105->(C105 C172 C131 C190)
> +B106->(C106 C149 C108 C167)
> +B107->(C107 C126 C185 C144)
> +B108->(C108 C103 C162 C121)
> +B109->(C109 C180 C139 C198)
> +B110->(C110 C157 C116 C175)
> +B111->(C111 C134 C193 C152)
> +B112->(C112 C111 C170 C129)
> +B113->(C113 C188 C147 C106)
> +B114->(C114 C165 C124 C183)
> +B115->(C115 C142 C101 C160)
> +B116->(C116 C119 C178 C137)
> +B117->(C117 C196 C155 C114)
> +B118->(C118 C173 C132 C191)
> +B119->(C119 C150 C109 C168)
> +B120->(C120 C127 C186 C145)
> +B121->(C121 C104 C163 C122)
> +B122->(C122 C181 C140 C199)
> +B123->(C123 C158 C117 C176)
> +B124->(C124 C135 C194 C153)
> +B125->(C125 C112 C171 C130)
> +B126->(C126 C189 C148 C107)
> +B127->(C127 C166 C125 C184)
> +B128->(C128 C143 C102 C161)
> +B129->(C129 C120 C179 C138)
> +B130->(C130 C197 C156 C115)
> +B131->(C131 C174 C133 C192)
> +B132->(C132 C151 C110 C169)
> +B133->(C133 C128 C187 C146)
> +B134->(C134 C105 C164 C123)
> +B135->(C135 C182 C141 C200)
> +B136->(C136 C159 C118 C177)
> +B137->(C137 C136 C195 C154)
> +B138->(C138 C113 C172 C131)
> +B139->(C139 C190 C149 C108)
> +B140->(C140 C167 C126 C185)
> +B141->(C141 C144 C103 C162)
> +B142->(C142 C121 C180 C139)
> +B143->(C143 C198 C157 C116)
> +B144->(C144 C175 C134 C193)
> +B145->(C145 C152 C111 C170)
> +B146->(C146 C129 C188 C147)
> +B147->(C147 C106 C165 C124)
> +B148->(C148 C183 C142 C101)
> +B149->(C149 C160 C119 C178)
> +B150->(C150 C137 C196 C155)
> +B151->(C151 C114 C173 C132)
> +B152->(C152 C191 C150 C109)
> +B153->(C153 C168 C127 C186)
> +B154->(C154 C145 C104 C163)
> +B155->(C155 C122 C181 C140)
> +B156->(C156 C199 C158 C117)
> +B157->(C157 C176 C135 C194)
> +B158->(C158 C153 C112 C171)
> +B159->(C159 C130 C189 C148)
> +B160->(C160 C107 C166 C125)
> +B161->(C161 C184 C143 C102)
> +B162->(C162 C161 C120 C179)
> +B163->(C163 C138 C197 C156)
> +B164->(C164 C115 C174 C133)
> +B165->(C165 C192 C151 C110)
> +B166->(C166 C169 C128 C187)
> +B167->(C167 C146 C105 C164)
> +B168->(C168 C123 C182 C141)
> +B169->(C169 C200 C159 C118)
> +B170->(C170 C177 C136 C195)
> +B171->(C171 C154 C113 C172)
> +B172->(C172 C131 C190 C149)
> +B173->(C173 C108 C167 C126)
> +B174->(C174 C185 C144 C103)
> +B175->(C175 C162 C121 C180)
> +B176->(C176 C139 C198 C157)
> +B177->(C177 C116 C175 C134)
> +B178->(C178 C193 C152 C111)
> +B179->(C179 C170 C129 C188)
> +B180->(C180 C147 C106 C165)
> +B181->(C181 C124 C183 C142)
> +B182->(C182 C101 C160 C119)
> +B183->(C183 C178 C137 C196)
> +B184->(C184 C155 C114 C173)
> +B185->(C185 C132 C191 C150)
> +B186->(C186 C109 C168 C127)
> +B187->(C187 C186 C145 C104)
> +B188->(C188 C163 C122 C181)
> +B189->(C189 C140 C199 C158)
> +B190->(C190 C117 C176 C135)
> +B191->(C191 C194 C153 C112)
> +B192->(C192 C171 C130 C189)
> +B193->(C193 C148 C107 C166)
> +B194->(C194 C125 C184 C143)
> +B195->(C195 C102 C161 C120)
> +B196->(C196 C179 C138 C197)
> +B197->(C197 C156 C115 C174)
> +B198->(C198 C133 C192 C151)
> +B199->(C199 C110 C169 C128)
> +B200->(C200 C187 C146 C105)
> +C101->(A165 A124)
> +C102->(A183 A142)
> +C103->(A101 A160)
> +C104->(A119 A178)
> +C105->(A137 A196)
> +C106->(A155 A114)
> +C107->(A173 A132)
> +C108->(A191 A150)
> +C109->(A109 A168)
> +C110->(A127 A186)
> +C111->(A145 A104)
> +C112->(A163 A122)
> +C113->(A181 A140)
> +C114->(A199 A158)
> +C115->(A117 A176)
> +C116->(A135 A194)
> +C117->(A153 A112)
> +C118->(A171 A130)
> +C119->(A189 A148)
> +C120->(A107 A166)
> +C121->(A125 A184)
> +C122->(A143 A102)
> +C123->(A161 A120)
> +C124->(A179 A138)
> +C125->(A197 A156)
> +C126->(A115 A174)
> +C127->(A133 A192)
> +C128->(A151 A110)
> +C129->(A169 A128)
> +C130->(A187 A146)
> +C131->(A105 A164)
> +C132->(A123 A182)
> +C133->(A141 A200)
> +C134->(A159 A118)
> +C135->(A177 A136)
> +C136->(A195 A154)
> +C137->(A113 A172)
> +C138->(A131 A190)
> +C139->(A149 A108)
> +C140->(A167 A126)
> +C141->(A185 A144)
> +C142->(A103 A162)
> +C143->(A121 A180)
> +C144->(A139 A198)
> +C145->(A157 A116)
> +C146->(A175 A134)
> +C147->(A193 A152)
> +C148->(A111 A170)
> +C149->(A129 A188)
> +C150->(A147 A106)
> +C151->(A165 A124)
> +C152->(A183 A142)
> +C153->(A101 A160)
> +C154->(A119 A178)
> +C155->(A137 A196)
> +C156->(A155 A114)
> +C157->(A173 A132)
> +C158->(A191 A150)
> +C159->(A109 A168)
> +C160->(A127 A186)
> +C161->(A145 A104)
> +C162->(A163 A122)
> +C163->(A181 A140)
> +C164->(A199 A158)
> +C165->(A117 A176)
> +C166->(A135 A194)
> +C167->(A153 A112)
> +C168->(A171 A130)
> +C169->(A189 A148)
> +C170->(A107 A166)
> +C171->(A125 A184)
> +C172->(A143 A102)
> +C173->(A161 A120)
> +C174->(A179 A138)
> +C175->(A197 A156)
> +C176->(A115 A174)
> +C177->(A133 A192)
> +C178->(A151 A110)
> +C179->(A169 A128)
> +C180->(A187 A146)
> +C181->(A105 A164)
> +C182->(A123 A182)
> +C183->(A141 A200)
> +C184->(A159 A118)
> +C185->(A177 A136)
> +C186->(A195 A154)
> +C187->(A113 A172)
> +C188->(A131 A190)
> +C189->(A149 A108)
> +C190->(A167 A126)
> +C191->(A185 A144)
> +C192->(A103 A162)
> +C193->(A121 A180)
> +C194->(A139 A198)
> +C195->(A157 A116)
> +C196->(A175 A134)
> +C197->(A193 A152)
> +C198->(A111 A170)
> +C199->(A129 A188)
> +C200->(A147 A106)
> +M11X11->(M13X14 M12X13 M12X12 M12X11)
> +M11X12->(M13X25 M12X24 M12X23 M12X22)
> +M11X13->(M13X21 M12X20 M12X19 M12X18)
> +M11X14->(M13X17 M12X16 M12X15 M12X14)
> +M11X15->(M13X13 M12X12 M12X11 M12X25)
> +M11X16->(M13X24 M12X23 M12X22 M12X21)
> +M11X17->(M13X20 M12X19 M12X18 M12X17)
> +M11X18->(M13X16 M12X15 M12X14 M12X13)
> +M11X19->(M13X12 M12X11 M12X25 M12X24)
> +M11X20->(M13X23 M12X22 M12X21 M12X20)
> +M11X21->(M13X19 M12X18 M12X17 M12X16)
> +M11X22->(M13X15 M12X14 M12X13 M12X12)
> +M11X23->(M13X11 M12X25 M12X24 M12X23)
> +M11X24->(M13X22 M12X21 M12X20 M12X19)
> +M11X25->(M13X18 M12X17 M12X16 M12X15)
> +M12X11->(M14X14 M13X13 M13X12 M13X11)
> +M12X12->(M14X25 M13X24 M13X23 M13X22)
> +M12X13->(M14X21 M13X20 M13X19 M13X18)
> +M12X14->(M14X17 M13X16 M13X15 M13X14)
> +M12X15->(M14X13 M13X12 M13X11 M13X25)
> +M12X16->(M14X24 M13X23 M13X22 M13X21)
> +M12X17->(M14X20 M13X19 M13X18 M13X17)
> +M12X18->(M14X16 M13X15 M13X14 M13X13)
> +M12X19->(M14X12 M13X11 M13X25 M13X24)
> +M12X20->(M14X23 M13X22 M13X21 M13X20)
> +M12X21->(M14X19 M13X18 M13X17 M13X16)
> +M12X22->(M14X15 M13X14 M13X13 M13X12)
> +M12X23->(M14X11 M13X25 M13X24 M13X23)
> +M12X24->(M14X22 M13X21 M13X20 M13X19)
> +M12X25->(M14X18 M13X17 M13X16 M13X15)
> +M13X11->(M15X14 M14X13 M14X12 M14X11)
> +M13X12->(M15X25 M14X24 M14X23 M14X22)
> +M13X13->(M15X21 M14X20 M14X19 M14X18)
> +M13X14->(M15X17 M14X16 M14X15 M14X14)
> +M13X15->(M15X13 M14X12 M14X11 M14X25)
> +M13X16->(M15X24 M14X23 M14X22 M14X21)
> +M13X17->(M15X20 M14X19 M14X18 M14X17)
> +M13X18->(M15X16 M14X15 M14X14 M14X13)
> +M13X19->(M15X12 M14X11 M14X25 M14X24)
> +M13X20->(M15X23 M14X22 M14X21 M14X20)
> +M13X21->(M15X19 M14X18 M14X17 M14X16)
> +M13X22->(M15X15 M14X14 M14X13 M14X12)
> +M13X23->(M15X11 M14X25 M14X24 M14X23)
> +M13X24->(M15X22 M14X21 M14X20 M14X19)
> +M13X25->(M15X18 M14X17 M14X16 M14X15)
> +M14X11->(M16X14 M15X13 M15X12 M15X11)
> +M14X12->(M16X25 M15X24 M15X23 M15X22)
> +M14X13->(M16X21 M15X20 M15X19 M15X18)
> +M14X14->(M16X17 M15X16 M15X15 M15X14)
> +M14X15->(M16X13 M15X12 M15X11 M15X25)
> +M14X16->(M16X24 M15X23 M15X22 M15X21)
> +M14X17->(M16X20 M15X19 M15X18 M15X17)
> +M14X18->(M16X16 M15X15 M15X14 M15X13)
> +M14X19->(M16X12 M15X11 M15X25 M15X24)
> +M14X20->(M16X23 M15X22 M15X21 M15X20)
> +M14X21->(M16X19 M15X18 M15X17 M15X16)
> +M14X22->(M16X15 M15X14 M15X13 M15X12)
> +M14X23->(M16X11 M15X25 M15X24 M15X23)
> +M14X24->(M16X22 M15X21 M15X20 M15X19)
> +M14X25->(M16X18 M15X17 M15X16 M15X15)
> +M15X11->(M17X14 M16X13 M16X12 M16X11)
> +M15X12->(M17X25 M16X24 M16X23 M16X22)
> +M15X13->(M17X21 M16X20 M16X19 M16X18)
> +M15X14->(M17X17 M16X16 M16X15 M16X14)
> +M15X15->(M17X13 M16X12 M16X11 M16X25)
> +M15X16->(M17X24 M16X23 M16X22 M16X21)
> +M15X17->(M17X20 M16X19 M16X18 M16X17)
> +M15X18->(M17X16 M16X15 M16X14 M16X13)
> +M15X19->(M17X12 M16X11 M16X25 M16X24)
> +M15X20->(M17X23 M16X22 M16X21 M16X20)
> +M15X21->(M17X19 M16X18 M16X17 M16X16)
> +M15X22->(M17X15 M16X14 M16X13 M16X12)
> +M15X23->(M17X11 M16X25 M16X24 M16X23)
> +M15X24->(M17X22 M16X21 M16X20 M16X19)
> +M15X25->(M17X18 M16X17 M16X16 M16X15)
> +M16X11->(M18X14 M17X13 M17X12 M17X11)
> +M16X12->(M18X25 M17X24 M17X23 M17X22)
> +M16X13->(M18X21 M17X20 M17X19 M17X18)
> +M16X14->(M18X17 M17X16 M17X15 M17X14)
> +M16X15->(M18X13 M17X12 M17X11 M17X25)
> +M16X16->(M18X24 M17X23 M17X22 M17X21)
> +M16X17->(M18X20 M17X19 M17X18 M17X17)
> +M16X18->(M18X16 M17X15 M17X14 M17X13)
> +M16X19->(M18X12 M17X11 M17X25 M17X24)
> +M16X20->(M18X23 M17X22 M17X21 M17X20)
> +M16X21->(M18X19 M17X18 M17X17 M17X16)
> +M16X22->(M18X15 M17X14 M17X13 M17X12)
> +M16X23->(M18X11 M17X25 M17X24 M17X23)
> +M16X24->(M18X22 M17X21 M17X20 M17X19)
> +M16X25->(M18X18 M17X17 M17X16 M17X15)
> +M17X11->(M19X14 M18X13 M18X12 M18X11)
> +M17X12->(M19X25 M18X24 M18X23 M18X22)
> +M17X13->(M19X21 M18X20 M18X19 M18X18)
> +M17X14->(M19X17 M18X16 M18X15 M18X14)
> +M17X15->(M19X13 M18X12 M18X11 M18X25)
> +M17X16->(M19X24 M18X23 M18X22 M18X21)
> +M17X17->(M19X20 M18X19 M18X18 M18X17)
> +M17X18->(M19X16 M18X15 M18X14 M18X13)
> +M17X19->(M19X12 M18X11 M18X25 M18X24)
> +M17X20->(M19X23 M18X22 M18X21 M18X20)
> +M17X21->(M19X19 M18X18 M18X17 M18X16)
> +M17X22->(M19X15 M18X14 M18X13 M18X12)
> +M17X23->(M19X11 M18X25 M18X24 M18X23)
> +M17X24->(M19X22 M18X21 M18X20 M18X19)
> +M17X25->(M19X18 M18X17 M18X16 M18X15)
> +M18X11->(M20X14 M19X13 M19X12 M19X11)
> +M18X12->(M20X25 M19X24 M19X23 M19X22)
> +M18X13->(M20X21 M19X20 M19X19 M19X18)
> +M18X14->(M20X17 M19X16 M19X15 M19X14)
> +M18X15->(M20X13 M19X12 M19X11 M19X25)
> +M18X16->(M20X24 M19X23 M19X22 M19X21)
> +M18X17->(M20X20 M19X19 M19X18 M19X17)
> +M18X18->(M20X16 M19X15 M19X14 M19X13)
> +M18X19->(M20X12 M19X11 M19X25 M19X24)
> +M18X20->(M20X23 M19X22 M19X21 M19X20)
> +M18X21->(M20X19 M19X18 M19X17 M19X16)
> +M18X22->(M20X15 M19X14 M19X13 M19X12)
> +M18X23->(M20X11 M19X25 M19X24 M19X23)
> +M18X24->(M20X22 M19X21 M19X20 M19X19)
> +M18X25->(M20X18 M19X17 M19X16 M19X15)
> +M19X11->(M21X14 M20X13 M20X12 M20X11)
> +M19X12->(M21X25 M20X24 M20X23 M20X22)
> +M19X13->(M21X21 M20X20 M20X19 M20X18)
> +M19X14->(M21X17 M20X16 M20X15 M20X14)
> +M19X15->(M21X13 M20X12 M20X11 M20X25)
> +M19X16->(M21X24 M20X23 M20X22 M20X21)
> +M19X17->(M21X20 M20X19 M20X18 M20X17)
> +M19X18->(M21X16 M20X15 M20X14 M20X13)
> +M19X19->(M21X12 M20X11 M20X25 M20X24)
> +M19X20->(M21X23 M20X22 M20X21 M20X20)
> +M19X21->(M21X19 M20X18 M20X17 M20X16)
> +M19X22->(M21X15 M20X14 M20X13 M20X12)
> +M19X23->(M21X11 M20X25 M20X24 M20X23)
> +M19X24->(M21X22 M20X21 M20X20 M20X19)
> +M19X25->(M21X18 M20X17 M20X16 M20X15)
> +M20X11->(M22X14 M21X13 M21X12 M21X11)
> +M20X12->(M22X25 M21X24 M21X23 M21X22)
> +M20X13->(M22X21 M21X20 M21X19 M21X18)
> +M20X14->(M22X17 M21X16 M21X15 M21X14)
> +M20X15->(M22X13 M21X12 M21X11 M21X25)
> +M20X16->(M22X24 M21X23 M21X22 M21X21)
> +M20X17->(M22X20 M21X19 M21X18 M21X17)
> +M20X18->(M22X16 M21X15 M21X14 M21X13)
> +M20X19->(M22X12 M21X11 M21X25 M21X24)
> +M20X20->(M22X23 M21X22 M21X21 M21X20)
> +M20X21->(M22X19 M21X18 M21X17 M21X16)
> +M20X22->(M22X15 M21X14 M21X13 M21X12)
> +M20X23->(M22X11 M21X25 M21X24 M21X23)
> +M20X24->(M22X22 M21X21 M21X20 M21X19)
> +M20X25->(M22X18 M21X17 M21X16 M21X15)
> +M21X11->(M23X15 M22X14 M22X13 M22X12)
> +M21X12->(M11X11 M23X25 M22X24 M22X23 M22X22)
> +M21X13->(M23X21 M22X20 M22X19 M22X18)
> +M21X14->(M23X17 M22X16 M22X15 M22X14)
> +M21X15->(M23X13 M22X12 M22X11 M22X25)
> +M21X16->(M23X24 M22X23 M22X22 M22X21)
> +M21X17->(M23X20 M22X19 M22X18 M22X17)
> +M21X18->(M23X16 M22X15 M22X14 M22X13)
> +M21X19->(M23X12 M22X11 M22X25 M22X24)
> +M21X20->(M23X23 M22X22 M22X21 M22X20)
> +M21X21->(M23X19 M22X18 M22X17 M22X16)
> +M21X22->(M23X15 M22X14 M22X13 M22X12)
> +M21X23->(M23X11 M22X25 M22X24 M22X23)
> +M21X24->(M23X22 M22X21 M22X20 M22X19)
> +M21X25->(M23X18 M22X17 M22X16 M22X15)
> +M22X11->(M24X16 M23X15 M23X14 M23X13)
> +M22X12->(M12X12 M24X11 M23X25 M23X24 M23X23)
> +M22X13->(M24X22 M23X21 M23X20 M23X19)
> +M22X14->(M24X18 M23X17 M23X16 M23X15)
> +M22X15->(M24X14 M23X13 M23X12 M23X11)
> +M22X16->(M24X25 M23X24 M23X23 M23X22)
> +M22X17->(M24X21 M23X20 M23X19 M23X18)
> +M22X18->(M24X17 M23X16 M23X15 M23X14)
> +M22X19->(M24X13 M23X12 M23X11 M23X25)
> +M22X20->(M24X24 M23X23 M23X22 M23X21)
> +M22X21->(M24X20 M23X19 M23X18 M23X17)
> +M22X22->(M24X16 M23X15 M23X14 M23X13)
> +M22X23->(M24X12 M23X11 M23X25 M23X24)
> +M22X24->(M24X23 M23X22 M23X21 M23X20)
> +M22X25->(M24X19 M23X18 M23X17 M23X16)
> +M23X11->(M25X17 M24X16 M24X15 M24X14)
> +M23X12->(M13X13 M25X12 M24X11 M24X25 M24X24)
> +M23X13->(M25X23 M24X22 M24X21 M24X20)
> +M23X14->(M25X19 M24X18 M24X17 M24X16)
> +M23X15->(M25X15 M24X14 M24X13 M24X12)
> +M23X16->(M25X11 M24X25 M24X24 M24X23)
> +M23X17->(M25X22 M24X21 M24X20 M24X19)
> +M23X18->(M25X18 M24X17 M24X16 M24X15)
> +M23X19->(M25X14 M24X13 M24X12 M24X11)
> +M23X20->(M25X25 M24X24 M24X23 M24X22)
> +M23X21->(M25X21 M24X20 M24X19 M24X18)
> +M23X22->(M25X17 M24X16 M24X15 M24X14)
> +M23X23->(M25X13 M24X12 M24X11 M24X25)
> +M23X24->(M25X24 M24X23 M24X22 M24X21)
> +M23X25->(M25X20 M24X19 M24X18 M24X17)
> +M24X11->(M26X18 M25X17 M25X16 M25X15)
> +M24X12->(M14X14 M26X13 M25X12 M25X11 M25X25)
> +M24X13->(M26X24 M25X23 M25X22 M25X21)
> +M24X14->(M26X20 M25X19 M25X18 M25X17)
> +M24X15->(M26X16 M25X15 M25X14 M25X13)
> +M24X16->(M26X12 M25X11 M25X25 M25X24)
> +M24X17->(M26X23 M25X22 M25X21 M25X20)
> +M24X18->(M26X19 M25X18 M25X17 M25X16)
> +M24X19->(M26X15 M25X14 M25X13 M25X12)
> +M24X20->(M26X11 M25X25 M25X24 M25X23)
> +M24X21->(M26X22 M25X21 M25X20 M25X19)
> +M24X22->(M26X18 M25X17 M25X16 M25X15)
> +M24X23->(M26X14 M25X13 M25X12 M25X11)
> +M24X24->(M26X25 M25X24 M25X23 M25X22)
> +M24X25->(M26X21 M25X20 M25X19 M25X18)
> +M25X11->(M27X19 M26X18 M26X17 M26X16)
> +M25X12->(M15X15 M27X14 M26X13 M26X12 M26X11)
> +M25X13->(M27X25 M26X24 M26X23 M26X22)
> +M25X14->(M27X21 M26X20 M26X19 M26X18)
> +M25X15->(M27X17 M26X16 M26X15 M26X14)
> +M25X16->(M27X13 M26X12 M26X11 M26X25)
> +M25X17->(M27X24 M26X23 M26X22 M26X21)
> +M25X18->(M27X20 M26X19 M26X18 M26X17)
> +M25X19->(M27X16 M26X15 M26X14 M26X13)
> +M25X20->(M27X12 M26X11 M26X25 M26X24)
> +M25X21->(M27X23 M26X22 M26X21 M26X20)
> +M25X22->(M27X19 M26X18 M26X17 M26X16)
> +M25X23->(M27X15 M26X14 M26X13 M26X12)
> +M25X24->(M27X11 M26X25 M26X24 M26X23)
> +M25X25->(M27X22 M26X21 M26X20 M26X19)
> +M26X11->(M28X20 M27X19 M27X18 M27X17)
> +M26X12->(M16X16 M28X15 M27X14 M27X13 M27X12)
> +M26X13->(M28X11 M27X25 M27X24 M27X23)
> +M26X14->(M28X22 M27X21 M27X20 M27X19)
> +M26X15->(M28X18 M27X17 M27X16 M27X15)
> +M26X16->(M28X14 M27X13 M27X12 M27X11)
> +M26X17->(M28X25 M27X24 M27X23 M27X22)
> +M26X18->(M28X21 M27X20 M27X19 M27X18)
> +M26X19->(M28X17 M27X16 M27X15 M27X14)
> +M26X20->(M28X13 M27X12 M27X11 M27X25)
> +M26X21->(M28X24 M27X23 M27X22 M27X21)
> +M26X22->(M28X20 M27X19 M27X18 M27X17)
> +M26X23->(M28X16 M27X15 M27X14 M27X13)
> +M26X24->(M28X12 M27X11 M27X25 M27X24)
> +M26X25->(M28X23 M27X22 M27X21 M27X20)
> +M27X11->(M29X21 M28X20 M28X19 M28X18)
> +M27X12->(M17X17 M29X16 M28X15 M28X14 M28X13)
> +M27X13->(M29X12 M28X11 M28X25 M28X24)
> +M27X14->(M29X23 M28X22 M28X21 M28X20)
> +M27X15->(M29X19 M28X18 M28X17 M28X16)
> +M27X16->(M29X15 M28X14 M28X13 M28X12)
> +M27X17->(M29X11 M28X25 M28X24 M28X23)
> +M27X18->(M29X22 M28X21 M28X20 M28X19)
> +M27X19->(M29X18 M28X17 M28X16 M28X15)
> +M27X20->(M29X14 M28X13 M28X12 M28X11)
> +M27X21->(M29X25 M28X24 M28X23 M28X22)
> +M27X22->(M29X21 M28X20 M28X19 M28X18)
> +M27X23->(M29X17 M28X16 M28X15 M28X14)
> +M27X24->(M29X13 M28X12 M28X11 M28X25)
> +M27X25->(M29X24 M28X23 M28X22 M28X21)
> +M28X11->(M30X22 M29X21 M29X20 M29X19)
> +M28X12->(M18X18 M30X17 M29X16 M29X15 M29X14)
> +M28X13->(M30X13 M29X12 M29X11 M29X25)
> +M28X14->(M30X24 M29X23 M29X22 M29X21)
> +M28X15->(M30X20 M29X19 M29X18 M29X17)
> +M28X16->(M30X16 M29X15 M29X14 M29X13)
> +M28X17->(M30X12 M29X11 M29X25 M29X24)
> +M28X18->(M30X23 M29X22 M29X21 M29X20)
> +M28X19->(M30X19 M29X18 M29X17 M29X16)
> +M28X20->(M30X15 M29X14 M29X13 M29X12)
> +M28X21->(M30X11 M29X25 M29X24 M29X23)
> +M28X22->(M30X22 M29X21 M29X20 M29X19)
> +M28X23->(M30X18 M29X17 M29X16 M29X15)
> +M28X24->(M30X14 M29X13 M29X12 M29X11)
> +M28X25->(M30X25 M29X24 M29X23 M29X22)
> +M29X11->(M30X22 M30X21 M30X20)
> +M29X12->(M30X17 M30X16 M30X15)
> +M29X13->(M30X13 M30X12 M30X11)
> +M29X14->(M30X24 M30X23 M30X22)
> +M29X15->(M30X20 M30X19 M30X18)
> +M29X16->(M30X16 M30X15 M30X14)
> +M29X17->(M30X12 M30X11 M30X25)
> +M29X18->(M30X23 M30X22 M30X21)
> +M29X19->(M30X19 M30X18 M30X17)
> +M29X20->(M30X15 M30X14 M30X13)
> +M29X21->(M30X11 M30X25 M30X24)
> +M29X22->(M30X22 M30X21 M30X20)
> +M29X23->(M30X18 M30X17 M30X16)
> +M29X24->(M30X14 M30X13 M30X12)
> +M29X25->(M30X25 M30X24 M30X23)
> +M30X11
> +M30X12
> +M30X13
> +M30X14
> +M30X15
> +M30X16
> +M30X17
> +M30X18
> +M30X19
> +M30X20
> +M30X21
> +M30X22
> +M30X23
> +M30X24
> +M30X25
> +output(glibc.rtld.dynamic_sort=1): M30X19>M30X15>M30X16>M30X11>M30X12>M30X17>M30X13>M30X14>M29X20>M30X23>M30X24>M30X20>M30X18>M29X15>M29X12>M30X22>M30X21>M29X22>M30X25>M29X19>M29X23>M29X16>M29X24>M29X13>M29X17>M29X18>M28X19>M29X21>M29X25>M29X14>M28X20>M28X15>M28X16>M28X21>M27X18>M29X11>M28X17>M28X11>M28X22>M27X14>M28X18>M27X15>M28X13>M27X11>M28X23>M27X25>M28X14>M28X25>M27X23>M27X22>M28X24>M27X21>M27X13>M27X19>M27X17>M26X11>M26X23>M26X21>M26X22>M26X20>M26X16>M25X21>M17X22>M15X15>M20X14>M20X16>M18X18>M28X12>M27X24>M25X17>M27X20>M26X18>M26X17>M27X16>M26X19>M25X18>M26X24>M25X20>M24X17>M23X18>M25X13>M26X13>M17X23>M16X16>M26X12>M25X12>M26X15>M24X19>M25X23>M25X24>M25X25>M24X20>M25X19>M24X21>M23X17>M22X21>M24X14>M23X22>M24X24>M22X20>M24X13>M25X11>M24X12>M25X15>M23X15>M25X16>M24X22>M23X13>M24X18>M23X14>M22X22>M21X20>M24X25>M23X16>M22X25>M21X19>M22X14>M23X11>M22X15>M21X18>M22X19>M21X17>M20X17>M19X17>M21X24>M21X12>M20X22>M19X16>M18X25>M19X21>M19X20>M18X24>M20X12>M19X11>M23X20>M22X24>M22X16>M21X21>M25X14>M23X19>M23X24>M20X24>M19X12>M18X15>M17X14>M16X18>M14X25>M16X22>M16X20>M17X17>M22X12>M21X11>M20X15>M18X22>M19X24>M19X18>M18X21>M17X16>M17X18>M16X21>M15X20>M19X22>M18X20>M18X11>M17X19>M16X17>M15X21>M16X14>M16X13>M15X22>M14X20>M17X25>M16X19>M14X21>M13X24>M12X12>M16X24>M15X23>M14X16>M16X15>M15X25>M15X11>M15X12>M14X15>M13X14>M14X22>M13X20>M12X13>M11X11>M22X23>M21X15>M21X16>M20X21>M20X20>M18X17>M19X25>M18X23>M21X13>M15X17>M15X18>M18X19>M17X24>M16X12>M17X13>M20X25>M19X23>M15X19>M14X13>M13X18>M15X13>M17X12>M16X11>M18X13>M18X12>M14X11>M14X24>M13X19>M15X14>M17X20>M20X11>M20X13>M21X14>M15X24>M14X12>M13X22>M14X23>M13X23>M14X19>M17X15>M16X25>M17X11>M18X14>M19X19>M21X25>M13X12>M13X11>M14X18>M13X13>M12X11>M15X16>M14X14>M27X12>M17X21>M20X23>M22X13>M21X22>M24X16>M24X15>M26X25>M23X25>M26X14>M23X12>M22X18>M24X11>M16X23>M19X14>M19X13>M21X23>M22X17>M23X23>M23X21>M25X22>M18X16>M19X15>M20X18>M20X19>M22X11>M24X23>C156>C118>C143>C137>C147>C106>C168>C113>C163>C155>C105>C146>C187>A150>C139>C180>C164>C193>C157>A191>C158>B188>A159>C184>C121>C154>B171>A105>C131>C104>B104>C161>C111>B145>C160>B155>A163>C112>C142>B148>C133>B198>A198>A115>C114>B157>A156>C175>B144>A120>C173>B184>A174>C126>B107>A139>C194>B194>A194>C116>B116>C166>B160>B110>A110>C128>B128>A128>C179>B162>A154>C186>B187>A179>C124>B181>A101>C153>B158>A136>C135>C176>A192>B133>A133>C177>B177>A177>C185>C103>B141>A141>C183>A162>C192>C129>B179>C144>B124>B183>C127>B127>A127>B108>A112>B153>A153>C167>B167>A186>A122>C162>A144>B149>C174>B131>A185>C141>B106>A126>A167>C140>B122>A170>C198>B143>C117>C123>B123>A147>A106>C200>B169>C191>B175>A123>B118>A182>C132>B151>A145>A104>A109>C159>C150>B119>A119>A178>B164>B114>A164>C181>A102>C122>B134>A157>A116>C195>B191>B111>C172>B172>A118>B129>A129>C149>A107>C170>B197>A197>A173>B168>A132>C107>B165>A160>A131>C188>A168>B109>C178>A189>A148>C119>C190>C120>B166>B176>C108>B135>B139>A103>B178>A169>B132>C125>C138>B163>A111>B170>C110>A165>C151>C169>C199>A138>C182>A135>B101>B142>C101>C148>B193>B152>A158>A199>C136>B137>A161>B120>A108>A149>A125>B113>A184>C171>A134>A175>A124>B150>B161>B102>A146>A187>C130>B192>B200>A200>A142>A183>C102>B105>B156>A176>C165>B147>A137>A196>B190>A190>B125>C134>C189>B126>B186>A166>B136>B195>A195>B154>B138>B112>B173>A117>B159>B182>A181>A140>C145>B117>A152>A193>C197>B130>A172>A113>A151>B115>A143>B140>B185>B103>A121>A180>A130>A171>B199>C196>B146>B180>C115>B174>B121>A188>B196>B189>C152>C109>A155>A114>M14X17>M13X15>M13X16>M13X17>M12X17>M12X21>M12X25>M12X14>M13X25>M12X15>M13X21>M12X16>M12X18>M12X19>M12X20>M12X22>M12X23>M12X24>M11X25>M11X24>M11X23>M11X22>M11X21>M11X20>M11X19>M11X18>M11X17>M11X16>M11X15>M11X14>M11X13>M11X12>{}<M11X12<M11X13<M11X14<M11X15<M11X16<M11X17<M11X18<M11X19<M11X20<M11X21<M11X22<M11X23<M11X24<M11X25<M12X24<M12X23<M12X22<M12X20<M12X19<M12X18<M12X16<M13X21<M12X15<M13X25<M12X14<M12X25<M12X21<M12X17<M13X17<M13X16<M13X15<M14X17<A114<A155<C109<C152<B189<B196<A188<B121<B174<C115<B180<B146<C196<B199<A171<A130<A180<A121<B103<B185<B140<A143<B115<A151<A113<A172<B130<C197<A193<A152<B117<C145<A140<A181<B182<B159<A117<B173<B112<B138<B154<A195<B195<B136<A166<B186<B126<C189<C134<B125<A190<B190<A196<A137<B147<C165<A176<B156<B105<C102<A183<A142<A200<B200<B192<C130<A187<A146<B102<B161<B150<A124<A175<A134<C171<A184<B113<A125<A149<A108<B120<A161<B137<C136<A199<A158<B152<B193<C148<C101<B142<B101<A135<C182<A138<C199<C169<C151<A165<C110<B170<A111<B163<C138<C125<B132<A169<B178<A103<B139<B135<C108<B176<B166<C120<C190<C119<A148<A189<C178<B109<A168<C188<A131<A160<B165<C107<A132<B168<A173<A197<B197<C170<A107<C149<A129<B129<A118<B172<C172<B111<B191<C195<A116<A157<B134<C122<A102<C181<A164<B114<B164<A178<A119<B119<C150<C159<A109<A104<A145<B151<C132<A182<B118<A123<B175<C191<B169<C200<A106<A147<B123<C123<C117<B143<C198<A170<B122<C140<A167<A126<B106<C141<A185<B131<C174<B149<A144<C162<A122<A186<B167<C167<A153<B153<A112<B108<A127<B127<C127<B183<B124<C144<B179<C129<C192<A162<C183<A141<B141<C103<C185<A177<B177<C177<A133<B133<A192<C176<C135<A136<B158<C153<A101<B181<C124<A179<B187<C186<A154<B162<C179<A128<B128<C128<A110<B110<B160<C166<B116<C116<A194<B194<C194<A139<B107<C126<A174<B184<C173<A120<B144<C175<A156<B157<C114<A115<A198<B198<C133<B148<C142<C112<A163<B155<C160<B145<C111<C161<B104<C104<C131<A105<B171<C154<C121<C184<A159<B188<C158<A191<C157<C193<C164<C180<C139<A150<C187<C146<C105<C155<C163<C113<C168<C106<C147<C137<C143<C118<C156<M24X23<M22X11<M20X19<M20X18<M19X15<M18X16<M25X22<M23X21<M23X23<M22X17<M21X23<M19X13<M19X14<M16X23<M24X11<M22X18<M23X12<M26X14<M23X25<M26X25<M24X15<M24X16<M21X22<M22X13<M20X23<M17X21<M27X12<M14X14<M15X16<M12X11<M13X13<M14X18<M13X11<M13X12<M21X25<M19X19<M18X14<M17X11<M16X25<M17X15<M14X19<M13X23<M14X23<M13X22<M14X12<M15X24<M21X14<M20X13<M20X11<M17X20<M15X14<M13X19<M14X24<M14X11<M18X12<M18X13<M16X11<M17X12<M15X13<M13X18<M14X13<M15X19<M19X23<M20X25<M17X13<M16X12<M17X24<M18X19<M15X18<M15X17<M21X13<M18X23<M19X25<M18X17<M20X20<M20X21<M21X16<M21X15<M22X23<M11X11<M12X13<M13X20<M14X22<M13X14<M14X15<M15X12<M15X11<M15X25<M16X15<M14X16<M15X23<M16X24<M12X12<M13X24<M14X21<M16X19<M17X25<M14X20<M15X22<M16X13<M16X14<M15X21<M16X17<M17X19<M18X11<M18X20<M19X22<M15X20<M16X21<M17X18<M17X16<M18X21<M19X18<M19X24<M18X22<M20X15<M21X11<M22X12<M17X17<M16X20<M16X22<M14X25<M16X18<M17X14<M18X15<M19X12<M20X24<M23X24<M23X19<M25X14<M21X21<M22X16<M22X24<M23X20<M19X11<M20X12<M18X24<M19X20<M19X21<M18X25<M19X16<M20X22<M21X12<M21X24<M19X17<M20X17<M21X17<M22X19<M21X18<M22X15<M23X11<M22X14<M21X19<M22X25<M23X16<M24X25<M21X20<M22X22<M23X14<M24X18<M23X13<M24X22<M25X16<M23X15<M25X15<M24X12<M25X11<M24X13<M22X20<M24X24<M23X22<M24X14<M22X21<M23X17<M24X21<M25X19<M24X20<M25X25<M25X24<M25X23<M24X19<M26X15<M25X12<M26X12<M16X16<M17X23<M26X13<M25X13<M23X18<M24X17<M25X20<M26X24<M25X18<M26X19<M27X16<M26X17<M26X18<M27X20<M25X17<M27X24<M28X12<M18X18<M20X16<M20X14<M15X15<M17X22<M25X21<M26X16<M26X20<M26X22<M26X21<M26X23<M26X11<M27X17<M27X19<M27X13<M27X21<M28X24<M27X22<M27X23<M28X25<M28X14<M27X25<M28X23<M27X11<M28X13<M27X15<M28X18<M27X14<M28X22<M28X11<M28X17<M29X11<M27X18<M28X21<M28X16<M28X15<M28X20<M29X14<M29X25<M29X21<M28X19<M29X18<M29X17<M29X13<M29X24<M29X16<M29X23<M29X19<M30X25<M29X22<M30X21<M30X22<M29X12<M29X15<M30X18<M30X20<M30X24<M30X23<M29X20<M30X14<M30X13<M30X17<M30X12<M30X11<M30X16<M30X15<M30X19
> +output(glibc.rtld.dynamic_sort=2): M30X19>M30X15>M30X16>M30X11>M30X12>M30X17>M30X13>M30X14>M29X20>M30X23>M30X24>M30X20>M30X18>M29X15>M29X12>M30X22>M30X21>M29X22>M30X25>M29X19>M29X23>M29X16>M29X24>M29X13>M29X17>M29X18>M28X19>M29X21>M29X25>M29X14>M28X20>M28X15>M28X16>M28X21>M27X18>M29X11>M28X17>M28X11>M28X22>M28X24>M28X23>M27X21>M28X13>M27X20>M27X19>M26X14>M27X25>M28X18>M27X11>M28X25>M27X24>M26X24>M27X15>M27X14>M27X13>M26X23>M27X17>M26X22>M25X13>M28X14>M27X16>M26X19>M26X18>M27X23>M27X22>M26X17>M25X18>M26X21>M25X17>M26X20>M26X15>M26X13>M25X19>M24X14>M25X23>M26X11>M26X25>M25X16>M25X15>M24X22>M25X21>M25X20>M24X21>M25X25>M25X24>M24X20>M23X13>M22X15>M25X14>M24X19>M23X17>M24X25>M23X24>M24X13>M23X15>M24X18>M23X14>M22X11>M24X15>M23X22>M24X11>M23X19>M22X21>M24X24>M23X21>M22X20>M23X25>M22X19>M21X24>M20X23>M22X22>M25X11>M23X16>M22X18>M23X20>M22X17>M21X21>M21X20>M20X24>M22X14>M22X13>M21X11>M21X17>M22X23>M21X16>M20X25>M19X23>M18X16>M21X22>M20X20>M20X19>M21X13>M20X18>M19X13>M21X18>M20X21>M19X24>M18X12>M20X14>M20X13>M22X25>M20X12>M20X15>M19X14>M18X22>M19X18>M20X17>M19X17>M19X16>M18X21>M17X20>M19X19>M18X13>M17X11>M18X17>M19X25>M18X15>M17X25>M18X19>M17X24>M16X19>M15X17>M17X21>M16X24>M18X23>M17X16>M16X25>M19X15>M18X25>M17X23>M16X23>M15X23>M18X14>M17X14>M16X14>M17X18>M16X13>M17X22>M16X12>M15X22>M14X16>M17X12>M16X22>M15X12>M16X11>M15X11>M16X15>M15X25>M14X15>M13X14>M15X18>M16X21>M15X16>M14X21>M15X14>M16X20>M15X13>M14X22>M15X20>M14X20>M13X20>M14X11>M15X19>M14X24>M13X19>M14X13>M13X18>M12X13>M15X24>M14X23>M13X12>M14X12>M13X11>M12X11>M11X11>M21X12>M20X11>M19X11>M18X11>M17X15>M16X18>M14X25>M14X19>M13X24>M13X23>M13X22>M12X12>M22X12>M21X15>M19X22>M18X20>M16X17>M14X14>M24X12>M23X23>M22X16>M21X14>M20X22>M18X24>M16X16>M26X12>M24X16>M23X11>M21X23>M19X20>M17X17>M27X12>M26X16>M25X22>M24X17>M23X18>M21X25>M19X12>M17X19>M15X21>M14X18>M13X13>M23X12>M21X19>M19X21>M17X13>M15X15>M25X12>M24X23>M22X24>M20X16>M18X18>M28X12>A150>C158>B112>A112>C167>B146>A146>C180>B180>A180>C143>B143>A115>C126>B126>A126>C190>B190>A190>C138>B138>A138>C174>B174>A102>C122>B122>A122>C162>B162>A162>C142>B142>A142>C102>B102>A174>C176>B176>A176>C115>B115>A143>C172>B172>A172>C187>B187>A187>C130>B130>A130>C118>B118>A118>C184>B184>A184>C171>B171>A171>C168>B182>A182>C182>B168>A168>C109>B109>A109>C159>B159>A159>C134>B134>A134>C146>B167>A167>C140>B140>A140>C163>B163>A163>C112>B158>A158>C164>B164>A164>C131>B131>A131>C188>B188>A188>C199>B199>A199>C114>B114>A114>C106>B106>A106>C200>B200>A200>C183>B183>A183>C152>B152>A152>C147>B147>A147>C150>B150>A198>C144>B144>A144>C191>B191>A191>C108>B108>A108>C139>B139>A139>C194>B194>A194>C166>B166>A166>C120>B120>A120>C123>B123>A123>C132>B132>A132>C107>B107>A107>C170>B170>A170>C198>B198>A156>C125>B125>A125>C121>B121>A121>C193>B193>A193>C197>B197>A197>C175>B175>A175>C196>B196>A196>C105>B105>A105>C181>B181>A181>C113>B113>A113>C137>B137>A137>C155>B155>A155>C156>B156>A110>C128>B128>A128>C179>B179>A179>C124>B124>A124>C151>B151>A151>C178>B178>A178>C104>B104>A104>C111>B111>A111>C148>B148>A148>C169>B169>A169>C129>B129>A129>C149>B149>A149>C189>B189>A189>C119>B119>A119>C154>B154>A154>C136>B136>A136>C135>B135>A135>C116>B116>A116>C145>B145>A145>C161>B161>A161>C173>B173>A173>C157>B157>A157>C195>B195>A195>C186>B186>A186>C160>B160>A160>C153>B153>A153>C117>B117>A117>C165>B165>A165>C101>B101>A101>C103>B103>A103>C192>B192>A192>C177>B177>A177>C185>B185>A185>C141>B141>A141>C133>B133>A133>C127>B127>A127>C110>B110>M14X17>M13X15>M13X16>M13X17>M12X17>M12X21>M12X25>M12X14>M13X25>M12X15>M13X21>M12X16>M12X18>M12X19>M12X20>M12X22>M12X23>M12X24>M11X25>M11X24>M11X23>M11X22>M11X21>M11X20>M11X19>M11X18>M11X17>M11X16>M11X15>M11X14>M11X13>M11X12>{}<M11X12<M11X13<M11X14<M11X15<M11X16<M11X17<M11X18<M11X19<M11X20<M11X21<M11X22<M11X23<M11X24<M11X25<M12X24<M12X23<M12X22<M12X20<M12X19<M12X18<M12X16<M13X21<M12X15<M13X25<M12X14<M12X25<M12X21<M12X17<M13X17<M13X16<M13X15<M14X17<B110<C110<A127<B127<C127<A133<B133<C133<A141<B141<C141<A185<B185<C185<A177<B177<C177<A192<B192<C192<A103<B103<C103<A101<B101<C101<A165<B165<C165<A117<B117<C117<A153<B153<C153<A160<B160<C160<A186<B186<C186<A195<B195<C195<A157<B157<C157<A173<B173<C173<A161<B161<C161<A145<B145<C145<A116<B116<C116<A135<B135<C135<A136<B136<C136<A154<B154<C154<A119<B119<C119<A189<B189<C189<A149<B149<C149<A129<B129<C129<A169<B169<C169<A148<B148<C148<A111<B111<C111<A104<B104<C104<A178<B178<C178<A151<B151<C151<A124<B124<C124<A179<B179<C179<A128<B128<C128<A110<B156<C156<A155<B155<C155<A137<B137<C137<A113<B113<C113<A181<B181<C181<A105<B105<C105<A196<B196<C196<A175<B175<C175<A197<B197<C197<A193<B193<C193<A121<B121<C121<A125<B125<C125<A156<B198<C198<A170<B170<C170<A107<B107<C107<A132<B132<C132<A123<B123<C123<A120<B120<C120<A166<B166<C166<A194<B194<C194<A139<B139<C139<A108<B108<C108<A191<B191<C191<A144<B144<C144<A198<B150<C150<A147<B147<C147<A152<B152<C152<A183<B183<C183<A200<B200<C200<A106<B106<C106<A114<B114<C114<A199<B199<C199<A188<B188<C188<A131<B131<C131<A164<B164<C164<A158<B158<C112<A163<B163<C163<A140<B140<C140<A167<B167<C146<A134<B134<C134<A159<B159<C159<A109<B109<C109<A168<B168<C182<A182<B182<C168<A171<B171<C171<A184<B184<C184<A118<B118<C118<A130<B130<C130<A187<B187<C187<A172<B172<C172<A143<B115<C115<A176<B176<C176<A174<B102<C102<A142<B142<C142<A162<B162<C162<A122<B122<C122<A102<B174<C174<A138<B138<C138<A190<B190<C190<A126<B126<C126<A115<B143<C143<A180<B180<C180<A146<B146<C167<A112<B112<C158<A150<M28X12<M18X18<M20X16<M22X24<M24X23<M25X12<M15X15<M17X13<M19X21<M21X19<M23X12<M13X13<M14X18<M15X21<M17X19<M19X12<M21X25<M23X18<M24X17<M25X22<M26X16<M27X12<M17X17<M19X20<M21X23<M23X11<M24X16<M26X12<M16X16<M18X24<M20X22<M21X14<M22X16<M23X23<M24X12<M14X14<M16X17<M18X20<M19X22<M21X15<M22X12<M12X12<M13X22<M13X23<M13X24<M14X19<M14X25<M16X18<M17X15<M18X11<M19X11<M20X11<M21X12<M11X11<M12X11<M13X11<M14X12<M13X12<M14X23<M15X24<M12X13<M13X18<M14X13<M13X19<M14X24<M15X19<M14X11<M13X20<M14X20<M15X20<M14X22<M15X13<M16X20<M15X14<M14X21<M15X16<M16X21<M15X18<M13X14<M14X15<M15X25<M16X15<M15X11<M16X11<M15X12<M16X22<M17X12<M14X16<M15X22<M16X12<M17X22<M16X13<M17X18<M16X14<M17X14<M18X14<M15X23<M16X23<M17X23<M18X25<M19X15<M16X25<M17X16<M18X23<M16X24<M17X21<M15X17<M16X19<M17X24<M18X19<M17X25<M18X15<M19X25<M18X17<M17X11<M18X13<M19X19<M17X20<M18X21<M19X16<M19X17<M20X17<M19X18<M18X22<M19X14<M20X15<M20X12<M22X25<M20X13<M20X14<M18X12<M19X24<M20X21<M21X18<M19X13<M20X18<M21X13<M20X19<M20X20<M21X22<M18X16<M19X23<M20X25<M21X16<M22X23<M21X17<M21X11<M22X13<M22X14<M20X24<M21X20<M21X21<M22X17<M23X20<M22X18<M23X16<M25X11<M22X22<M20X23<M21X24<M22X19<M23X25<M22X20<M23X21<M24X24<M22X21<M23X19<M24X11<M23X22<M24X15<M22X11<M23X14<M24X18<M23X15<M24X13<M23X24<M24X25<M23X17<M24X19<M25X14<M22X15<M23X13<M24X20<M25X24<M25X25<M24X21<M25X20<M25X21<M24X22<M25X15<M25X16<M26X25<M26X11<M25X23<M24X14<M25X19<M26X13<M26X15<M26X20<M25X17<M26X21<M25X18<M26X17<M27X22<M27X23<M26X18<M26X19<M27X16<M28X14<M25X13<M26X22<M27X17<M26X23<M27X13<M27X14<M27X15<M26X24<M27X24<M28X25<M27X11<M28X18<M27X25<M26X14<M27X19<M27X20<M28X13<M27X21<M28X23<M28X24<M28X22<M28X11<M28X17<M29X11<M27X18<M28X21<M28X16<M28X15<M28X20<M29X14<M29X25<M29X21<M28X19<M29X18<M29X17<M29X13<M29X24<M29X16<M29X23<M29X19<M30X25<M29X22<M30X21<M30X22<M29X12<M29X15<M30X18<M30X20<M30X24<M30X23<M29X20<M30X14<M30X13<M30X17<M30X12<M30X11<M30X16<M30X15<M30X19

Again, I think we need a comment here explaining the salient differences
between the two sort algorithms and why the second is desirable in this case.

> diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py
> new file mode 100644
> index 0000000000..6e2e36ab87
> --- /dev/null
> +++ b/scripts/dso-ordering-test.py
> @@ -0,0 +1,947 @@
> +#!/usr/bin/python3
> +# Generate testcase files and Makefile fragments for DSO sorting test
> +# Copyright (C) 2020 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
> +# <http://www.gnu.org/licenses/>.

OK.

> +
> +"""Generate testcase files and Makefile fragments for DSO sorting test
> +
> +This script takes a small description string language, and generates
> +testcases for displaying the ELF dynamic linker's dependency sorting
> +behavior, allowing verification.
> +
> +Testcase descriptions are semicolon-separated description strings, and
> +this tool generates a testcase from the description, including main program,
> +associated modules, and Makefile fragments for including into elf/Makefile.
> +
> +This allows automation of what otherwise would be very laborous manual
> +construction of complex dependency cases, however it must be noted that this
> +is only a tool to speed up testcase construction, and thus the generation
> +features are largely mechanical in nature; inconsistencies or errors may occur
> +if the input description was itself erroronous or have unforeseen interactions.
> +
> +The format of the input test description files are:
> +
> +  # Each test description has a name, lines of description,
> +  # and an expected output specification. Comments use '#'.
> +  testname1: <test-description-line>
> +  output: <expected-output-string>
> +
> +  # Test descriptions can use multiple lines, which will all be merged
> +  # together, so order is not important.
> +  testname1: <test-description-line>
> +  <test-description-line>
> +  <test-description-line>
> +  ...
> +  output: <expected-output-string>
> +
> +  # Multiple expected outputs can also be specified, with an associated
> +  # tunable option in (), which multiple tests will be run with each
> +  # GLIBC_TUNABLES=... option tried.
> +  testname3:
> +  <test-description-line>
> +  ...
> +  output(<glibc-tunable-string1>): <expected-output-string-1>
> +  output(<glibc-tunable-string2>): <expected-output-string-2>
> +
> +On the description language used, an example description line string:
> +
> +  a->b!->[cdef];c=>g=>h;{+c;%c;-c}->a
> +
> +Each identifier represents a shared object module, currently sequences of
> +letters/digits are allowed, case-sensitive.
> +
> +All such shared objects have a constructor/destructor generated for them
> +that emits its name followed by a '>' for constructors, and '<' followed by
> +its name for destructors, e.g. if the name is 'obj1', then "obj1>" and "<obj1"
> +is printed by its constructor/destructor respectively.
> +
> +The -> operator specifies a link time dependency, these can be chained for
> +convenience (e.g. a->b->c->d).
> +
> +The => operator creates a call-reference, e.g. for a=>b, an fn_a() function
> +is created inside module 'a', which calls fn_b() in module 'b'.
> +These module functions emit 'name()' output in nested form,
> +e.g. a=>b emits 'a(b())'
> +
> +For single character object names, square brackets [] in the description
> +allows specifiying multiple objects; e.g. a->[bcd]->e is equivalent to
> + a->b->e;a->c->e;a->d->e
> +
> +The () parenthesis construct with space separated names is also allowed for
> +specifying objects. For names with integer suffixes a range can also be used,
> +e.g. (foo1 bar2-5), specifies DSOs foo1, bar2, bar2, bar3, bar4, bar5.
> +
> +A {} construct specifies the main test program, and its link dependencies
> +are also specified using ->. Inside {}, a few ;-seperated constructs are
> +allowed:
> +         +a   Loads module a using dlopen(RTLD_LAZY|RTLD_GLOBAL)
> +         ^a   Loads module a using dlopen(RTLD_LAZY)
> +         %a   Use dlsym() to load and call fn_a()
> +         @a   Calls fn_a() directly.
> +         -a   Unloads module a using dlclose()
> +
> +The generated main program outputs '{' '}' with all output from above
> +constructs in between. The other output before/after {} are the ordered
> +constructor/destructor output.
> +
> +If no {} construct is present, a default empty main program is linked
> +against all objects which have no dependency linked to it. e.g. for
> +'[ab]->c;d->e', the default main program is equivalent to '{}->[abd]'
> +
> +Sometimes for very complex or large testcases, besides specifying a
> +few explicit dependencies from main{}, the above default dependency
> +behavior is still useful to automatically have, but is turned off
> +upon specifying a single explicit {}->dso_name.
> +In this case, add {}->* to explicitly add this generation behavior:
> +
> +   # Main program links to 'foo', and all other objects which have no
> +   # dependency linked to it.
> +   {}->foo,{}->*
> +
> +Note that '*' works not only on main{}, but can be used as the
> +dependency target of any object. Note that it only works as a target,
> +not a dependency source.
> +
> +The '!' operator after object names turns on permutation of its
> +dependencies, e.g. while a->[bcd] only generates one set of objects,
> +with 'a.so' built with a link line of "b.so c.so d.so", for a!->[bcd]
> +permutations of a's dependencies creates multiple testcases with
> +different link line orders: "b.so c.so d.so", "c.so b.so d.so",
> +"b.so d.so c.so", etc. Note that for a <test-name> specified on
> +the script command-line, multiple <test-name_1>, <test-name_2>, etc.
> +tests will be generated (e.g. for a!->[bc]!->[de], eight tests with
> +different link orders for a, b, and c will be generated)

Can we please get a little more comments explaining the test output?
The test input is specified well, but some examples of test output
and what they mean would go a long way to helping new developers
understand the DSL.

> +
> +"""
> +import sys
> +import re
> +import os
> +import subprocess
> +import argparse
> +from collections import OrderedDict
> +import itertools
> +
> +# BUILD_GCC is only used under the --build option,
> +# which builds the generated testcase, including DSOs using BUILD_GCC.
> +# Mainly for testing purposes, especially debugging of this script,
> +# and can be changed here to another toolchain path if needed.
> +build_gcc = "gcc"
> +
> +parser = argparse.ArgumentParser("")
> +parser.add_argument("description",
> +                    help="Description string of DSO dependency test to be "
> +                    "generated (see script source for documentation of "
> +                    "description language), either specified here as "
> +                    "command line argument, or by input file using "
> +                    "-f/--description-file option",
> +                    nargs="?", default="")
> +parser.add_argument("test_name", help="Identifier for testcase being "
> +                    "generated", nargs="?", default="")
> +parser.add_argument("--objpfx",
> +                    help="Path to place generated files, defaults to "
> +                    "current directory if none specified",
> +                    nargs="?", default="./")
> +parser.add_argument("-m", "--output-makefile",
> +                    help="File to write Makefile fragment to, defaults to "
> +                    "stdout when option not present", nargs="?", default="")
> +parser.add_argument("-f", "--description-file",
> +                    help="Input file containing testcase descriptions",
> +                    nargs="?", default="")
> +parser.add_argument("--build", help="After C testcase generated, build it "
> +                    "using gcc (for manual testing purposes)",
> +                    action="store_true")
> +parser.add_argument("--debug-output", help="Prints some internal data "
> +                    "structures; used for debugging of this script",
> +                    action="store_true")

OK.

> +cmdlineargs = parser.parse_args()
> +test_name = cmdlineargs.test_name
> +description = cmdlineargs.description
> +objpfx = cmdlineargs.objpfx
> +description_file = cmdlineargs.description_file
> +output_makefile = cmdlineargs.output_makefile
> +makefile = ""
> +
> +if (test_name or description) and description_file:
> +    print ("Error: both command-line testcase and input file specified")
> +    exit (-1)
> +if test_name and not description:
> +    print ("Error: command-line testcase name without description string")
> +    exit (-1)
> +
> +# Main class type describing a testcase.
> +class TestDescr:
> +    def __init__(self):
> +        self.objs = []                        # list of all DSO objects
> +        self.deps = OrderedDict()             # map of DSO object -> list of dependencies
> +        self.callrefs = OrderedDict()         # map of DSO object -> list of call refs
> +        self.dep_permutations = OrderedDict() # map of DSO object
> +                                              #   -> list of permutations of dependencies
> +        self.main_program = []                # list of main program operations
> +        self.main_program_needs_ldl = False   # set if main program needs -ldl
> +        self.main_program_default_deps = True # set if default dependencies added to main
> +        self.test_name = ""                   # name of testcase
> +        self.expected_outputs = OrderedDict() # expected outputs of testcase
> +
> +    # Add 'object -> [object, object, ...]' relations to CURR_MAP
> +    def __add_deps_internal (self, src_objs, dst_objs, curr_map):
> +        for src in src_objs:
> +            for dst in dst_objs:
> +                if not src in curr_map:
> +                    curr_map[src] = []
> +                if not dst in curr_map[src]:
> +                    curr_map[src].append (dst)
> +    def add_deps (self, src_objs, dst_objs):
> +        self.__add_deps_internal (src_objs, dst_objs, self.deps)
> +    def add_callrefs (self, src_objs, dst_objs):
> +        self.__add_deps_internal (src_objs, dst_objs, self.callrefs)
> +
> +# For inside the {} construct
> +def process_main_program (test_descr, mainprog_str):
> +    if mainprog_str:
> +        test_descr.main_program = mainprog_str.split (';')
> +    for s in test_descr.main_program:
> +        m = re.match (r"^([+\-%^@])([0-9a-zA-Z]+)$", s)
> +        if not m:
> +            print ("Error: '%s' is not recognized main program operation" % (s))
> +            exit (-1)
> +        opr = m.group(1)
> +        if (opr == '+' or opr == '^' or opr == '%' or opr == '-'):
> +            # Determined the main program needs libdl
> +            test_descr.main_program_needs_ldl = True
> +        obj = m.group(2)
> +        if not obj in test_descr.objs:
> +            test_descr.objs.append (obj)
> +        if opr == '%' or opr == '@':
> +            test_descr.add_callrefs (['#'], [obj])
> +    # We have a main program specified, turn this off
> +    test_descr.main_program_default_deps = False
> +
> +# For (a1 a2 b1-12) object set descriptions, expand into an object list
> +def expand_object_set_string (descr_str):
> +    obj_list = []
> +    descr_list = descr_str.split ()
> +    for descr in descr_list:
> +        m = re.match (r"^([a-zA-Z][0-9a-zA-Z]*)(-[0-9]+)?$", descr)
> +        if not m:
> +            print ("Error: '%s' is not a valid object set description" % (descr))
> +            exit (-1)
> +        obj = m.group (1)
> +        idx_end = m.group (2)
> +        if not idx_end:
> +            if not obj in obj_list:
> +                obj_list.append (obj)
> +        else:
> +            idx_end = int (idx_end[1:])
> +            m = re.match (r"^([0-9a-zA-Z][a-zA-Z]*)([0-9]+)$", obj)
> +            if not m:
> +                print ("Error: object description '%s' is malformed" % (obj))
> +                exit (-1)
> +            obj_name = m.group (1)
> +            idx_start = int (m.group (2))
> +            if idx_start > idx_end:
> +                print ("Error: index range %s-%s invalid" % (idx_start, idx_end))
> +                exit (-1)
> +            for i in range(idx_start, idx_end + 1):
> +                o = obj_name + str(i)
> +                if not o in obj_list:
> +                    obj_list.append (o)
> +    return obj_list
> +
> +# Lexer for tokens
> +tokenspec = [ ("OBJ",      r"([0-9a-zA-Z]+)"),
> +              ("DEP",      r"->"),
> +              ("CALLREF",  r"=>"),
> +              ("OBJSET",   r"\[([0-9a-zA-Z]+)\]"),
> +              ("OBJSET2",  r"\(([0-9a-zA-Z \-]+)\)"),
> +              ("OBJSET3",  r"\*"),
> +              ("PROG",     r"{([0-9a-zA-Z;+^\-%]*)}"),
> +              ("PERMUTE",  r"!"),
> +              ("SEMICOL",  r";"),
> +              ("ERROR",    r".") ]
> +tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tokenspec)

OK.

> +
> +# Main line parser of description language
> +def parse_description_string (t, descr_str):
> +
> +    # State used when parsing dependencies
> +    curr_objs = []
> +    in_dep = False
> +    in_callref = False
> +    def clear_dep_state ():
> +        nonlocal in_dep, in_callref
> +        in_dep = in_callref = False
> +
> +    for m in re.finditer(tok_re, descr_str):
> +        kind = m.lastgroup
> +        value = m.group ()
> +        if kind == "OBJ":
> +            if in_dep:
> +                t.add_deps (curr_objs, [value])
> +            elif in_callref:
> +                t.add_callrefs (curr_objs, [value])
> +            clear_dep_state ()
> +            curr_objs = [value]
> +            if not value in t.objs:
> +                t.objs.append (value)
> +
> +        elif kind == "OBJSET":
> +            objset = value[1:len(value)-1]
> +            if in_dep:
> +                t.add_deps (curr_objs, list (objset))
> +            elif in_callref:
> +                t.add_callrefs (curr_objs, list (objset))
> +            clear_dep_state ()
> +            curr_objs = list (objset)
> +            for o in list (objset):
> +                if not o in t.objs:
> +                    t.objs.append (o)
> +
> +        elif kind == "OBJSET2":
> +            descr_str = value[1:len(value)-1]
> +            descr_str.strip ()
> +            objs = expand_object_set_string (descr_str)
> +            if not objs:
> +                print ("Error: empty object set '%s'" % (value))
> +                exit (-1)
> +            if in_dep:
> +                t.add_deps (curr_objs, objs)
> +            elif in_callref:
> +                t.add_callrefs (curr_objs, objs)
> +            clear_dep_state ()
> +            curr_objs = objs
> +            for o in objs:
> +                if not o in t.objs:
> +                    t.objs.append (o)
> +
> +        elif kind == "OBJSET3":
> +            if in_dep:
> +                t.add_deps (curr_objs, ['*'])
> +            elif in_callref:
> +                t.add_callrefs (curr_objs, ['*'])
> +            else:
> +                print ("Error: non-dependence target set '*' can only be used "
> +                       "as target of ->/=> operations")
> +                exit (-1)
> +            clear_dep_state ()
> +            curr_objs = ['*']
> +
> +        elif kind == "PERMUTE":
> +            if in_dep or in_callref:
> +                print ("Error: syntax error, permute operation invalid here")
> +                exit (-1)
> +            if not curr_objs:
> +                print ("Error: syntax error, no objects to permute here")
> +                exit (-1)
> +
> +            for obj in curr_objs:
> +                if not obj in t.dep_permutations:
> +                    # Signal this object has permutated dependencies
> +                    t.dep_permutations[obj] = []
> +
> +        elif kind == "PROG":
> +            if t.main_program:
> +                print ("Error: cannot have more than one main program")
> +                exit (-1)
> +            if in_dep:
> +                print ("Error: objects cannot have dependency on main program")
> +                exit (-1)
> +            if in_callref:
> +                # TODO: A DSO can resolve to a symbol in the main binary,
> +                # which we syntactically allow here, but haven't yet implemented.
> +                t.add_callrefs (curr_objs, ["#"])
> +            process_main_program (t, value[1:len(value)-1])
> +            clear_dep_state ()
> +            curr_objs = ["#"]
> +
> +        elif kind == "DEP":
> +            if in_dep or in_callref:
> +                print ("Error: syntax error, multiple contiguous ->,=> operations")
> +                exit (-1)
> +            if '*' in curr_objs:
> +                print ("Error: non-dependence target set '*' can only be used "
> +                       "as target of ->/=> operations")
> +                exit (-1)
> +            in_dep = True
> +
> +        elif kind == "CALLREF":
> +            if in_dep or in_callref:
> +                print ("Error: syntax error, multiple contiguous ->,=> operations")
> +                exit (-1)
> +            if '*' in curr_objs:
> +                print ("Error: non-dependence target set '*' can only be used "
> +                       "as target of ->/=> operations")
> +                exit (-1)
> +            in_callref = True
> +
> +        elif kind == "SEMICOL":
> +            curr_objs = []
> +            clear_dep_state ()
> +
> +        else:
> +            print ("Error: unknown token '%s'" % (value))
> +            exit (-1)
> +    return t

OK.

> +
> +
> +
> +def process_testcase (t):
> +    global objpfx
> +    assert t.test_name
> +
> +    base_test_name = t.test_name
> +    test_subdir = base_test_name + "-dir"
> +    testpfx = objpfx + test_subdir + "/"
> +
> +    if not os.path.exists (testpfx):
> +        os.mkdir (testpfx)
> +
> +    def find_objs_not_depended_on (t):
> +        objs_not_depended_on = []
> +        for obj in t.objs:
> +            skip = False
> +            for r in t.deps.items():
> +                if obj in r[1]:
> +                    skip = True
> +                    break
> +            if not skip:
> +                objs_not_depended_on.append (obj)
> +        return objs_not_depended_on
> +
> +    non_dep_tgt_objs = find_objs_not_depended_on (t)
> +    for obj in t.objs:
> +        if obj in t.deps:
> +            deps = t.deps[obj]
> +            if '*' in deps:
> +                t.deps[obj].remove ('*')
> +                t.add_deps ([obj], non_dep_tgt_objs)
> +        if obj in t.callrefs:
> +            deps = t.callrefs[obj]
> +            if '*' in deps:
> +                t.deps[obj].remove ('*')
> +                t.add_callrefs ([obj], non_dep_tgt_objs)
> +    if "#" in t.deps:
> +        deps = t.deps["#"]
> +        if '*' in deps:
> +            t.deps["#"].remove ('*')
> +            t.add_deps (["#"], non_dep_tgt_objs)
> +
> +    # If no main program was specified in dependency description, make a
> +    # default main program with deps pointing to all DSOs which are not
> +    # depended by another DSO.

OK.

> +    if t.main_program_default_deps:
> +        main_deps = non_dep_tgt_objs
> +        if not main_deps:
> +            print ("Error: no objects for default main program to point "
> +                   "dependency to (all objects strongly connected?)")
> +            exit (-1)
> +        t.add_deps (["#"], main_deps)
> +
> +    # Some debug output
> +    if cmdlineargs.debug_output:
> +        print ("Testcase: %s" % (t.test_name))
> +        print ("All objects: %s" % (t.objs))
> +        print ("--- Static link dependencies ---")
> +        for r in t.deps.items():
> +            print ("%s -> %s" % (r[0], r[1]))
> +        print ("--- Objects whose dependencies are to be permutated ---")

s/permutated/permuted/g

> +        for r in t.dep_permutations.items():
> +            print ("%s" % (r[0]))
> +        print ("--- Call reference dependencies ---")
> +        for r in t.callrefs.items():
> +            print ("%s => %s" % (r[0], r[1]))
> +        print ("--- main program ---")
> +        print (t.main_program)
> +
> +    # Main testcase generation routine, does Makefile fragment generation,
> +    # testcase source generation, and if --build specified builds testcase.
> +    def generate_testcase (test_descr, test_suffix):
> +
> +        test_name = test_descr.test_name + test_suffix
> +
> +        # Print out needed Makefile fragments for use in glibc/elf/Makefile.
> +        module_names = ""
> +        for o in test_descr.objs:
> +            module_names += " " + test_subdir + "/" + test_name + "-" + o
> +        makefile.write ("modules-names +=%s\n" % (module_names))
> +
> +        # Depth-first traversal, executing FN(OBJ) in post-order
> +        def dfs (t, fn):
> +            def dfs_rec (obj, fn, obj_visited):
> +                if obj in obj_visited:
> +                    return
> +                obj_visited[obj] = True
> +                if obj in t.deps:
> +                    for dep in t.deps[obj]:
> +                        dfs_rec (dep, fn, obj_visited)
> +                fn (obj)
> +
> +            obj_visited = {}
> +            for obj in t.objs:
> +                dfs_rec (obj, fn, obj_visited)
> +
> +        # Generate link dependencies for all DSOs, done in a DFS fashion. Usually
> +        # this doesn't need to be this complex, just listing the direct dependencies
> +        # is enough. However to support creating circular dependency situations,
> +        # traversing it by DFS and tracking processing status is the natural way to
> +        # do it.

OK. Agreed.

> +        obj_processed = {}
> +        fake_created = {}
> +        def gen_link_deps (obj):
> +            if obj in test_descr.deps:
> +                dso = test_subdir + "/" + test_name + "-" + obj + ".so"
> +                dependencies = ""
> +                for dep in test_descr.deps[obj]:
> +                    if dep in obj_processed:
> +                        depstr = (" $(objpfx)" + test_subdir + "/"
> +                                  + test_name + "-" + dep + ".so")
> +                    else:
> +                        # A circular dependency is satisfied by making a fake DSO
> +                        # tagged with the correct SONAME
> +                        depstr = (" $(objpfx)" + test_subdir + "/"
> +                                  + test_name + "-" + dep + ".FAKE.so")
> +                        # Create empty C file and Makefile fragments for fake object.
> +                        # This only needs to be done at most once for an object name.
> +                        if not dep in fake_created:
> +                            f = open (testpfx + test_name + "-" + dep + ".FAKE.c", "w")
> +                            f.write (" \n")
> +                            f.close ()
> +                            # Generate rule to create fake object
> +                            makefile.write ("LDFLAGS-%s = -Wl,--no-as-needed "
> +                                            "-Wl,-soname=%s\n"
> +                                            % (test_name + "-" + dep + ".FAKE.so",
> +                                               ("$(objpfx)" + test_subdir + "/"
> +                                                + test_name + "-" + dep + ".so")))
> +                            makefile.write ("modules-names += %s\n"
> +                                            % (test_subdir + "/"
> +                                               + test_name + "-" + dep + ".FAKE"))
> +                            fake_created[dep] = True
> +                    dependencies += depstr
> +                makefile.write ("$(objpfx)%s:%s\n" % (dso, dependencies))
> +            # Mark obj as processed
> +            obj_processed[obj] = True
> +
> +        dfs (test_descr, gen_link_deps)
> +
> +        # Print LDFLAGS-* and *-no-z-defs
> +        for o in test_descr.objs:
> +            dso = test_name + "-" + o + ".so"
> +            makefile.write ("LDFLAGS-%s = -Wl,--no-as-needed\n" % (dso))
> +            if o in test_descr.callrefs:
> +                makefile.write ("%s-no-z-defs = yes\n" % (dso))
> +
> +        # Print dependencies for main test program
> +        depstr = ""
> +        if '#' in test_descr.deps:
> +            for o in test_descr.deps['#']:
> +                depstr += (" $(objpfx)" + test_subdir + "/"
> +                           + test_name + "-" + o + ".so")
> +        if test_descr.main_program_needs_ldl:
> +            depstr += " $(libdl)"
> +        makefile.write ("$(objpfx)%s/%s:%s\n" % (test_subdir, test_name, depstr))
> +        makefile.write ("LDFLAGS-%s = -Wl,--no-as-needed\n" % (test_name))
> +
> +        not_depended_objs = find_objs_not_depended_on (test_descr)
> +        if not_depended_objs:
> +            depstr = ""
> +            for dep in not_depended_objs:
> +                depstr += (" $(objpfx)" + test_subdir + "/"
> +                           + test_name + "-" + dep + ".so")
> +            makefile.write ("$(objpfx)%s.out:%s\n" % (base_test_name, depstr))
> +
> +        # Add main executable to test-srcs
> +        makefile.write ("test-srcs += %s/%s\n" % (test_subdir, test_name))
> +        # Add dependency on main executable of test
> +        makefile.write ("$(objpfx)%s.out: $(objpfx)%s/%s\n"
> +                        % (base_test_name, test_subdir, test_name))
> +
> +        for r in test_descr.expected_outputs.items ():
> +            tunable_env = ""
> +            tunable_sfx = ""
> +            if r[0] != "":
> +                tunable_env = "GLIBC_TUNABLES=%s " % r[0]
> +                tunable_sfx = "-" + r[0].replace("=","_")
> +            # Write out fragment of shell script for this single test.
> +            test_descr.sh.write ("%s${test_wrapper_env} ${run_program_env} \\\n"
> +                    "${common_objpfx}elf/ld.so \\\n"
> +                    "--library-path ${common_objpfx}elf/%s:\\\n"
> +                    "${common_objpfx}elf:${common_objpfx}.:${common_objpfx}dlfcn \\\n"
> +                    "  ${common_objpfx}elf/%s/%s > ${common_objpfx}elf/%s/%s%s.output\n"
> +                    % (tunable_env ,test_subdir,
> +                       test_subdir, test_name, test_subdir, test_name,
> +                       tunable_sfx))
> +            # Generate a run of each test and compare with expected out
> +            test_descr.sh.write ("if [ $? -ne 0 ]; then\n"
> +                                 "  echo 'FAIL: %s execution test'\n"
> +                                 "  something_failed=true\n"
> +                                 "fi\n"
> +                                 "diff -wu ${common_objpfx}elf/%s/%s%s.output \\\n"
> +                                 "         ${common_objpfx}elf/%s/%s%s.exp\n"
> +                                 "if [ $? -ne 0 ]; then\n"
> +                                 "  echo 'FAIL: %s%s expected output comparison'\n"
> +                                 "  something_failed=true\n"
> +                                 "fi\n"
> +                                 % (test_name,
> +                                    test_subdir, test_name, tunable_sfx,
> +                                    test_subdir, base_test_name, tunable_sfx,
> +                                    test_name, ("(%s)" % tunable_env.strip()
> +                                                if tunable_env else "")))
> +
> +        # Generate C files according to dependency and calling relations from
> +        # description string.
> +        for obj in test_descr.objs:
> +            src_name = test_name + "-" + obj + ".c"
> +            f = open (testpfx + src_name, "w")
> +            if obj in test_descr.callrefs:
> +                called_objs = test_descr.callrefs[obj]
> +                for callee in called_objs:
> +                    f.write ("extern void fn_%s (void);\n" % (callee))
> +            if len(obj) == 1:
> +                f.write ("extern int putchar(int);\n")
> +                f.write ("static void __attribute__((constructor)) " +
> +                         "init(void){putchar('%s');putchar('>');}\n" % (obj))
> +                f.write ("static void __attribute__((destructor)) " +
> +                         "fini(void){putchar('<');putchar('%s');}\n" % (obj))
> +            else:
> +                f.write ('extern int printf(const char *, ...);\n')
> +                f.write ('static void __attribute__((constructor)) ' +
> +                         'init(void){printf("%s>");}\n' % (obj))
> +                f.write ('static void __attribute__((destructor)) ' +
> +                         'fini(void){printf("<%s");}\n' % (obj))
> +            if obj in test_descr.callrefs:
> +                called_objs = test_descr.callrefs[obj]
> +                if len (obj) != 1:
> +                    f.write ("extern int putchar(int);\n")
> +                f.write ("void fn_%s (void) {\n" % (obj))
> +                if len (obj) == 1:
> +                    f.write ("  putchar ('%s');\n" % (obj));
> +                    f.write ("  putchar ('(');\n");
> +                else:
> +                    f.write ('  printf ("%s(");\n' % (obj));
> +                for callee in called_objs:
> +                    f.write ("  fn_%s ();\n" % (callee))
> +                f.write ("  putchar (')');\n");
> +                f.write ("}\n")
> +            else:
> +                for callref in test_descr.callrefs.items():
> +                    if obj in callref[1]:
> +                        if len (obj) == 1:
> +                            # We need to declare printf here in this case.
> +                            f.write ('extern int printf(const char *, ...);\n')
> +                        f.write ("void fn_%s (void) {\n" % (obj))
> +                        f.write ('  printf ("%s()");\n' % (obj))
> +                        f.write ("}\n")
> +                        break
> +            f.close ()
> +
> +        # Open C file for writing main program
> +        f = open (testpfx + test_name + ".c", "w")
> +
> +        # if there are some operations in main(), it means we need -ldl
> +        if test_descr.main_program_needs_ldl:
> +            f.write ("#include <dlfcn.h>\n")
> +        f.write ("#include <stdio.h>\n")
> +        f.write ("#include <stdlib.h>\n")
> +        for s in test_descr.main_program:
> +            if s[0] == '@':
> +                f.write ("extern void fn_%s (void);\n", s[1]);
> +        f.write ("int main (void) {\n")
> +        f.write ("  putchar('{');\n")
> +
> +        # Helper routine for generating sanity checking code.
> +        def put_fail_check (fail_cond, action_desc):
> +            f.write ('  if (%s) { printf ("\\n%s failed: %%s\\n", '
> +                     'dlerror ()); exit (1);}\n' % (fail_cond, action_desc))
> +        i = 0
> +        while i < len(test_descr.main_program):
> +            s = test_descr.main_program[i]
> +            obj = s[len(s)-1]
> +            dso = test_name + "-" + obj
> +            if s[0] == '+' or s[0] == '^':
> +                if s[0] == '+':
> +                    dlopen_flags = "RTLD_LAZY|RTLD_GLOBAL"
> +                    f.write ("  putchar('+');\n");
> +                else:
> +                    dlopen_flags = "RTLD_LAZY"
> +                    f.write ("  putchar(':');\n");
> +                if len (obj) == 1:
> +                    f.write ("  putchar('%s');\n" % (obj));
> +                else:
> +                    f.write ('  printf("%s");\n' % (obj));
> +                f.write ("  putchar('[');\n");
> +                f.write ('  void *%s = dlopen ("%s.so", %s);\n'
> +                         % (obj, dso, dlopen_flags))
> +                put_fail_check ("!%s" % (obj),
> +                                "%s.so dlopen" % (dso))
> +                f.write ("  putchar(']');\n");
> +            elif s[0] == '-':
> +                f.write ("  putchar('-');\n");
> +                if len (obj) == 1:
> +                    f.write ("  putchar('%s');\n" % (obj));
> +                else:
> +                    f.write ('  printf("%s");\n' % (obj));
> +                f.write ("  putchar('[');\n");
> +                put_fail_check ("dlclose (%s) != 0" % (obj),
> +                                "%s.so dlclose" % (dso))
> +                f.write ("  putchar(']');\n");
> +            elif s[0] == '%':
> +                f.write ("  putchar('%');\n");
> +                f.write ('  void (*fn_%s)(void) = dlsym (%s, "fn_%s");\n'
> +                         % (obj, obj, obj))
> +                put_fail_check ("!fn_%s" % (obj),
> +                                "dlsym(fn_%s) from %s.so" % (obj, dso))
> +                f.write ("  fn_%s ();\n" % (obj))
> +            elif s[0] == '@':
> +                f.write ("  putchar('@');\n");
> +                f.write ("  fn_%s ();\n" % (obj))
> +            f.write ("  putchar(';');\n");
> +            i += 1
> +        f.write ("  putchar('}');\n")
> +        f.write ("  return 0;\n")
> +        f.write ("}\n")
> +        f.close ()
> +
> +        # --build option processing: build generated sources using 'build_gcc'
> +        if cmdlineargs.build:
> +            # Helper routine to run a shell command, for running GCC below
> +            def run_cmd (args):
> +                cmd = str.join (' ', args)
> +                if cmdlineargs.debug_output:
> +                    print (cmd)
> +                p = subprocess.Popen (args)
> +                p.wait ()
> +                if p.returncode != 0:
> +                    print ("Error running: %s" % (cmd))
> +                    exit (-1)
> +
> +            # Compile individual .os files
> +            for obj in test_descr.objs:
> +                src_name = test_name + "-" + obj + ".c"
> +                obj_name = test_name + "-" + obj + ".os"
> +                run_cmd ([build_gcc, "-c", "-fPIC", testpfx + src_name,
> +                          "-o", testpfx + obj_name])
> +
> +            obj_processed = {}
> +            fake_created = {}
> +            # Function to create <test_name>-<obj>.so
> +            def build_dso (obj):
> +                obj_name = test_name + "-" + obj + ".os"
> +                dso_name = test_name + "-" + obj + ".so"
> +                deps = []
> +                if obj in test_descr.deps:
> +                    for dep in test_descr.deps[obj]:
> +                        if dep in obj_processed:
> +                            deps.append (dep)
> +                        else:
> +                            deps.append (dep + ".FAKE")
> +                            if not dep in fake_created:
> +                                cmd = [build_gcc, "-Wl,--no-as-needed",
> +                                       ("-Wl,-soname=" + testpfx
> +                                        + test_name + "-" + dep + ".so"),
> +                                       "-shared",
> +                                       testpfx + test_name + "-" + dep + ".FAKE.c",
> +                                       "-o",
> +                                       testpfx + test_name + "-" + dep + ".FAKE.so"]
> +                                run_cmd (cmd)
> +                                fake_created[dep] = True
> +                dso_deps = map (lambda d: testpfx + test_name + "-" + d + ".so", deps)
> +                cmd = ([build_gcc, "-shared", "-o", testpfx + dso_name,
> +                        testpfx + obj_name, "-Wl,--no-as-needed"] + list(dso_deps))
> +                run_cmd (cmd)
> +                obj_processed[obj] = True
> +
> +            # Build all DSOs, this needs to be in topological dependency order,
> +            # or link will fail
> +            dfs (test_descr, build_dso)
> +
> +            # Build main program
> +            deps = []
> +            if '#' in test_descr.deps:
> +                deps = test_descr.deps['#']
> +            main_deps = map (lambda d: testpfx + test_name + "-" + d + ".so", deps)
> +            cmd = ([build_gcc, "-Wl,--no-as-needed", "-o", testpfx + test_name,
> +                    testpfx + test_name + ".c", "-L%s" % (os.getcwd ()),
> +                    "-Wl,-rpath-link=%s" % (os.getcwd ())]
> +                   + list (main_deps))
> +            if test_descr.main_program_needs_ldl:
> +                cmd += ["-ldl"]
> +            run_cmd (cmd)
> +
> +    # Check if we need to enumerate permutations of dependencies
> +    need_permutation_processing = False
> +    if t.dep_permutations:
> +        # Adjust dep_permutations into map of object -> dependency permutations
> +        for r in t.dep_permutations.items():
> +            obj = r[0]
> +            if obj in t.deps and len(t.deps[obj]) > 1:
> +                deps = t.deps[obj]
> +                t.dep_permutations[obj] = list (itertools.permutations (deps))
> +                need_permutation_processing = True
> +
> +    def enum_permutations (t, perm_list):
> +        test_subindex = 1
> +        curr_perms = []
> +        def enum_permutations_rec (t, perm_list):
> +            nonlocal test_subindex, curr_perms
> +            if len(perm_list) >= 1:
> +                curr = perm_list[0]
> +                obj = curr[0]
> +                perms = curr[1]
> +                if not perms:
> +                    # This may be an empty list if no multiple dependencies to
> +                    # permute were found, skip to next in this case
> +                    enum_permutations_rec (t, perm_list[1:])
> +                else:
> +                    for deps in perms:
> +                        t.deps[obj] = deps
> +                        permstr = "" if obj == "#" else obj + "_"
> +                        permstr += str.join ('', deps)
> +                        curr_perms.append (permstr)
> +                        enum_permutations_rec (t, perm_list[1:])
> +                        curr_perms = curr_perms[0:len(curr_perms)-1]
> +            else:
> +                # t.deps is now instantiated with one dependency order
> +                # permutation (across all objects that have multiple
> +                # permutations), now process a testcase
> +                generate_testcase (t, ("_" + str (test_subindex)
> +                                       + "-" + str.join ('-', curr_perms)))
> +                test_subindex += 1
> +        enum_permutations_rec (t, perm_list)
> +
> +    # Create *.exp files with expected outputs
> +    for r in t.expected_outputs.items ():
> +        sfx = ""
> +        if r[0] != "":
> +            sfx = "-" + r[0].replace("=","_")
> +        f = open (testpfx + t.test_name + sfx + ".exp", "w")
> +        f.write ('%s' % r[1])
> +        f.close ()
> +
> +    # Create header part of top-level testcase shell script, to wrap execution
> +    # and output comparison together.
> +    t.sh = open (testpfx + t.test_name + ".sh", "w")
> +    t.sh.write ("#!/bin/sh\n")
> +    t.sh.write ("# Test driver for %s, generated by "
> +                "dso-ordering-test.py\n" % (t.test_name))
> +    t.sh.write ("common_objpfx=$1\n")
> +    t.sh.write ("test_wrapper_env=$2\n")
> +    t.sh.write ("run_program_env=$3\n")
> +    t.sh.write ("something_failed=false\n")
> +
> +    # Starting part of Makefile fragment
> +    makefile.write ("ifeq (yes,$(build-shared))\n")
> +
> +    if need_permutation_processing:
> +        enum_permutations (t, list (t.dep_permutations.items()))
> +    else:
> +        # We have no permutations to enumerate, just process testcase normally
> +        generate_testcase (t, "")
> +
> +    # Output end part of Makefile fragment
> +    expected_output_files = ""
> +    for r in t.expected_outputs.items ():
> +        sfx = ""
> +        if r[0] != "":
> +            sfx = "-" + r[0].replace("=","_")
> +        expected_output_files += " $(objpfx)%s/%s%s.exp" % (test_subdir,
> +                                                            t.test_name, sfx)
> +    makefile.write ("$(objpfx)%s.out: $(objpfx)%s/%s.sh%s\n"
> +                    % (t.test_name, test_subdir, t.test_name,
> +                       expected_output_files))
> +    makefile.write ("\t$(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' "
> +                    "'$(run-program-env)' > $@; $(evaluate-test)\n")

OK. Good using test-wrapper-env.

> +    makefile.write ("ifeq ($(run-built-tests),yes)\n")
> +    makefile.write ("tests-special += $(objpfx)%s.out\n" % (t.test_name))
> +    makefile.write ("endif\n")
> +    makefile.write ("endif\n")
> +
> +    # Write ending part of shell script generation
> +    t.sh.write ("if $something_failed; then\n"
> +                "  exit 1\n"
> +                "else\n"
> +                "  echo 'PASS: all tests for %s succeeded'\n"
> +                "  exit 0\n"
> +                "fi\n" % (t.test_name))
> +    t.sh.close ()
> +
> +# Decription file parsing
> +def parse_description_file (filename):
> +    f = open (filename)
> +    if not f:
> +        print ("Error: cannot open description file %s" % (filename))
> +        exit (-1)
> +    descrfile_lines = f.readlines ()
> +    t = None
> +    for line in descrfile_lines:
> +        p = re.compile (r"#.*$")
> +        line = p.sub ("", line) # Filter out comments
> +        line = line.strip () # Remove excess whitespace
> +        m = re.match (r"^([^:]+):\s*(.*)$", line)
> +        if m:
> +            o = re.match (r"^output(.*)$", m.group (1))
> +            if o:
> +                if not t:
> +                    print ("Error: output specification without testcase "
> +                           "description")
> +                    exit (-1)
> +                tsstr = ""
> +                if o.group(1):
> +                    ts = re.match (r"^\(([a-zA-Z0-9_.=]*)\)$", o.group (1))
> +                    if not ts:
> +                        print ("Error: tunable option malformed '%s'" % o.group(1))
> +                        exit (-1)
> +                    tsstr = ts.group(1)
> +                t.expected_outputs[tsstr] = m.group (2)
> +            else:
> +                if t:
> +                    # Starting a new test description, end and process
> +                    # current one.
> +                    process_testcase (t)
> +                t = TestDescr ()
> +                t.test_name = m.group (1)
> +                descr_string = m.group (2)
> +                parse_description_string (t, descr_string)
> +            continue
> +        else:
> +            if line:
> +                if not t:
> +                    print ("Error: no active testcase description")
> +                    exit (-1)
> +                parse_description_string (t, line)
> +    # Process last completed test description
> +    if t:
> +        process_testcase (t)
> +
> +# Setup Makefile output to file or stdout as selected
> +if output_makefile:
> +    output_makefile_dir = os.path.dirname (output_makefile)
> +    if output_makefile_dir:
> +        os.makedirs (output_makefile_dir, exist_ok = True)
> +    makefile = open (output_makefile, "w")
> +else:
> +    makefile = open (sys.stdout.fileno (), "w")
> +
> +# Finally, the main top-level calling of above parsing routines.
> +if description_file:
> +    parse_description_file (description_file)
> +else:
> +    t = TestDescr ()
> +    t.test_name = test_name
> +    parse_description_string (t, description)
> +    process_testcase (t)
> +
> +# Close Makefile fragment output
> +makefile.close ()

OK.

-- 
Cheers,
Carlos.

  parent reply	other threads:[~2020-06-18 21:31 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-19 13:24 [PATCH v2 1/2] BZ #17645, fix slow DSO sorting behavior in dynamic loader Chung-Lin Tang
2020-05-19 14:21 ` Andreas Schwab
2020-05-19 14:33   ` Chung-Lin Tang
2020-05-19 14:50     ` Andreas Schwab
2020-05-20 14:38       ` [PATCH v2.1 " Chung-Lin Tang
2020-06-05 13:56         ` Carlos O'Donell via Libc-alpha
2020-06-18 21:30         ` Carlos O'Donell via Libc-alpha [this message]
2020-07-22 18:10           ` [PATCH v3 " Chung-Lin Tang
2020-07-27  0:40             ` Carlos O'Donell via Libc-alpha
2020-07-27 10:00               ` Chung-Lin Tang
2020-07-27 10:12                 ` Florian Weimer via Libc-alpha
2020-08-08  4:42               ` Carlos O'Donell via Libc-alpha

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/libc/involved.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=456fbddc-7915-19da-3ffe-e58b86ae7980@redhat.com \
    --to=libc-alpha@sourceware.org \
    --cc=carlos@redhat.com \
    --cc=clm@codesourcery.com \
    --cc=cltang@codesourcery.com \
    --cc=schwab@linux-m68k.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).