Add -k OSNAME flag to apelink (#1383)

Let's say you pass the `-M blink-mips.elf` flag to apelink, so that your
ape binary will bundle a compressed build of blink, and the shell script
will extract that binary and launch your program under it, if running on
a MIPS system. However, for any given microprocessor architecture, we'll
need a separate loader for each operating system. The issue is ELF OSABI
isn't very useful. As an example, SerenityOS and Linux both have SYSV in
the OSABI field. So to tell their binaries apart we'd have to delve into
various other conventions, like special sections and PT_NOTE structures.

To make things simple this change introduces the `-k OS` flag to apelink
which generate shell script content that ensures `OS` matches `uname -s`
before attempting to execute a loader. For example, you could say:

    apelink -k Linux -M blink-linux-arm.elf -M blink-linux-mips.elf \
            -k Darwin -M blink-darwin-ppc.elf \
            ...

To introduce support for old 32-bit architectures on multiple OSes, when
building your cosmo binary.
This commit is contained in:
Brett Jia 2025-03-12 16:26:51 -04:00 committed by GitHub
parent b235492e71
commit 7b69652854
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -85,6 +85,13 @@
" executable will self-modify its header on\n" \
" the first run, to use the platform format\n" \
"\n" \
" -k KERNEL test for maching kernel name [repeatable]\n" \
" when set, the shell script for subsequent\n" \
" loader executables will check if uname -s\n" \
" output matches the kernel string, only if\n" \
" the loader executable architecture is not\n" \
" an architecture in the input binary list\n" \
"\n" \
" -M PATH bundle ape loader source code file for m1\n" \
" processors running the xnu kernel so that\n" \
" it can be compiled on the fly by xcode\n" \
@ -213,6 +220,7 @@ struct Loader {
char *ddarg_size1;
char *ddarg_skip2;
char *ddarg_size2;
const char *kernel;
};
struct Loaders {
@ -244,6 +252,7 @@ static struct Inputs inputs;
static char ape_heredoc[15];
static enum Strategy strategy;
static struct Loaders loaders;
static const char *loader_kernel;
static const char *custom_sh_code;
static bool force_bypass_binfmt_misc;
static bool generate_debuggable_binary;
@ -979,13 +988,19 @@ static void AddLoader(const char *path) {
if (loaders.n == ARRAYLEN(loaders.p)) {
Die(prog, "too many loaders");
}
loaders.p[loaders.n++].path = path;
struct Loader *loader = &loaders.p[loaders.n++];
loader->path = path;
loader->kernel = loader_kernel;
}
static void SetLoaderKernel(const char *kernel) {
loader_kernel = kernel;
}
static void GetOpts(int argc, char *argv[]) {
int opt, bits;
bool got_support_vector = false;
while ((opt = getopt(argc, argv, "hvgsGBo:l:S:M:V:")) != -1) {
while ((opt = getopt(argc, argv, "hvgsGBo:l:k:S:M:V:")) != -1) {
switch (opt) {
case 'o':
outpath = optarg;
@ -1009,6 +1024,10 @@ static void GetOpts(int argc, char *argv[]) {
HashInputString("-l");
AddLoader(optarg);
break;
case 'k':
HashInputString("-k");
SetLoaderKernel(optarg);
break;
case 'S':
HashInputString("-S");
HashInputString(optarg);
@ -1632,16 +1651,24 @@ static char *GenerateScriptIfMachine(char *p, struct Input *in) {
static char *GenerateScriptIfLoaderMachine(char *p, struct Loader *loader) {
if (loader->machine == EM_NEXGEN32E) {
return stpcpy(p, "if [ \"$m\" = x86_64 ] || [ \"$m\" = amd64 ]; then\n");
p = stpcpy(p, "if [ \"$m\" = x86_64 ] || [ \"$m\" = amd64 ]");
} else if (loader->machine == EM_AARCH64) {
return stpcpy(p, "if [ \"$m\" = aarch64 ] || [ \"$m\" = arm64 ]; then\n");
p = stpcpy(p, "if [ \"$m\" = aarch64 ] || [ \"$m\" = arm64 ]");
} else if (loader->machine == EM_PPC64) {
return stpcpy(p, "if [ \"$m\" = ppc64le ]; then\n");
p = stpcpy(p, "if [ \"$m\" = ppc64le ]");
} else if (loader->machine == EM_MIPS) {
return stpcpy(p, "if [ \"$m\" = mips64 ]; then\n");
p = stpcpy(p, "if [ \"$m\" = mips64 ]");
} else {
Die(loader->path, "unsupported cpu architecture");
}
if (loader->kernel) {
p = stpcpy(p, " && [ \"$k\" = ");
p = stpcpy(p, loader->kernel);
p = stpcpy(p, " ]");
}
return stpcpy(p, "; then\n");
}
static char *FinishGeneratingDosHeader(char *p) {
@ -1892,7 +1919,8 @@ int main(int argc, char *argv[]) {
for (i = 0; i < loaders.n; ++i) {
for (j = i + 1; j < loaders.n; ++j) {
if (loaders.p[i].os == loaders.p[j].os &&
loaders.p[i].machine == loaders.p[j].machine) {
loaders.p[i].machine == loaders.p[j].machine &&
strcmp(loaders.p[i].kernel, loaders.p[j].kernel) == 0) {
Die(prog, "multiple ape loaders specified for the same platform");
}
}
@ -2206,6 +2234,10 @@ int main(int argc, char *argv[]) {
}
// extract the ape loader for non-input architectures
// if the user requested a host kernel check, get the host kernel
if (loader_kernel) {
p = stpcpy(p, "k=$(uname -s 2>/dev/null) || k=unknown\n");
}
for (i = 0; i < loaders.n; ++i) {
struct Loader *loader = loaders.p + i;
if (loader->used) {