This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH] i386: Add _startup_sbrk and _startup_fatal [BZ #21913]
- From: "H.J. Lu" <hjl dot tools at gmail dot com>
- To: GNU C Library <libc-alpha at sourceware dot org>
- Date: Sun, 6 Aug 2017 15:26:06 -0700
- Subject: [PATCH] i386: Add _startup_sbrk and _startup_fatal [BZ #21913]
- Authentication-results: sourceware.org; auth=none
On Linux/x86, there are 3 ways to make a system call:
1. call *%gs:SYSINFO_OFFSET. This requires TLS initialization.
2. call *_dl_sysinfo. This requires relocation of _dl_sysinfo.
3. int $0x80. This works everywhere.
When an object file is compiled with PIC, #1 is prefered since it is
faster than #3 and doesn't require relocation of _dl_sysinfo. For
dynamic executables, ld.so initializes TLS. However, for static
executables, before TLS is initialized by __libc_setup_tls, #3 should
be used for syscalls. This patch adds _startup_sbrk and _startup_fatal
to be used in static executables before __libc_setup_tls is called. By
default, they are defined to __sbrk and __libc_fatal, respectively. On
x86, a special _startup_sbrk is provided and _startup_fatal is turned
into ABORT_INSTRUCTION.
Any comments?
H.J.
---
[BZ #21913]
* csu/libc-tls.c: Include <startup.h>.
(__libc_setup_tls): Call _startup_sbrk instead of __sbrk. Call
_startup_fatal instead of __libc_fatal.
* elf/dl-tunables.c: Include <startup.h>.
(tunables_strdup): Call _startup_sbrk instead of __sbrk.
* sysdeps/generic/startup.h: New file.
* sysdeps/unix/sysv/linux/i386/startup.h: Likewise.
* sysdeps/unix/sysv/linux/i386/startup_sbrk.c: Likewise.
* sysdeps/unix/sysv/linux/i386/Makefile (sysdep_routine): Add
startup_sbrk if default to PIC.
(static-only-routines): Likewise.
---
csu/libc-tls.c | 13 +++---
elf/dl-tunables.c | 8 +++-
sysdeps/generic/startup.h | 30 +++++++++++++
sysdeps/unix/sysv/linux/i386/Makefile | 4 ++
sysdeps/unix/sysv/linux/i386/startup.h | 38 ++++++++++++++++
sysdeps/unix/sysv/linux/i386/startup_sbrk.c | 67 +++++++++++++++++++++++++++++
6 files changed, 152 insertions(+), 8 deletions(-)
create mode 100644 sysdeps/generic/startup.h
create mode 100644 sysdeps/unix/sysv/linux/i386/startup.h
create mode 100644 sysdeps/unix/sysv/linux/i386/startup_sbrk.c
diff --git a/csu/libc-tls.c b/csu/libc-tls.c
index 3c897bf28b..6f0e698220 100644
--- a/csu/libc-tls.c
+++ b/csu/libc-tls.c
@@ -16,11 +16,12 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <unistd.h>
+#include <stdio.h>
+#include <startup.h>
#include <errno.h>
#include <ldsodefs.h>
#include <tls.h>
-#include <unistd.h>
-#include <stdio.h>
#include <sys/param.h>
@@ -142,11 +143,11 @@ __libc_setup_tls (void)
_dl_allocate_tls_storage (in elf/dl-tls.c) does using __libc_memalign
and dl_tls_static_align. */
tcb_offset = roundup (memsz + GL(dl_tls_static_size), max_align);
- tlsblock = __sbrk (tcb_offset + TLS_INIT_TCB_SIZE + max_align);
+ tlsblock = _startup_sbrk (tcb_offset + TLS_INIT_TCB_SIZE + max_align);
#elif TLS_DTV_AT_TP
tcb_offset = roundup (TLS_INIT_TCB_SIZE, align ?: 1);
- tlsblock = __sbrk (tcb_offset + memsz + max_align
- + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
+ tlsblock = _startup_sbrk (tcb_offset + memsz + max_align
+ + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
tlsblock += TLS_PRE_TCB_SIZE;
#else
/* In case a model with a different layout for the TCB and DTV
@@ -193,7 +194,7 @@ __libc_setup_tls (void)
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif
if (__builtin_expect (lossage != NULL, 0))
- __libc_fatal (lossage);
+ _startup_fatal (lossage);
/* Update the executable's link map with enough information to make
the TLS routines happy. */
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 231fb8ca93..23c89b2c03 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -18,9 +18,11 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <unistd.h>
+#include <stdio.h>
+#include <startup.h>
#include <stdint.h>
#include <stdbool.h>
-#include <unistd.h>
#include <stdlib.h>
#include <sysdep.h>
#include <fcntl.h>
@@ -42,7 +44,9 @@ tunables_strdup (const char *in)
size_t i = 0;
while (in[i++] != '\0');
- char *out = __sbrk (i);
+
+ /* Can't use __sbrk before __libc_setup_tls is called. */
+ char *out = _startup_sbrk (i);
/* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
set the thread-local errno since the TCB has not yet been set up. This
diff --git a/sysdeps/generic/startup.h b/sysdeps/generic/startup.h
new file mode 100644
index 0000000000..aa63b31181
--- /dev/null
+++ b/sysdeps/generic/startup.h
@@ -0,0 +1,30 @@
+/* Generic definitions of functions used by static libc main startup.
+ Copyright (C) 2017 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/>. */
+
+static inline void *
+_startup_sbrk (intptr_t __delta)
+{
+ return __sbrk (__delta);
+}
+
+__attribute__ ((__noreturn__))
+static inline void
+_startup_fatal (const char *__message)
+{
+ __libc_fatal (__message);
+}
diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
index 4080b8c966..cefa1511f6 100644
--- a/sysdeps/unix/sysv/linux/i386/Makefile
+++ b/sysdeps/unix/sysv/linux/i386/Makefile
@@ -31,6 +31,10 @@ sysdep_routines += divdi3
shared-only-routines += divdi3
CPPFLAGS-divdi3.c = -Din_divdi3_c
endif
+ifneq (,$(pic-default))
+sysdep_routines += startup_sbrk
+static-only-routines += startup_sbrk
+endif
endif
ifeq ($(subdir),nptl)
diff --git a/sysdeps/unix/sysv/linux/i386/startup.h b/sysdeps/unix/sysv/linux/i386/startup.h
new file mode 100644
index 0000000000..ccfba45153
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/startup.h
@@ -0,0 +1,38 @@
+/* Linux/i386 definitions of functions used by static libc main startup.
+ Copyright (C) 2017 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/>. */
+
+#if defined PIC && !defined SHARED
+# include <abort-instr.h>
+
+/* Can't use "call *%gs:SYSINFO_OFFSET" during statup in static PIE. */
+# define I386_USE_SYSENTER 0
+
+extern void * _startup_sbrk (intptr_t) attribute_hidden;
+
+__attribute__ ((__noreturn__))
+static inline void
+_startup_fatal (const char *__message __attribute__ ((unused)))
+{
+ /* This is only called very early during startup in static PIE.
+ FIXME: How can it be improved? */
+ ABORT_INSTRUCTION;
+ __builtin_unreachable ();
+}
+#else
+# include_next <startup.h>
+#endif
diff --git a/sysdeps/unix/sysv/linux/i386/startup_sbrk.c b/sysdeps/unix/sysv/linux/i386/startup_sbrk.c
new file mode 100644
index 0000000000..8239938ddf
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/startup_sbrk.c
@@ -0,0 +1,67 @@
+/* Linux/i386 definitions of _startup_sbrk.
+ Copyright (C) 2017 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/>. */
+
+#include <unistd.h>
+#include <startup.h>
+#include <errno.h>
+#include <sysdep.h>
+
+/* Defined in brk.c. */
+extern void *__curbrk attribute_hidden;
+
+static int
+startup_brk (void *addr)
+{
+ INTERNAL_SYSCALL_DECL (err);
+ void *newbrk = (void *) INTERNAL_SYSCALL_CALL (brk, err, addr);
+ __curbrk = newbrk;
+ if (newbrk < addr)
+ _startup_fatal (NULL);
+ return 0;
+}
+
+/* Extend the process's data space by INCREMENT. If INCREMENT is negative,
+ shrink data space by - INCREMENT. Return start of new space allocated,
+ or call _startup_fatal for errors. */
+
+void *
+_startup_sbrk (intptr_t increment)
+{
+ void *oldbrk;
+
+ /* 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)
+ if (startup_brk (0) < 0) /* Initialize the break. */
+ _startup_fatal (NULL);
+
+ if (increment == 0)
+ return __curbrk;
+
+ oldbrk = __curbrk;
+ if (increment > 0
+ ? ((uintptr_t) oldbrk + (uintptr_t) increment < (uintptr_t) oldbrk)
+ : ((uintptr_t) oldbrk < (uintptr_t) -increment))
+ _startup_fatal (NULL);
+
+ if (startup_brk (oldbrk + increment) < 0)
+ _startup_fatal (NULL);
+
+ return oldbrk;
+}
--
2.13.3