Skip to content

Commit

Permalink
Prepend getcwd to exename early in init (#1048)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdomino authored Jan 1, 2024
1 parent 2f89c24 commit 68dbe53
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 22 deletions.
52 changes: 30 additions & 22 deletions libc/calls/getprogramexecutablename.greg.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/ok.h"

#ifdef __x86_64__
__static_yoink("_init_program_executable_name");
#endif

#define CTL_KERN 1
#define KERN_PROC 14
#define KERN_PROC_PATHNAME_FREEBSD 12
Expand Down Expand Up @@ -88,31 +92,37 @@ static int OldApeLoader(char *s) {
(!strncmp((b = basename(s)), ".ape-", 5) && AllNumDot(b + 5));
}

// if q exists then turn it into an absolute path. we also try adding
// a .com suffix since the ape auto-appends it when resolving
static int TryPath(const char *q, int com) {
char c, *p, *e;
if (!q) return 0;
p = g_prog.u.buf;
e = p + sizeof(g_prog.u.buf);
static char *CopyWithCwd(const char *q, char *p, char *e) {
char c;
if (*q != '/') {
if (q[0] == '.' && q[1] == '/') {
q += 2;
}
int got = __getcwd(p, e - p - 1 /* '/' */ - com * 4);
int got = __getcwd(p, e - p - 1 /* '/' */);
if (got != -1) {
p += got - 1;
*p++ = '/';
}
}
while ((c = *q++)) {
if (p + com * 4 + 1 /* nul */ < e) {
if (p + 1 /* nul */ < e) {
*p++ = c;
} else {
return 0;
return NULL;
}
}
*p = 0;
return p;
}

// if q exists then turn it into an absolute path. we also try adding
// a .com suffix since the ape auto-appends it when resolving
static int TryPath(const char *q, int com) {
char *p;
if (!(p = CopyWithCwd(q, g_prog.u.buf,
g_prog.u.buf + sizeof(g_prog.u.buf) - com * 4))) {
return 0;
}
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return 1;
if (!com) return 0;
p = WRITE32LE(p, READ32LE(".com"));
Expand All @@ -121,6 +131,16 @@ static int TryPath(const char *q, int com) {
return 0;
}

// if the loader passed a relative path, prepend cwd to it.
// called early in init.
void __init_program_executable_name(void) {
if (__program_executable_name && *__program_executable_name != '/' &&
CopyWithCwd(__program_executable_name, g_prog.u.buf,
g_prog.u.buf + sizeof(g_prog.u.buf))) {
__program_executable_name = g_prog.u.buf;
}
}

static inline void InitProgramExecutableNameImpl(void) {
size_t n;
ssize_t got;
Expand Down Expand Up @@ -167,18 +187,6 @@ static inline void InitProgramExecutableNameImpl(void) {
goto UseEmpty;
}
}
if (*__program_executable_name == '/') {
return;
}
// loader passed us a relative path; prepend cwd.
if (TryPath(__program_executable_name, 0)) {
goto UseBuf;
}
/* if TryPath fails, it probably failed because getcwd() was too long.
we are out of options now; KERN_PROC_PATHNAME et al will return the
name of the loader not the binary, and argv et al will at best have
the same problem. just use the relative path we got from the loader
as-is, and accept that if we chdir then things will break. */
return;
}

Expand Down
27 changes: 27 additions & 0 deletions libc/calls/program_executable_name_init.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/macros.internal.h"

.init.start 305,_init_program_executable_name
push %rdi
push %rsi
call __init_program_executable_name
pop %rsi
pop %rdi
.init.end 305,_init_program_executable_name
3 changes: 3 additions & 0 deletions libc/runtime/cosmo2.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1, char *exename,
// initialize file system
__init_fds(argc, argv, envp);

// prepend cwd to executable path
__init_program_executable_name();

__enable_tls();

#if 0
Expand Down
1 change: 1 addition & 0 deletions libc/runtime/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ void *__mmap_unlocked(void *, size_t, int, int, int, int64_t);
int __munmap_unlocked(char *, size_t);
void __on_arithmetic_overflow(void);
void __init_fds(int, char **, char **);
void __init_program_executable_name(void);

COSMOPOLITAN_C_END_
#endif /* ANSI */
Expand Down
9 changes: 9 additions & 0 deletions test/libc/calls/getprogramexecutablename_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
Expand Down Expand Up @@ -57,6 +58,9 @@ void SetUpOnce(void) {

__attribute__((__constructor__)) static void Child(int argc, char *argv[]) {
if (argc >= 2 && !strcmp(argv[1], "Child")) {
if (sys_chdir("/")) {
exit(122);
}
if (strcmp(argv[2], GetProgramExecutableName())) {
exit(123);
}
Expand Down Expand Up @@ -138,4 +142,9 @@ TEST(GetProgramExecutableName, movedSelf) {
execve(buf, (char *[]){"hello", "Child", buf, "hello", 0}, (char *[]){0});
abort();
EXITS(0);
SPAWN(fork);
execve("./test", (char *[]){"hello", "Child", buf, "hello", 0},
(char *[]){0});
abort();
EXITS(0);
}

0 comments on commit 68dbe53

Please sign in to comment.