Skip to content

Commit 613adc2

Browse files
[libc] implement sys/getauxval
1 parent 0fe20aa commit 613adc2

File tree

10 files changed

+252
-1
lines changed

10 files changed

+252
-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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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.config.linux.app_h
15+
libc.src.fcntl.open
16+
libc.src.unistd.read
17+
libc.src.unistd.close
18+
)

libc/src/sys/auxv/linux/getauxval.cpp

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

0 commit comments

Comments
 (0)