Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imgact_elf: Load APE executables faster #1334

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 115 additions & 9 deletions sys/kern/imgact_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@

static int __elfN(check_header)(const Elf_Ehdr *hdr);
static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp,
const char *interp, int32_t *osrel, uint32_t *fctl0);
const char *interp, int32_t *osrel, uint32_t *fctl0, const Elf_Ehdr *hdr);
static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr,
u_long *entry);
static int __elfN(load_section)(const struct image_params *imgp,
Expand All @@ -97,7 +97,7 @@
static bool kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel);
static bool __elfN(check_note)(struct image_params *imgp,
Elf_Brandnote *checknote, int32_t *osrel, bool *has_fctl0,
uint32_t *fctl0);
uint32_t *fctl0, const Elf_Ehdr *hdr);
static vm_prot_t __elfN(trans_prot)(Elf_Word);
static Elf_Word __elfN(untrans_prot)(vm_prot_t);
static size_t __elfN(prepare_register_notes)(struct thread *td,
Expand Down Expand Up @@ -335,9 +335,8 @@

static Elf_Brandinfo *
__elfN(get_brandinfo)(struct image_params *imgp, const char *interp,
int32_t *osrel, uint32_t *fctl0)
int32_t *osrel, uint32_t *fctl0, const Elf_Ehdr *hdr)
{
const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
Elf_Brandinfo *bi, *bi_m;
bool ret, has_fctl0;
int i, interp_name_len;
Expand Down Expand Up @@ -365,7 +364,7 @@
*fctl0 = 0;
*osrel = 0;
ret = __elfN(check_note)(imgp, bi->brand_note, osrel,
&has_fctl0, fctl0);
&has_fctl0, fctl0, hdr);
/* Give brand a chance to veto check_note's guess */
if (ret && bi->header_supported) {
ret = bi->header_supported(imgp, osrel,
Expand Down Expand Up @@ -999,6 +998,103 @@
return (0);
}


static int
__elfN(ape_find_printf)(const char *page, int i)
{
for (; i + 8 < 4096; ++i) {
if (memcmp(page + i, "printf '", 8) == 0)
return (i + 8);
}
return (-1);
}

static int
__elfN(ape_parse_octal)(const char *page, int i, int *pc)
{
int c;
if ('0' <= page[i] && page[i] <= '7') {
c = page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
}
}
*pc = c;
}
return (i);
}

/*
* Files beginning with "MZqFpD" are Actually Portable Executables,
* which have a printf statement in the first 4096 bytes with octal
* codes that specify the ELF header. APE also specifies `jartsr='`
* as an alternative prefix, intended for binaries that do not want
* to be identified as Windows executables. Like the \177ELF magic,
* all these prefixes decode as x86 jump instructions that could be
* used for 16-bit bootloaders or 32-bit / 64-bit flat executables.
* Most importantly they provide a fallback path for Thompson shell
* compatible command interpreters, which do not require a shebang,
* e.g. bash, zsh, fish, bourne, almquist, etc. Please note that in
* order to meet the requirements of POSIX.1, the single quote must
* be followed by a newline character, before any null bytes occur.
* See also: https://www.austingroupbugs.net/view.php?id=1250
*/
static int
__elfN(ape_decode_elf)(const char *page, char *ebuf)
{
int c;
int i;
int retval;
Elf_Ehdr *ehdr;
int ebuf_index;
int desired_machine;

#if defined(__aarch64__)
desired_machine = EM_AARCH64;
#elif defined(__powerpc64__)
desired_machine = EM_PPC64;
#elif defined(__riscv)
desired_machine = EM_RISCV;
#else
desired_machine = EM_X86_64;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer an explicit match for x86-64 and a #error default case. It's almost certainly wrong that armv7 and i386 will get EM_X86_64. Is the APE format 64-bit only and thus this all needs a appropriate guard?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just ELF_ARCH?

#endif

i = 0;
keep_looking:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a requirement, but this feels like it would be a pretty natural use of do {...} while (ehdr->e_machine != desired_machine);

retval = __elfN(ape_find_printf)(page, i);
if (retval < 0)
return (-1);

i = retval;
retval = -1;
ebuf_index = 0;
for (;;) {
if (i + 4 > 4096)
return (-1);
c = page[i++] & 255;
if (c == '\'')
break;
if (ebuf_index >= 64)
goto keep_looking;
if (c == '\\')
i = __elfN(ape_parse_octal)(page, i, &c);
ebuf[ebuf_index++] = c;
}
if (ebuf_index != 64)
goto keep_looking;
if (memcmp(ebuf, ELFMAG, SELFMAG) != 0)
goto keep_looking;
ehdr = (Elf_Ehdr *)ebuf;
if (ehdr->e_machine != desired_machine)
goto keep_looking;

return (0);
}

static int
__elfN(get_interp)(struct image_params *imgp, const Elf_Phdr *phdr,
char **interpp, bool *free_interpp)
Expand Down Expand Up @@ -1112,9 +1208,21 @@
int32_t osrel;
bool free_interp;
int error, i, n;
union {
char buf[64];
Elf_Ehdr hdr;
} ape;

hdr = (const Elf_Ehdr *)imgp->image_header;

/*
* Check if this is an Actually Portable Executable.
*/
if ((memcmp(imgp->image_header, "MZqFpD='", 8) == 0 ||
memcmp(imgp->image_header, "jartsr='", 8) == 0) &&
__elfN(ape_decode_elf)(imgp->image_header, ape.buf) == 0)
hdr = &ape.hdr;

/*
* Do we have a valid ELF header ?
*
Expand Down Expand Up @@ -1227,7 +1335,7 @@
}
}

brand_info = __elfN(get_brandinfo)(imgp, interp, &osrel, &fctl0);
brand_info = __elfN(get_brandinfo)(imgp, interp, &osrel, &fctl0, hdr);
if (brand_info == NULL) {
uprintf("ELF binary type \"%u\" not known.\n",
hdr->e_ident[EI_OSABI]);
Expand Down Expand Up @@ -2850,18 +2958,16 @@
*/
static bool
__elfN(check_note)(struct image_params *imgp, Elf_Brandnote *brandnote,
int32_t *osrel, bool *has_fctl0, uint32_t *fctl0)
int32_t *osrel, bool *has_fctl0, uint32_t *fctl0, const Elf_Ehdr *hdr)
{
const Elf_Phdr *phdr;
const Elf_Ehdr *hdr;
struct brandnote_cb_arg b_arg;
struct fctl_cb_arg f_arg;
int i, j;

hdr = (const Elf_Ehdr *)imgp->image_header;
phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
b_arg.brandnote = brandnote;
b_arg.osrel = osrel;

Check warning on line 2970 in sys/kern/imgact_elf.c

View workflow job for this annotation

GitHub Actions / Style Checker

Missing Signed-off-by: line
f_arg.has_fctl0 = has_fctl0;
f_arg.fctl0 = fctl0;

Expand Down
Loading