Skip to content

Commit 0db5ba0

Browse files
[libc] Refactor AUXV handling with new auxv.h header library (#162326)
Closes #153666 This patch introduces a new centralized AUXV (auxiliary vector) handling mechanism for LLVM libc on Linux, replacing the previous scattered implementation across multiple files. ## Key Changes: ### New Files: - **libc/src/__support/OSUtil/linux/auxv.h**: New header library providing a clean interface for AUXV access with: - `auxv::Entry` struct for AUXV entries (type and value) - `auxv::Vector` class with iterator support for traversing AUXV - `auxv::get()` function for retrieving specific AUXV values - Thread-safe initialization with fallback mechanisms (prctl and /proc/self/auxv) ### Modified Files: 1. **libc/src/__support/OSUtil/linux/CMakeLists.txt**: - Added `auxv` header library declaration with proper dependencies: - libc.hdr.fcntl_macros - libc.src.__support.OSUtil.osutil - libc.src.__support.common - libc.src.__support.CPP.optional - libc.src.__support.threads.callonce 2. **libc/config/linux/app.h**: - Removed `AuxEntry` struct (moved to auxv.h as `auxv::Entry`) - Removed `auxv_ptr` from `AppProperties` struct - Simplified application properties structure 3. **libc/src/sys/auxv/linux/getauxval.cpp**: - Completely refactored to use new auxv.h interface - Removed ~200 lines of complex initialization code - Simplified to just call `auxv::get()` function - Removed dependencies to external symbols (mman, prctl, fcntl, read, close, open) 4. **libc/src/sys/auxv/linux/CMakeLists.txt**: - Updated dependencies to use new auxv header library - Removed dependencies to external symbols (prctl, mman, fcntl, unistd, etc.) 5. **libc/startup/linux/do_start.cpp**: - Updated to use new `auxv::Vector` interface - Changed from pointer-based to iterator-based AUXV traversal - Updated field names (`aux_entry->id` → `aux_entry.type`, `aux_entry->value` → `aux_entry.val`) - Added call to `auxv::Vector::initialize_unsafe()` for early AUXV setup 6. **libc/startup/linux/CMakeLists.txt**: - Added dependency on `libc.src.__support.OSUtil.linux.auxv`
1 parent 801b1dc commit 0db5ba0

File tree

13 files changed

+210
-304
lines changed

13 files changed

+210
-304
lines changed

libc/config/linux/app.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,6 @@ struct TLSImage {
3535
uintptr_t align;
3636
};
3737

38-
// Linux manpage on `proc(5)` says that the aux vector is an array of
39-
// unsigned long pairs.
40-
// (see: https://man7.org/linux/man-pages/man5/proc.5.html)
41-
using AuxEntryType = unsigned long;
42-
// Using the naming convention from `proc(5)`.
43-
// TODO: Would be nice to use the aux entry structure from elf.h when available.
44-
struct AuxEntry {
45-
AuxEntryType id;
46-
AuxEntryType value;
47-
};
48-
4938
struct Args {
5039
uintptr_t argc;
5140

@@ -70,9 +59,6 @@ struct AppProperties {
7059

7160
// Environment data.
7261
uintptr_t *env_ptr;
73-
74-
// Auxiliary vector data.
75-
AuxEntry *auxv_ptr;
7662
};
7763

7864
[[gnu::weak]] extern AppProperties app;

libc/src/__support/OSUtil/linux/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ add_object_library(
2424
libc.include.sys_syscall
2525
)
2626

27+
add_header_library(
28+
auxv
29+
HDRS
30+
auxv.h
31+
DEPENDS
32+
libc.hdr.fcntl_macros
33+
libc.src.__support.OSUtil.osutil
34+
libc.src.__support.common
35+
libc.src.__support.CPP.optional
36+
libc.src.__support.threads.callonce
37+
)
38+
2739
add_header_library(
2840
getrandom
2941
HDRS
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H
10+
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H
11+
12+
#include "hdr/fcntl_macros.h" // For open flags
13+
#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_ macros
18+
#include <linux/mman.h> // For mmap flags
19+
#include <linux/prctl.h> // For prctl
20+
#include <sys/syscall.h> // For syscall numbers
21+
22+
namespace LIBC_NAMESPACE_DECL {
23+
24+
namespace auxv {
25+
struct Entry {
26+
unsigned long type; // Entry type
27+
unsigned long val; // Integer value
28+
};
29+
30+
class Vector {
31+
LIBC_INLINE_VAR static constexpr Entry END = {AT_NULL, AT_NULL};
32+
LIBC_INLINE_VAR static const Entry *entries = &END;
33+
LIBC_INLINE_VAR static CallOnceFlag init_flag = callonce_impl::NOT_CALLED;
34+
LIBC_INLINE_VAR constexpr static size_t FALLBACK_AUXV_ENTRIES = 64;
35+
36+
LIBC_INLINE static void fallback_initialize_unsync();
37+
LIBC_INLINE static const Entry *get_entries() {
38+
if (LIBC_LIKELY(entries != &END))
39+
return entries;
40+
callonce(&init_flag, fallback_initialize_unsync);
41+
return entries;
42+
}
43+
44+
public:
45+
class Iterator {
46+
const Entry *current;
47+
48+
public:
49+
LIBC_INLINE explicit Iterator(const Entry *entry) : current(entry) {}
50+
LIBC_INLINE Iterator &operator++() {
51+
++current;
52+
return *this;
53+
}
54+
LIBC_INLINE const Entry &operator*() const { return *current; }
55+
LIBC_INLINE bool operator!=(const Iterator &other) const {
56+
return current->type != other.current->type;
57+
}
58+
LIBC_INLINE bool operator==(const Iterator &other) const {
59+
return current->type == other.current->type;
60+
}
61+
};
62+
using iterator = Iterator;
63+
LIBC_INLINE static Iterator begin() { return Iterator(get_entries()); }
64+
LIBC_INLINE static Iterator end() { return Iterator(&END); }
65+
LIBC_INLINE static void initialize_unsafe(const Entry *auxv);
66+
};
67+
68+
// Initializes the auxv entries.
69+
// This function is intended to be called once inside crt0.
70+
LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) {
71+
init_flag = callonce_impl::FINISH;
72+
entries = auxv;
73+
}
74+
75+
// When CRT0 does not setup the global array, this function is called.
76+
// As its name suggests, this function is not thread-safe and should be
77+
// backed by a callonce guard.
78+
// This initialize routine will do a mmap to allocate a memory region.
79+
// Since auxv tends to live throughout the program lifetime, we do not
80+
// munmap it.
81+
[[gnu::cold]]
82+
LIBC_INLINE void Vector::fallback_initialize_unsync() {
83+
constexpr size_t AUXV_MMAP_SIZE = FALLBACK_AUXV_ENTRIES * sizeof(Entry);
84+
long mmap_ret = syscall_impl<long>(SYS_mmap, nullptr, AUXV_MMAP_SIZE,
85+
PROT_READ | PROT_WRITE,
86+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
87+
// We do not proceed if mmap fails.
88+
if (mmap_ret <= 0)
89+
return;
90+
91+
// Initialize the auxv array with AT_NULL entries.
92+
Entry *vector = reinterpret_cast<Entry *>(mmap_ret);
93+
for (size_t i = 0; i < FALLBACK_AUXV_ENTRIES; ++i) {
94+
vector[i].type = AT_NULL;
95+
vector[i].val = AT_NULL;
96+
}
97+
size_t avaiable_size = AUXV_MMAP_SIZE - sizeof(Entry);
98+
99+
// Attempt 1: use PRCTL to get the auxv.
100+
// We guarantee that the vector is always padded with AT_NULL entries.
101+
long prctl_ret = syscall_impl<long>(SYS_prctl, PR_GET_AUXV,
102+
reinterpret_cast<unsigned long>(vector),
103+
avaiable_size, 0, 0);
104+
if (prctl_ret >= 0) {
105+
entries = vector;
106+
return;
107+
}
108+
109+
// Attempt 2: read /proc/self/auxv.
110+
#ifdef SYS_openat
111+
int fd = syscall_impl<int>(SYS_openat, AT_FDCWD, "/proc/self/auxv",
112+
O_RDONLY | O_CLOEXEC);
113+
#else
114+
int fd = syscall_impl<int>(SYS_open, "/proc/self/auxv", O_RDONLY | O_CLOEXEC);
115+
#endif
116+
if (fd < 0) {
117+
syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE);
118+
return;
119+
}
120+
uint8_t *cursor = reinterpret_cast<uint8_t *>(vector);
121+
bool has_error = false;
122+
while (avaiable_size != 0) {
123+
long bytes_read = syscall_impl<long>(SYS_read, fd, cursor, avaiable_size);
124+
if (bytes_read <= 0) {
125+
if (bytes_read == -EINTR)
126+
continue;
127+
has_error = bytes_read < 0;
128+
break;
129+
}
130+
avaiable_size -= bytes_read;
131+
cursor += bytes_read;
132+
}
133+
syscall_impl<long>(SYS_close, fd);
134+
if (has_error) {
135+
syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE);
136+
return;
137+
}
138+
entries = vector;
139+
}
140+
141+
LIBC_INLINE cpp::optional<unsigned long> get(unsigned long type) {
142+
Vector auxvec;
143+
for (const auto &entry : auxvec)
144+
if (entry.type == type)
145+
return entry.val;
146+
return cpp::nullopt;
147+
}
148+
} // namespace auxv
149+
} // namespace LIBC_NAMESPACE_DECL
150+
151+
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H

libc/src/__support/threads/callonce.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,8 @@
2323
#endif
2424

2525
namespace LIBC_NAMESPACE_DECL {
26-
27-
// Common definitions
28-
using CallOnceCallback = void(void);
29-
namespace callonce_impl {
30-
int callonce_slowpath(CallOnceFlag *flag, CallOnceCallback *callback);
31-
} // namespace callonce_impl
32-
33-
LIBC_INLINE int callonce(CallOnceFlag *flag, CallOnceCallback *callback) {
26+
template <class CallOnceCallback>
27+
LIBC_INLINE int callonce(CallOnceFlag *flag, CallOnceCallback callback) {
3428
if (LIBC_LIKELY(callonce_impl::callonce_fastpath(flag)))
3529
return 0;
3630

libc/src/__support/threads/linux/CMakeLists.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,8 @@ add_object_library(
9797
# value other than 0 is dangerous. We know.
9898
)
9999

100-
add_object_library(
100+
add_header_library(
101101
callonce
102-
SRCS
103-
callonce.cpp
104102
HDRS
105103
../callonce.h
106104
callonce.h

libc/src/__support/threads/linux/callonce.cpp

Lines changed: 0 additions & 40 deletions
This file was deleted.

libc/src/__support/threads/linux/callonce.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ static constexpr FutexWordType FINISH = 0x33;
2626
LIBC_INLINE bool callonce_fastpath(CallOnceFlag *flag) {
2727
return flag->load(cpp::MemoryOrder::RELAXED) == FINISH;
2828
}
29+
30+
template <class CallOnceCallback>
31+
[[gnu::noinline, gnu::cold]] int callonce_slowpath(CallOnceFlag *flag,
32+
CallOnceCallback callback) {
33+
34+
auto *futex_word = reinterpret_cast<Futex *>(flag);
35+
36+
FutexWordType not_called = NOT_CALLED;
37+
38+
// The call_once call can return only after the called function |func|
39+
// returns. So, we use futexes to synchronize calls with the same flag value.
40+
if (futex_word->compare_exchange_strong(not_called, START)) {
41+
callback();
42+
auto status = futex_word->exchange(FINISH);
43+
if (status == WAITING)
44+
futex_word->notify_all();
45+
return 0;
46+
}
47+
48+
FutexWordType status = START;
49+
if (futex_word->compare_exchange_strong(status, WAITING) || status == WAITING)
50+
futex_word->wait(WAITING);
51+
52+
return 0;
53+
}
2954
} // namespace callonce_impl
3055

3156
} // namespace LIBC_NAMESPACE_DECL

libc/src/pthread/pthread_once.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ namespace LIBC_NAMESPACE_DECL {
1717

1818
LLVM_LIBC_FUNCTION(int, pthread_once,
1919
(pthread_once_t * flag, __pthread_once_func_t func)) {
20-
return callonce(reinterpret_cast<CallOnceFlag *>(flag),
21-
reinterpret_cast<CallOnceCallback *>(func));
20+
return callonce(reinterpret_cast<CallOnceFlag *>(flag), func);
2221
}
2322

2423
} // namespace LIBC_NAMESPACE_DECL

libc/src/sys/auxv/linux/CMakeLists.txt

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@ add_entrypoint_object(
55
HDRS
66
../getauxval.h
77
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.app_h
15-
libc.src.fcntl.open
16-
libc.src.unistd.read
17-
libc.src.unistd.close
18-
libc.include.sys_auxv
8+
libc.src.__support.OSUtil.linux.auxv
9+
libc.src.__support.libc_errno
1910
)

0 commit comments

Comments
 (0)