| //===------------- Linux AUXV Header --------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H |
| #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H |
| |
| #include "hdr/fcntl_macros.h" // For open flags |
| #include "src/__support/OSUtil/syscall.h" |
| #include "src/__support/common.h" |
| #include "src/__support/threads/callonce.h" |
| |
| #include <linux/auxvec.h> // For AT_ macros |
| #include <linux/mman.h> // For mmap flags |
| #include <linux/param.h> // For EXEC_PAGESIZE |
| #include <linux/prctl.h> // For prctl |
| #include <sys/syscall.h> // For syscall numbers |
| |
| namespace LIBC_NAMESPACE_DECL { |
| |
| namespace auxv { |
| struct Entry { |
| unsigned long type; // Entry type |
| unsigned long val; // Integer value |
| }; |
| |
| class Vector { |
| LIBC_INLINE_VAR static constexpr Entry END = {AT_NULL, AT_NULL}; |
| LIBC_INLINE_VAR static const Entry *entries = &END; |
| LIBC_INLINE_VAR static CallOnceFlag init_flag = callonce_impl::NOT_CALLED; |
| LIBC_INLINE_VAR constexpr static size_t FALLBACK_AUXV_ENTRIES = 64; |
| |
| LIBC_INLINE static void fallback_initialize_unsync(); |
| LIBC_INLINE static const Entry *get_entries() { |
| if (LIBC_LIKELY(entries != &END)) |
| return entries; |
| callonce(&init_flag, fallback_initialize_unsync); |
| return entries; |
| } |
| |
| public: |
| class Iterator { |
| const Entry *current; |
| |
| public: |
| LIBC_INLINE explicit Iterator(const Entry *entry) : current(entry) {} |
| LIBC_INLINE Iterator &operator++() { |
| ++current; |
| return *this; |
| } |
| LIBC_INLINE const Entry &operator*() const { return *current; } |
| LIBC_INLINE bool operator!=(const Iterator &other) const { |
| return current->type != other.current->type; |
| } |
| LIBC_INLINE bool operator==(const Iterator &other) const { |
| return current->type == other.current->type; |
| } |
| }; |
| using iterator = Iterator; |
| LIBC_INLINE static Iterator begin() { return Iterator(get_entries()); } |
| LIBC_INLINE static Iterator end() { return Iterator(&END); } |
| LIBC_INLINE static void initialize_unsafe(const Entry *auxv); |
| }; |
| |
| // Initializes the auxv entries. |
| // This function is intended to be called once inside crt0. |
| LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) { |
| init_flag = callonce_impl::FINISH; |
| entries = auxv; |
| } |
| |
| // When CRT0 does not setup the global array, this function is called. |
| // As its name suggests, this function is not thread-safe and should be |
| // backed by a callonce guard. |
| // This initialize routine will do a mmap to allocate a memory region. |
| // Since auxv tends to live throughout the program lifetime, we do not |
| // munmap it. |
| [[gnu::cold]] |
| LIBC_INLINE void Vector::fallback_initialize_unsync() { |
| constexpr size_t AUXV_MMAP_SIZE = FALLBACK_AUXV_ENTRIES * sizeof(Entry); |
| #ifdef SYS_mmap2 |
| constexpr int MMAP_SYSNO = SYS_mmap2; |
| #else |
| constexpr int MMAP_SYSNO = SYS_mmap; |
| #endif |
| long mmap_ret = syscall_impl<long>(MMAP_SYSNO, nullptr, AUXV_MMAP_SIZE, |
| PROT_READ | PROT_WRITE, |
| MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| // We do not proceed if mmap fails. |
| if (!linux_utils::is_valid_mmap(mmap_ret)) |
| return; |
| |
| // Initialize the auxv array with AT_NULL entries. |
| Entry *vector = reinterpret_cast<Entry *>(mmap_ret); |
| for (size_t i = 0; i < FALLBACK_AUXV_ENTRIES; ++i) { |
| vector[i].type = AT_NULL; |
| vector[i].val = AT_NULL; |
| } |
| size_t avaiable_size = AUXV_MMAP_SIZE - sizeof(Entry); |
| |
| // Attempt 1: use PRCTL to get the auxv. |
| // We guarantee that the vector is always padded with AT_NULL entries. |
| #ifdef PR_GET_AUXV |
| long prctl_ret = syscall_impl<long>(SYS_prctl, PR_GET_AUXV, |
| reinterpret_cast<unsigned long>(vector), |
| avaiable_size, 0, 0); |
| if (prctl_ret >= 0) { |
| entries = vector; |
| return; |
| } |
| #endif |
| |
| // Attempt 2: read /proc/self/auxv. |
| #ifdef SYS_openat |
| int fd = syscall_impl<int>(SYS_openat, AT_FDCWD, "/proc/self/auxv", |
| O_RDONLY | O_CLOEXEC); |
| #else |
| int fd = syscall_impl<int>(SYS_open, "/proc/self/auxv", O_RDONLY | O_CLOEXEC); |
| #endif |
| if (fd < 0) { |
| syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE); |
| return; |
| } |
| uint8_t *cursor = reinterpret_cast<uint8_t *>(vector); |
| bool has_error = false; |
| while (avaiable_size != 0) { |
| long bytes_read = syscall_impl<long>(SYS_read, fd, cursor, avaiable_size); |
| if (bytes_read <= 0) { |
| if (bytes_read == -EINTR) |
| continue; |
| has_error = bytes_read < 0; |
| break; |
| } |
| avaiable_size -= bytes_read; |
| cursor += bytes_read; |
| } |
| syscall_impl<long>(SYS_close, fd); |
| if (has_error) { |
| syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE); |
| return; |
| } |
| entries = vector; |
| } |
| |
| LIBC_INLINE cpp::optional<unsigned long> get(unsigned long type) { |
| Vector auxvec; |
| for (const auto &entry : auxvec) |
| if (entry.type == type) |
| return entry.val; |
| return cpp::nullopt; |
| } |
| } // namespace auxv |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H |