1 //===-- Implementation file for getauxval function --------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "src/sys/auxv/getauxval.h" 10 #include "config/app.h" 11 #include "src/__support/common.h" 12 #include "src/__support/macros/config.h" 13 #include "src/errno/libc_errno.h" 14 #include <linux/auxvec.h> 15 16 // for guarded initialization 17 #include "src/__support/threads/callonce.h" 18 #include "src/__support/threads/linux/futex_word.h" 19 20 // for mallocing the global auxv 21 #include "src/sys/mman/mmap.h" 22 #include "src/sys/mman/munmap.h" 23 24 // for reading /proc/self/auxv 25 #include "src/fcntl/open.h" 26 #include "src/sys/prctl/prctl.h" 27 #include "src/unistd/close.h" 28 #include "src/unistd/read.h" 29 30 // getauxval will work either with or without __cxa_atexit support. 31 // In order to detect if __cxa_atexit is supported, we define a weak symbol. 32 // We prefer __cxa_atexit as it is always defined as a C symbol whileas atexit 33 // may not be created via objcopy yet. Also, for glibc, atexit is provided via 34 // libc_nonshared.a rather than libc.so. So, it is may not be made ready for 35 // overlay builds. 36 extern "C" [[gnu::weak]] int __cxa_atexit(void (*callback)(void *), 37 void *payload, void *); 38 39 namespace LIBC_NAMESPACE_DECL { 40 41 constexpr static size_t MAX_AUXV_ENTRIES = 64; 42 43 // Helper to recover or set errno 44 class AuxvErrnoGuard { 45 public: 46 AuxvErrnoGuard() : saved(libc_errno), failure(false) {} 47 ~AuxvErrnoGuard() { libc_errno = failure ? ENOENT : saved; } 48 void mark_failure() { failure = true; } 49 50 private: 51 int saved; 52 bool failure; 53 }; 54 55 // Helper to manage the memory 56 static AuxEntry *auxv = nullptr; 57 58 class AuxvMMapGuard { 59 public: 60 constexpr static size_t AUXV_MMAP_SIZE = sizeof(AuxEntry) * MAX_AUXV_ENTRIES; 61 62 AuxvMMapGuard() 63 : ptr(mmap(nullptr, AUXV_MMAP_SIZE, PROT_READ | PROT_WRITE, 64 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) {} 65 ~AuxvMMapGuard() { 66 if (ptr != MAP_FAILED) 67 munmap(ptr, AUXV_MMAP_SIZE); 68 } 69 void submit_to_global() { 70 // atexit may fail, we do not set it to global in that case. 71 int ret = __cxa_atexit( 72 [](void *) { 73 munmap(auxv, AUXV_MMAP_SIZE); 74 auxv = nullptr; 75 }, 76 nullptr, nullptr); 77 78 if (ret != 0) 79 return; 80 81 auxv = reinterpret_cast<AuxEntry *>(ptr); 82 ptr = MAP_FAILED; 83 } 84 bool allocated() const { return ptr != MAP_FAILED; } 85 void *get() const { return ptr; } 86 87 private: 88 void *ptr; 89 }; 90 91 class AuxvFdGuard { 92 public: 93 AuxvFdGuard() : fd(open("/proc/self/auxv", O_RDONLY | O_CLOEXEC)) {} 94 ~AuxvFdGuard() { 95 if (fd != -1) 96 close(fd); 97 } 98 bool valid() const { return fd != -1; } 99 int get() const { return fd; } 100 101 private: 102 int fd; 103 }; 104 105 static void initialize_auxv_once(void) { 106 // If we cannot get atexit, we cannot register the cleanup function. 107 if (&__cxa_atexit == nullptr) 108 return; 109 110 AuxvMMapGuard mmap_guard; 111 if (!mmap_guard.allocated()) 112 return; 113 auto *ptr = reinterpret_cast<AuxEntry *>(mmap_guard.get()); 114 115 // We get one less than the max size to make sure the search always 116 // terminates. MMAP private pages are zeroed out already. 117 size_t available_size = AuxvMMapGuard::AUXV_MMAP_SIZE - sizeof(AuxEntryType); 118 // PR_GET_AUXV is only available on Linux kernel 6.1 and above. If this is not 119 // defined, we direcly fall back to reading /proc/self/auxv. In case the libc 120 // is compiled and run on separate kernels, we also check the return value of 121 // prctl. 122 #ifdef PR_GET_AUXV 123 int ret = prctl(PR_GET_AUXV, reinterpret_cast<unsigned long>(ptr), 124 available_size, 0, 0); 125 if (ret >= 0) { 126 mmap_guard.submit_to_global(); 127 return; 128 } 129 #endif 130 AuxvFdGuard fd_guard; 131 if (!fd_guard.valid()) 132 return; 133 auto *buf = reinterpret_cast<char *>(ptr); 134 libc_errno = 0; 135 bool error_detected = false; 136 // Read until we use up all the available space or we finish reading the file. 137 while (available_size != 0) { 138 ssize_t bytes_read = read(fd_guard.get(), buf, available_size); 139 if (bytes_read <= 0) { 140 if (libc_errno == EINTR) 141 continue; 142 // Now, we either have an non-recoverable error or we have reached the end 143 // of the file. Mark `error_detected` accordingly. 144 if (bytes_read == -1) 145 error_detected = true; 146 break; 147 } 148 buf += bytes_read; 149 available_size -= bytes_read; 150 } 151 // If we get out of the loop without an error, the auxv is ready. 152 if (!error_detected) 153 mmap_guard.submit_to_global(); 154 } 155 156 static AuxEntry read_entry(int fd) { 157 AuxEntry buf; 158 size_t size = sizeof(AuxEntry); 159 char *ptr = reinterpret_cast<char *>(&buf); 160 while (size > 0) { 161 ssize_t ret = read(fd, ptr, size); 162 if (ret < 0) { 163 if (libc_errno == EINTR) 164 continue; 165 // Error detected, return AT_NULL 166 buf.id = AT_NULL; 167 buf.value = AT_NULL; 168 break; 169 } 170 ptr += ret; 171 size -= ret; 172 } 173 return buf; 174 } 175 176 LLVM_LIBC_FUNCTION(unsigned long, getauxval, (unsigned long id)) { 177 // Fast path when libc is loaded by its own initialization code. In this case, 178 // app.auxv_ptr is already set to the auxv passed on the initial stack of the 179 // process. 180 AuxvErrnoGuard errno_guard; 181 182 auto search_auxv = [&errno_guard](AuxEntry *auxv, 183 unsigned long id) -> AuxEntryType { 184 for (auto *ptr = auxv; ptr->id != AT_NULL; ptr++) 185 if (ptr->id == id) 186 return ptr->value; 187 188 errno_guard.mark_failure(); 189 return AT_NULL; 190 }; 191 192 // App is a weak symbol that is only defined if libc is linked to its own 193 // initialization routine. We need to check if it is null. 194 if (&app != nullptr) 195 return search_auxv(app.auxv_ptr, id); 196 197 static FutexWordType once_flag; 198 callonce(reinterpret_cast<CallOnceFlag *>(&once_flag), initialize_auxv_once); 199 if (auxv != nullptr) 200 return search_auxv(auxv, id); 201 202 // Fallback to use read without mmap 203 AuxvFdGuard fd_guard; 204 if (fd_guard.valid()) { 205 while (true) { 206 AuxEntry buf = read_entry(fd_guard.get()); 207 if (buf.id == AT_NULL) 208 break; 209 if (buf.id == id) 210 return buf.value; 211 } 212 } 213 214 // cannot find the entry after all methods, mark failure and return 0 215 errno_guard.mark_failure(); 216 return AT_NULL; 217 } 218 } // namespace LIBC_NAMESPACE_DECL 219