Skip to content

Commit 8c47a25

Browse files
committed
Avoid size bloat on programs that don't use chdir
1 parent 2c1c280 commit 8c47a25

File tree

6 files changed

+205
-122
lines changed

6 files changed

+205
-122
lines changed

Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ LIBC_BOTTOM_HALF_SOURCES = $(LIBC_BOTTOM_HALF_DIR)/sources
4444
LIBC_BOTTOM_HALF_ALL_SOURCES = \
4545
$(shell find $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -name \*.c) \
4646
$(shell find $(LIBC_BOTTOM_HALF_SOURCES) -name \*.c)
47+
48+
# Move `chdir.c` to the end of libc.a since the symbols it provides are used to
49+
# satisfy weak references elsewhere in libc.a, and we don't want to eagerly
50+
# satisfy those references unless `chdir` is pulled in. Some more discussion of
51+
# this can be found on #214
52+
LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c,$(LIBC_BOTTOM_HALF_ALL_SOURCES))
53+
LIBC_BOTTOM_HALF_ALL_SOURCES := $(LIBC_BOTTOM_HALF_ALL_SOURCES) $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c
54+
4755
LIBWASI_EMULATED_MMAN_SOURCES = \
4856
$(shell find $(LIBC_BOTTOM_HALF_DIR)/mman -name \*.c)
4957
LIBWASI_EMULATED_SIGNAL_SOURCES = \

expected/wasm32-wasi/defined-symbols.txt

+2
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ __wasilibc_ensure_environ
255255
__wasilibc_environ
256256
__wasilibc_environ
257257
__wasilibc_fd_renumber
258+
__wasilibc_find_abspath
258259
__wasilibc_find_relpath
260+
__wasilibc_find_relpath_alloc
259261
__wasilibc_initialize_environ
260262
__wasilibc_open_nomode
261263
__wasilibc_openat_nomode

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

+23-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ extern "C" {
1010
* map. If a suitable entry is found, then the file descriptor for that entry
1111
* is returned. Additionally the absolute path of the directory's file
1212
* descriptor is returned in `abs_prefix` and the relative portion that needs
13-
* to be opened is stored in `relative_path`. The size of the `relative_path`
14-
* buffer is provided in the `relative_path_len` argument.
13+
* to be opened is stored in `*relative_path`.
14+
*
15+
* The `relative_path` argument must be a pointer to a buffer valid for
16+
* `relative_path_len` bytes, and this may be used as storage for the relative
17+
* portion of the path being returned through `*relative_path`.
1518
*
1619
* Returns -1 on failure. Errno is set to either:
1720
*
@@ -21,9 +24,26 @@ extern "C" {
2124
*/
2225
int __wasilibc_find_relpath(const char *path,
2326
const char **__restrict__ abs_prefix,
24-
char *relative_path,
27+
char **relative_path,
2528
size_t relative_path_len);
2629

30+
/**
31+
* Look up the given `path`, which is interpreted as absolute, in the preopened
32+
* directory map. If a suitable entry is found, then the file descriptor for
33+
* that entry is returned. Additionally the relative portion of the path to
34+
* where the fd is opened is returned through `relative_path`, the absolute
35+
* prefix which was matched is stored to `abs_prefix`, and `relative_path` may
36+
* be an interior pointer to the `abspath` string.
37+
*
38+
* Returns -1 on failure. Errno is set to either:
39+
*
40+
* * ENOMEM - failed to allocate memory for internal routines.
41+
* * ENOENT - the `path` could not be found relative to any preopened dir.
42+
*/
43+
int __wasilibc_find_abspath(const char *abspath,
44+
const char **__restrict__ abs_prefix,
45+
const char **__restrict__ relative_path);
46+
2747
#ifdef __cplusplus
2848
}
2949
#endif

libc-bottom-half/sources/chdir.c

+93-20
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,42 @@ static int __wasilibc_cwd_mallocd = 0;
1313

1414
int chdir(const char *path)
1515
{
16+
#ifdef _REENTRANT
17+
#error "chdir doesn't yet support multiple threads"
18+
#endif
1619
static char *relative_buf = NULL;
1720
static size_t relative_buf_len = 0;
1821

1922
// Find a preopen'd directory as well as a relative path we're anchored
2023
// from which we're changing directories to.
2124
const char *abs;
22-
int parent_fd;
23-
while (1) {
24-
parent_fd = __wasilibc_find_relpath(path, &abs, relative_buf, relative_buf_len);
25-
if (parent_fd != -1)
26-
break;
27-
if (errno != ERANGE)
28-
return -1;
29-
size_t new_len = relative_buf_len == 0 ? 16 : 2 * relative_buf_len;
30-
relative_buf = realloc(relative_buf, new_len);
31-
if (relative_buf == NULL) {
32-
errno = ENOMEM;
33-
return -1;
34-
}
35-
relative_buf_len = new_len;
36-
}
25+
int parent_fd = __wasilibc_find_relpath_alloc(path, &abs, &relative_buf, &relative_buf_len, 1);
26+
if (parent_fd == -1)
27+
return -1;
3728

3829
// Make sure that this directory we're accessing is indeed a directory.
3930
struct stat dirinfo;
4031
int ret = fstatat(parent_fd, relative_buf, &dirinfo, 0);
41-
if (ret == -1) {
32+
if (ret == -1)
4233
return -1;
43-
}
4434
if (!S_ISDIR(dirinfo.st_mode)) {
4535
errno = ENOTDIR;
4636
return -1;
4737
}
4838

4939
// Copy over our new absolute path into `__wasilibc_cwd`. Only copy over
5040
// the relative portion of the path if it's not equal to `.`
51-
size_t len = strlen(abs);
41+
size_t len = strlen(abs) + 1;
5242
int copy_relative = strcmp(relative_buf, ".") != 0;
5343
char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) : 0));
5444
if (new_cwd == NULL) {
5545
errno = ENOMEM;
5646
return -1;
5747
}
58-
strcpy(new_cwd, abs);
48+
new_cwd[0] = '/';
49+
strcpy(new_cwd + 1, abs);
5950
if (copy_relative)
60-
strcpy(new_cwd + strlen(abs), relative_buf);
51+
strcpy(new_cwd + 1 + strlen(abs), relative_buf);
6152

