160 lines · c
1//===------------- Linux AUXV Header --------------------------*- 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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H10#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H11 12#include "hdr/fcntl_macros.h" // For open flags13#include "src/__support/OSUtil/syscall.h"14#include "src/__support/common.h"15#include "src/__support/threads/callonce.h"16 17#include <linux/auxvec.h> // For AT_ macros18#include <linux/mman.h> // For mmap flags19#include <linux/param.h> // For EXEC_PAGESIZE20#include <linux/prctl.h> // For prctl21#include <sys/syscall.h> // For syscall numbers22 23namespace LIBC_NAMESPACE_DECL {24 25namespace auxv {26struct Entry {27 unsigned long type; // Entry type28 unsigned long val; // Integer value29};30 31class Vector {32 LIBC_INLINE_VAR static constexpr Entry END = {AT_NULL, AT_NULL};33 LIBC_INLINE_VAR static const Entry *entries = &END;34 LIBC_INLINE_VAR static CallOnceFlag init_flag = callonce_impl::NOT_CALLED;35 LIBC_INLINE_VAR constexpr static size_t FALLBACK_AUXV_ENTRIES = 64;36 37 LIBC_INLINE static void fallback_initialize_unsync();38 LIBC_INLINE static const Entry *get_entries() {39 if (LIBC_LIKELY(entries != &END))40 return entries;41 callonce(&init_flag, fallback_initialize_unsync);42 return entries;43 }44 45public:46 class Iterator {47 const Entry *current;48 49 public:50 LIBC_INLINE explicit Iterator(const Entry *entry) : current(entry) {}51 LIBC_INLINE Iterator &operator++() {52 ++current;53 return *this;54 }55 LIBC_INLINE const Entry &operator*() const { return *current; }56 LIBC_INLINE bool operator!=(const Iterator &other) const {57 return current->type != other.current->type;58 }59 LIBC_INLINE bool operator==(const Iterator &other) const {60 return current->type == other.current->type;61 }62 };63 using iterator = Iterator;64 LIBC_INLINE static Iterator begin() { return Iterator(get_entries()); }65 LIBC_INLINE static Iterator end() { return Iterator(&END); }66 LIBC_INLINE static void initialize_unsafe(const Entry *auxv);67};68 69// Initializes the auxv entries.70// This function is intended to be called once inside crt0.71LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) {72 init_flag = callonce_impl::FINISH;73 entries = auxv;74}75 76// When CRT0 does not setup the global array, this function is called.77// As its name suggests, this function is not thread-safe and should be78// backed by a callonce guard.79// This initialize routine will do a mmap to allocate a memory region.80// Since auxv tends to live throughout the program lifetime, we do not81// munmap it.82[[gnu::cold]]83LIBC_INLINE void Vector::fallback_initialize_unsync() {84 constexpr size_t AUXV_MMAP_SIZE = FALLBACK_AUXV_ENTRIES * sizeof(Entry);85#ifdef SYS_mmap286 constexpr int MMAP_SYSNO = SYS_mmap2;87#else88 constexpr int MMAP_SYSNO = SYS_mmap;89#endif90 long mmap_ret = syscall_impl<long>(MMAP_SYSNO, nullptr, AUXV_MMAP_SIZE,91 PROT_READ | PROT_WRITE,92 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);93 // We do not proceed if mmap fails.94 if (!linux_utils::is_valid_mmap(mmap_ret))95 return;96 97 // Initialize the auxv array with AT_NULL entries.98 Entry *vector = reinterpret_cast<Entry *>(mmap_ret);99 for (size_t i = 0; i < FALLBACK_AUXV_ENTRIES; ++i) {100 vector[i].type = AT_NULL;101 vector[i].val = AT_NULL;102 }103 size_t avaiable_size = AUXV_MMAP_SIZE - sizeof(Entry);104 105// Attempt 1: use PRCTL to get the auxv.106// We guarantee that the vector is always padded with AT_NULL entries.107#ifdef PR_GET_AUXV108 long prctl_ret = syscall_impl<long>(SYS_prctl, PR_GET_AUXV,109 reinterpret_cast<unsigned long>(vector),110 avaiable_size, 0, 0);111 if (prctl_ret >= 0) {112 entries = vector;113 return;114 }115#endif116 117 // Attempt 2: read /proc/self/auxv.118#ifdef SYS_openat119 int fd = syscall_impl<int>(SYS_openat, AT_FDCWD, "/proc/self/auxv",120 O_RDONLY | O_CLOEXEC);121#else122 int fd = syscall_impl<int>(SYS_open, "/proc/self/auxv", O_RDONLY | O_CLOEXEC);123#endif124 if (fd < 0) {125 syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE);126 return;127 }128 uint8_t *cursor = reinterpret_cast<uint8_t *>(vector);129 bool has_error = false;130 while (avaiable_size != 0) {131 long bytes_read = syscall_impl<long>(SYS_read, fd, cursor, avaiable_size);132 if (bytes_read <= 0) {133 if (bytes_read == -EINTR)134 continue;135 has_error = bytes_read < 0;136 break;137 }138 avaiable_size -= bytes_read;139 cursor += bytes_read;140 }141 syscall_impl<long>(SYS_close, fd);142 if (has_error) {143 syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE);144 return;145 }146 entries = vector;147}148 149LIBC_INLINE cpp::optional<unsigned long> get(unsigned long type) {150 Vector auxvec;151 for (const auto &entry : auxvec)152 if (entry.type == type)153 return entry.val;154 return cpp::nullopt;155}156} // namespace auxv157} // namespace LIBC_NAMESPACE_DECL158 159#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H160