Skip to content

[libc][WIP] try to make sysconf work #74166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.wait.wait4
libc.src.sys.wait.waitpid

# sys/auxv.h entrypoints
libc.src.sys.auxv.getauxval

# sys/prctl.h entrypoints
libc.src.sys.prctl.prctl

# termios.h entrypoints
libc.src.termios.cfgetispeed
libc.src.termios.cfgetospeed
Expand Down
33 changes: 23 additions & 10 deletions libc/config/linux/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,32 @@
#ifndef LLVM_LIBC_CONFIG_LINUX_APP_H
#define LLVM_LIBC_CONFIG_LINUX_APP_H

#include "src/__support/macros/attributes.h"
#include "src/__support/macros/properties/architectures.h"

#include <linux/auxvec.h>
#include <stdint.h>

namespace LIBC_NAMESPACE {

// Data structure to capture properties of the linux/ELF TLS image.
struct TLSImage {
// The load address of the TLS.
uintptr_t address;
uintptr_t address = 0;

// The byte size of the TLS image consisting of both initialized and
// uninitialized memory. In ELF executables, it is size of .tdata + size of
// .tbss. Put in another way, it is the memsz field of the PT_TLS header.
uintptr_t size;
uintptr_t size = 0;

// The byte size of initialized memory in the TLS image. In ELF exectubles,
// this is the size of .tdata. Put in another way, it is the filesz of the
// PT_TLS header.
uintptr_t init_size;
uintptr_t init_size = 0;

// The alignment of the TLS layout. It assumed that the alignment
// value is a power of 2.
uintptr_t align;
uintptr_t align = 0;
};

#if defined(LIBC_TARGET_ARCH_IS_X86_64) || \
Expand All @@ -49,11 +51,19 @@ typedef uintptr_t ArgcType;
typedef uintptr_t ArgVEntryType;

typedef uintptr_t EnvironType;
typedef uintptr_t AuxEntryType;
#else
#error "argc and argv types are not defined for the target platform."
#endif

// According the manpage associated to /proc/<pid>/auxv:
// ... The format is one unsigned long ID plus one unsigned long
// value for each entry ...
// https://man7.org/linux/man-pages/man5/proc.5.html
struct AuxEntryType {
unsigned long id;
unsigned long value;
};

struct Args {
ArgcType argc;

Expand All @@ -69,18 +79,21 @@ struct Args {
// Data structure which captures properties of a linux application.
struct AppProperties {
// Page size used for the application.
uintptr_t pageSize;
uintptr_t page_size = 0;

Args *args;
Args *args = nullptr;

// The properties of an application's TLS image.
TLSImage tls;
TLSImage tls{};

// Environment data.
EnvironType *envPtr;
EnvironType *env_ptr = nullptr;

// Auxiliary vector data.
const AuxEntryType *auxv_ptr = nullptr;
};

extern AppProperties app;
LIBC_INLINE_VAR AppProperties app{};

// The descriptor of a thread's TLS area.
struct TLSDescriptor {
Expand Down
6 changes: 6 additions & 0 deletions libc/config/linux/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ set(TARGET_LIBC_ENTRYPOINTS
# sys/mman.h entrypoints
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap

# sys/auxv.h entrypoints
libc.src.sys.auxv.getauxval

# sys/prctl.h entrypoints
libc.src.sys.prctl.prctl
)

set(TARGET_LIBM_ENTRYPOINTS
Expand Down
6 changes: 6 additions & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.wait.wait4
libc.src.sys.wait.waitpid

# sys/auxv.h entrypoints
libc.src.sys.auxv.getauxval

# sys/prctl.h entrypoints
libc.src.sys.prctl.prctl

# termios.h entrypoints
libc.src.termios.cfgetispeed
libc.src.termios.cfgetospeed
Expand Down
6 changes: 6 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.wait.wait4
libc.src.sys.wait.waitpid

# sys/auxv.h entrypoints
libc.src.sys.auxv.getauxval

# sys/prctl.h entrypoints
libc.src.sys.prctl.prctl

# termios.h entrypoints
libc.src.termios.cfgetispeed
libc.src.termios.cfgetospeed
Expand Down
2 changes: 1 addition & 1 deletion libc/src/stdlib/getenv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(char *, getenv, (const char *name)) {
char **env_ptr = reinterpret_cast<char **>(LIBC_NAMESPACE::app.envPtr);
char **env_ptr = reinterpret_cast<char **>(LIBC_NAMESPACE::app.env_ptr);

if (name == nullptr || env_ptr == nullptr)
return nullptr;
Expand Down
2 changes: 2 additions & 0 deletions libc/src/sys/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ add_subdirectory(sendfile)
add_subdirectory(stat)
add_subdirectory(utsname)
add_subdirectory(wait)
add_subdirectory(auxv)
add_subdirectory(prctl)
10 changes: 10 additions & 0 deletions libc/src/sys/auxv/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()

add_entrypoint_object(
getauxval
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.getauxval
)
20 changes: 20 additions & 0 deletions libc/src/sys/auxv/getauxval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for getauxval ----------------------*- 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_SYS_AUXV_GETAUXVAL_H
#define LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H

#include <sys/auxv.h>

namespace LIBC_NAMESPACE {

unsigned long getauxval(unsigned long id);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
18 changes: 18 additions & 0 deletions libc/src/sys/auxv/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_entrypoint_object(
getauxval
SRCS
getauxval.cpp
HDRS
../getauxval.h
DEPENDS
libc.include.sys_auxv
libc.src.errno.errno
libc.src.fcntl.open
libc.src.unistd.read
libc.src.unistd.close
libc.src.sys.prctl.prctl
libc.src.sys.mman.mmap
libc.src.sys.mman.munmap
libc.config.linux.app_h
libc.src.__support.macros.optimization
)
141 changes: 141 additions & 0 deletions libc/src/sys/auxv/linux/getauxval.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//===---------- Linux implementation of the getauxval function ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "src/sys/auxv/getauxval.h"
#include "config/linux/app.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h"
#include "src/errno/libc_errno.h"
#include "src/fcntl/open.h"
#include "src/sys/mman/mmap.h"
#include "src/sys/mman/munmap.h"
#include "src/sys/prctl/prctl.h"
#include "src/unistd/close.h"
#include "src/unistd/read.h"

#include <linux/auxvec.h> // AT_NULL
#include <sys/mman.h> // MAP_FAILED, MAP_PRIVATE, MAP_ANONYMOUS

namespace LIBC_NAMESPACE {

constexpr size_t AUXV_MMAP_SIZE = sizeof(AuxEntryType) * 64;

struct ErrorNo {
ErrorNo() : errno_backup(libc_errno), failure(false) {}
~ErrorNo() { libc_errno = failure ? ENOENT : errno_backup; }
void mark_failure() { failure = true; }
int errno_backup;
bool failure;
};

struct TempAuxv {
TempAuxv() : ptr(nullptr) {}
~TempAuxv() {
if (ptr != nullptr)
munmap(ptr, AUXV_MMAP_SIZE);
}
AuxEntryType *ptr;
};

struct TempFD {
TempFD(const char *path) : fd(open(path, O_RDONLY | O_CLOEXEC)) {}
~TempFD() {
if (fd >= 0)
close(fd);
}
operator int() const { return fd; }
int fd;
};

static AuxEntryType read_entry(int fd) {
AuxEntryType buf;
ssize_t size = sizeof(AuxEntryType);
while (size > 0) {
ssize_t ret = read(fd, &buf, size);
if (ret < 0) {
if (libc_errno == EINTR)
continue;
buf.id = AT_NULL;
buf.value = AT_NULL;
break;
}
size -= ret;
}
return buf;
}

LLVM_LIBC_FUNCTION(unsigned long, getauxval, (unsigned long id)) {
// if llvm-libc's loader is applied, app.auxv_ptr should have been
// initialized, then we can directly get the auxillary vector
const AuxEntryType *auxv_ptr = app.auxv_ptr;
ErrorNo errno_holder;
TempAuxv temp_auxv;

// Compatible layer for the overlay mode.
// first check if PR_GET_AUXV is supported. Unfortunately, this is not
// supported until Linux 6.0+. We check this syscall first since it may be
// the case that /proc is not mounted.
#ifdef PR_GET_AUXV
do {
if (LIBC_UNLIKELY(auxv_ptr == nullptr)) {
// no need to backup errno: once it has error, we will modify it
void *ptr = mmap(nullptr, AUXV_MMAP_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

if (LIBC_UNLIKELY(ptr == MAP_FAILED))
break;

temp_auxv.ptr = reinterpret_cast<AuxEntryType *>(ptr);
for (size_t i = 0; i < AUXV_MMAP_SIZE / sizeof(AuxEntryType); ++i) {
temp_auxv.ptr[i].id = AT_NULL;
temp_auxv.ptr[i].value = AT_NULL;
}

// We keeps the last entry to be AT_NULL, so we can always terminate the
// loop.
int ret =
prctl(PR_GET_AUXV, reinterpret_cast<unsigned long>(temp_auxv.ptr),
AUXV_MMAP_SIZE - sizeof(AuxEntryType), 0, 0);
if (ret < 0)
break;

auxv_ptr = temp_auxv.ptr;
}
} while (0);
#endif

if (LIBC_LIKELY(auxv_ptr != nullptr)) {
for (; auxv_ptr->id != AT_NULL; ++auxv_ptr) {
if (auxv_ptr->id == id)
return auxv_ptr->value;
}
errno_holder.mark_failure();
return 0;
}

// now attempt to read from /proc/self/auxv
do {
TempFD fd{"/proc/self/auxv"};
if (fd < 0)
break;

while (true) {
AuxEntryType buf = read_entry(fd);
if (buf.id == AT_NULL)
break;
if (buf.id == id)
return buf.value;
}

} while (0);

errno_holder.mark_failure();
return 0;
}

} // namespace LIBC_NAMESPACE
10 changes: 10 additions & 0 deletions libc/src/sys/prctl/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()

add_entrypoint_object(
prctl
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.prctl
)
12 changes: 12 additions & 0 deletions libc/src/sys/prctl/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_entrypoint_object(
prctl
SRCS
prctl.cpp
HDRS
../prctl.h
DEPENDS
libc.include.sys_prctl
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)
Loading