6253
// And set our new malloc'd buffer into the global cwd, freeing the
6354
// previous one if necessary.
@@ -68,3 +59,85 @@ int chdir(const char *path)
6859
__wasilibc_cwd_mallocd = 1;
6960
return 0;
7061
}
62+
63+
static const char *make_absolute(const char *path) {
64+
static char *make_absolute_buf = NULL;
65+
static size_t make_absolute_len = 0;
66+
67+
// If this path is absolute, then we return it as-is.
68+
if (path[0] == '/') {
69+
return path;
70+
}
71+
72+
// If the path is empty, or points to the current directory, then return
73+
// the current directory.
74+
if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) {
75+
return __wasilibc_cwd;
76+
}
77+
78+
// If the path starts with `./` then we won't be appending that to the cwd.
79+
if (path[0] == '.' && path[1] == '/')
80+
path += 2;
81+
82+
// Otherwise we'll take the current directory, add a `/`, and then add the
83+
// input `path`. Note that this doesn't do any normalization (like removing
84+
// `/./`).
85+
size_t cwd_len = strlen(__wasilibc_cwd);
86+
size_t path_len = strlen(path);
87+
int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1;
88+
size_t alloc_len = cwd_len + path_len + 1 + need_slash;
89+
if (alloc_len > make_absolute_len) {
90+
make_absolute_buf = realloc(make_absolute_buf, alloc_len);
91+
if (make_absolute_buf == NULL)
92+
return NULL;
93+
make_absolute_len = alloc_len;
94+
}
95+
strcpy(make_absolute_buf, __wasilibc_cwd);
96+
if (need_slash)
97+
strcpy(make_absolute_buf + cwd_len, "/");
98+
strcpy(make_absolute_buf + cwd_len + need_slash, path);
99+
return make_absolute_buf;
100+
}
101+
102+
// Helper function defined only in this object file and weakly referenced from
103+
// `preopens.c` and `posix.c` This function isn't necessary unless `chdir` is
104+
// pulled in because all paths are otherwise absolute or relative to the root.
105+
int __wasilibc_find_relpath_alloc(
106+
const char *path,
107+
const char **abs_prefix,
108+
char **relative_buf,
109+
size_t *relative_buf_len,
110+
int can_realloc
111+
) {
112+
// First, make our path absolute taking the cwd into account.
113+
const char *abspath = make_absolute(path);
114+
if (abspath == NULL) {
115+
errno = ENOMEM;
116+
return -1;
117+
}
118+
119+
// Next use our absolute path and split it. Find the preopened `fd` parent
120+
// directory and set `abs_prefix`. Next up we'll be trying to fit `rel`
121+
// into `relative_buf`.
122+
const char *rel;
123+
int fd = __wasilibc_find_abspath(abspath, abs_prefix, &rel);
124+
if (fd == -1)
125+
return -1;
126+
127+
size_t rel_len = strlen(rel);
128+
if (*relative_buf_len < rel_len + 1) {
129+
if (!can_realloc) {
130+
errno = ERANGE;
131+
return -1;
132+
}
133+
char *tmp = realloc(*relative_buf, rel_len + 1);
134+
if (tmp == NULL) {
135+
errno = ENOMEM;
136+
return -1;
137+
}
138+
*relative_buf = tmp;
139+
*relative_buf_len = rel_len + 1;
140+
}
141+
strcpy(*relative_buf, rel);
142+
return fd;
143+
}

0 commit comments

Comments
 (0)