aboutsummaryrefslogtreecommitdiffstats
path: root/main/musl/0016-rework-ldso-handling-of-global-symbol-table-for-cons.patch
blob: 03e1da813b45cb0f3c626406607d68d3886da690 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
From 4ff234f6cba96403b5de6d29d48a59fd73252040 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Sun, 12 Mar 2017 21:03:05 -0400
Subject: [PATCH] rework ldso handling of global symbol table for consistency

when loading libraries with dlopen, the caller can request that the
library's symbols become part of the global symbol table, or that they
only be used for resolving relocations in the loaded library and its
dependencies. in the latter case, a subsequent dlopen of the same
library can upgrade it to global status.

previously, if a library was upgraded from local to global mode, its
symbols entered the symbol lookup search order at the point where the
library was originally loaded. this means that a new call to dlopen
could change the value of a symbol that already had a visible
definition, an inconsistency which applications could observe.

POSIX is unclear whether this should happen or whether it's permitted
to happen, but the resolution of Austin Group issue #982 made it
formally unspecified.

with this patch, a library whose mode is upgraded from local to global
enters the symbol lookup order at the point where it was made global,
so that symbol resolution before and after the upgrade are consistent.

in order to implement this change, the per-dso global flag is replaced
with a separate set of linked-list pointers for participation in the
global symbol table. this permits the order of dso objects for symbol
resolution to differ from the order used for iteration of all loaded
libraries. it also improves performance of find_sym, by avoiding a
branch per iteration and skipping, and especially in the case where
many non-global libraries have been loaded, by allowing the loop to
skip over them entirely. logic for temporarily adding non-global
libraries to the symbol table for relocation purposes is also mildly
simplified.
---
 ldso/dynlink.c | 97 ++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 53 insertions(+), 44 deletions(-)

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index d00827a3..0e394e0d 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -58,11 +58,11 @@ struct dso {
 	uint32_t *ghashtab;
 	int16_t *versym;
 	char *strings;
+	struct dso *syms_next;
 	unsigned char *map;
 	size_t map_len;
 	dev_t dev;
 	ino_t ino;
-	signed char global;
 	char relocated;
 	char constructed;
 	char kernel_mapped;
@@ -113,7 +113,7 @@ static struct builtin_tls {
 static size_t *saved_addends, *apply_addends_to;
 
 static struct dso ldso;
-static struct dso *head, *tail, *fini_head;
+static struct dso *head, *tail, *fini_head, *syms_tail;
 static char *env_path, *sys_path;
 static unsigned long long gencnt;
 static int runtime;
@@ -261,9 +261,8 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
 	uint32_t h = 0, gh, gho, *ght;
 	size_t ghm = 0;
 	struct symdef def = {0};
-	for (; dso; dso=dso->next) {
+	for (; dso; dso=dso->syms_next) {
 		Sym *sym;
-		if (!dso->global) continue;
 		if ((ght = dso->ghashtab)) {
 			if (!ghm) {
 				gh = gnu_hash(s);
@@ -329,7 +328,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
 		if (sym_index) {
 			sym = syms + sym_index;
 			name = strings + sym->st_name;
-			ctx = type==REL_COPY ? head->next : head;
+			ctx = type==REL_COPY ? head->syms_next : head;
 			def = (sym->st_info&0xf) == STT_SECTION
 				? (struct symdef){ .dso = dso, .sym = sym }
 				: find_sym(ctx, name, type==REL_PLT);
@@ -932,7 +931,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
 		if (!ldso.prev) {
 			tail->next = &ldso;
 			ldso.prev = tail;
-			tail = ldso.next ? ldso.next : &ldso;
+			tail = &ldso;
 		}
 		return &ldso;
 	}
@@ -1113,9 +1112,24 @@ static void load_preload(char *s)
 	}
 }
 
-static void make_global(struct dso *p)
+static void add_syms(struct dso *p)
 {
-	for (; p; p=p->next) p->global = 1;
+	if (!p->syms_next && syms_tail != p) {
+		syms_tail->syms_next = p;
+		syms_tail = p;
+	}
+}
+
+static void revert_syms(struct dso *old_tail)
+{
+	struct dso *p, *next;
+	/* Chop off the tail of the list of dsos that participate in
+	 * the global symbol table, reverting them to RTLD_LOCAL. */
+	for (p=old_tail; p; p=next) {
+		next = p->syms_next;
+		p->syms_next = 0;
+	}
+	syms_tail = old_tail;
 }
 
 static void do_mips_relocs(struct dso *p, size_t *got)
@@ -1344,7 +1358,6 @@ void __dls2(unsigned char *base, size_t *sp)
 	}
 	Ehdr *ehdr = (void *)ldso.base;
 	ldso.name = ldso.shortname = "libc.so";
-	ldso.global = 1;
 	ldso.phnum = ehdr->e_phnum;
 	ldso.phdr = laddr(&ldso, ehdr->e_phoff);
 	ldso.phentsize = ehdr->e_phentsize;
@@ -1532,7 +1545,6 @@ _Noreturn void __dls3(size_t *sp)
 #endif
 		tls_align = MAXP2(tls_align, app.tls.align);
 	}
-	app.global = 1;
 	decode_dyn(&app);
 	if (DL_FDPIC) {
 		makefuncdescs(&app);
@@ -1547,7 +1559,21 @@ _Noreturn void __dls3(size_t *sp)
 		argv[-3] = (void *)app.loadmap;
 	}
 
-	/* Attach to vdso, if provided by the kernel */
+	/* Initial dso chain consists only of the app. */
+	head = tail = syms_tail = &app;
+
+	/* Donate unused parts of app and library mapping to malloc */
+	reclaim_gaps(&app);
+	reclaim_gaps(&ldso);
+
+	/* Load preload/needed libraries, add symbols to global namespace. */
+	if (env_preload) load_preload(env_preload);
+ 	load_deps(&app);
+	for (struct dso *p=head; p; p=p->next)
+		add_syms(p);
+
+	/* Attach to vdso, if provided by the kernel, last so that it does
+	 * not become part of the global namespace.  */
 	if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR) && vdso_base) {
 		Ehdr *ehdr = (void *)vdso_base;
 		Phdr *phdr = vdso.phdr = (void *)(vdso_base + ehdr->e_phoff);
@@ -1561,26 +1587,13 @@ _Noreturn void __dls3(size_t *sp)
 		}
 		vdso.name = "";
 		vdso.shortname = "linux-gate.so.1";
