| 1 | /* Dynamic linker system dependencies for Linux. |
| 2 | Copyright (C) 1995-2016 Free Software Foundation, Inc. |
| 3 | This file is part of the GNU C Library. |
| 4 | |
| 5 | The GNU C Library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2.1 of the License, or (at your option) any later version. |
| 9 | |
| 10 | The GNU C Library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Lesser General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Lesser General Public |
| 16 | License along with the GNU C Library; if not, see |
| 17 | <http://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | /* Linux needs some special initialization, but otherwise uses |
| 20 | the generic dynamic linker system interface code. */ |
| 21 | |
| 22 | #include <string.h> |
| 23 | #include <fcntl.h> |
| 24 | #include <unistd.h> |
| 25 | #include <sys/param.h> |
| 26 | #include <sys/utsname.h> |
| 27 | #include <ldsodefs.h> |
| 28 | |
| 29 | #ifdef SHARED |
| 30 | # define DL_SYSDEP_INIT frob_brk () |
| 31 | |
| 32 | static inline void |
| 33 | frob_brk (void) |
| 34 | { |
| 35 | __brk (0); /* Initialize the break. */ |
| 36 | } |
| 37 | |
| 38 | # include <elf/dl-sysdep.c> |
| 39 | #endif |
| 40 | |
| 41 | |
| 42 | int |
| 43 | attribute_hidden |
| 44 | _dl_discover_osversion (void) |
| 45 | { |
| 46 | #if defined NEED_DL_SYSINFO_DSO && defined SHARED |
| 47 | if (GLRO(dl_sysinfo_map) != NULL) |
| 48 | { |
| 49 | /* If the kernel-supplied DSO contains a note indicating the kernel's |
| 50 | version, we don't need to call uname or parse any strings. */ |
| 51 | |
| 52 | static const struct |
| 53 | { |
| 54 | ElfW(Nhdr) hdr; |
| 55 | char vendor[8]; |
| 56 | } expected_note = { { sizeof "Linux" , sizeof (ElfW(Word)), 0 }, "Linux" }; |
| 57 | const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr; |
| 58 | const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum; |
| 59 | for (uint_fast16_t i = 0; i < phnum; ++i) |
| 60 | if (phdr[i].p_type == PT_NOTE) |
| 61 | { |
| 62 | const ElfW(Addr) start = (phdr[i].p_vaddr |
| 63 | + GLRO(dl_sysinfo_map)->l_addr); |
| 64 | const ElfW(Nhdr) *note = (const void *) start; |
| 65 | while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz) |
| 66 | { |
| 67 | if (!memcmp (note, &expected_note, sizeof expected_note)) |
| 68 | return *(const ElfW(Word) *) ((const void *) note |
| 69 | + sizeof expected_note); |
| 70 | #define ROUND(len) (((len) + sizeof note->n_type - 1) & -sizeof note->n_type) |
| 71 | note = ((const void *) (note + 1) |
| 72 | + ROUND (note->n_namesz) + ROUND (note->n_descsz)); |
| 73 | #undef ROUND |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | #endif |
| 78 | |
| 79 | char bufmem[64]; |
| 80 | char *buf = bufmem; |
| 81 | unsigned int version; |
| 82 | int parts; |
| 83 | char *cp; |
| 84 | struct utsname uts; |
| 85 | |
| 86 | /* Try the uname system call. */ |
| 87 | if (__uname (&uts)) |
| 88 | { |
| 89 | /* This was not successful. Now try reading the /proc filesystem. */ |
| 90 | int fd = __open ("/proc/sys/kernel/osrelease" , O_RDONLY); |
| 91 | if (fd < 0) |
| 92 | return -1; |
| 93 | ssize_t reslen = __read (fd, bufmem, sizeof (bufmem)); |
| 94 | __close (fd); |
| 95 | if (reslen <= 0) |
| 96 | /* This also didn't work. We give up since we cannot |
| 97 | make sure the library can actually work. */ |
| 98 | return -1; |
| 99 | buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0'; |
| 100 | } |
| 101 | else |
| 102 | buf = uts.release; |
| 103 | |
| 104 | /* Now convert it into a number. The string consists of at most |
| 105 | three parts. */ |
| 106 | version = 0; |
| 107 | parts = 0; |
| 108 | cp = buf; |
| 109 | while ((*cp >= '0') && (*cp <= '9')) |
| 110 | { |
| 111 | unsigned int here = *cp++ - '0'; |
| 112 | |
| 113 | while ((*cp >= '0') && (*cp <= '9')) |
| 114 | { |
| 115 | here *= 10; |
| 116 | here += *cp++ - '0'; |
| 117 | } |
| 118 | |
| 119 | ++parts; |
| 120 | version <<= 8; |
| 121 | version |= here; |
| 122 | |
| 123 | if (*cp++ != '.' || parts == 3) |
| 124 | /* Another part following? */ |
| 125 | break; |
| 126 | } |
| 127 | |
| 128 | if (parts < 3) |
| 129 | version <<= 8 * (3 - parts); |
| 130 | |
| 131 | return version; |
| 132 | } |
| 133 | |