Skip to content

Commit 9efc2f4

Browse files
authoredMar 19, 2020
Lazy-initialize the environment variables. (#184)
* Lazy-initialize the environment variables. This is the first in a series of PRs to make it easier to use WASI libc in Wasm modules that don't have a `main` function. By initializing the environment on demand, we avoid depending on having `__wasm_call_ctors` run. This uses weak symbols strategically to ensure that if `environ` is used, it is initialized eagerly, but if only `getenv` and friends are used, the environment is initialized lazily. Eventually, I expect we'll have a convention for wasm modules without main functions which will allow the `__wasm_call_ctors` function to be called automatically, but this helps in simple cases for now. Fixes #180. * Add comments explaining the libc-environ-compat.h header usage.
1 parent 38b930a commit 9efc2f4

File tree

13 files changed

+124
-17
lines changed

13 files changed

+124
-17
lines changed
 

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ __env_rm_add
3939
__env_rm_add
4040
__env_rm_add
4141
__env_rm_add
42-
__environ
4342
__exp2f_data
4443
__exp_data
4544
__expo2
@@ -252,8 +251,12 @@ __uflow
252251
__unlist_locked_file
253252
__uselocale
254253
__utc
254+
__wasilibc_ensure_environ
255+
__wasilibc_environ
256+
__wasilibc_environ
255257
__wasilibc_fd_renumber
256258
__wasilibc_find_relpath
259+
__wasilibc_initialize_environ
257260
__wasilibc_open_nomode
258261
__wasilibc_openat_nomode
259262
__wasilibc_register_preopened_fd

‎expected/wasm32-wasi/include-all.c

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
#include <unistd.h>
167167
#include <values.h>
168168
#include <wasi/api.h>
169+
#include <wasi/libc-environ.h>
169170
#include <wasi/libc-find-relpath.h>
170171
#include <wasi/libc.h>
171172
#include <wchar.h>

‎expected/wasm32-wasi/predefined-macros.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,7 @@
30983098
#define __va_copy(d,s) __builtin_va_copy(d,s)
30993099
#define __wasi__ 1
31003100
#define __wasi_api_h
3101+
#define __wasi_libc_environ_h
31013102
#define __wasi_libc_find_relpath_h
31023103
#define __wasi_libc_h
31033104
#define __wasilibc___errno_values_h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef __wasi_libc_environ_h
2+
#define __wasi_libc_environ_h
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
/// Initialize the global environment variable state. Only needs to be
9+
/// called once; most users should call `__wasilibc_ensure_environ` instead.
10+
void __wasilibc_initialize_environ(void);
11+
12+
/// If `__wasilibc_initialize_environ` has not yet been called, call it.
13+
void __wasilibc_ensure_environ(void);
14+
15+
#ifdef __cplusplus
16+
}
17+
#endif
18+
19+
#endif

