| /* Dynamic linker system dependencies for Linux. |
| Copyright (C) 1995,1997,2001,2004,2005,2006, 2008 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, write to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| /* Linux needs some special initialization, but otherwise uses |
| the generic dynamic linker system interface code. */ |
| |
| #include <string.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/utsname.h> |
| #include <ldsodefs.h> |
| #include <kernel-features.h> |
| |
| #ifdef SHARED |
| # define DL_SYSDEP_INIT frob_brk () |
| |
| static inline void |
| frob_brk (void) |
| { |
| __brk (0); /* Initialize the break. */ |
| |
| #if ! __ASSUME_BRK_PAGE_ROUNDED |
| /* If the dynamic linker was executed as a program, then the break may |
| start immediately after our data segment. However, dl-minimal.c has |
| already stolen the remainder of the page for internal allocations. |
| If we don't adjust the break location recorded by the kernel, the |
| normal program startup will inquire, find the value at our &_end, |
| and start allocating its own data there, clobbering dynamic linker |
| data structures allocated there during startup. |
| |
| Later Linux kernels have changed this behavior so that the initial |
| break value is rounded up to the page boundary before we start. */ |
| |
| extern char *__curbrk attribute_hidden; |
| extern char _end[] attribute_hidden; |
| char *const endpage = (void *) 0 + (((__curbrk - (char *) 0) |
| + GLRO(dl_pagesize) - 1) |
| & -GLRO(dl_pagesize)); |
| if (__builtin_expect (__curbrk >= _end && __curbrk < endpage, 0)) |
| __brk (endpage); |
| #endif |
| } |
| |
| # include <elf/dl-sysdep.c> |
| #endif |
| |
| |
| int |
| attribute_hidden |
| _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(Nhdr) hdr; |
| 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 ElfW(Nhdr) *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 note->n_type - 1) & -sizeof note->n_type) |
| note = ((const void *) (note + 1) |
| + ROUND (note->n_namesz) + ROUND (note->n_descsz)); |
| #undef ROUND |
| } |
| } |
| } |
| #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'; |
| } |
| |
| ++parts; |
| version <<= 8; |
| version |= here; |
| |
| if (*cp++ != '.' || parts == 3) |
| /* Another part following? */ |
| break; |
| } |
| |
| if (parts < 3) |
| version <<= 8 * (3 - parts); |
| |
| return version; |
| } |