/* $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) */