‎libc-bottom-half/libpreopen/libpreopen.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ __wasilibc_find_relpath(
524524
/// This is referenced by weak reference from crt1.c and lives in the same source
525525
/// file as `__wasilibc_find_relpath` so that it's linked in when it's needed.
526526
// Concerning the 51 -- see the comment by the constructor priority in
527-
// libc-bottom-half/sources/__environ.c.
527+
// libc-bottom-half/sources/__wasilibc_environ.c.
528528
__attribute__((constructor(51)))
529529
static void
530530
__wasilibc_populate_libpreopen(void)

‎libc-bottom-half/sources/__environ.c ‎libc-bottom-half/sources/__wasilibc_initialize_environ.c

+25-15
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,38 @@
33
#include <sysexits.h>
44
#include <wasi/api.h>
55
#include <wasi/libc.h>
6+
#include <wasi/libc-environ.h>
67

7-
static char *empty_environ[1] = { NULL };
8-
char **__environ = empty_environ;
9-
extern __typeof(__environ) _environ __attribute__((weak, alias("__environ")));
10-
extern __typeof(__environ) environ __attribute__((weak, alias("__environ")));
8+
/// If the program doesn't use `environ`, it'll get this version of
9+
/// `__wasilibc_environ`, which isn't initialized with a constructor function.
10+
/// `getenv` etc. call `__wasilibc_ensure_environ()` before accessing it.
11+
/// Statically-initialize it to an invalid pointer value so that we can
12+
/// detect if it's been explicitly initialized (we can't use `NULL` because
13+
/// `clearenv` sets it to NULL.
14+
char **__wasilibc_environ __attribute__((weak)) = (char **)-1;
1115

12-
// We define this function here in the same source file as __environ, so that
13-
// this function is called in iff environment variable support is used.
14-
// Concerning the 50 -- levels up to 100 are reserved for the implementation,
15-
// so we an arbitrary number in the middle of the range to allow other
16-
// reserved things to go before or after.
17-
__attribute__((constructor(50)))
18-
static void __wasilibc_populate_environ(void) {
19-
__wasi_errno_t err;
16+
// See the comments in libc-environ.h.
17+
void __wasilibc_ensure_environ(void) {
18+
if (__wasilibc_environ == (char **)-1) {
19+
__wasilibc_initialize_environ();
20+
}
21+
}
22+
23+
/// Avoid dynamic allocation for the case where there are no environment
24+
/// variables, but we still need a non-NULL pointer to an (empty) array.
25+
static char *empty_environ[1] = { NULL };
2026

27+
// See the comments in libc-environ.h.
28+
void __wasilibc_initialize_environ(void) {
2129
// Get the sizes of the arrays we'll have to create to copy in the environment.
2230
size_t environ_count;
2331
size_t environ_buf_size;
24-
err = __wasi_environ_sizes_get(&environ_count, &environ_buf_size);
32+
__wasi_errno_t err = __wasi_environ_sizes_get(&environ_count, &environ_buf_size);
2533
if (err != __WASI_ERRNO_SUCCESS) {
2634
goto oserr;
2735
}
2836
if (environ_count == 0) {
37+
__wasilibc_environ = empty_environ;
2938
return;
3039
}
3140

@@ -49,7 +58,8 @@ static void __wasilibc_populate_environ(void) {
4958
goto software;
5059
}
5160

52-
// Fill the environment chars, and the __environ array with pointers into those chars.
61+
// Fill the environment chars, and the `__wasilibc_environ` array with
62+
// pointers into those chars.
5363
// TODO: Remove the casts on `environ_ptrs` and `environ_buf` once the witx is updated with char8 support.
5464
err = __wasi_environ_get((uint8_t **)environ_ptrs, (uint8_t *)environ_buf);
5565
if (err != __WASI_ERRNO_SUCCESS) {
@@ -58,7 +68,7 @@ static void __wasilibc_populate_environ(void) {
5868
goto oserr;
5969
}
6070

61-
__environ = environ_ptrs;
71+
__wasilibc_environ = environ_ptrs;
6272
return;
6373
oserr:
6474
_Exit(EX_OSERR);

‎libc-bottom-half/sources/environ.c

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <unistd.h>
2+
#include <stdlib.h>
3+
#include <sysexits.h>
4+
#include <wasi/api.h>
5+
#include <wasi/libc.h>
6+
#include <wasi/libc-environ.h>
7+
8+
// If the program does use `environ`, it'll get this version of
9+
// `__wasilibc_environ`, which is initialized with a constructor function, so
10+
// that it's initialized whenever user code might want to access it.
11+
char **__wasilibc_environ;
12+
extern __typeof(__wasilibc_environ) _environ
13+
__attribute__((weak, alias("__wasilibc_environ")));
14+
extern __typeof(__wasilibc_environ) environ
15+
__attribute__((weak, alias("__wasilibc_environ")));
16+
17+
// We define this function here in the same source file as
18+
// `__wasilibc_environ`, so that this function is called in iff environment
19+
// variable support is used.
20+
// Concerning the 50 -- levels up to 100 are reserved for the implementation,
21+
// so we an arbitrary number in the middle of the range to allow other
22+
// reserved things to go before or after.
23+
__attribute__((constructor(50)))
24+
static void __wasilibc_initialize_environ_eagerly(void) {
25+
__wasilibc_initialize_environ();
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This header file is meant to be included withinin the body of a function
2+
// which uses `__environ`. Code using `__environ` expects it will be initialized
3+
// eagerly. `__wasilibc_environ` is initialized lazily. Provide `__environ` as
4+
// an alias and arrange for the lazy initialization to be performed.
5+
6+
extern char **__wasilibc_environ;
7+
8+
__wasilibc_ensure_environ();
9+
10+
#ifndef __wasilibc_environ
11+
#define __environ __wasilibc_environ
12+
#endif

‎libc-top-half/musl/src/env/clearenv.c

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ weak_alias(dummy, __env_rm_add);
77

88
int clearenv()
99
{
10+
#ifdef __wasilibc_unmodified_upstream // Lazy environment variable init.
11+
#else
12+
// This specialized header is included within the function body to arranges for
13+
// the environment variables to be lazily initialized. It redefined `__environ`,
14+
// so don't remove or reorder it with respect to other code.
15+
#include "wasi/libc-environ-compat.h"
16+
#endif
1017
char **e = __environ;
1118
__environ = 0;
1219
if (e) while (*e) __env_rm_add(*e++, 0);

‎libc-top-half/musl/src/env/getenv.c

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
char *getenv(const char *name)
66
{
7+
#ifdef __wasilibc_unmodified_upstream // Lazy environment variable init.
8+
#else
9+
// This specialized header is included within the function body to arranges for
10+
// the environment variables to be lazily initialized. It redefined `__environ`,
11+
// so don't remove or reorder it with respect to other code.
12+
#include "wasi/libc-environ-compat.h"
13+
#endif
714
size_t l = __strchrnul(name, '=') - name;
815
if (l && !name[l] && __environ)
916
for (char **e = __environ; *e; e++)

‎libc-top-half/musl/src/env/putenv.c

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ weak_alias(dummy, __env_rm_add);
77

88
int __putenv(char *s, size_t l, char *r)
99
{
10+
#ifdef __wasilibc_unmodified_upstream // Lazy environment variable init.
11+
#else
12+
// This specialized header is included within the function body to arranges for
13+
// the environment variables to be lazily initialized. It redefined `__environ`,
14+
// so don't remove or reorder it with respect to other code.
15+
#include "wasi/libc-environ-compat.h"
16+
#endif
1017
size_t i=0;
1118
if (__environ) {
1219
for (char **e = __environ; *e; e++, i++)

‎libc-top-half/musl/src/env/unsetenv.c

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ int unsetenv(const char *name)
1313
errno = EINVAL;
1414
return -1;
1515
}
16+
#ifdef __wasilibc_unmodified_upstream // Lazy environment variable init.
17+
#else
18+
// This specialized header is included within the function body to arranges for
19+
// the environment variables to be lazily initialized. It redefined `__environ`,
20+
// so don't remove or reorder it with respect to other code.
21+
#include "wasi/libc-environ-compat.h"
22+
#endif
1623
if (__environ) {
1724
char **e = __environ, **eo = e;
1825
for (; *e; e++)

‎libc-top-half/musl/src/include/unistd.h

+7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33

44
#include "../../include/unistd.h"
55

6+
#ifdef __wasilibc_unmodified_upstream // Lazy environment variable init.
67
extern char **__environ;
8+
#else
9+
// To support lazy initialization of environment variables, `__environ` is
10+
// omitted, and a lazy `__wasilibc_environ` is used instead. Use
11+
// "wasi/libc-environ-compat.h" in functions that use `__environ`.
12+
#include "wasi/libc-environ.h"
13+
#endif
714

815
hidden int __dup3(int, int, int);
916
hidden int __mkostemps(char *, int, int);

0 commit comments

Comments
 (0)
Please sign in to comment.