Skip to content

Commit aa37a32

Browse files
authored
Make $prog.ape more reliable on Apple Silicon (#1071)
Now it doesn't matter what argv `$prog.ape` is invoked with. We just get our executable path the Apple way.
1 parent c4205f8 commit aa37a32

File tree

2 files changed

+71
-82
lines changed

2 files changed

+71
-82
lines changed

ape/ape-m1.c

Lines changed: 70 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -103,36 +103,39 @@ struct Syslib {
103103
char *(*dlerror)(void);
104104
};
105105

106-
#define ELFCLASS32 1
107-
#define ELFDATA2LSB 1
108-
#define EM_AARCH64 183
109-
#define ET_EXEC 2
110-
#define ET_DYN 3
111-
#define PT_LOAD 1
112-
#define PT_DYNAMIC 2
113-
#define PT_INTERP 3
114-
#define EI_CLASS 4
115-
#define EI_DATA 5
116-
#define PF_X 1
117-
#define PF_W 2
118-
#define PF_R 4
119-
#define AT_PHDR 3
120-
#define AT_PHENT 4
121-
#define AT_PHNUM 5
122-
#define AT_PAGESZ 6
123-
#define AT_BASE 7
124-
#define AT_ENTRY 9
125-
#define AT_UID 11
126-
#define AT_EUID 12
127-
#define AT_GID 13
128-
#define AT_EGID 14
129-
#define AT_HWCAP 16
130-
#define AT_HWCAP2 16
131-
#define AT_SECURE 23
132-
#define AT_RANDOM 25
133-
#define AT_EXECFN 31
134-
135-
#define AUXV_WORDS 29
106+
#define ELFCLASS32 1
107+
#define ELFDATA2LSB 1
108+
#define EM_AARCH64 183
109+
#define ET_EXEC 2
110+
#define ET_DYN 3
111+
#define PT_LOAD 1
112+
#define PT_DYNAMIC 2
113+
#define PT_INTERP 3
114+
#define EI_CLASS 4
115+
#define EI_DATA 5
116+
#define PF_X 1
117+
#define PF_W 2
118+
#define PF_R 4
119+
#define AT_PHDR 3
120+
#define AT_PHENT 4
121+
#define AT_PHNUM 5
122+
#define AT_PAGESZ 6
123+
#define AT_BASE 7
124+
#define AT_FLAGS 8
125+
#define AT_FLAGS_PRESERVE_ARGV0_BIT 0
126+
#define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT)
127+
#define AT_ENTRY 9
128+
#define AT_UID 11
129+
#define AT_EUID 12
130+
#define AT_GID 13
131+
#define AT_EGID 14
132+
#define AT_HWCAP 16
133+
#define AT_HWCAP2 16
134+
#define AT_SECURE 23
135+
#define AT_RANDOM 25
136+
#define AT_EXECFN 31
137+
138+
#define AUXV_WORDS 31
136139

137140
/* from the xnu codebase */
138141
#define _COMM_PAGE_START_ADDRESS 0x0000000FFFFFC000ul
@@ -322,17 +325,7 @@ static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) {
322325
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
323326
memmove(ps->path + pathlen, ps->name, ps->namelen);
324327
ps->path[pathlen + ps->namelen] = 0;
325-
if (!access(ps->path, X_OK)) {
326-
if (ps->indirect) {
327-
ps->namelen -= 4;
328-
ps->path[pathlen + ps->namelen] = 0;
329-
if (access(ps->path, X_OK) < 0) {
330-
Pexit(ps->path, -errno, "access(X_OK)");
331-
}
332-
}
333-
return 1;
334-
}
335-
return 0;
328+
return !access(ps->path, X_OK);
336329
}
337330

