|
|
1feee8 |
commit 1df71d32fe5f5905ffd5d100e5e9ca8ad6210891
|
|
|
1feee8 |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
1feee8 |
Date: Tue Sep 20 11:00:42 2022 +0200
|
|
|
1feee8 |
|
|
|
1feee8 |
elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937)
|
|
|
1feee8 |
|
|
|
1feee8 |
The implementation in _dl_close_worker requires that the first
|
|
|
1feee8 |
element of l_initfini is always this very map (“We are always the
|
|
|
1feee8 |
zeroth entry, and since we don't include ourselves in the
|
|
|
1feee8 |
dependency analysis start at 1.”). Rather than fixing that
|
|
|
1feee8 |
assumption, this commit adds an implementation of the force_first
|
|
|
1feee8 |
argument to the new dependency sorting algorithm. This also means
|
|
|
1feee8 |
that the directly dlopen'ed shared object is always initialized last,
|
|
|
1feee8 |
which is the least surprising behavior in the presence of cycles.
|
|
|
1feee8 |
|
|
|
1feee8 |
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
1feee8 |
|
|
|
1feee8 |
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
|
|
|
1feee8 |
index 7c614bca5d8115c6..e8ef5e8b3588ab53 100644
|
|
|
1feee8 |
--- a/elf/dl-sort-maps.c
|
|
|
1feee8 |
+++ b/elf/dl-sort-maps.c
|
|
|
1feee8 |
@@ -182,8 +182,9 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
|
|
|
1feee8 |
|
|
|
1feee8 |
static void
|
|
|
1feee8 |
_dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
|
|
|
1feee8 |
- bool force_first __attribute__ ((unused)), bool for_fini)
|
|
|
1feee8 |
+ bool force_first, bool for_fini)
|
|
|
1feee8 |
{
|
|
|
1feee8 |
+ struct link_map *first_map = maps[0];
|
|
|
1feee8 |
for (int i = nmaps - 1; i >= 0; i--)
|
|
|
1feee8 |
maps[i]->l_visited = 0;
|
|
|
1feee8 |
|
|
|
1feee8 |
@@ -208,14 +209,6 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
|
|
|
1feee8 |
Adjusting the order so that maps[0] is last traversed naturally avoids
|
|
|
1feee8 |
this problem.
|
|
|
1feee8 |
|
|
|
1feee8 |
- Further, the old "optimization" of skipping the main object at maps[0]
|
|
|
1feee8 |
- from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general
|
|
|
1feee8 |
- no longer valid, since traversing along object dependency-links
|
|
|
1feee8 |
- may "find" the main object even when it is not included in the initial
|
|
|
1feee8 |
- order (e.g. a dlopen()'ed shared object can have circular dependencies
|
|
|
1feee8 |
- linked back to itself). In such a case, traversing N-1 objects will
|
|
|
1feee8 |
- create a N-object result, and raise problems.
|
|
|
1feee8 |
-
|
|
|
1feee8 |
To summarize, just passing in the full list, and iterating from back
|
|
|
1feee8 |
to front makes things much more straightforward. */
|
|
|
1feee8 |
|
|
|
1feee8 |
@@ -274,6 +267,27 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
|
|
|
1feee8 |
}
|
|
|
1feee8 |
|
|
|
1feee8 |
memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
|
|
|
1feee8 |
+
|
|
|
1feee8 |
+ /* Skipping the first object at maps[0] is not valid in general,
|
|
|
1feee8 |
+ since traversing along object dependency-links may "find" that
|
|
|
1feee8 |
+ first object even when it is not included in the initial order
|
|
|
1feee8 |
+ (e.g., a dlopen'ed shared object can have circular dependencies
|
|
|
1feee8 |
+ linked back to itself). In such a case, traversing N-1 objects
|
|
|
1feee8 |
+ will create a N-object result, and raise problems. Instead,
|
|
|
1feee8 |
+ force the object back into first place after sorting. This naive
|
|
|
1feee8 |
+ approach may introduce further dependency ordering violations
|
|
|
1feee8 |
+ compared to rotating the cycle until the first map is again in
|
|
|
1feee8 |
+ the first position, but as there is a cycle, at least one
|
|
|
1feee8 |
+ violation is already present. */
|
|
|
1feee8 |
+ if (force_first && maps[0] != first_map)
|
|
|
1feee8 |
+ {
|
|
|
1feee8 |
+ int i;
|
|
|
1feee8 |
+ for (i = 0; maps[i] != first_map; ++i)
|
|
|
1feee8 |
+ ;
|
|
|
1feee8 |
+ assert (i < nmaps);
|
|
|
1feee8 |
+ memmove (&maps[1], maps, i * sizeof (maps[0]));
|
|
|
1feee8 |
+ maps[0] = first_map;
|
|
|
1feee8 |
+ }
|
|
|
1feee8 |
}
|
|
|
1feee8 |
|
|
|
1feee8 |
void
|
|
|
1feee8 |
diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def
|
|
|
1feee8 |
index 5f7f18ef270bc12d..4bf9052db16fb352 100644
|
|
|
1feee8 |
--- a/elf/dso-sort-tests-1.def
|
|
|
1feee8 |
+++ b/elf/dso-sort-tests-1.def
|
|
|
1feee8 |
@@ -64,3 +64,10 @@ output: b>a>{}
|
|
|
1feee8 |
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
|
|
|
1feee8 |
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[
|
|
|
1feee8 |
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[
|
|
|
1feee8 |
+
|
|
|
1feee8 |
+# Test that even in the presence of dependency loops involving dlopen'ed
|
|
|
1feee8 |
+# object, that object is initialized last (and not unloaded prematurely).
|
|
|
1feee8 |
+# Final destructor order is indeterminate due to the cycle.
|
|
|
1feee8 |
+tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1
|
|
|
1feee8 |
+output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}
|
|
|
1feee8 |
+output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}
|