I've done a lot more work on this, and I have the impression that this is a bug in macOS, at least on 12 and 13 Intel.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/syslimits.h>
// used for debugging apple pointer issues, see
// https://bugs.kde.org/show_bug.cgi?id=517304
#define DEBUG_ENV
// On Darwin there's this secret fourth argument, 'apple'.
// That's kind of like a cut down obfuscated version of auxv.
// For the moment we only support the first entry, executable_path=
int main(int argc, char *argv[], char *envp[], char *apple[])
{
char *pargv = calloc((PATH_MAX+1), sizeof(char)),
*pappl = calloc((PATH_MAX+1), sizeof(char));
int i;
for (i = 0; envp[i]; i++) {
#if defined(DEBUG_ENV)
fprintf(stderr, "apple-main-arg: i %d &envp[i] %p envp[i] %s\n", i, &envp[i], envp[i]);
#endif
}
#if defined(DEBUG_ENV)
fprintf(stderr, "2 slots after envp\n");
fprintf(stderr, "apple-main-arg: i %d &envp[i] %p envp[i] %s\n", i, &envp[i], envp[i]);
fprintf(stderr, "apple-main-arg: i %d &envp[i] %p envp[i] %s\n", i+1, &envp[i+1], envp[i+1]);
fprintf(stderr, "apple-main-arg: i %d &envp[i] %p envp[i] %s\n", i+2, &envp[i+2], envp[i+2]);
fprintf(stderr, "apple %p\n", apple);
int j = 0;
while (apple[j]) {
fprintf(stderr, "apple-main-arg: j %d &apple[j] %p apple[j] %s\n", j, &apple[j], apple[j]);
++j;
}
if (j == 0) {
fprintf(stderr, "apple-main-arg: j %d &apple[j] %p apple[j] %s\n", j, &apple[j], apple[j]);
fprintf(stderr, "apple-main-arg: 1 slot after apple\n");
fprintf(stderr, "apple-main-arg: j %d &apple[j] %p apple[j] %s\n", j+1, &apple[j+1], apple[j+1]);
} else {
fprintf(stderr, "apple-main-arg: 1 slot after apple\n");
fprintf(stderr, "apple-main-arg: j %d &apple[j] %p apple[j] %s\n", j, &apple[j], apple[j]);
}
#endif
// envp[i]==NULL; envp[i+1]==apple[0]==executable_path
assert(envp[i+1] == apple[0]);
// Make sure realpath(argv[0]) == realpath(apple[0]). (realpath resolves
// symlinks.)
// PJF this changed with macOS 10.14, apple path now has a prefix
const char prefix[] = "executable_path=";
const size_t prefix_len = strlen(prefix);
assert(strncmp(apple[0], prefix, prefix_len) == 0);
realpath(apple[0]+prefix_len, pappl);
realpath(argv[0], pargv);
assert(0 == strcmp(pargv, pappl));
free(pargv);
free(pappl);
return 0;
}
If I build and run the above then, on its own it will run OK.
If I run it with
export DYLD_INSERT_LIBRARIES=/Users/paulf/scratch/valgrind/none/tests/darwin/../../../.in_place/vgpreload_core-amd64-darwin.so
then it fails
apple-main-arg: i 24 &envp[i] 0x7ff7b1a48af8 envp[i] A__z="*SHLVL
2 slots after envp
apple-main-arg: i 25 &envp[i] 0x7ff7b1a48b00 envp[i] (null)
apple-main-arg: i 26 &envp[i] 0x7ff7b1a48b08 envp[i] executable_path=./apple-main-arg
apple-main-arg: i 27 &envp[i] 0x7ff7b1a48b10 envp[i] (null)
apple 0x7ff7b1a48b10
apple-main-arg: j 0 &apple[j] 0x7ff7b1a48b10 apple[j] (null)
apple-main-arg: 1 slot after apple
apple-main-arg: j 1 &apple[j] 0x7ff7b1a48b18 apple[j] (null)
Assertion failed: (envp[i+1] == apple[0]), function main, file apple-main-arg.c, line 51.
./test.ksh: line 7: 3524: Abort
Abort