Loaders rewrite argv[0] for old binaries (#1170)

For this to work, a loader has to be able to tell the difference between
an ‘old’ and a ‘new’ binary. This is achieved via a repurposing of ELF’s
e_flags field. We previously tried to use the padding in e_ident for it,
but binutils was resetting it to zero in e.g. strip.

This introduces one new ELF flag for cosmopolitan binaries. It is called
`EF_APE_MODERN`. We choose 0x101ca75, "lol cat 5".

It should now be safe to install the ape loader binfmt registration with
the `P` flag.
This commit is contained in:
Jōshin 2024-05-07 17:42:18 -07:00 committed by GitHub
parent 57c0b065c8
commit 7d31fc311a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 27 additions and 3 deletions

View file

@ -146,6 +146,9 @@ struct Syslib {
#define AT_RANDOM 25 #define AT_RANDOM 25
#define AT_EXECFN 31 #define AT_EXECFN 31
#define EF_APE_MODERN 0x101ca75
#define EF_APE_MODERN_MASK 0x1ffffff
#define AUXV_WORDS 31 #define AUXV_WORDS 31
/* from the xnu codebase */ /* from the xnu codebase */
@ -799,7 +802,7 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
} }
static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
const char *exe, int fd, long *sp, long *auxv, char *exe, int fd, long *sp, long *auxv,
char *execfn) { char *execfn) {
long i, rc; long i, rc;
unsigned size; unsigned size;
@ -820,6 +823,10 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
if (e->e_machine != EM_AARCH64) { if (e->e_machine != EM_AARCH64) {
return "couldn't find ELF header with ARM64 machine type"; return "couldn't find ELF header with ARM64 machine type";
} }
if ((e->e_flags & EF_APE_MODERN_MASK) != EF_APE_MODERN && sp[0] > 0) {
/* change argv[0] to resolved path for older binaries */
((char **)(sp + 1))[0] = exe;
}
if (e->e_phentsize != sizeof(struct ElfPhdr)) { if (e->e_phentsize != sizeof(struct ElfPhdr)) {
Pexit(exe, 0, "e_phentsize is wrong"); Pexit(exe, 0, "e_phentsize is wrong");
} }

View file

@ -196,7 +196,7 @@ ape_mz:
.quad ape_elf_entry // 18: e_entry .quad ape_elf_entry // 18: e_entry
.quad ape_elf_phoff // 20: e_phoff .quad ape_elf_phoff // 20: e_phoff
.quad ape_elf_shoff // 28: e_shoff .quad ape_elf_shoff // 28: e_shoff
.long 0 // 30: e_flags .long 0x101ca75 // 30: ape e_flags
.short 64 // 34: e_ehsize .short 64 // 34: e_ehsize
.short 56 // 36: e_phentsize .short 56 // 36: e_phentsize
.short ape_elf_phnum // 38: e_phnum .short ape_elf_phnum // 38: e_phnum
@ -669,7 +669,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
.shstub ape_elf_entry,8 // 18: e_entry .shstub ape_elf_entry,8 // 18: e_entry
.shstub ape_elf_phoff,8 // 20: e_phoff .shstub ape_elf_phoff,8 // 20: e_phoff
.shstub ape_elf_shoff,8 // 28: e_shoff .shstub ape_elf_shoff,8 // 28: e_shoff
.ascii "\\0\\0\\0\\0" // 30: e_flags .ascii "\\165\\312\\1\\1" // 30: ape e_flags
.ascii "\\100\\0" // 34: e_ehsize .ascii "\\100\\0" // 34: e_ehsize
.ascii "\\070\\0" // 36: e_phentsize .ascii "\\070\\0" // 36: e_phentsize
.shstub ape_elf_phnum,2 // 38: e_phnum .shstub ape_elf_phnum,2 // 38: e_phnum

View file

@ -152,6 +152,9 @@
#define PR_SET_MM 35 #define PR_SET_MM 35
#define PR_SET_MM_EXE_FILE 13 #define PR_SET_MM_EXE_FILE 13
#define EF_APE_MODERN 0x101ca75
#define EF_APE_MODERN_MASK 0x1ffffff
#define READ32(S) \ #define READ32(S) \
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \ ((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000) (unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
@ -834,6 +837,10 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
return "couldn't find ELF header with x86-64 machine type"; return "couldn't find ELF header with x86-64 machine type";
} }
#endif #endif
if ((e->e_flags & EF_APE_MODERN_MASK) != EF_APE_MODERN && sp[0] > 0) {
/* change argv[0] to resolved path for older binaries */
((char **)(sp + 1))[0] = exe;
}
if (e->e_phentsize != sizeof(struct ElfPhdr)) { if (e->e_phentsize != sizeof(struct ElfPhdr)) {
Pexit(os, exe, 0, "e_phentsize is wrong"); Pexit(os, exe, 0, "e_phentsize is wrong");
} }

View file

@ -85,6 +85,10 @@
#define EM_RISCV 243 #define EM_RISCV 243
#define EM_BPF 247 #define EM_BPF 247
/* the ape flag, "lol cat 5" */
#define EF_APE_MODERN 0x101ca75
#define EF_APE_MODERN_MASK 0x1ffffff
#define GRP_COMDAT 1 #define GRP_COMDAT 1
#define STN_UNDEF 0 #define STN_UNDEF 0

View file

@ -327,6 +327,11 @@ static void UseFreebsdOsAbi(void) {
elf->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; elf->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
} }
static void WriteApeFlags(void) {
/* try to be forward-compatible */
elf->e_flags = (elf->e_flags & ~EF_APE_MODERN_MASK) | EF_APE_MODERN;
}
/** /**
* Improve GCC11 `-fpatchable-function-entry` codegen. * Improve GCC11 `-fpatchable-function-entry` codegen.
* *
@ -668,6 +673,7 @@ static void FixupObject(void) {
UseFreebsdOsAbi(); UseFreebsdOsAbi();
} }
if (elf->e_type != ET_REL) { if (elf->e_type != ET_REL) {
WriteApeFlags();
PurgeIfuncSections(); PurgeIfuncSections();
RelinkZipFiles(); RelinkZipFiles();
} }