Skip to content

Commit 0478641

Browse files
[libc] implement sys/getauxval
1 parent da7462a commit 0478641

File tree

10 files changed

+239
-1
lines changed

10 files changed

+239
-1
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ set(TARGET_LIBC_ENTRYPOINTS
168168
# sys/prctl.h entrypoints
169169
libc.src.sys.prctl.prctl
170170

171+
# sys/auxv.h entrypoints
172+
libc.src.sys.auxv.getauxval
173+
171174
# termios.h entrypoints
172175
libc.src.termios.cfgetispeed
173176
libc.src.termios.cfgetospeed

libc/config/linux/app.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ struct AppProperties {
9393
AuxEntry *auxv_ptr;
9494
};
9595

96-
extern AppProperties app;
96+
[[gnu::weak]] extern AppProperties app;
9797

9898
// The descriptor of a thread's TLS area.
9999
struct TLSDescriptor {

libc/config/linux/arm/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ set(TARGET_LIBC_ENTRYPOINTS
9595

9696
# sys/prctl.h entrypoints
9797
libc.src.sys.prctl.prctl
98+
99+
# sys/auxv.h entrypoints
100+
libc.src.sys.auxv.getauxval
98101
)
99102

100103
set(TARGET_LIBM_ENTRYPOINTS

libc/config/linux/riscv/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ set(TARGET_LIBC_ENTRYPOINTS
174174
# sys/prctl.h entrypoints
175175
libc.src.sys.prctl.prctl
176176

177+
# sys/auxv.h entrypoints
178+
libc.src.sys.auxv.getauxval
179+
177180
# termios.h entrypoints
178181
libc.src.termios.cfgetispeed
179182
libc.src.termios.cfgetospeed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ set(TARGET_LIBC_ENTRYPOINTS
174174
# sys/prctl.h entrypoints
175175
libc.src.sys.prctl.prctl
176176

177+
# sys/auxv.h entrypoints
178+
libc.src.sys.auxv.getauxval
179+
177180
# termios.h entrypoints
178181
libc.src.termios.cfgetispeed
179182
libc.src.termios.cfgetospeed

libc/src/sys/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
add_subdirectory(auxv)
12
add_subdirectory(mman)
23
add_subdirectory(random)
34
add_subdirectory(resource)

libc/src/sys/auxv/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
2+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
3+
endif()
4+
5+
add_entrypoint_object(
6+
getauxval
7+
ALIAS
8+
DEPENDS
9+
.${LIBC_TARGET_OS}.getauxval
10+
)

libc/src/sys/auxv/getauxval.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for getauxval function ------------*- 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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
10+
#define LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
11+
12+
#include <sys/auxv.h>
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
unsigned long getauxval(unsigned long id);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
add_entrypoint_object(
2+
getauxval
3+
SRCS
4+
getauxval.cpp
5+
HDRS
6+
../getauxval.h
7+
DEPENDS
8+
libc.src.sys.prctl.prctl
9+
libc.src.sys.mman.mmap
10+
libc.src.sys.mman.munmap
11+
libc.src.__support.threads.callonce
12+
libc.src.__support.common
13+
libc.src.errno.errno
14+
libc.src.stdlib.atexit
15+
libc.config.linux.app_h
16+
libc.src.fcntl.open
17+
libc.src.unistd.read
18+
libc.src.unistd.close
19+
)
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//===-- Implementation file for getauxval function --------------*- 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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/sys/auxv/getauxval.h"
10+
#include "config/linux/app.h"
11+
#include "src/__support/CPP/atomic.h"
12+
#include "src/__support/common.h"
13+
#include "src/__support/threads/callonce.h"
14+
#include "src/__support/threads/linux/futex_word.h"
15+
#include "src/errno/libc_errno.h"
16+
#include "src/fcntl/open.h"
17+
#include "src/stdlib/atexit.h"
18+
#include "src/sys/mman/mmap.h"
19+
#include "src/sys/mman/munmap.h"
20+
#include "src/sys/prctl/prctl.h"
21+
#include "src/unistd/close.h"
22+
#include "src/unistd/read.h"
23+
#include <linux/auxvec.h>
24+
25+
namespace LIBC_NAMESPACE {
26+
27+
constexpr static size_t AUXV_SIZE = 64;
28+
29+
// Helper to recover or set errno
30+
struct AuxvErrnoGuard {
31+
int saved;
32+
bool failure;
33+
AuxvErrnoGuard() : saved(libc_errno), failure(false) {}
34+
~AuxvErrnoGuard() { libc_errno = failure ? ENOENT : saved; }
35+
void mark_failure() { failure = true; }
36+
};
37+
38+
// Helper to manage the memory
39+
static AuxEntry *auxv = nullptr;
40+
41+
struct AuxvMMapGuard {
42+
constexpr static size_t AUXV_MMAP_SIZE = sizeof(AuxEntry) * AUXV_SIZE;
43+
void *ptr;
44+
AuxvMMapGuard(size_t size)
45+
: ptr(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, -1, 0)) {}
46+
~AuxvMMapGuard() {
47+
if (ptr != MAP_FAILED) {
48+
munmap(ptr, AUXV_MMAP_SIZE);
49+
}
50+
}
51+
void submit_to_global() {
52+
// atexit may fail, we do not set it to global in that case.
53+
int ret = atexit([]() {
54+
munmap(auxv, AUXV_MMAP_SIZE);
55+
auxv = nullptr;
56+
});
57+
58+
if (ret != 0)
59+
return;
60+
61+
auxv = reinterpret_cast<AuxEntry *>(ptr);
62+
ptr = MAP_FAILED;
63+
}
64+
bool allocated() { return ptr != MAP_FAILED; }
65+
};
66+
67+
struct AuxvFdGuard {
68+
int fd;
69+
AuxvFdGuard() : fd(open("/proc/self/auxv", O_RDONLY | O_CLOEXEC)) {}
70+
~AuxvFdGuard() {
71+
if (fd != -1) {
72+
close(fd);
73+
}
74+
}
75+
bool valid() { return fd != -1; }
76+
};
77+
78+
static void initialize_auxv_once(void) {
79+
AuxvMMapGuard mmap_guard(AuxvMMapGuard::AUXV_MMAP_SIZE);
80+
if (!mmap_guard.allocated())
81+
return;
82+
auto *ptr = reinterpret_cast<AuxEntry *>(mmap_guard.ptr);
83+
// we get one less than the max size to make sure the search always
84+
// terminates.
85+
size_t available_size = AuxvMMapGuard::AUXV_MMAP_SIZE - sizeof(AuxEntryType);
86+
#if defined(PR_GET_AUXV)
87+
int ret = prctl(PR_GET_AUXV, reinterpret_cast<unsigned long>(ptr),
88+
available_size, 0, 0);
89+
if (ret >= 0) {
90+
mmap_guard.submit_to_global();
91+
return;
92+
}
93+
#endif
94+
AuxvFdGuard fd_guard;
95+
if (!fd_guard.valid())
96+
return;
97+
auto *buf = reinterpret_cast<char *>(ptr);
98+
libc_errno = 0;
99+
bool error_detected = false;
100+
while (true) {
101+
ssize_t bytes_read = read(fd_guard.fd, buf, available_size);
102+
if (bytes_read <= 0) {
103+
if (libc_errno == EINTR)
104+
continue;
105+
error_detected = bytes_read < 0;
106+
break;
107+
}
108+
available_size -= bytes_read;
109+
}
110+
if (!error_detected) {
111+
mmap_guard.submit_to_global();
112+
}
113+
}
114+
115+
static AuxEntry read_entry(int fd) {
116+
AuxEntry buf;
117+
ssize_t size = sizeof(AuxEntry);
118+
while (size > 0) {
119+
ssize_t ret = read(fd, &buf, size);
120+
if (ret < 0) {
121+
if (libc_errno == EINTR)
122+
continue;
123+
buf.id = AT_NULL;
124+
buf.value = AT_NULL;
125+
break;
126+
}
127+
size -= ret;
128+
}
129+
return buf;
130+
}
131+
132+
LLVM_LIBC_FUNCTION(unsigned long, getauxval, (unsigned long id)) {
133+
// Fast path when libc is loaded by its own initialization code. In this case,
134+
// app.auxv_ptr is already set to the auxv passed on the initial stack of the
135+
// process.
136+
AuxvErrnoGuard errno_guard;
137+
138+
auto search_auxv = [&errno_guard](AuxEntry *auxv, unsigned long id) {
139+
for (auto *ptr = auxv; ptr->id != AT_NULL; ptr++) {
140+
if (ptr->id == id) {
141+
return ptr->value;
142+
}
143+
}
144+
errno_guard.mark_failure();
145+
return AuxEntryType{0};
146+
};
147+
148+
if (&app != nullptr) {
149+
return search_auxv(app.auxv_ptr, id);
150+
}
151+
152+
static cpp::Atomic<FutexWordType> once_flag;
153+
callonce(reinterpret_cast<CallOnceFlag *>(&once_flag), initialize_auxv_once);
154+
if (auxv != nullptr) {
155+
return search_auxv(auxv, id);
156+
}
157+
158+
// fallback to use read without mmap
159+
do {
160+
AuxvFdGuard fd_guard;
161+
if (!fd_guard.valid())
162+
break;
163+
164+
while (true) {
165+
AuxEntry buf = read_entry(fd_guard.fd);
166+
if (buf.id == AT_NULL)
167+
break;
168+
if (buf.id == id)
169+
return buf.value;
170+
}
171+
172+
} while (0);
173+
errno_guard.mark_failure();
174+
return 0;
175+
}
176+
} // namespace LIBC_NAMESPACE

0 commit comments

Comments
 (0)