brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.2 KiB · 9c07c54 Raw
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