338331
static char SearchPath(struct PathSearcher *ps) {
@@ -358,21 +351,17 @@ static char FindCommand(struct PathSearcher *ps) {
358351
ps->path[0] = 0;
359352

360353
/* paths are always 100% taken literally when a slash exists
361-
$ ape foo/bar.com arg1 arg2 */
362-
if (memchr(ps->name, '/', ps->namelen)) {
363-
return AccessCommand(ps, 0);
364-
}
365-
366-
/* we don't run files in the current directory
354+
$ ape foo/bar.com arg1 arg2
355+
we don't run files in the current directory
367356
$ ape foo.com arg1 arg2
368357
unless $PATH has an empty string entry, e.g.
369358
$ expert PATH=":/bin"
370359
$ ape foo.com arg1 arg2
371360
however we will execute this
372361
$ ape - foo.com foo.com arg1 arg2
373362
because cosmo's execve needs it */
374-
if (ps->literally && AccessCommand(ps, 0)) {
375-
return 1;
363+
if (ps->literally || memchr(ps->name, '/', ps->namelen)) {
364+
return AccessCommand(ps, 0);
376365
}
377366

378367
/* otherwise search for name on $PATH */
@@ -382,7 +371,8 @@ static char FindCommand(struct PathSearcher *ps) {
382371
static char *Commandv(struct PathSearcher *ps, const char *name,
383372
const char *syspath) {
384373
ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin";
385-
if (!(ps->namelen = StrLen((ps->name = name)))) return 0;
374+
ps->name = name;
375+
if (!(ps->namelen = ps->indirect ? ps->indirect : StrLen(ps->name))) return 0;
386376
if (ps->namelen + 1 > sizeof(ps->path)) return 0;
387377
if (FindCommand(ps)) {
388378
return ps->path;
@@ -861,25 +851,27 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
861851
auxv[7] = ebuf->ehdr.e_entry;
862852
auxv[8] = AT_PAGESZ;
863853
auxv[9] = pagesz;
864-
auxv[10] = AT_UID;
865-
auxv[11] = getuid();
866-
auxv[12] = AT_EUID;
867-
auxv[13] = geteuid();
868-
auxv[14] = AT_GID;
869-
auxv[15] = getgid();
870-
auxv[16] = AT_EGID;
871-
auxv[17] = getegid();
872-
auxv[18] = AT_HWCAP;
873-
auxv[19] = 0xffb3ffffu;
874-
auxv[20] = AT_HWCAP2;
875-
auxv[21] = 0x181;
876-
auxv[22] = AT_SECURE;
877-
auxv[23] = issetugid();
878-
auxv[24] = AT_RANDOM;
879-
auxv[25] = (long)M->rando;
880-
auxv[26] = AT_EXECFN;
881-
auxv[27] = (long)execfn;
882-
auxv[28] = 0;
854+
auxv[10] = AT_FLAGS;
855+
auxv[11] = M->ps.literally ? AT_FLAGS_PRESERVE_ARGV0 : 0;
856+
auxv[12] = AT_UID;
857+
auxv[13] = getuid();
858+
auxv[14] = AT_EUID;
859+
auxv[15] = geteuid();
860+
auxv[16] = AT_GID;
861+
auxv[17] = getgid();
862+
auxv[18] = AT_EGID;
863+
auxv[19] = getegid();
864+
auxv[20] = AT_HWCAP;
865+
auxv[21] = 0xffb3ffffu;
866+
auxv[22] = AT_HWCAP2;
867+
auxv[23] = 0x181;
868+
auxv[24] = AT_SECURE;
869+
auxv[25] = issetugid();
870+
auxv[26] = AT_RANDOM;
871+
auxv[27] = (long)M->rando;
872+
auxv[28] = AT_EXECFN;
873+
auxv[29] = (long)execfn;
874+
auxv[30] = 0;
883875

884876
/* we're now ready to load */
885877
Spawn(exe, fd, sp, e, p, &M->lib, M->ps.path);
@@ -891,7 +883,7 @@ int main(int argc, char **argv, char **envp) {
891883
struct ApeLoader *M;
892884
long *sp, *sp2, *auxv;
893885
union ElfEhdrBuf *ebuf;
894-
char *p, *pe, *exe, *prog, *execfn, *shell;
886+
char *p, *pe, *exe, *prog, *execfn;
895887

896888
/* allocate loader memory in program's arg block */
897889
n = sizeof(struct ApeLoader);
@@ -954,12 +946,16 @@ int main(int argc, char **argv, char **envp) {
954946
M->lib.dlerror = dlerror;
955947

956948
/* getenv("_") is close enough to at_execfn */
957-
execfn = argc > 0 ? argv[0] : 0;
949+
execfn = 0;
958950
for (i = 0; envp[i]; ++i) {
959951
if (envp[i][0] == '_' && envp[i][1] == '=') {
960952
execfn = envp[i] + 2;
961953
}
962954
}
955+
prog = GetEnv(envp + i + 1, "executable_path");
956+
if (!execfn) {
957+
execfn = prog;
958+
}
963959

964960
/* sneak the system five abi back out of args */
965961
sp = (long *)(argv - 1);
@@ -981,8 +977,8 @@ int main(int argc, char **argv, char **envp) {
981977
sp = sp2;
982978

983979
/* interpret command line arguments */
984-
if ((M->ps.indirect = argc > 0 ? GetIndirectOffset(argv[0]) : 0)) {
985-
/* if argv[0] is $prog.ape, then we strip off the .ape and run
980+
if ((M->ps.indirect = GetIndirectOffset(prog))) {
981+
/* if called as $prog.ape, then strip off the .ape and run the
986982
$prog. This allows you to use symlinks to trick the OS when
987983
a native executable is required. For example, let's say you
988984
want to use the APE binary /opt/cosmos/bin/bash as a system
@@ -991,13 +987,7 @@ int main(int argc, char **argv, char **envp) {
991987
but it will if you say:
992988
ln -sf /usr/local/bin/ape /opt/cosmos/bin/bash.ape
993989
and then use #!/opt/cosmos/bin/bash.ape instead. */
994-
M->ps.literally = 0;
995-
if (*argv[0] == '-' && (shell = GetEnv(envp, "SHELL")) &&
996-
!StrCmp(argv[0] + 1, BaseName(shell))) {
997-
execfn = prog = shell;
998-
} else {
999-
prog = (char *)sp[1];
1000-
}
990+
M->ps.literally = 1;
1001991
argc = sp[0];
1002992
argv = (char **)(sp + 1);
1003993
} else if ((M->ps.literally = argc >= 3 && !StrCmp(argv[1], "-"))) {

test/libc/calls/getprogramexecutablename_test.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ void SetUpOnce(void) {
5858
skiptests = IsOpenbsd() || (IsXnu() && !IsXnuSilicon());
5959
}
6060
} else {
61-
skiparg0 =
62-
!(IsXnuSilicon() || (getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0));
61+
skiparg0 = !(getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0);
6362
}
6463
fprintf(stderr, loaded ? "loaded\n" : "not loaded\n");
6564
if (skiptests) {

0 commit comments

Comments
 (0)