| /* Dynamic linker system dependencies for Linux. |
| Copyright (C) 1995-2014 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/>. */ |
| |
| /* Linux needs some special initialization, but otherwise uses |
| the generic dynamic linker system interface code. */ |
| |
| #include <assert.h> |
| #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. */ |
| } |
| |
| # include <elf/dl-sysdep.c> |
| #endif |
| |
| |
| int |
| attribute_hidden |
| _dl_discover_osversion (void) |
| { |
| #if 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; |
| } |
| |
| /* Mask every signal, returning the previous sigmask in OLD. */ |
| void |
| internal_function |
| _dl_mask_all_signals (sigset_t *old) |
| { |
| int ret; |
| sigset_t new; |
| |
| sigfillset (&new); |
| |
| /* This function serves as a replacement to pthread_sigmask, which |
| isn't available from within the dynamic linker since it would require |
| linking with libpthread. We duplicate some of the functionality here |
| to avoid requiring libpthread. This isn't quite identical to |
| pthread_sigmask in that we do not mask internal signals used for |
| cancellation and setxid handling. This disables asynchronous |
| cancellation for the duration the signals are disabled, but it's a |
| small window, and prevents any problems with the use of TLS variables |
| in the signal handlers that would have executed. */ |
| |
| /* It's very important we don't touch errno here, as that's TLS; since this |
| gets called from get_tls_addr we might end up recursing. */ |
| |
| INTERNAL_SYSCALL_DECL (err); |
| |
| ret = INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &new, old, |
| _NSIG / 8); |
| |
| assert (ret == 0); |
| } |
| |
| /* Return sigmask to what it was before a call to _dl_mask_all_signals. */ |
| void |
| internal_function |
| _dl_unmask_signals (sigset_t *old) |
| { |
| int ret; |
| INTERNAL_SYSCALL_DECL (err); |
| |
| ret = INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, old, NULL, |
| _NSIG / 8); |
| |
| assert (ret == 0); |
| } |