make embedding wrapper executable less sensitive to argv[0]

Running a `starter`-based executable with an argv[0] different
than the executable's path can make sense in various situations, but
it doesn't work for finding code embedded in the enxecutable. On
platforms where it's possible to get the current process's executable
(not looking at you, OpenBSD), then use that instead of argv[0] for
the purposes of loading embedded code.

Related to #3685
This commit is contained in:
Matthew Flatt 2021-03-12 07:25:52 -07:00
parent 4a06e4a1e3
commit 6f58ef5458
10 changed files with 186 additions and 116 deletions

View File

@ -201,6 +201,11 @@ flags:
@racket[module-predefined?]. This option is normally embedded
in a stand-alone binary that also embeds Racket code.}
@item{@FlagFirst{Y} @nonterm{file} @nonterm{n} @nonterm{m} @nonterm{p} :
Like @Flag{k} @nonterm{n} @nonterm{m} @nonterm{p}, but reading
from @nonterm{file} (without any adjustment on Mac OS or Windows
for a segment or resource offset).}
@item{@FlagFirst{m} or @DFlagFirst{main} : Evaluates a call to
@racketidfont{main} as bound in the top-level environment. All
of the command-line arguments that are not processed as
@ -473,7 +478,8 @@ Extra arguments following the last option are available from the
@history[#:changed "6.90.0.17" @elem{Added @Flag{O}/@DFlag{stdout}.}
#:changed "7.1.0.5" @elem{Added @Flag{M}/@DFlag{compile-any}.}
#:changed "7.8.0.6" @elem{Added @Flag{Z}.}
#:changed "8.0.0.10" @elem{Added @Flag{E}.}]
#:changed "8.0.0.10" @elem{Added @Flag{E}.}
#:changed "8.0.0.11" @elem{Added @Flag{Y}.}]
@; ----------------------------------------------------------------------

View File

@ -53,13 +53,13 @@ char *add_to_str(const char *addr, long amt)
}
#endif
static char *make_embedded_load(const char *start, const char *end)
static char *make_embedded_load(const char *file, const char *start, const char *end)
{
char *s;
int slen, elen;
int slen, elen, flen;
#if defined(OS_X) || defined(DOS_FILE_SYSTEM)
{
if (file == NULL) {
long fileoff;
fileoff = get_segment_offset();
start = add_to_str(start, fileoff);
@ -67,12 +67,16 @@ static char *make_embedded_load(const char *start, const char *end)
}
#endif
if (!file) file = "";
slen = strlen(start);
elen = strlen(end);
flen = strlen(file);
s = (char *)malloc(slen + elen + 2);
s = (char *)malloc(slen + elen + flen + 3);
memcpy(s, start, slen + 1);
memcpy(s + slen + 1, end, elen + 1);
memcpy(s + slen + elen + 2, file, flen + 1);
return s;
}
@ -701,6 +705,7 @@ static int run_from_cmd_line(int argc, char *_argv[],
int *eval_kind, num_enl;
int no_more_switches = 0;
int show_vers = 0;
char *embedding_file;
#endif
#if !defined(DONT_RUN_REP) || !defined(DONT_PARSE_COMMAND_LINE)
int use_repl = 0;
@ -998,6 +1003,18 @@ static int run_from_cmd_line(int argc, char *_argv[],
no_init_ns = 1;
break;
case 'k':
case 'Y':
if (*str == 'Y') {
if (argc < 2) {
PRINTF("%s: missing file name after %s switch\n",
prog, real_switch);
goto show_need_help;
}
argv++;
--argc;
embedding_file = argv[0];
} else
embedding_file = NULL;
if (argc < 4) {
PRINTF("%s: missing %s after %s switch\n",
prog,
@ -1007,12 +1024,12 @@ static int run_from_cmd_line(int argc, char *_argv[],
}
argv++;
--argc;
se = make_embedded_load(argv[0], argv[1]);
se = make_embedded_load(embedding_file, argv[0], argv[1]);
evals_and_loads[num_enl] = se;
argv++;
--argc;
eval_kind[num_enl++] = mzcmd_EMBEDDED_REG;
se = make_embedded_load(argv[0], argv[1]);
se = make_embedded_load(embedding_file, argv[0], argv[1]);
evals_and_loads[num_enl] = se;
argv++;
--argc;
@ -1418,6 +1435,7 @@ static int run_from_cmd_line(int argc, char *_argv[],
" -r <file>, --script <file> : Same as -f <file> -N <file> --\n"
" -u <file>, --require-script <file> : Same as -t <file> -N <file> --\n"
" -k <n> <m> <p> : Load executable-embedded code from offset <n> to <p>\n"
" -Y <file> <n> <m> <p> : Like -k <n> <m> <p>, but from <file>\n"
" -m, --main : Call `main' with command-line arguments, print results\n"
" [*] Also `require's a `main' submodule, if any\n"
" Interaction options:\n"

View File

@ -60,7 +60,7 @@ MZDYNDEP = ../mzdyn.o $(srcdir)/../include/ext.exp $(srcdir)/../include/racket.e
dynexmpl.o: $(srcdir)/dynexmpl.c $(HEADERS)
$(PLAIN_CC) $(ALL_CFLAGS) -c $(srcdir)/dynexmpl.c -o dynexmpl.o
../starter@NOT_MINGW@@EXE_SUFFIX@: $(srcdir)/../../start/ustart.c
../starter@NOT_MINGW@@EXE_SUFFIX@: $(srcdir)/../../start/ustart.c $(srcdir)/../../start/unix_self.inc
$(PLAIN_CC) $(ALL_CFLAGS) -o ../starter@EXE_SUFFIX@ $(srcdir)/../../start/ustart.c
PARSE_CMDL = $(srcdir)/../../start/parse_cmdl.inc

View File

@ -3756,15 +3756,28 @@ Scheme_Object *scheme_eval_string_multi_with_prompt(const char *str, Scheme_Env
void scheme_embedded_load(intptr_t len, const char *desc, int predefined)
{
Scheme_Object *s, *e, *a[4], *eload;
Scheme_Object *s, *e, *f, *a[5], *eload;
int argc = 4;
eload = scheme_get_startup_export("embedded-load");
if (len < 0) {
/* description mode */
/* description mode: string embeds start, end, and filename, where
a 0-length filename means to find the executable via
`(system-path 'exec-file)`. */
int slen, elen, foff;
slen = strlen(desc);
elen = strlen(desc XFORM_OK_PLUS (slen + 1));
foff = slen + 1 + elen + 1;
s = scheme_make_utf8_string(desc);
e = scheme_make_utf8_string(desc XFORM_OK_PLUS strlen(desc) XFORM_OK_PLUS 1);
e = scheme_make_utf8_string(desc XFORM_OK_PLUS (slen + 1));
if (desc[foff] != 0) {
f = scheme_make_byte_string(desc XFORM_OK_PLUS foff);
argc = 5;
} else
f = scheme_false;
a[0] = s;
a[1] = e;
a[2] = scheme_false;
a[4] = f;
} else {
/* content mode */
a[0] = scheme_false;
@ -3773,7 +3786,7 @@ void scheme_embedded_load(intptr_t len, const char *desc, int predefined)
a[2] = s;
}
a[3] = (predefined ? scheme_true : scheme_false);
(void)scheme_apply(eload, 4, a);
(void)scheme_apply(eload, argc, a);
}
int scheme_is_predefined_module_path(Scheme_Object *m)

View File

@ -421,7 +421,9 @@ DEF_COLLECTS_DIR@MINGW@ =
DEF_CONFIG_DIR@MINGW@ =
DEF_C_DIRS = $(DEF_COLLECTS_DIR) $(DEF_CONFIG_DIR)
MAIN_DEPS = $(srcdir)/main.c $(srcdir)/boot.h $(srcdir)/../../start/config.inc cs_config.h
MAIN_DEPS = $(srcdir)/main.c $(srcdir)/boot.h cs_config.h \
$(srcdir)/../../start/config.inc \
$(srcdir)/../../start/unix_self.inc
main.o: $(MAIN_DEPS)
$(CC) $(CFLAGS) $(DEF_C_DIRS) -c -o main.o $(srcdir)/main.c
@ -432,7 +434,7 @@ grmain.o: $(srcdir)/grmain.c $(MAIN_DEPS) $(srcdir)/../../start/gui_filter.inc
boot.o: $(srcdir)/boot.c $(srcdir)/../../rktio/rktio.inc $(srcdir)/boot.h
$(CC) $(CFLAGS) -c -o boot.o $(srcdir)/boot.c
starter@NOT_MINGW@: $(srcdir)/../../start/ustart.c
starter@NOT_MINGW@: $(srcdir)/../../start/ustart.c $(srcdir)/../../start/unix_self.inc
$(CC) $(CFLAGS) -o starter $(srcdir)/../../start/ustart.c

View File

@ -57,30 +57,10 @@ PRESERVE_IN_EXECUTABLE
char *boot_file_data = "BooT FilE OffsetS:\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
static int boot_file_offset = 18;
#define USE_GENERIC_GET_SELF_PATH
#include "../../start/unix_self.inc"
#ifdef OS_X
# include <mach-o/dyld.h>
static char *get_self_path(char *exec_file)
{
char buf[1024], *s;
uint32_t size = sizeof(buf);
int r;
r = _NSGetExecutablePath(buf, &size);
if (!r)
return strdup(buf);
else {
s = malloc(size);
r = _NSGetExecutablePath(s, &size);
if (!r)
return s;
fprintf(stderr, "failed to get self\n");
exit(1);
}
}
# undef USE_GENERIC_GET_SELF_PATH
static long find_rktboot_section(char *me)
{
const struct mach_header *mh;
@ -147,68 +127,6 @@ static char *path_append(const char *p1, char *p2) {
#endif
#if defined(__linux__)
# include <errno.h>
static char *get_self_path(char *exec_file)
{
char buf[256], *s = buf;
ssize_t len, blen = sizeof(buf);
while (1) {
len = readlink("/proc/self/exe", s, blen-1);
if (len == (blen-1)) {
if (s != buf) free(s);
blen *= 2;
s = malloc(blen);
} else if (len < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
} else
break;
}
buf[len] = 0;
return strdup(buf);
}
# undef USE_GENERIC_GET_SELF_PATH
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/sysctl.h>
# include <errno.h>
static char *get_self_path(char *exec_file)
{
int mib[4];
char *s;
size_t len;
int r;
mib[0] = CTL_KERN;
#if defined(__NetBSD__)
mib[1] = KERN_PROC_ARGS;
mib[2] = getpid();
mib[3] = KERN_PROC_PATHNAME;
#else
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
#endif
r = sysctl(mib, 4, NULL, &len, NULL, 0);
if (r < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
}
s = malloc(len);
r = sysctl(mib, 4, s, &len, NULL, 0);
if (r < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
}
return s;
}
# undef USE_GENERIC_GET_SELF_PATH
#endif
#ifdef ELF_FIND_BOOT_SECTION
# include <elf.h>
# include <fcntl.h>
@ -289,7 +207,7 @@ static int scheme_utf8_encode(unsigned int *path, int zero_offset, int len,
#endif
#ifdef USE_GENERIC_GET_SELF_PATH
/* Get executable path via argv[0] and the `PATH` encironment variable */
/* Get executable path via argv[0] and the `PATH` environment variable */
static int has_slash(char *s)
{

View File

@ -403,10 +403,13 @@
(loop))))
loads))
(flags-loop rest-args (see saw 'non-config 'top)))]
[("-k")
(let*-values ([(n rest-args) (next-arg "starting and ending offsets" arg within-arg args)]
[(m rest-args) (next-arg "first ending offset" arg within-arg (cons "-k" rest-args))]
[(p rest-args) (next-arg "second ending offset" arg within-arg (cons "-k" rest-args))])
[("-k" "-Y")
(let*-values ([(f rest-args) (if (equal? arg "-Y")
(next-arg "file" arg within-arg args)
(values #f (cdr args)))]
[(n rest-args) (next-arg "starting and ending offsets" arg within-arg (cons arg rest-args))]
[(m rest-args) (next-arg "first ending offset" arg within-arg (cons arg rest-args))]
[(p rest-args) (next-arg "second ending offset" arg within-arg (cons arg rest-args))])
(let* ([add-segment-offset
(lambda (s what)
(let ([n (#%string->number s)])
@ -419,9 +422,9 @@
(set! loads
(cons
(lambda ()
(set! embedded-load-in-places (cons (list #f n m #f) embedded-load-in-places))
(embedded-load n m #f #t)
(embedded-load m p #f #f))
(set! embedded-load-in-places (cons (list f n m #f) embedded-load-in-places))
(embedded-load n m #f #t f)
(embedded-load m p #f #f f))
loads)))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config)))]

View File

@ -22,6 +22,7 @@
" -r <file>, --script <file> : Same as -f <file> -N <file> --\n"
" -u <file>, --require-script <file> : Same as -t <file> -N <file> --\n"
" -k <n> <m> <p> : Load executable-embedded code from offset <n> to <p>\n"
" -Y <file> <n> <m> <p> : Like -k <n> <m> <p>, but from <file>\n"
" -m, --main : Call `main' with command-line arguments, print results\n"
" [*] Also `require's a `main' submodule, if any\n"
" Interaction options:\n"
@ -70,6 +71,7 @@
" --cross-server <mach> <comp> <lib> : Drive cross-compiler (as only option)\n"
" Meta options:\n"
" -- : No argument following this switch is used as a switch\n"
" -Z : Ignore the argument following this switch\n"
" -h, --help : Show this information and exits, ignoring other options\n"
"Default options:\n"
" If only configuration options are provided, -i is added\n"

View File

@ -0,0 +1,86 @@
#define USE_GENERIC_GET_SELF_PATH
#if defined(__linux__)
# include <errno.h>
static char *get_self_path(char *exec_file)
{
char buf[256], *s = buf;
ssize_t len, blen = sizeof(buf);
while (1) {
len = readlink("/proc/self/exe", s, blen-1);
if (len == (blen-1)) {
if (s != buf) free(s);
blen *= 2;
s = malloc(blen);
} else if (len < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
} else
break;
}
buf[len] = 0;
return strdup(buf);
}
# undef USE_GENERIC_GET_SELF_PATH
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/sysctl.h>
# include <errno.h>
static char *get_self_path(char *exec_file)
{
int mib[4];
char *s;
size_t len;
int r;
mib[0] = CTL_KERN;
#if defined(__NetBSD__)
mib[1] = KERN_PROC_ARGS;
mib[2] = getpid();
mib[3] = KERN_PROC_PATHNAME;
#else
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
#endif
r = sysctl(mib, 4, NULL, &len, NULL, 0);
if (r < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
}
s = malloc(len);
r = sysctl(mib, 4, s, &len, NULL, 0);
if (r < 0) {
fprintf(stderr, "failed to get self (%d)\n", errno);
exit(1);
}
return s;
}
# undef USE_GENERIC_GET_SELF_PATH
#endif
#if defined(__APPLE__) && defined(__MACH__)
# include <mach-o/dyld.h>
static char *get_self_path(char *exec_file)
{
char buf[1024], *s;
uint32_t size = sizeof(buf);
int r;
r = _NSGetExecutablePath(buf, &size);
if (!r)
return strdup(buf);
else {
s = malloc(size);
r = _NSGetExecutablePath(s, &size);
if (!r)
return s;
fprintf(stderr, "failed to get self\n");
exit(1);
}
}
# undef USE_GENERIC_GET_SELF_PATH
#endif

View File

@ -262,6 +262,20 @@ static char *next_string(char *s)
return s + strlen(s) + 1;
}
#include "unix_self.inc"
#ifdef USE_GENERIC_GET_SELF_PATH
static char *adjust_self_reference(char *me)
{
return me;
}
#else
static char *adjust_self_reference(char *me)
{
return get_self_path(me);
}
#endif
typedef unsigned short ELF__Half;
typedef unsigned int ELF__Word;
typedef unsigned long ELF__Xword;
@ -353,7 +367,7 @@ static int try_elf_section(const char *me, int *_start, int *_decl_end, int *_pr
int main(int argc, char **argv)
{
char *me = argv[0], *data, **new_argv;
char *me = argv[0], *embedding_me, *data, **new_argv;
char *exe_path, *lib_path, *dll_path;
int start, decl_end, prog_end, end, count, fd, v, en, x11;
int argpos, inpos, collcount = 1, fix_argv;
@ -434,6 +448,11 @@ int main(int argc, char **argv)
}
}
/* use `me` for `-k`, unless we have a way to more directly get the
executable file that contains embedded code; if we do, then
argv[0] doesn't have to match the executable */
embedding_me = adjust_self_reference(me);
start = as_int(config + 8);
decl_end = as_int(config + 12);
prog_end = as_int(config + 16);
@ -441,7 +460,7 @@ int main(int argc, char **argv)
count = as_int(config + 24);
x11 = as_int(config + 28);
fix_argv = try_elf_section(me, &start, &decl_end, &prog_end, &end);
fix_argv = try_elf_section(embedding_me, &start, &decl_end, &prog_end, &end);
{
int offset, len;
@ -456,14 +475,14 @@ int main(int argc, char **argv)
}
data = (char *)malloc(end - prog_end);
new_argv = (char **)malloc((count + argc + (2 * collcount) + 12) * sizeof(char*));
new_argv = (char **)malloc((count + argc + (2 * collcount) + 13) * sizeof(char*));
fd = open(me, O_RDONLY, 0);
fd = open(embedding_me, O_RDONLY, 0);
lseek(fd, prog_end, SEEK_SET);
{
int expected_length = end - prog_end;
if (expected_length != read(fd, data, expected_length)) {
printf("read failed to read all %i bytes from file %s\n", expected_length, me);
printf("read failed to read all %i bytes from file %s\n", expected_length, embedding_me);
abort();
}
}
@ -548,9 +567,11 @@ int main(int argc, char **argv)
new_argv[argpos++] = absolutize(_configdir + _configdir_offset, me);
if (fix_argv) {
/* next three args are "-k" and numbers; fix
the numbers to match start, decl_end, and prog_end */
fix_argv = argpos + 1;
/* next four args are "-k" and numbers; leave room to insert the
filename in place of "-k", and fix the numbers to match start,
decl_end, and prog_end */
new_argv[argpos++] = "-Y";
fix_argv = argpos;
}
/* Add built-in flags: */
@ -567,9 +588,10 @@ int main(int argc, char **argv)
new_argv[argpos] = NULL;
if (fix_argv) {
new_argv[fix_argv] = num_to_string(start);
new_argv[fix_argv+1] = num_to_string(decl_end);
new_argv[fix_argv+2] = num_to_string(prog_end);
new_argv[fix_argv] = embedding_me;
new_argv[fix_argv+1] = num_to_string(start);
new_argv[fix_argv+2] = num_to_string(decl_end);
new_argv[fix_argv+3] = num_to_string(prog_end);
}
/* Execute the original binary: */