#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H
#include "hdr/fcntl_macros.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/threads/callonce.h"
#include <linux/auxvec.h>
#include <linux/mman.h>
#include <linux/param.h>
#include <linux/prctl.h>
#include <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
namespace auxv {
struct Entry {
unsigned long type;
unsigned long val;
};
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);
};
LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) {
init_flag = callonce_impl::FINISH;
entries = auxv;
}
[[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);
if (!linux_utils::is_valid_mmap(mmap_ret))
return;
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);
#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
#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;
}
}
}
#endif