Skip to content

Commit a6f8713

Browse files
authored
Convert preopen initialization to be lazy. (#408)
* Convert preopen initialization to be lazy. Insteead of eagerly initializing the preopens in a static constructor, perform preopen initialization the first time it's needed, or before a close or a renumber which might disrupt the file descriptor space. And, use a weak symbol with a stub function for use by `close` or `fd_renumber`, we that they can trigger preopen initialization only if it's actually needed. This way, if a program doesn't contain any calls to any function that needs preopens, it can avoid linking in the preopen initialization code. And if it contains calls but doesn't execute them at runtime, it can avoid executing the preopen intiailization code. A downside here is that this may cause problems for users that call `__wasi_fd_close` or `__wasi_fd_renumber` directly and close over overwrite some preopens before libc has a chance to scan them. To partially address this, this PR does add a declaration for `__wasilibc_populate_preopens` to <wasi/libc.h> so that users can call it manually if they need to. * Fix calling `internal_register_preopened_fd` with the lock held. Factor out the lock acquisition from the implementation of `internal_register_preopened_fd` so that we can call it from `__wasilibc_populate_preopens` with the lock held.
1 parent 3189cd1 commit a6f8713

File tree

6 files changed

+71
-33
lines changed

6 files changed

+71
-33
lines changed

expected/wasm32-wasi-threads/defined-symbols.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ __wasilibc_nocwd_scandirat
390390
__wasilibc_nocwd_symlinkat
391391
__wasilibc_nocwd_utimensat
392392
__wasilibc_open_nomode
393+
__wasilibc_populate_preopens
393394
__wasilibc_pthread_self
394395
__wasilibc_register_preopened_fd
395396
__wasilibc_rename_newat

expected/wasm32-wasi/defined-symbols.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ __wasilibc_nocwd_scandirat
329329
__wasilibc_nocwd_symlinkat
330330
__wasilibc_nocwd_utimensat
331331
__wasilibc_open_nomode
332+
__wasilibc_populate_preopens
332333
__wasilibc_register_preopened_fd
333334
__wasilibc_rename_newat
334335
__wasilibc_rename_oldat

libc-bottom-half/cloudlibc/src/libc/unistd/close.c

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

libc-bottom-half/headers/public/wasi/libc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ extern "C" {
1111
struct stat;
1212
struct timespec;
1313

14+
/// Populate libc's preopen tables. This is normally done automatically
15+
/// just before it's needed, however if you call `__wasi_fd_renumber` or
16+
/// `__wasi_fd_close` directly, and you need the preopens to be accurate
17+
/// afterward, you should call this before doing so.
18+
void __wasilibc_populate_preopens(void);
19+
1420
/// Register the given pre-opened file descriptor under the given path.
1521
///
1622
/// This function does not take ownership of `prefix` (it makes its own copy).

libc-bottom-half/sources/__wasilibc_fd_renumber.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,32 @@
44
#include <unistd.h>
55

66
int __wasilibc_fd_renumber(int fd, int newfd) {
7+
// Scan the preopen fds before making any changes.
8+
__wasilibc_populate_preopens();
9+
710
__wasi_errno_t error = __wasi_fd_renumber(fd, newfd);
811
if (error != 0) {
912
errno = error;
1013
return -1;
1114
}
1215
return 0;
1316
}
17+
18+
int close(int fd) {
19+
// Scan the preopen fds before making any changes.
20+
__wasilibc_populate_preopens();
21+
22+
__wasi_errno_t error = __wasi_fd_close(fd);
23+
if (error != 0) {
24+
errno = error;
25+
return -1;
26+
}
27+
28+
return 0;
29+
}
30+
31+
weak void __wasilibc_populate_preopens(void) {
32+
// This version does nothing. It may be overridden by a version which does
33+
// something if `__wasilibc_find_abspath` or `__wasilibc_find_relpath` are
34+
// used.
35+
}

libc-bottom-half/sources/preopens.c

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ typedef struct preopen {
2525
} preopen;
2626

2727
/// A simple growable array of `preopen`.
28+
static _Atomic _Bool preopens_populated = false;
2829
static preopen *preopens;
2930
static size_t num_preopens;
3031
static size_t preopen_capacity;
@@ -100,35 +101,42 @@ static const char *strip_prefixes(const char *path) {
100101
return path;
101102
}
102103

103-
/// Register the given preopened file descriptor under the given path.
104-
///
105-
/// This function takes ownership of `prefix`.
106-
static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) {
107-
LOCK(lock);
108-
104+
/// Similar to `internal_register_preopened_fd_unlocked` but does not
105+
/// take a lock.
106+
static int internal_register_preopened_fd_unlocked(__wasi_fd_t fd, const char *relprefix) {
109107
// Check preconditions.
110108
assert_invariants();
111109
assert(fd != AT_FDCWD);
112110
assert(fd != -1);
113111
assert(relprefix != NULL);
114112

115113
if (num_preopens == preopen_capacity && resize() != 0) {
116-
UNLOCK(lock);
117114
return -1;
118115
}
119116

120117
char *prefix = strdup(strip_prefixes(relprefix));
121118
if (prefix == NULL) {
122-
UNLOCK(lock);
123119
return -1;
124120
}
125121
preopens[num_preopens++] = (preopen) { prefix, fd, };
126122

127123
assert_invariants();
128-
UNLOCK(lock);
129124
return 0;
130125
}
131126

127+
/// Register the given preopened file descriptor under the given path.
128+
///
129+
/// This function takes ownership of `prefix`.
130+
static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) {
131+
LOCK(lock);
132+
133+
int r = internal_register_preopened_fd_unlocked(fd, relprefix);
134+
135+
UNLOCK(lock);
136+
137+
return r;
138+
}
139+
132140
/// Are the `prefix_len` bytes pointed to by `prefix` a prefix of `path`?
133141
static bool prefix_matches(const char *prefix, size_t prefix_len, const char *path) {
134142
// Allow an empty string as a prefix of any relative path.
@@ -152,6 +160,8 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa
152160

153161
// See the documentation in libc.h
154162
int __wasilibc_register_preopened_fd(int fd, const char *prefix) {
163+
__wasilibc_populate_preopens();
164+
155165
return internal_register_preopened_fd((__wasi_fd_t)fd, prefix);
156166
}
157167

@@ -172,6 +182,8 @@ int __wasilibc_find_relpath(const char *path,
172182
int __wasilibc_find_abspath(const char *path,
173183
const char **abs_prefix,
174184
const char **relative_path) {
185+
__wasilibc_populate_preopens();
186+
175187
// Strip leading `/` characters, the prefixes we're mataching won't have
176188
// them.
177189
while (*path == '/')
@@ -219,13 +231,20 @@ int __wasilibc_find_abspath(const char *path,
219231
return fd;
220232
}
221233

222-
/// This is referenced by weak reference from crt1.c and lives in the same
223-
/// source file as `__wasilibc_find_relpath` so that it's linked in when it's
224-
/// needed.
225-
// Concerning the 51 -- see the comment by the constructor priority in
226-
// libc-bottom-half/sources/environ.c.
227-
__attribute__((constructor(51)))
228-
static void __wasilibc_populate_preopens(void) {
234+
void __wasilibc_populate_preopens(void) {
235+
// Fast path: If the preopens are already initialized, do nothing.
236+
if (preopens_populated) {
237+
return;
238+
}
239+
240+
LOCK(lock);
241+
242+
// Check whether another thread initialized the preopens already.
243+
if (preopens_populated) {
244+
UNLOCK(lock);
245+
return;
246+
}
247+
229248
// Skip stdin, stdout, and stderr, and count up until we reach an invalid
230249
// file descriptor.
231250
for (__wasi_fd_t fd = 3; fd != 0; ++fd) {
@@ -249,7 +268,7 @@ static void __wasilibc_populate_preopens(void) {
249268
goto oserr;
250269
prefix[prestat.u.dir.pr_name_len] = '\0';
251270

252-
if (internal_register_preopened_fd(fd, prefix) != 0)
271+
if (internal_register_preopened_fd_unlocked(fd, prefix) != 0)
253272
goto software;
254273
free(prefix);
255274

@@ -260,6 +279,11 @@ static void __wasilibc_populate_preopens(void) {
260279
}
261280
}
262281

282+
// Preopens are now initialized.
283+
preopens_populated = true;
284+
285+
UNLOCK(lock);
286+
263287
return;
264288
oserr:
265289
_Exit(EX_OSERR);

0 commit comments

Comments
 (0)