Back to index

glibc  2.9
dl-sysdep.c
Go to the documentation of this file.
00001 /* Dynamic linker system dependencies for Linux.
00002    Copyright (C) 1995,1997,2001,2004,2005,2006, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 /* Linux needs some special initialization, but otherwise uses
00021    the generic dynamic linker system interface code.  */
00022 
00023 #include <string.h>
00024 #include <fcntl.h>
00025 #include <unistd.h>
00026 #include <sys/utsname.h>
00027 #include <ldsodefs.h>
00028 #include <kernel-features.h>
00029 
00030 #ifdef SHARED
00031 # define DL_SYSDEP_INIT frob_brk ()
00032 
00033 static inline void
00034 frob_brk (void)
00035 {
00036   __brk (0);                /* Initialize the break.  */
00037 
00038 #if ! __ASSUME_BRK_PAGE_ROUNDED
00039   /* If the dynamic linker was executed as a program, then the break may
00040      start immediately after our data segment.  However, dl-minimal.c has
00041      already stolen the remainder of the page for internal allocations.
00042      If we don't adjust the break location recorded by the kernel, the
00043      normal program startup will inquire, find the value at our &_end,
00044      and start allocating its own data there, clobbering dynamic linker
00045      data structures allocated there during startup.
00046 
00047      Later Linux kernels have changed this behavior so that the initial
00048      break value is rounded up to the page boundary before we start.  */
00049 
00050   extern void *__curbrk attribute_hidden;
00051   extern void _end attribute_hidden;
00052   void *const endpage = (void *) 0 + (((__curbrk - (void *) 0)
00053                                    + GLRO(dl_pagesize) - 1)
00054                                   & -GLRO(dl_pagesize));
00055   if (__builtin_expect (__curbrk >= &_end && __curbrk < endpage, 0))
00056     __brk (endpage);
00057 #endif
00058 }
00059 
00060 # include <elf/dl-sysdep.c>
00061 #endif
00062 
00063 
00064 int
00065 attribute_hidden
00066 _dl_discover_osversion (void)
00067 {
00068 #if (defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO) && defined SHARED
00069   if (GLRO(dl_sysinfo_map) != NULL)
00070     {
00071       /* If the kernel-supplied DSO contains a note indicating the kernel's
00072         version, we don't need to call uname or parse any strings.  */
00073 
00074       static const struct
00075       {
00076        ElfW(Nhdr) hdr;
00077        char vendor[8];
00078       } expected_note = { { sizeof "Linux", sizeof (ElfW(Word)), 0 }, "Linux" };
00079       const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr;
00080       const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum;
00081       for (uint_fast16_t i = 0; i < phnum; ++i)
00082        if (phdr[i].p_type == PT_NOTE)
00083          {
00084            const ElfW(Addr) start = (phdr[i].p_vaddr
00085                                   + GLRO(dl_sysinfo_map)->l_addr);
00086            const ElfW(Nhdr) *note = (const void *) start;
00087            while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
00088              {
00089               if (!memcmp (note, &expected_note, sizeof expected_note))
00090                 return *(const ElfW(Word) *) ((const void *) note
00091                                           + sizeof expected_note);
00092 #define ROUND(len) (((len) + sizeof note->n_type - 1) & -sizeof note->n_type)
00093               note = ((const void *) (note + 1)
00094                      + ROUND (note->n_namesz) + ROUND (note->n_descsz));
00095 #undef ROUND
00096              }
00097          }
00098     }
00099 #endif
00100 
00101   char bufmem[64];
00102   char *buf = bufmem;
00103   unsigned int version;
00104   int parts;
00105   char *cp;
00106   struct utsname uts;
00107 
00108   /* Try the uname system call.  */
00109   if (__uname (&uts))
00110     {
00111       /* This was not successful.  Now try reading the /proc filesystem.  */
00112       int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY);
00113       if (fd < 0)
00114        return -1;
00115       ssize_t reslen = __read (fd, bufmem, sizeof (bufmem));
00116       __close (fd);
00117       if (reslen <= 0)
00118        /* This also didn't work.  We give up since we cannot
00119           make sure the library can actually work.  */
00120        return -1;
00121       buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0';
00122     }
00123   else
00124     buf = uts.release;
00125 
00126   /* Now convert it into a number.  The string consists of at most
00127      three parts.  */
00128   version = 0;
00129   parts = 0;
00130   cp = buf;
00131   while ((*cp >= '0') && (*cp <= '9'))
00132     {
00133       unsigned int here = *cp++ - '0';
00134 
00135       while ((*cp >= '0') && (*cp <= '9'))
00136        {
00137          here *= 10;
00138          here += *cp++ - '0';
00139        }
00140 
00141       ++parts;
00142       version <<= 8;
00143       version |= here;
00144 
00145       if (*cp++ != '.' || parts == 3)
00146        /* Another part following?  */
00147        break;
00148     }
00149 
00150   if (parts < 3)
00151     version <<= 8 * (3 - parts);
00152 
00153   return version;
00154 }