about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/dl-osinfo.h
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/dl-osinfo.h')
-rw-r--r--sysdeps/unix/sysv/linux/dl-osinfo.h226
1 files changed, 163 insertions, 63 deletions
diff --git a/sysdeps/unix/sysv/linux/dl-osinfo.h b/sysdeps/unix/sysv/linux/dl-osinfo.h
index abc67fcdcf..3c93218dfc 100644
--- a/sysdeps/unix/sysv/linux/dl-osinfo.h
+++ b/sysdeps/unix/sysv/linux/dl-osinfo.h
@@ -1,5 +1,5 @@
-/* Operating system specific code  for generic dynamic loader functions.
-   Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
+/* Operating system specific code for generic dynamic loader functions.  Linux.
+   Copyright (C) 2000,2001,2002,2004,2005,2006 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
@@ -18,10 +18,14 @@
    02111-1307 USA.  */
 
 #include <string.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <sys/sysctl.h>
 #include <sys/utsname.h>
-#include "kernel-features.h"
+#include <kernel-features.h>
+#include <dl-sysdep.h>
+#include <stdint.h>
+#include <hp-timing.h>
+#include <endian.h>
 
 #ifndef MIN
 # define MIN(a,b) (((a)<(b))?(a):(b))
@@ -39,71 +43,167 @@ dl_fatal (const char *str)
 }
 #endif
 
