Skip to content

Commit

Permalink
Improve executable file path detection
Browse files Browse the repository at this point in the history
  • Loading branch information
ur4t committed Oct 21, 2023
1 parent ea56574 commit 6f87bf7
Showing 1 changed file with 179 additions and 83 deletions.
262 changes: 179 additions & 83 deletions c/scheme.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@
#include <fcntl.h>
#include <stddef.h>

#if defined(__APPLE__) && defined(__MACH__)
#include <mach-o/dyld.h>
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif /* O_BINARY */
Expand Down Expand Up @@ -464,7 +460,8 @@ void S_generic_invoke(ptr tc, ptr code) {
/* MISCELLANEOUS HELPERS */

/* locally defined functions */
static IBOOL next_path(char *path, const char *name, const char *ext, const char **sp, const char **dsp);
static IBOOL next_path(const char *execpath, char *path, const char *name,
const char *ext, const char **sp, const char **dsp);
static const char *path_last(const char *path);
static char *get_defaultheapdirs(void);

Expand All @@ -489,6 +486,159 @@ static const char *path_last(const char *p) {
return p;
}


#ifdef WIN32

static char *get_self_path(const char *execpath) {
wchar_t buf[BOOT_PATH_MAX];
DWORD n = GetModuleFileNameW(NULL, buf, BOOT_PATH_MAX);
if (n == 0 || (n == BOOT_PATH_MAX && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
return NULL;
}
return Swide_to_utf8(buf);
}

#else

static char *path_append(const char *s1, const char *s2) {
size_t l1 = strlen(s1);
size_t l2 = strlen(s2);
char *r = (char *)malloc(l1 + l2 + 2);
memcpy(r, s1, l1);
if (r[l1 - 1] != '/') {
r[l1++] = '/';
}
memcpy(r + l1, s2, l2);
r[l1 + l2] = '\0';
return r;
}

/* Get executable path via argv[0] and the `PATH` environment variable */
static char *get_self_path_generic(const char *execpath) {
if (strchr(execpath, '/')) {
return strdup(execpath);
}
char *pv = getenv("PATH");
if (pv == NULL) {
return NULL;
}
char *s = strdup(pv);
if (s == NULL) {
return NULL;
}
char *state = NULL;
for (char *t = strtok_r(s, ":", &state); t != NULL;
t = strtok_r(NULL, ":", &state)) {
char *r = path_append(t, execpath);
if (access(r, X_OK) == 0) {
free(s);
return r;
}
free(r);
}
free(s);
return NULL;
}

#if defined(__sun__) && defined(__svr4__)
static char *get_self_path_platform() {
char * r = getexecname();
if (r != NULL) {
return strdup(r);
}
return NULL;
}
#endif

#if defined(__OpenBSD__)
static char *get_self_path_platform() { return NULL; }
#endif

#if defined(__linux__) || defined(__CYGWIN__) || defined(__gnu_hurd__)
#define PROC_SELF_EXE_FILESYSTEM_PATH "/proc/self/exe"
#endif

#if defined(__NetBSD__) || defined(__minix) || defined(__DragonFly__) || \
defined(__FreeBSD_kernel__) || defined(_AIX)
#define PROC_SELF_EXE_FILESYSTEM_PATH "/proc/curproc/file"
#endif

#if defined(PROC_SELF_EXE_FILESYSTEM_PATH)
static char *get_self_path_platform() { return strdup(PROC_SELF_EXE_FILESYSTEM_PATH); }
#undef PROC_SELF_EXE_FILESYSTEM_PATH
#endif

#if defined(__FreeBSD__)
#include <osreldate.h>
#if __FreeBSD_version >= 1300057
#include <sys/auxv.h>
#else
#include <machine/elf.h>
extern char **environ;
#endif
static char *get_self_path_platform() {
/* On FreeBSD if the exec path specified in ELF auxiliary vectors is
preferred, if available. /proc/curproc/file and the KERN_PROC_PATHNAME
sysctl may not return the desired path if there are multiple hardlinks
to the file. */
#if __FreeBSD_version >= 1300057
for (size_t bufsize = 256;; bufsize *= 2) {
char *buf = (char *)malloc(bufsize);
if (elf_aux_info(AT_EXECPATH, buf, bufsize) == 0) {
return buf;
}
free(buf);
if (errno != EINVAL) {
break;
}
}
#else
/* elf_aux_info(AT_EXECPATH, ... is not available in all supported versions,
fall back to finding the ELF auxiliary vectors after the process's
environment. */
char **p = environ;
while (*p++ != 0)
;
/* Iterate through auxiliary vectors for AT_EXECPATH. */
for (Elf_Auxinfo *aux = (Elf_Auxinfo *)p; aux->a_type != AT_NULL; aux++) {
if (aux->a_type == AT_EXECPATH) {
return strdup((char *)aux->a_un.a_ptr);
}
}
#endif
return NULL;
}
#endif

#if defined(__APPLE__) && defined(__MACH__)
#include <mach-o/dyld.h>
static char *get_self_path_platform() {
uint32_t bufsize = 256;
char *buf = (char *)malloc(bufsize);
if (_NSGetExecutablePath(buf, &bufsize) == 0) {
return buf;
}
free(buf);
buf = (char *)malloc(bufsize);
if (_NSGetExecutablePath(buf, &bufsize) == 0) {
return buf;
}
return NULL;
}
#endif

char *get_self_path(const char *exec_file) {
char *r = get_self_path_platform();
if (r == NULL) {
r = get_self_path_generic(exec_file);
}
char *rr = realpath(r, NULL);
free(r);
return rr;
}

#endif /* WIN32 */

#ifdef WIN32
#ifndef DEFAULT_HEAP_PATH
/* by default, look in executable directory or in parallel boot directory */
Expand Down Expand Up @@ -530,7 +680,8 @@ static char *get_defaultheapdirs() {
* the search path. path should be a pointer to an unoccupied buffer
* BOOT_PATH_MAX characters long. either or both of sp/dsp may be empty,
* but neither may be null, i.e., (char *)0. */
static IBOOL next_path(char *path, const char *name, const char *ext,
static IBOOL next_path(const char *execpath, char *path,
const char *name, const char *ext,
const char **sp, const char **dsp) {
char *p;
const char *s, *t;
Expand All @@ -548,70 +699,19 @@ static IBOOL next_path(char *path, const char *name, const char *ext,
switch (*s) {
case 'x': {
s += 1;
char *tstart = get_self_path(execpath);
if (tstart == NULL) {
#ifdef WIN32
wchar_t exepath[BOOT_PATH_MAX]; DWORD n;
n = GetModuleFileNameW(NULL, exepath, BOOT_PATH_MAX);
if (n == 0 || (n == BOOT_PATH_MAX && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
fprintf(stderr, "warning: executable path is too long; ignoring %%x\n");
} else {
char *tstart;
const char *tend;
tstart = Swide_to_utf8(exepath);
t = tstart;
tend = path_last(t);
if (tend != t) tend -= 1; /* back up to directory separator */
while (t != tend) setp(*t++);
free(tstart);
}
fprintf(stderr, "warning: failed to get executable path (%s); ignoring %%x\n", "Path is too long");
#else
#if defined(__APPLE__) && defined(__MACH__)
char exepath[BOOT_PATH_MAX]; uint32_t n;
if (_NSGetExecutablePath(exepath, &n) != 0) {
fprintf(stderr, "warning: executable path is too long; ignoring %%x\n");
break;
}
#elif defined(__sun__) && defined(__svr4__)
char exepath[BOOT_PATH_MAX]; ssize_t n;
int fd = OPEN("/proc/self/execname", O_RDONLY);
if (fd == -1) {
fprintf(stderr, "warning: failed to open execname (%s); ignoring %%x\n", strerror(errno));
break;
}
n = READ(fd, exepath, BOOT_PATH_MAX);
CLOSE(fd);
if (n < 0) {
fprintf(stderr, "warning: failed to read execname (%s); ignoring %%x\n", strerror(errno));
break;
}
if (n == BOOT_PATH_MAX) {
fprintf(stderr, "warning: executable path is too long; ignoring %%x\n");
break;
}
exepath[n] = '\0';
#elif defined(__linux__) || defined(__gnu_hurd__)
char *exepath = "/proc/self/exe";
#elif defined(__FreeBSD_kernel__) || defined(__NetBSD__)
char *exepath = "/proc/curproc/file";
#else
char *exepath;
fprintf(stderr, "warning: %%x is not supported; ignoring %%x\n");
break;
fprintf(stderr, "warning: failed to get executable path (%s); ignoring %%x\n", strerror(errno));
#endif
char tstart[BOOT_PATH_MAX];
if (realpath(exepath, tstart) == NULL) {
if (errno == ENAMETOOLONG) {
fprintf(stderr, "warning: executable path is too long; ignoring %%x\n");
} else {
fprintf(stderr, "warning: failed to get executable path (%s); ignoring %%x\n", strerror(errno));
}
} else {
const char *tend;
t = tstart;
tend = path_last(t);
if (tend != t) tend -= 1; /* back up to directory separator */
while (t != tend) setp(*t++);
}
#endif
const char *tend = path_last(tstart);
t = tstart;
if (tend != t) tend -= 1; /* back up to directory separator */
while (t != tend) setp(*t++);
free(tstart);
break;
}
case 'm':
Expand Down Expand Up @@ -756,7 +856,7 @@ static void finish_dependencies_header(int fd, const char *path, int c) {
}
}

static IBOOL find_boot(const char *name, const char *ext, IBOOL direct_pathp,
static IBOOL find_boot(const char *execpath, const char *name, const char *ext, IBOOL direct_pathp,
int fd,
IBOOL errorp) {
char pathbuf[BOOT_PATH_MAX], buf[BOOT_PATH_MAX];
Expand Down Expand Up @@ -798,7 +898,7 @@ static IBOOL find_boot(const char *name, const char *ext, IBOOL direct_pathp,

path = pathbuf;
while (1) {
if (!next_path(pathbuf, name, ext, &sp, &dsp)) {
if (!next_path(execpath, pathbuf, name, ext, &sp, &dsp)) {
if (errorp) {
fprintf(stderr, "cannot find compatible boot file %s%s in search path:\n \"%s%s\"\n",
name, ext,
Expand Down Expand Up @@ -845,7 +945,7 @@ static IBOOL find_boot(const char *name, const char *ext, IBOOL direct_pathp,
CLOSE(fd);
S_abnormal_exit();
}
if (find_boot(buf, ".boot", 0, -1, 0)) break;
if (find_boot(execpath, buf, ".boot", 0, -1, 0)) break;
if (c == ')') {
char *sep; char *wastebuf[8];
fprintf(stderr, "cannot find subordinate boot file");
Expand Down Expand Up @@ -1136,17 +1236,17 @@ static void check_boot_file_state(const char *who) {

extern void Sregister_boot_file(const char *name) {
check_boot_file_state("Sregister_boot_file");
find_boot(name, "", 0, -1, 1);
find_boot("", name, "", 0, -1, 1);
}

extern void Sregister_boot_direct_file(const char *name) {
check_boot_file_state("Sregister_boot_direct_file");
find_boot(name, "", 1, -1, 1);
find_boot("", name, "", 1, -1, 1);
}

extern void Sregister_boot_file_fd(const char *name, int fd) {
check_boot_file_state("Sregister_boot_file_fd");
find_boot(name, "", 1, fd, 1);
find_boot("", name, "", 1, fd, 1);
}

extern void Sregister_boot_file_fd_region(const char *name,
Expand Down Expand Up @@ -1175,14 +1275,10 @@ extern void Sregister_heap_file(UNUSED const char *path) {
S_abnormal_exit();
}

extern void Sbuild_heap(const char *kernel, void (*custom_init)(void)) {
extern void Sbuild_heap(const char *execpath, void (*custom_init)(void)) {
ptr tc = Svoid; /* initialize to make gcc happy */
ptr p;

#if defined(ALWAYS_USE_BOOT_FILE)
kernel = ALWAYS_USE_BOOT_FILE;
#endif

switch (current_state) {
case UNINITIALIZED:
case DEINITIALIZED:
Expand All @@ -1201,14 +1297,14 @@ extern void Sbuild_heap(const char *kernel, void (*custom_init)(void)) {
S_boot_time = 1;

if (boot_count == 0) {
const char *name;

if (!kernel) {
const char *name = path_last(execpath);
#if defined(ALWAYS_USE_BOOT_FILE)
name = ALWAYS_USE_BOOT_FILE;
#endif
if (!name) {
fprintf(stderr, "no boot file or executable name specified\n");
S_abnormal_exit();
}

name = path_last(kernel);
if (strlen(name) >= BOOT_PATH_MAX) {
fprintf(stderr, "executable name too long: %s\n", name);
S_abnormal_exit();
Expand All @@ -1228,7 +1324,7 @@ extern void Sbuild_heap(const char *kernel, void (*custom_init)(void)) {
}
#endif

if (!find_boot(name, ".boot", 0, -1, 0)) {
if (!find_boot(execpath, name, ".boot", 0, -1, 0)) {
fprintf(stderr, "cannot find compatible %s.boot in search path\n \"%s%s\"\n",
name,
Sschemeheapdirs, Sdefaultheapdirs);
Expand Down

0 comments on commit 6f87bf7

Please sign in to comment.