/* $NetBSD: loadfile_elf32.c,v 1.59 2020/09/13 13:31:36 jmcneill Exp $ */ /* * Copyright (c) 1997, 2008, 2017 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center, by Christos Zoulas, and by Maxime Villard. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* If not included by exec_elf64.c, ELFSIZE won't be defined. */ #ifndef ELFSIZE #define ELFSIZE 32 #endif #ifdef _STANDALONE #include #include #else #include #include #include #include #include #include #include #endif #include #include #include "loadfile.h" #if ((ELFSIZE == 32) && defined(BOOT_ELF32)) || \ ((ELFSIZE == 64) && defined(BOOT_ELF64)) #define ELFROUND (ELFSIZE / 8) #ifndef _STANDALONE /* * Byte swapping may be necessary in the non-_STANDLONE case because * we may be built with a host compiler. */ #ifndef LIBSA_BIENDIAN_SUPPORT #define LIBSA_BIENDIAN_SUPPORT #endif #endif #ifdef LIBSA_BIENDIAN_SUPPORT #include "byteorder.h" #define E16(f) \ f = (bo == ELFDATA2LSB) ? sa_htole16(f) : sa_htobe16(f) #define E32(f) \ f = (bo == ELFDATA2LSB) ? sa_htole32(f) : sa_htobe32(f) #define E64(f) \ f = (bo == ELFDATA2LSB) ? sa_htole64(f) : sa_htobe64(f) #define I16(f) \ f = (bo == ELFDATA2LSB) ? sa_le16toh(f) : sa_be16toh(f) #define I32(f) \ f = (bo == ELFDATA2LSB) ? sa_le32toh(f) : sa_be32toh(f) #define I64(f) \ f = (bo == ELFDATA2LSB) ? sa_le64toh(f) : sa_be64toh(f) static void internalize_ehdr(Elf_Byte bo, Elf_Ehdr *ehdr) { #if ELFSIZE == 32 I16(ehdr->e_type); I16(ehdr->e_machine); I32(ehdr->e_version); I32(ehdr->e_entry); I32(ehdr->e_phoff); I32(ehdr->e_shoff); I32(ehdr->e_flags); I16(ehdr->e_ehsize); I16(ehdr->e_phentsize); I16(ehdr->e_phnum); I16(ehdr->e_shentsize); I16(ehdr->e_shnum); I16(ehdr->e_shstrndx); #elif ELFSIZE == 64 I16(ehdr->e_type); I16(ehdr->e_machine); I32(ehdr->e_version); I64(ehdr->e_entry); I64(ehdr->e_phoff); I64(ehdr->e_shoff); I32(ehdr->e_flags); I16(ehdr->e_ehsize); I16(ehdr->e_phentsize); I16(ehdr->e_phnum); I16(ehdr->e_shentsize); I16(ehdr->e_shnum); I16(ehdr->e_shstrndx); #else #error ELFSIZE is not 32 or 64 #endif } static void externalize_ehdr(Elf_Byte bo, Elf_Ehdr *ehdr) { #if ELFSIZE == 32 E16(ehdr->e_type); E16(ehdr->e_machine); E32(ehdr->e_version); E32(ehdr->e_entry); E32(ehdr->e_phoff); E32(ehdr->e_shoff); E32(ehdr->e_flags); E16(ehdr->e_ehsize); E16(ehdr->e_phentsize); E16(ehdr->e_phnum); E16(ehdr->e_shentsize); E16(ehdr->e_shnum); E16(ehdr->e_shstrndx); #elif ELFSIZE == 64 E16(ehdr->e_type); E16(ehdr->e_machine); E32(ehdr->e_version); E64(ehdr->e_entry); E64(ehdr->e_phoff); E64(ehdr->e_shoff); E32(ehdr->e_flags); E16(ehdr->e_ehsize); E16(ehdr->e_phentsize); E16(ehdr->e_phnum); E16(ehdr->e_shentsize); E16(ehdr->e_shnum); E16(ehdr->e_shstrndx); #else #error ELFSIZE is not 32 or 64 #endif } static void internalize_phdr(Elf_Byte bo, Elf_Phdr *phdr) { #if ELFSIZE == 32 I32(phdr->p_type); I32(phdr->p_offset); I32(phdr->p_vaddr); I32(phdr->p_paddr); I32(phdr->p_filesz); I32(phdr->p_memsz); I32(phdr->p_flags); I32(phdr->p_align); #elif ELFSIZE == 64 I32(phdr->p_type); I64(phdr->p_offset); I64(phdr->p_vaddr); I64(phdr->p_paddr); I64(phdr->p_filesz); I64(phdr->p_memsz); I32(phdr->p_flags); I64(phdr->p_align); #else #error ELFSIZE is not 32 or 64 #endif } static void internalize_shdr(Elf_Byte bo, Elf_Shdr *shdr) { #if ELFSIZE == 32 I32(shdr->sh_name); I32(shdr->sh_type); I32(shdr->sh_flags); I32(shdr->sh_addr); I32(shdr->sh_offset); I32(shdr->sh_size); I32(shdr->sh_link); I32(shdr->sh_info); I32(shdr->sh_addralign); I32(shdr->sh_entsize); #elif ELFSIZE == 64 I32(shdr->sh_name); I32(shdr->sh_type); I64(shdr->sh_flags); I64(shdr->sh_addr); I64(shdr->sh_offset); I64(shdr->sh_size); I32(shdr->sh_link); I32(shdr->sh_info); I64(shdr->sh_addralign); I64(shdr->sh_entsize); #else #error ELFSIZE is not 32 or 64 #endif } static void externalize_shdr(Elf_Byte bo, Elf_Shdr *shdr) { #if ELFSIZE == 32 E32(shdr->sh_name); E32(shdr->sh_type); E32(shdr->sh_flags); E32(shdr->sh_addr); E32(shdr->sh_offset); E32(shdr->sh_size); E32(shdr->sh_link); E32(shdr->sh_info); E32(shdr->sh_addralign); E32(shdr->sh_entsize); #elif ELFSIZE == 64 E32(shdr->sh_name); E32(shdr->sh_type); E64(shdr->sh_flags); E64(shdr->sh_addr); E64(shdr->sh_offset); E64(shdr->sh_size); E32(shdr->sh_link); E32(shdr->sh_info); E64(shdr->sh_addralign); E64(shdr->sh_entsize); #else #error ELFSIZE is not 32 or 64 #endif } #else /* LIBSA_BIENDIAN_SUPPORT */ /* * Byte swapping is never necessary in the !LIBSA_BIENDIAN_SUPPORT case * because we are being built with the target compiler. */ #define internalize_ehdr(bo, ehdr) /* nothing */ #define externalize_ehdr(bo, ehdr) /* nothing */ #define internalize_phdr(bo, phdr) /* nothing */ #define internalize_shdr(bo, shdr) /* nothing */ #define externalize_shdr(bo, shdr) /* nothing */ #endif /* LIBSA_BIENDIAN_SUPPORT */ #define IS_TEXT(p) (p.p_flags & PF_X) #define IS_DATA(p) ((p.p_flags & PF_X) == 0) #define IS_BSS(p) (p.p_filesz < p.p_memsz) #ifndef MD_LOADSEG /* Allow processor ABI specific segment loads */ #define MD_LOADSEG(a) /*CONSTCOND*/0 #endif /* -------------------------------------------------------------------------- */ #define KERNALIGN_SMALL (1 << 12) /* XXX should depend on marks[] */ #define KERNALIGN_LARGE (1 << 21) /* XXX should depend on marks[] */ /* * Read some data from a file, and put it in the bootloader memory (local). */ static int ELFNAMEEND(readfile_local)(int fd, Elf_Off elfoff, void *addr, size_t size) { ssize_t nr; if (lseek(fd, elfoff, SEEK_SET) == -1) { WARN(("lseek section headers")); return -1; } nr = read(fd, addr, size); if (nr == -1) { WARN(("read section headers")); return -1; } if (nr != (ssize_t)size) { errno = EIO; WARN(("read section headers")); return -1; } return 0; } /* * Read some data from a file, and put it in wherever in memory (global). */ static int ELFNAMEEND(readfile_global)(int fd, u_long offset, Elf_Off elfoff, Elf_Addr addr, size_t size) { ssize_t nr; /* some ports dont use the offset */ (void)&offset; if (lseek(fd, elfoff, SEEK_SET) == -1) { WARN(("lseek section")); return -1; } nr = READ(fd, addr, size); if (nr == -1) { WARN(("read section")); return -1; } if (nr != (ssize_t)size) { errno = EIO; WARN(("read section")); return -1; } return 0; } /* * Load a dynamic ELF binary into memory. Layout of the memory: * +------------+--------------+------------+------------------------+ * | ELF HEADER | SECT HEADERS | KERN SECTS | REL/RELA/SYM/STR SECTS | * +------------+--------------+------------+------------------------+ * The ELF HEADER start address is marks[MARK_END]. We then map the rest * by increasing maxp. An alignment is enforced between the code sections. * * The offsets of the KERNEL and SYM+REL sections are relative to the start * address of the ELF HEADER. We just give the kernel a pointer to the ELF * HEADER, and we let the kernel find the location and number of symbols by * itself. */ static int ELFNAMEEND(loadfile_dynamic)(int fd, Elf_Ehdr *elf, u_long *marks, int flags) { const u_long offset = marks[MARK_START]; Elf_Shdr *shdr; Elf_Addr shpp, addr; int i, j, loaded; size_t size, shdrsz, align; Elf_Addr maxp, elfp = 0; int ret; maxp = marks[MARK_END] - offset; internalize_ehdr(elf->e_ident[EI_DATA], elf); if (elf->e_type != ET_REL) { errno = EINVAL; return 1; } /* Create a local copy of the SECTION HEADERS. */ shdrsz = elf->e_shnum * sizeof(Elf_Shdr); shdr = ALLOC(shdrsz); ret = ELFNAMEEND(readfile_local)(fd, elf->e_shoff, shdr, shdrsz); if (ret == -1) { goto out; } /* * Load the ELF HEADER. Update the section offset, to be relative to * elfp. */ elf->e_phoff = 0; elf->e_shoff = sizeof(Elf_Ehdr); elf->e_phentsize = 0; elf->e_phnum = 0; elfp = maxp; externalize_ehdr(elf->e_ident[EI_DATA], elf); BCOPY(elf, elfp, sizeof(*elf)); internalize_ehdr(elf->e_ident[EI_DATA], elf); maxp += sizeof(Elf_Ehdr); #ifdef LIBSA_BIENDIAN_SUPPORT for (i = 0; i < elf->e_shnum; i++) internalize_shdr(elf->e_ident[EI_DATA], &shdr[i]); #endif /* Save location of the SECTION HEADERS. */ shpp = maxp; maxp += roundup(shdrsz, ELFROUND); /* * Load the KERNEL SECTIONS. */ maxp = roundup(maxp, KERNALIGN_SMALL); for (i = 0; i < elf->e_shnum; i++) { if (!(shdr[i].sh_flags & SHF_ALLOC)) { continue; } size = (size_t)shdr[i].sh_size; if (size <= KERNALIGN_SMALL) { align = KERNALIGN_SMALL; } else { align = KERNALIGN_LARGE; } addr = roundup(maxp, align); loaded = 0; switch (shdr[i].sh_type) { case SHT_NOBITS: /* Zero out bss. */ BZERO(addr, size); loaded = 1; break; case SHT_PROGBITS: ret = ELFNAMEEND(readfile_global)(fd, offset, shdr[i].sh_offset, addr, size); if (ret == -1) { goto out; } loaded = 1; break; default: loaded = 0; break; } if (loaded) { shdr[i].sh_offset = addr - elfp; maxp = roundup(addr + size, align); } } maxp = roundup(maxp, KERNALIGN_LARGE); /* * Load the REL/RELA/SYM/STR SECTIONS. */ maxp = roundup(maxp, ELFROUND); for (i = 0; i < elf->e_shnum; i++) { addr = maxp; size = (size_t)shdr[i].sh_size; switch (shdr[i].sh_type) { case SHT_STRTAB: for (j = 0; j < elf->e_shnum; j++) if (shdr[j].sh_type == SHT_SYMTAB && shdr[j].sh_link == (unsigned int)i) goto havesym; if (elf->e_shstrndx == i) goto havesym; /* * Don't bother with any string table that isn't * referenced by a symbol table. */ shdr[i].sh_offset = 0; break; havesym: case SHT_REL: case SHT_RELA: case SHT_SYMTAB: ret = ELFNAMEEND(readfile_global)(fd, offset, shdr[i].sh_offset, addr, size); if (ret == -1) { goto out; } shdr[i].sh_offset = maxp - elfp; maxp += roundup(size, ELFROUND); break; } } maxp = roundup(maxp, KERNALIGN_SMALL); /* * Finally, load the SECTION HEADERS. */ #ifdef LIBSA_BIENDIAN_SUPPORT for (i = 0; i < elf->e_shnum; i++) externalize_shdr(elf->e_ident[EI_DATA], &shdr[i]); #endif BCOPY(shdr, shpp, shdrsz); DEALLOC(shdr, shdrsz); /* * Just update MARK_SYM and MARK_END without touching the rest. */ marks[MARK_SYM] = LOADADDR(elfp); marks[MARK_END] = LOADADDR(maxp); return 0; out: DEALLOC(shdr, shdrsz); return 1; } /* -------------------------------------------------------------------------- */ /* * See comment below. This function is in charge of loading the SECTION HEADERS. */ static int ELFNAMEEND(loadsym)(int fd, Elf_Ehdr *elf, Elf_Addr maxp, Elf_Addr elfp, u_long *marks, int flags, Elf_Addr *nmaxp) { const u_long offset = marks[MARK_START]; int boot_load_ctf = 1; Elf_Shdr *shp; Elf_Addr shpp; char *shstr = NULL; size_t sz; size_t i, j, shstrsz = 0; struct __packed { Elf_Nhdr nh; uint8_t name[ELF_NOTE_NETBSD_NAMESZ + 1]; uint8_t desc[ELF_NOTE_NETBSD_DESCSZ]; } note; int first; int ret; sz = elf->e_shnum * sizeof(Elf_Shdr); shp = ALLOC(sz); ret = ELFNAMEEND(readfile_local)(fd, elf->e_shoff, shp, sz); if (ret == -1) { goto out; } shpp = maxp; maxp += roundup(sz, ELFROUND); #ifdef LIBSA_BIENDIAN_SUPPORT for (i = 0; i < elf->e_shnum; i++) internalize_shdr(elf->e_ident[EI_DATA], &shp[i]); #endif /* * First load the section names section. Only useful for CTF. */ if (boot_load_ctf && (elf->e_shstrndx != SHN_UNDEF)) { Elf_Off shstroff = shp[elf->e_shstrndx].sh_offset; shstrsz = shp[elf->e_shstrndx].sh_size; if (flags & LOAD_SYM) { ret = ELFNAMEEND(readfile_global)(fd, offset, shstroff, maxp, shstrsz); if (ret == -1) { goto out; } } /* Create a local copy */ shstr = ALLOC(shstrsz); ret = ELFNAMEEND(readfile_local)(fd, shstroff, shstr, shstrsz); if (ret == -1) { goto out; } shp[elf->e_shstrndx].sh_offset = maxp - elfp; maxp += roundup(shstrsz, ELFROUND); } /* * Now load the symbol sections themselves. Make sure the sections are * ELFROUND-aligned. Update sh_offset to be relative to elfp. Set it to * zero when we don't want the sections to be taken care of, the kernel * will properly skip them. */ first = 1; for (i = 1; i < elf->e_shnum; i++) { if (i == elf->e_shstrndx) { /* already loaded this section */ continue; } switch (shp[i].sh_type) { case SHT_PROGBITS: if (boot_load_ctf && shstr) { /* got a CTF section? */ if (strncmp(&shstr[shp[i].sh_name], ".SUNW_ctf", 10) == 0) { goto havesym; } } shp[i].sh_offset = 0; break; case SHT_STRTAB: for (j = 1; j < elf->e_shnum; j++) if (shp[j].sh_type == SHT_SYMTAB && shp[j].sh_link == (unsigned int)i) goto havesym; /* * Don't bother with any string table that isn't * referenced by a symbol table. */ shp[i].sh_offset = 0; break; havesym: case SHT_SYMTAB: if (flags & LOAD_SYM) { PROGRESS(("%s%ld", first ? " [" : "+", (u_long)shp[i].sh_size)); ret = ELFNAMEEND(readfile_global)(fd, offset, shp[i].sh_offset, maxp, shp[i].sh_size); if (ret == -1) { goto out; } } shp[i].sh_offset = maxp - elfp; maxp += roundup(shp[i].sh_size, ELFROUND); first = 0; break; case SHT_NOTE: if ((flags & LOAD_NOTE) == 0) break; if (shp[i].sh_size < sizeof(note)) { shp[i].sh_offset = 0; break; } ret = ELFNAMEEND(readfile_local)(fd, shp[i].sh_offset, ¬e, sizeof(note)); if (ret == -1) { goto out; } if (note.nh.n_namesz == ELF_NOTE_NETBSD_NAMESZ && note.nh.n_descsz == ELF_NOTE_NETBSD_DESCSZ && note.nh.n_type == ELF_NOTE_TYPE_NETBSD_TAG && memcmp(note.name, ELF_NOTE_NETBSD_NAME, sizeof(note.name)) == 0) { memcpy(&netbsd_version, ¬e.desc, sizeof(netbsd_version)); } shp[i].sh_offset = 0; break; default: shp[i].sh_offset = 0; break; } } if (flags & LOAD_SYM) { #ifdef LIBSA_BIENDIAN_SUPPORT for (i = 0; i < elf->e_shnum; i++) externalize_shdr(elf->e_ident[EI_DATA], &shp[i]); #endif BCOPY(shp, shpp, sz); if (first == 0) PROGRESS(("]")); } *nmaxp = maxp; DEALLOC(shp, sz); if (shstr != NULL) DEALLOC(shstr, shstrsz); return 0; out: DEALLOC(shp, sz); if (shstr != NULL) DEALLOC(shstr, shstrsz); return -1; } /* -------------------------------------------------------------------------- */ /* * Load a static ELF binary into memory. Layout of the memory: * +-----------------+------------+-----------------+-----------------+ * | KERNEL SEGMENTS | ELF HEADER | SECTION HEADERS | SYMBOL SECTIONS | * +-----------------+------------+-----------------+-----------------+ * The KERNEL SEGMENTS start address is fixed by the segments themselves. We * then map the rest by increasing maxp. * * The offsets of the SYMBOL SECTIONS are relative to the start address of the * ELF HEADER. The shdr offset of ELF HEADER points to SECTION HEADERS. * * We just give the kernel a pointer to the ELF HEADER, which is enough for it * to find the location and number of symbols by itself later. */ static int ELFNAMEEND(loadfile_static)(int fd, Elf_Ehdr *elf, u_long *marks, int flags) { const u_long offset = marks[MARK_START]; Elf_Phdr *phdr; int i, first; size_t sz; Elf_Addr minp = ~0, maxp = 0, pos = 0, elfp = 0; int ret; /* for ports that define progress to nothing */ (void)&first; /* have not seen a data segment so far */ marks[MARK_DATA] = 0; internalize_ehdr(elf->e_ident[EI_DATA], elf); if (elf->e_type != ET_EXEC) { errno = EINVAL; return 1; } sz = elf->e_phnum * sizeof(Elf_Phdr); phdr = ALLOC(sz); ret = ELFNAMEEND(readfile_local)(fd, elf->e_phoff, phdr, sz); if (ret == -1) { goto freephdr; } first = 1; for (i = 0; i < elf->e_phnum; i++) { internalize_phdr(elf->e_ident[EI_DATA], &phdr[i]); if (MD_LOADSEG(&phdr[i])) goto loadseg; if (phdr[i].p_type != PT_LOAD || (phdr[i].p_flags & (PF_W|PF_R|PF_X)) == 0) continue; if ((IS_TEXT(phdr[i]) && (flags & LOAD_TEXT)) || (IS_DATA(phdr[i]) && (flags & LOAD_DATA))) { loadseg: /* XXX: Assume first address is lowest */ if (marks[MARK_DATA] == 0 && IS_DATA(phdr[i])) marks[MARK_DATA] = LOADADDR(phdr[i].p_vaddr); /* Read in segment. */ PROGRESS(("%s%lu", first ? "" : "+", (u_long)phdr[i].p_filesz)); ret = ELFNAMEEND(readfile_global)(fd, offset, phdr[i].p_offset, phdr[i].p_vaddr, phdr[i].p_filesz); if (ret == -1) { goto freephdr; } first = 0; } if ((IS_TEXT(phdr[i]) && (flags & (LOAD_TEXT|COUNT_TEXT))) || (IS_DATA(phdr[i]) && (flags & (LOAD_DATA|COUNT_DATA)))) { /* XXX: Assume first address is lowest */ if (marks[MARK_DATA] == 0 && IS_DATA(phdr[i])) marks[MARK_DATA] = LOADADDR(phdr[i].p_vaddr); pos = phdr[i].p_vaddr; if (minp > pos) minp = pos; pos += phdr[i].p_filesz; if (maxp < pos) maxp = pos; } /* Zero out bss. */ if (IS_BSS(phdr[i]) && (flags & LOAD_BSS)) { PROGRESS(("+%lu", (u_long)(phdr[i].p_memsz - phdr[i].p_filesz))); BZERO((phdr[i].p_vaddr + phdr[i].p_filesz), phdr[i].p_memsz - phdr[i].p_filesz); } if (IS_BSS(phdr[i]) && (flags & (LOAD_BSS|COUNT_BSS))) { pos += phdr[i].p_memsz - phdr[i].p_filesz; if (maxp < pos) maxp = pos; } } DEALLOC(phdr, sz); maxp = roundup(maxp, ELFROUND); /* * Load the ELF HEADER, SECTION HEADERS and possibly the SYMBOL * SECTIONS. */ if (flags & (LOAD_HDR|COUNT_HDR)) { elfp = maxp; maxp += sizeof(Elf_Ehdr); } if (flags & (LOAD_SYM|COUNT_SYM)) { if (ELFNAMEEND(loadsym)(fd, elf, maxp, elfp, marks, flags, &maxp) == -1) { return 1; } } /* * Update the ELF HEADER to give information relative to elfp. */ if (flags & LOAD_HDR) { elf->e_phoff = 0; elf->e_shoff = sizeof(Elf_Ehdr); elf->e_phentsize = 0; elf->e_phnum = 0; externalize_ehdr(elf->e_ident[EI_DATA], elf); BCOPY(elf, elfp, sizeof(*elf)); internalize_ehdr(elf->e_ident[EI_DATA], elf); } marks[MARK_START] = LOADADDR(minp); marks[MARK_ENTRY] = LOADADDR(elf->e_entry); marks[MARK_NSYM] = 1; /* XXX: Kernel needs >= 0 */ marks[MARK_SYM] = LOADADDR(elfp); marks[MARK_END] = LOADADDR(maxp); return 0; freephdr: DEALLOC(phdr, sz); return 1; } /* -------------------------------------------------------------------------- */ int ELFNAMEEND(loadfile)(int fd, Elf_Ehdr *elf, u_long *marks, int flags) { if (flags & LOAD_DYN) { return ELFNAMEEND(loadfile_dynamic)(fd, elf, marks, flags); } else { return ELFNAMEEND(loadfile_static)(fd, elf, marks, flags); } } #endif /* (ELFSIZE == 32 && BOOT_ELF32) || (ELFSIZE == 64 && BOOT_ELF64) */