/*

 This code was written by Fred Gilham, and has been placed in the
 public domain.  It is provided "as-is" and without warantee of any
 kind.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <elf.h>

#include "os.h"


static char elf_magic_string[] = {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3};

/* Note: FreeBSD has nice macros that let you use Elf_Ehdr
   etc. declarations; unfortunately Linux doesn't have this so you
   have to do Elf32_Ehdr etc.  Bad for moving to 64 bits.... */
#if defined(__linux__) || defined(sparc)
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
#endif

static Elf_Ehdr eh;

/* Names of the Lisp image ELF sections.  These names must be the same
   as the corresponding names found in the linker script and the
   process-core utility. */
static char *core_section_names[] = {"CORDYN", "CORSTA", "CORRO"};


static void
eread(int d, void *buf, size_t nbytes, const char *func)
{
  int res = read(d, buf, nbytes);

  if (res == -1) {
    perror(func);
    exit(-1);
  }

  if (res < nbytes) {
    fprintf(stderr, "Short read in %s!\n", func);
    exit(-1);
  }
}

static void
elseek(int d, off_t o, const char *func)
{
  if (lseek(d, o, SEEK_SET) == -1) {
    perror(func);
    exit(-1);
  }
}


/* Read the ELF header from a file descriptor and stuff it into a
   structure.  Make sure it is really an elf header etc. */
static void
read_elf_header(int fd, Elf_Ehdr *ehp)
{
  eread(fd, ehp, sizeof(Elf_Ehdr), "read_elf_header");

  if (strncmp(ehp->e_ident, elf_magic_string, 4)) {
    fprintf(stderr, "Bad ELF magic number --- not an elf file.  Exiting in %s.\n", "read_elf_header");
    exit(-1);
  }
}


static void
read_section_header_entry(int fd, Elf_Shdr *shp)
{
  eread(fd, shp, eh.e_shentsize, "read_section_header_entry");
}


/*
  Map the built-in lisp core sections. 

  NOTE!  We need to do this without using malloc because the memory
  layout is not set until some time after this is done.
*/
void
map_core_sections(char *exec_name)
{
  int exec_fd;
  Elf_Shdr sh;        /* A section header entry. */
  Elf_Shdr strsecent;
  char nambuf[10];
  int soff;
  int strsecoff;      /* File offset to string table section. */
  int sections_remaining = 3;
  int i, j;

  if (!(exec_fd = open(exec_name, O_RDONLY))) {
    perror("Can't open executable!");
    exit(-1);
  }

  read_elf_header(exec_fd, &eh);

  /* Find the section name string section.  Save its file offset. */
  soff = eh.e_shoff + eh.e_shstrndx * eh.e_shentsize;
  elseek(exec_fd, soff, "map_core_sections");
  read_section_header_entry(exec_fd, &strsecent);
  strsecoff = strsecent.sh_offset;

  for (i = 0; i < eh.e_shnum && sections_remaining > 0; i++) {

    /* Read an entry from the section header table. */
    elseek(exec_fd, eh.e_shoff + i * eh.e_shentsize, "map_core_sections");
    read_section_header_entry(exec_fd, &sh);

    /* Read the name from the string section. */
    elseek(exec_fd, strsecoff + sh.sh_name, "map_core_sections");
    eread(exec_fd, nambuf, 6, "map_core_sections");

    if (sh.sh_type == SHT_PROGBITS) {
      /* See if this section is one of the lisp core sections. */
      for (j = 0; j < 3; j++) {
	if (!strncmp(nambuf, core_section_names[j], 6)) {
	  /* Found a core section. Map it! */
	  if (os_map(exec_fd, sh.sh_offset, (os_vm_address_t)sh.sh_addr, sh.sh_size) == -1) {
	    fprintf(stderr, "Can't map section %s\n", core_section_names[j]);
	    exit(-1);
	  }
	  sections_remaining--;
	  /* Found a core section, don't check the other core section names. */
	  break;
	}
      }
    }
  }

  close(exec_fd);

  if (sections_remaining != 0) {
    fprintf(stderr, "Couldn't map all core sections!  Exiting!\n");
    exit(-1);
  }
}

