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.
This commit is contained in:
Jōshin 2024-01-07 10:13:20 -05:00 committed by GitHub
parent c4205f8305
commit aa37a327ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 81 deletions

View file

@ -103,36 +103,39 @@ struct Syslib {
char *(*dlerror)(void); char *(*dlerror)(void);
}; };
#define ELFCLASS32 1 #define ELFCLASS32 1
#define ELFDATA2LSB 1 #define ELFDATA2LSB 1
#define EM_AARCH64 183 #define EM_AARCH64 183
#define ET_EXEC 2 #define ET_EXEC 2
#define ET_DYN 3 #define ET_DYN 3
#define PT_LOAD 1 #define PT_LOAD 1
#define PT_DYNAMIC 2 #define PT_DYNAMIC 2
#define PT_INTERP 3 #define PT_INTERP 3
#define EI_CLASS 4 #define EI_CLASS 4
#define EI_DATA 5 #define EI_DATA 5
#define PF_X 1 #define PF_X 1
#define PF_W 2 #define PF_W 2
#define PF_R 4 #define PF_R 4
#define AT_PHDR 3 #define AT_PHDR 3
#define AT_PHENT 4 #define AT_PHENT 4
#define AT_PHNUM 5 #define AT_PHNUM 5
#define AT_PAGESZ 6 #define AT_PAGESZ 6
#define AT_BASE 7 #define AT_BASE 7
#define AT_ENTRY 9 #define AT_FLAGS 8
#define AT_UID 11 #define AT_FLAGS_PRESERVE_ARGV0_BIT 0
#define AT_EUID 12 #define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT)
#define AT_GID 13 #define AT_ENTRY 9
#define AT_EGID 14 #define AT_UID 11
#define AT_HWCAP 16 #define AT_EUID 12
#define AT_HWCAP2 16 #define AT_GID 13
#define AT_SECURE 23 #define AT_EGID 14
#define AT_RANDOM 25 #define AT_HWCAP 16
#define AT_EXECFN 31 #define AT_HWCAP2 16
#define AT_SECURE 23
#define AT_RANDOM 25
#define AT_EXECFN 31
#define AUXV_WORDS 29 #define AUXV_WORDS 31
/* from the xnu codebase */ /* from the xnu codebase */
#define _COMM_PAGE_START_ADDRESS 0x0000000FFFFFC000ul #define _COMM_PAGE_START_ADDRESS 0x0000000FFFFFC000ul
@ -322,17 +325,7 @@ static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) {
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
memmove(ps->path + pathlen, ps->name, ps->namelen); memmove(ps->path + pathlen, ps->name, ps->namelen);
ps->path[pathlen + ps->namelen] = 0; ps->path[pathlen + ps->namelen] = 0;
if (!access(ps->path, X_OK)) { return !access(ps->path, X_OK);
if (ps->indirect) {
ps->namelen -= 4;
ps->path[pathlen + ps->namelen] = 0;
if (access(ps->path, X_OK) < 0) {
Pexit(ps->path, -errno, "access(X_OK)");
}
}
return 1;
}
return 0;
} }
static char SearchPath(struct PathSearcher *ps) { static char SearchPath(struct PathSearcher *ps) {
@ -358,12 +351,8 @@ static char FindCommand(struct PathSearcher *ps) {
ps->path[0] = 0; ps->path[0] = 0;
/* paths are always 100% taken literally when a slash exists /* paths are always 100% taken literally when a slash exists
$ ape foo/bar.com arg1 arg2 */ $ ape foo/bar.com arg1 arg2
if (memchr(ps->name, '/', ps->namelen)) { we don't run files in the current directory
return AccessCommand(ps, 0);
}
/* we don't run files in the current directory
$ ape foo.com arg1 arg2 $ ape foo.com arg1 arg2
unless $PATH has an empty string entry, e.g. unless $PATH has an empty string entry, e.g.
$ expert PATH=":/bin" $ expert PATH=":/bin"
@ -371,8 +360,8 @@ static char FindCommand(struct PathSearcher *ps) {
however we will execute this however we will execute this
$ ape - foo.com foo.com arg1 arg2 $ ape - foo.com foo.com arg1 arg2
because cosmo's execve needs it */ because cosmo's execve needs it */
if (ps->literally && AccessCommand(ps, 0)) { if (ps->literally || memchr(ps->name, '/', ps->namelen)) {
return 1; return AccessCommand(ps, 0);
} }
/* otherwise search for name on $PATH */ /* otherwise search for name on $PATH */
@ -382,7 +371,8 @@ static char FindCommand(struct PathSearcher *ps) {
static char *Commandv(struct PathSearcher *ps, const char *name, static char *Commandv(struct PathSearcher *ps, const char *name,
const char *syspath) { const char *syspath) {
ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin"; ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin";
if (!(ps->namelen = StrLen((ps->name = name)))) return 0; ps->name = name;
if (!(ps->namelen = ps->indirect ? ps->indirect : StrLen(ps->name))) return 0;
if (ps->namelen + 1 > sizeof(ps->path)) return 0; if (ps->namelen + 1 > sizeof(ps->path)) return 0;
if (FindCommand(ps)) { if (FindCommand(ps)) {
return ps->path; return ps->path;
@ -861,25 +851,27 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
auxv[7] = ebuf->ehdr.e_entry; auxv[7] = ebuf->ehdr.e_entry;
auxv[8] = AT_PAGESZ; auxv[8] = AT_PAGESZ;
auxv[9] = pagesz; auxv[9] = pagesz;
auxv[10] = AT_UID; auxv[10] = AT_FLAGS;
auxv[11] = getuid(); auxv[11] = M->ps.literally ? AT_FLAGS_PRESERVE_ARGV0 : 0;
auxv[12] = AT_EUID; auxv[12] = AT_UID;
auxv[13] = geteuid(); auxv[13] = getuid();
auxv[14] = AT_GID; auxv[14] = AT_EUID;
auxv[15] = getgid(); auxv[15] = geteuid();
auxv[16] = AT_EGID; auxv[16] = AT_GID;
auxv[17] = getegid(); auxv[17] = getgid();
auxv[18] = AT_HWCAP; auxv[18] = AT_EGID;
auxv[19] = 0xffb3ffffu; auxv[19] = getegid();
auxv[20] = AT_HWCAP2; auxv[20] = AT_HWCAP;
auxv[21] = 0x181; auxv[21] = 0xffb3ffffu;
auxv[22] = AT_SECURE; auxv[22] = AT_HWCAP2;
auxv[23] = issetugid(); auxv[23] = 0x181;
auxv[24] = AT_RANDOM; auxv[24] = AT_SECURE;
auxv[25] = (long)M->rando; auxv[25] = issetugid();
auxv[26] = AT_EXECFN; auxv[26] = AT_RANDOM;
auxv[27] = (long)execfn; auxv[27] = (long)M->rando;
auxv[28] = 0; auxv[28] = AT_EXECFN;
auxv[29] = (long)execfn;
auxv[30] = 0;
/* we're now ready to load */ /* we're now ready to load */
Spawn(exe, fd, sp, e, p, &M->lib, M->ps.path); Spawn(exe, fd, sp, e, p, &M->lib, M->ps.path);
@ -891,7 +883,7 @@ int main(int argc, char **argv, char **envp) {
struct ApeLoader *M; struct ApeLoader *M;
long *sp, *sp2, *auxv; long *sp, *sp2, *auxv;
union ElfEhdrBuf *ebuf; union ElfEhdrBuf *ebuf;
char *p, *pe, *exe, *prog, *execfn, *shell; char *p, *pe, *exe, *prog, *execfn;
/* allocate loader memory in program's arg block */ /* allocate loader memory in program's arg block */
n = sizeof(struct ApeLoader); n = sizeof(struct ApeLoader);
@ -954,12 +946,16 @@ int main(int argc, char **argv, char **envp) {
M->lib.dlerror = dlerror; M->lib.dlerror = dlerror;
/* getenv("_") is close enough to at_execfn */ /* getenv("_") is close enough to at_execfn */
execfn = argc > 0 ? argv[0] : 0; execfn = 0;
for (i = 0; envp[i]; ++i) { for (i = 0; envp[i]; ++i) {
if (envp[i][0] == '_' && envp[i][1] == '=') { if (envp[i][0] == '_' && envp[i][1] == '=') {
execfn = envp[i] + 2; execfn = envp[i] + 2;
} }
} }
prog = GetEnv(envp + i + 1, "executable_path");
if (!execfn) {
execfn = prog;
}
/* sneak the system five abi back out of args */ /* sneak the system five abi back out of args */
sp = (long *)(argv - 1); sp = (long *)(argv - 1);
@ -981,8 +977,8 @@ int main(int argc, char **argv, char **envp) {
sp = sp2; sp = sp2;
/* interpret command line arguments */ /* interpret command line arguments */
if ((M->ps.indirect = argc > 0 ? GetIndirectOffset(argv[0]) : 0)) { if ((M->ps.indirect = GetIndirectOffset(prog))) {
/* if argv[0] is $prog.ape, then we strip off the .ape and run /* if called as $prog.ape, then strip off the .ape and run the
$prog. This allows you to use symlinks to trick the OS when $prog. This allows you to use symlinks to trick the OS when
a native executable is required. For example, let's say you a native executable is required. For example, let's say you
want to use the APE binary /opt/cosmos/bin/bash as a system 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) {
but it will if you say: but it will if you say:
ln -sf /usr/local/bin/ape /opt/cosmos/bin/bash.ape ln -sf /usr/local/bin/ape /opt/cosmos/bin/bash.ape
and then use #!/opt/cosmos/bin/bash.ape instead. */ and then use #!/opt/cosmos/bin/bash.ape instead. */
M->ps.literally = 0; M->ps.literally = 1;
if (*argv[0] == '-' && (shell = GetEnv(envp, "SHELL")) &&
!StrCmp(argv[0] + 1, BaseName(shell))) {
execfn = prog = shell;
} else {
prog = (char *)sp[1];
}
argc = sp[0]; argc = sp[0];
argv = (char **)(sp + 1); argv = (char **)(sp + 1);
} else if ((M->ps.literally = argc >= 3 && !StrCmp(argv[1], "-"))) { } else if ((M->ps.literally = argc >= 3 && !StrCmp(argv[1], "-"))) {

View file

@ -58,8 +58,7 @@ void SetUpOnce(void) {
skiptests = IsOpenbsd() || (IsXnu() && !IsXnuSilicon()); skiptests = IsOpenbsd() || (IsXnu() && !IsXnuSilicon());
} }
} else { } else {
skiparg0 = skiparg0 = !(getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0);
!(IsXnuSilicon() || (getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0));
} }
fprintf(stderr, loaded ? "loaded\n" : "not loaded\n"); fprintf(stderr, loaded ? "loaded\n" : "not loaded\n");
if (skiptests) { if (skiptests) {