-		vdso.global = 1;
 		vdso.relocated = 1;
 		decode_dyn(&vdso);
-		vdso.prev = &ldso;
-		ldso.next = &vdso;
+		vdso.prev = tail;
+		tail->next = &vdso;
+		tail = &vdso;
 	}
 
-	/* Initial dso chain consists only of the app. */
-	head = tail = &app;
-
-	/* Donate unused parts of app and library mapping to malloc */
-	reclaim_gaps(&app);
-	reclaim_gaps(&ldso);
-
-	/* Load preload/needed libraries, add their symbols to the global
-	 * namespace, and perform all remaining relocations. */
-	if (env_preload) load_preload(env_preload);
-	load_deps(&app);
-	make_global(&app);
-
 	for (i=0; app.dynv[i]; i+=2) {
 		if (!DT_DEBUG_INDIRECT && app.dynv[i]==DT_DEBUG)
 			app.dynv[i+1] = (size_t)&debug;
@@ -1641,7 +1654,7 @@ _Noreturn void __dls3(size_t *sp)
 
 void *dlopen(const char *file, int mode)
 {
-	struct dso *volatile p, *orig_tail, *next;
+	struct dso *volatile p, *orig_tail, *orig_syms_tail, *next;
 	struct tls_module *orig_tls_tail;
 	size_t orig_tls_cnt, orig_tls_offset, orig_tls_align;
 	size_t i;
@@ -1659,15 +1672,14 @@ void *dlopen(const char *file, int mode)
 	orig_tls_cnt = tls_cnt;
 	orig_tls_offset = tls_offset;
 	orig_tls_align = tls_align;
+	orig_syms_tail = syms_tail;
 	orig_tail = tail;
 	noload = mode & RTLD_NOLOAD;
 
 	rtld_fail = &jb;
 	if (setjmp(*rtld_fail)) {
 		/* Clean up anything new that was (partially) loaded */
-		if (p && p->deps) for (i=0; p->deps[i]; i++)
-			if (p->deps[i]->global < 0)
-				p->deps[i]->global = 0;
+		revert_syms(orig_syms_tail);
 		for (p=orig_tail->next; p; p=next) {
 			next = p->next;
 			while (p->td_index) {
@@ -1703,24 +1715,21 @@ void *dlopen(const char *file, int mode)
 	}
 
 	/* First load handling */
-	if (!p->deps) {
+	if (!p->relocated) {
 		load_deps(p);
+		/* Make new symbols global, at least temporarily, so we can do
+		 * relocations. If not RTLD_GLOBAL, this is reverted below. */
+		add_syms(p);
 		if (p->deps) for (i=0; p->deps[i]; i++)
-			if (!p->deps[i]->global)
-				p->deps[i]->global = -1;
-		if (!p->global) p->global = -1;
+			add_syms(p->deps[i]);
 		reloc_all(p);
-		if (p->deps) for (i=0; p->deps[i]; i++)
-			if (p->deps[i]->global < 0)
-				p->deps[i]->global = 0;
-		if (p->global < 0) p->global = 0;
 	}
 
-	if (mode & RTLD_GLOBAL) {
-		if (p->deps) for (i=0; p->deps[i]; i++)
-			p->deps[i]->global = 1;
-		p->global = 1;
-	}
+	/* If RTLD_GLOBAL was not specified, undo any new additions
+	 * to the global symbol table. This is a nop if the library was
+	 * previously loaded and already global. */
+	if (!(mode & RTLD_GLOBAL))
+		revert_syms(orig_syms_tail);
 
 	update_tls_size();
 	_dl_debug_state();
-- 
2.12.1