root/sys/lib/libsa/loadfile_elf32.c
/* $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 <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>
#else
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#endif

#include <sys/param.h>
#include <sys/exec.h>

#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,
                            &note, 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, &note.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) */