summaryrefslogtreecommitdiff
blob: be8ca1963cb9d755786840eabee62e1482bd1f48 (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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
When building glibc PIE (which is not something upstream support),
several modifications are necessary to the glibc build process.

First, any syscalls in PIEs must be of the PIC variant, otherwise
textrels ensue.  Then, any syscalls made before the initialisation
of the TLS will fail on i386, as the sysenter variant on i386 uses
the TLS, giving rise to a chicken-and-egg situation.  This patch
defines a PIC syscall variant that doesn't use sysenter, even when the sysenter
version is normally used, and uses the non-sysenter version for the brk
syscall that is performed by the TLS initialisation.  Further, the TLS
initialisation is moved in this case prior to the initialisation of
dl_osversion, as that requires further syscalls.

csu/libc-start.c: Move initial TLS initialization to before the
initialisation of dl_osversion, when INTERNAL_SYSCALL_NOSYSENTER is defined

csu/libc-tls.c: Use the no-sysenter version of sbrk when
INTERNAL_SYSCALL_NOSYSENTER is defined.

misc/sbrk.c: Define a no-sysenter version of sbrk, using the no-sysenter
version of brk - if INTERNAL_SYSCALL_NOSYSENTER is defined.

misc/brk.c: Define a no-sysenter version of brk if
INTERNAL_SYSCALL_NOSYSENTER is defined.

sysdeps/unix/sysv/linux/i386/sysdep.h: Define INTERNAL_SYSCALL_NOSYSENTER
Make INTERNAL_SYSCALL always use the PIC variant, even if not SHARED.

Patch by Kevin F. Quinn <kevquinn@gentoo.org>

--- csu/libc-start.c
+++ csu/libc-start.c
@@ -28,6 +28,7 @@
 extern int __libc_multiple_libcs;
 
 #include <tls.h>
+#include <sysdep.h>
 #ifndef SHARED
 # include <dl-osinfo.h>
 extern void __pthread_initialize_minimal (void);
@@ -129,6 +130,11 @@
 #  endif
   _dl_aux_init (auxvec);
 # endif
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+  /* Do the initial TLS initialization before _dl_osversion,
+     since the latter uses the uname syscall.  */
+  __pthread_initialize_minimal ();
+# endif
 # ifdef DL_SYSDEP_OSCHECK
   if (!__libc_multiple_libcs)
     {
@@ -138,10 +144,12 @@
     }
 # endif
 
+# ifndef INTERNAL_SYSCALL_NOSYSENTER
   /* Initialize the thread library at least a bit since the libgcc
      functions are using thread functions if these are available and
      we need to setup errno.  */
   __pthread_initialize_minimal ();
+# endif
 #endif
 
 # ifndef SHARED
--- csu/libc-tls.c
+++ csu/libc-tls.c
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/param.h>
+#include <sysdep.h>
 
 
 #ifdef SHARED
@@ -29,6 +30,9 @@
  #error makefile bug, this file is for static only
 #endif
 
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+extern void *__sbrk_nosysenter (intptr_t __delta);
+#endif
 extern ElfW(Phdr) *_dl_phdr;
 extern size_t _dl_phnum;
 
@@ -141,14 +145,26 @@
 
      The initialized value of _dl_tls_static_size is provided by dl-open.c
      to request some surplus that permits dynamic loading of modules with
-     IE-model TLS.  */
+     IE-model TLS.
+     
+     Where the normal sbrk would use a syscall that needs the TLS (i386)
+     use the special non-sysenter version instead.  */
 #if TLS_TCB_AT_TP
   tcb_offset = roundup (memsz + GL(dl_tls_static_size), tcbalign);
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+  tlsblock = __sbrk_nosysenter (tcb_offset + tcbsize + max_align);
+# else
   tlsblock = __sbrk (tcb_offset + tcbsize + max_align);
+# endif
 #elif TLS_DTV_AT_TP
   tcb_offset = roundup (tcbsize, align ?: 1);
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+  tlsblock = __sbrk_nosysenter (tcb_offset + memsz + max_align
+		     + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
+# else
   tlsblock = __sbrk (tcb_offset + memsz + max_align
 		     + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
+# endif
   tlsblock += TLS_PRE_TCB_SIZE;
 #else
   /* In case a model with a different layout for the TCB and DTV
--- misc/sbrk.c
+++ misc/sbrk.c
@@ -18,6 +18,7 @@
 
 #include <unistd.h>
 #include <errno.h>
+#include <sysdep.h>
 
 /* Defined in brk.c.  */
 extern void *__curbrk;
@@ -29,6 +30,35 @@
 /* Extend the process's data space by INCREMENT.
    If INCREMENT is negative, shrink data space by - INCREMENT.
    Return start of new space allocated, or -1 for errors.  */
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+/* This version is used by csu/libc-tls.c whem initialising the TLS
+   if the SYSENTER version requires the TLS (which it does on i386).
+   Obviously using the TLS before it is initialised is broken. */
+extern int __brk_nosysenter (void *addr);
+void *
+__sbrk_nosysenter (intptr_t increment)
+{
+  void *oldbrk;
+
+  /* If this is not part of the dynamic library or the library is used
+     via dynamic loading in a statically linked program update
+     __curbrk from the kernel's brk value.  That way two separate
+     instances of __brk and __sbrk can share the heap, returning
+     interleaved pieces of it.  */
+  if (__curbrk == NULL || __libc_multiple_libcs)
+    if (__brk_nosysenter (0) < 0)		/* Initialize the break.  */
+      return (void *) -1;
+
+  if (increment == 0)
+    return __curbrk;
+
+  oldbrk = __curbrk;
+  if (__brk_nosysenter (oldbrk + increment) < 0)
+    return (void *) -1;
+
+  return oldbrk;
+}
+#endif
 void *
 __sbrk (intptr_t increment)
 {
--- sysdeps/unix/sysv/linux/i386/brk.c
+++ sysdeps/unix/sysv/linux/i386/brk.c
@@ -31,6 +31,30 @@
    linker.  */
 weak_alias (__curbrk, ___brk_addr)
 
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+/* This version is used by csu/libc-tls.c whem initialising the TLS
+ * if the SYSENTER version requires the TLS (which it does on i386).
+ * Obviously using the TLS before it is initialised is broken. */
+int
+__brk_nosysenter (void *addr)
+{
+  void *__unbounded newbrk;
+
+  INTERNAL_SYSCALL_DECL (err);
+  newbrk = (void *__unbounded) INTERNAL_SYSCALL_NOSYSENTER (brk, err, 1,
+						 __ptrvalue (addr));
+
+  __curbrk = newbrk;
+
+  if (newbrk < addr)
+    {
+      __set_errno (ENOMEM);
+      return -1;
+    }
+
+  return 0;
+}
+#endif
 int
 __brk (void *addr)
 {
--- sysdeps/unix/sysv/linux/i386/sysdep.h
+++ sysdeps/unix/sysv/linux/i386/sysdep.h
@@ -187,7 +187,7 @@
 /* The original calling convention for system calls on Linux/i386 is
    to use int $0x80.  */
 #ifdef I386_USE_SYSENTER
-# ifdef SHARED
+# if defined SHARED || defined __PIC__
 #  define ENTER_KERNEL call *%gs:SYSINFO_OFFSET
 # else
 #  define ENTER_KERNEL call *_dl_sysinfo
@@ -358,7 +358,7 @@
    possible to use more than four parameters.  */
 #undef INTERNAL_SYSCALL
 #ifdef I386_USE_SYSENTER
-# ifdef SHARED
+# if defined SHARED || defined __PIC__
 #  define INTERNAL_SYSCALL(name, err, nr, args...) \
   ({									      \
     register unsigned int resultvar;					      \
@@ -384,6 +384,18 @@
     : "0" (name), "i" (offsetof (tcbhead_t, sysinfo))			      \
       ASMFMT_##nr(args) : "memory", "cc");				      \
     (int) resultvar; })
+#  define INTERNAL_SYSCALL_NOSYSENTER(name, err, nr, args...) \
+  ({									      \
+    register unsigned int resultvar;					      \
+    EXTRAVAR_##nr							      \
+    asm volatile (							      \
+    LOADARGS_NOSYSENTER_##nr						      \
+    "movl %1, %%eax\n\t"						      \
+    "int $0x80\n\t"							      \
+    RESTOREARGS_NOSYSENTER_##nr						      \
+    : "=a" (resultvar)							      \
+    : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc");		      \
+    (int) resultvar; })
 # else
 #  define INTERNAL_SYSCALL(name, err, nr, args...) \
   ({									      \
@@ -447,12 +459,20 @@
 
 #define LOADARGS_0
 #ifdef __PIC__
-# if defined I386_USE_SYSENTER && defined SHARED
+# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ )
 #  define LOADARGS_1 \
     "bpushl .L__X'%k3, %k3\n\t"
 #  define LOADARGS_5 \
     "movl %%ebx, %4\n\t"						      \
     "movl %3, %%ebx\n\t"
+#  define LOADARGS_NOSYSENTER_1 \
+    "bpushl .L__X'%k2, %k2\n\t"
+#  define LOADARGS_NOSYSENTER_2	LOADARGS_NOSYSENTER_1
+#  define LOADARGS_NOSYSENTER_3	LOADARGS_3
+#  define LOADARGS_NOSYSENTER_4	LOADARGS_3
+#  define LOADARGS_NOSYSENTER_5 \
+    "movl %%ebx, %3\n\t"						      \
+    "movl %2, %%ebx\n\t"
 # else
 #  define LOADARGS_1 \
     "bpushl .L__X'%k2, %k2\n\t"
@@ -474,11 +495,18 @@
 
 #define RESTOREARGS_0
 #ifdef __PIC__
-# if defined I386_USE_SYSENTER && defined SHARED
+# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ )
 #  define RESTOREARGS_1 \
     "bpopl .L__X'%k3, %k3\n\t"
 #  define RESTOREARGS_5 \
     "movl %4, %%ebx"
+#  define RESTOREARGS_NOSYSENTER_1 \
+    "bpopl .L__X'%k2, %k2\n\t"
+#  define RESTOREARGS_NOSYSENTER_2	RESTOREARGS_NOSYSENTER_1
+#  define RESTOREARGS_NOSYSENTER_3	RESTOREARGS_3
+#  define RESTOREARGS_NOSYSENTER_4	RESTOREARGS_3
+#  define RESTOREARGS_NOSYSENTER_5 \
+    "movl %3, %%ebx"
 # else
 #  define RESTOREARGS_1 \
     "bpopl .L__X'%k2, %k2\n\t"