/* 
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com
 */
#ifdef __hpux
#   define _LINUX_TYPES_H
#endif
#include "common.h"
#include "load.h"

#if defined(__linux__)
# include <linux/elf.h>
#else
# include <elf.h>
#endif

#include <stdio.h>

int prepare_ELF32_loadable(int fd, struct loadable *loadable, int *wide)
{
    Elf32_Ehdr eh;
    unsigned last = 0;
    int i;

    *wide = 0;

    GZIP_STRUCTREAD(loadable, fd, eh, 0);

    if (eh.e_ident[EI_MAG0] != ELFMAG0 ||
	eh.e_ident[EI_MAG1] != ELFMAG1 ||
	eh.e_ident[EI_MAG2] != ELFMAG2 ||
	eh.e_ident[EI_MAG3] != ELFMAG3 ||
	eh.e_ident[EI_CLASS] != ELFCLASS32 ||
	__be16_to_cpu(eh.e_type) != ET_EXEC)
    {
	return PREPARE_CONTINUE;
    }

    if (__be16_to_cpu(eh.e_machine) != EM_PARISC)
    {
	printf("Fatal - ELF, but not for PARISC\n");
	return PREPARE_FATAL;
    }

    printf("ELF32 executable\n");

    /* We like this kind of ELF... */
    eh.e_phnum = __be16_to_cpu(eh.e_phnum);
    for (i = 0; i < eh.e_phnum; i++)
    {
	Elf32_Phdr ep;
	struct loadsegment *seg;
	unsigned start, end;

	GZIP_STRUCTREAD(loadable, fd, ep, __be32_to_cpu(eh.e_phoff) + i * sizeof ep);
	if (__be32_to_cpu(ep.p_type) != PT_LOAD)
	    continue;

	/* vaddr or paddr? */
	start = __be32_to_cpu(ep.p_vaddr);
	if (loadable->first == 0 || start < loadable->first)
	{
	    loadable->first = start;
	}
	end = __be32_to_cpu(ep.p_vaddr) + __be32_to_cpu(ep.p_filesz);
	if (end > last)
	    last = end;

	seg = &loadable->segment[loadable->n++];
	seg->offset = __be32_to_cpu(ep.p_offset);

	if (loadable->offset0 == 0 || seg->offset < loadable->offset0)
	{
	    loadable->offset0 = seg->offset;
	}
	seg->length = __be32_to_cpu(ep.p_filesz);
	seg->mem = start;
	/* XXX: it seems the zeros count calculation is wrong, e.g. when building iplboot */
	/* specifically, the ELF32 value of ep.p_memsz seems way too big */
	seg->zeros = __be32_to_cpu(ep.p_memsz) - __be32_to_cpu(ep.p_filesz);
    }

    loadable->size = last - loadable->first;
    /* is entry a virtual or physical address? */
    loadable->entry = __be32_to_cpu(eh.e_entry);

    return PREPARE_OK;
}