+static inline int __attribute__ ((always_inline))
+_dl_discover_osversion (void)
+{
+#if (defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO) && defined SHARED
+  if (GLRO(dl_sysinfo_map) != NULL)
+    {
+      /* If the kernel-supplied DSO contains a note indicating the kernel's
+	 version, we don't need to call uname or parse any strings.  */
+
+      static const struct
+      {
+	ElfW(Word) vendorlen;
+	ElfW(Word) datalen;
+	ElfW(Word) type;
+	char vendor[8];
+      } expected_note = { sizeof "Linux", sizeof (ElfW(Word)), 0, "Linux" };
+      const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr;
+      const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum;
+      for (uint_fast16_t i = 0; i < phnum; ++i)
+	if (phdr[i].p_type == PT_NOTE)
+	  {
+	    const ElfW(Addr) start = (phdr[i].p_vaddr
+				      + GLRO(dl_sysinfo_map)->l_addr);
+	    const struct
+	    {
+	      ElfW(Word) vendorlen;
+	      ElfW(Word) datalen;
+	      ElfW(Word) type;
+	    } *note = (const void *) start;
+	    while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
+	      {
+		if (!memcmp (note, &expected_note, sizeof expected_note))
+		  return *(const ElfW(Word) *) ((const void *) note
+						+ sizeof expected_note);
+#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
+		note = ((const void *) (note + 1)
+			+ ROUND (note->vendorlen) + ROUND (note->datalen));
+	      }
+	  }
+    }
+#endif
+
+  char bufmem[64];
+  char *buf = bufmem;
+  unsigned int version;
+  int parts;
+  char *cp;
+  struct utsname uts;
+
+  /* Try the uname system call.  */
+  if (__uname (&uts))
+    {
+      /* This was not successful.  Now try reading the /proc filesystem.  */
+      int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY);
+      if (fd < 0)
+	return -1;
+      ssize_t reslen = __read (fd, bufmem, sizeof (bufmem));
+      __close (fd);
+      if (reslen <= 0)
+	/* This also didn't work.  We give up since we cannot
+	   make sure the library can actually work.  */
+	return -1;
+      buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0';
+    }
+  else
+    buf = uts.release;
+
+  /* Now convert it into a number.  The string consists of at most
+     three parts.  */
+  version = 0;
+  parts = 0;
+  cp = buf;
+  while ((*cp >= '0') && (*cp <= '9'))
+    {
+      unsigned int here = *cp++ - '0';
+
+      while ((*cp >= '0') && (*cp <= '9'))
+	{
+	  here *= 10;
+	  here += *cp++ - '0';
+	}
 
-#define DL_SYSDEP_OSCHECK(FATAL) \
+      ++parts;
+      version <<= 8;
+      version |= here;
+
+      if (*cp++ != '.')
+	/* Another part following?  */
+	break;
+    }
+
+  if (parts < 3)
+    version <<= 8 * (3 - parts);
+
+  return version;
+}
+
+#define DL_SYSDEP_OSCHECK(FATAL)					      \
   do {									      \
-    /* Test whether the kernel is new enough.  This test is only	      \
-       performed if the library is not compiled to run on all		      \
-       kernels.  */							      \
-    if (__LINUX_KERNEL_VERSION > 0)					      \
-      {									      \
-	char bufmem[64];						      \
-	char *buf = bufmem;						      \
-	unsigned int version;						      \
-	int parts;							      \
-	char *cp;							      \
-	struct utsname uts;						      \
-									      \
-	/* Try the uname syscall */					      \
-	if (__uname (&uts))					      	      \
-	  {							      	      \
-	    /* This was not successful.  Now try reading the /proc	      \
-	       filesystem.  */						      \
-	    ssize_t reslen;						      \
-	    int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY);	      \
-	    if (fd == -1						      \
-		|| (reslen = __read (fd, bufmem, sizeof (bufmem))) <= 0)      \
-  	      /* This also didn't work.  We give up since we cannot	      \
-		 make sure the library can actually work.  */		      \
-	      FATAL ("FATAL: cannot determine kernel version\n");	      \
-	    __close (fd);						      \
-	    buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0';	      \
-	  }								      \
-	else								      \
-          buf = uts.release;						      \
+    /* Test whether the kernel is new enough.  This test is only performed    \
+       if the library is not compiled to run on all kernels.  */	      \
 									      \
-	/* Now convert it into a number.  The string consists of at most      \
-	   three parts.  */						      \
-	version = 0;							      \
-	parts = 0;							      \
-	cp = buf;							      \
-	while ((*cp >= '0') && (*cp <= '9'))				      \
-	  {								      \
-	    unsigned int here = *cp++ - '0';				      \
-									      \
-	    while ((*cp >= '0') && (*cp <= '9'))			      \
-	      {								      \
-		here *= 10;						      \
-		here += *cp++ - '0';					      \
-	      }								      \
-									      \
-	    ++parts;							      \
-	    version <<= 8;						      \
-	    version |= here;						      \
-									      \
-	    if (*cp++ != '.')						      \
-	      /* Another part following?  */				      \
-	      break;							      \
-	  }								      \
-									      \
-	if (parts < 3)							      \
-	  version <<= 8 * (3 - parts);					      \
+    int version = _dl_discover_osversion ();				      \
+    if (__builtin_expect (version >= 0, 1))				      \
+      {									      \
+	if (__builtin_expect (GLRO(dl_osversion) == 0, 1)		      \
+	    || GLRO(dl_osversion) > version)				      \
+	  GLRO(dl_osversion) = version;					      \
 									      \
 	/* Now we can test with the required version.  */		      \
-	if (version < __LINUX_KERNEL_VERSION)				      \
+	if (__LINUX_KERNEL_VERSION > 0 && version < __LINUX_KERNEL_VERSION)   \
 	  /* Not sufficent.  */						      \
 	  FATAL ("FATAL: kernel too old\n");				      \
-									      \
-	GLRO(dl_osversion) = version;					      \
       }									      \
+    else if (__LINUX_KERNEL_VERSION > 0)				      \
+      FATAL ("FATAL: cannot determine kernel version\n");		      \
   } while (0)
+
+static inline uintptr_t __attribute__ ((always_inline))
+_dl_setup_stack_chk_guard (void)
+{
+  uintptr_t ret;
+#ifdef ENABLE_STACKGUARD_RANDOMIZE
+  int fd = __open ("/dev/urandom", O_RDONLY);
+  if (fd >= 0)
+    {
+      ssize_t reslen = __read (fd, &ret, sizeof (ret));
+      __close (fd);
+      if (reslen == (ssize_t) sizeof (ret))
+	return ret;
+    }
+#endif
+  ret = 0;
+  unsigned char *p = (unsigned char *) &ret;
+  p[sizeof (ret) - 1] = 255;
+  p[sizeof (ret) - 2] = '\n';
+#ifdef HP_TIMING_NOW
+  hp_timing_t hpt;
+  HP_TIMING_NOW (hpt);
+  hpt = (hpt & 0xffff) << 8;
+  ret ^= hpt;
+#endif
+  uintptr_t stk;
+  /* Avoid GCC being too smart.  */
+  asm ("" : "=r" (stk) : "r" (p));
+  stk &= 0x7ffff0;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+  stk <<= (__WORDSIZE - 23);
+#elif __WORDSIZE == 64
+  stk <<= 31;
+#endif
+  ret ^= stk;
+  /* Avoid GCC being too smart.  */
+  p = (unsigned char *) &errno;
+  asm ("" : "=r" (stk) : "r" (p));
+  stk &= 0x7fff00;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+  stk <<= (__WORDSIZE - 29);
+#else
+  stk >>= 8;
+#endif
+  ret ^= stk;
+  return ret;
+}