summaryrefslogblamecommitdiffstats
path: root/elf.c
blob: 7ab6005dd76aefb4036b7eb8a279d8e09664c129 (plain) (tree)






















                                                                            


                    
                       
      










                  
            
















                                               
                                                               


                  
                         
                                           






                                           
     








                                                        






                                      






                                          




                                      



                                          












                                                             
                          











                               
                          


                                                                              
                                                        




                                        
            
     



                                                  














                                                                  
                
         





                                                        









                                                                  
                
         





                                                    































                                                                          
                            
                                                              
                                                      
















                                                                  
                                



           
                                         
     


                                  

 
                             
                
                                 
 
                

                             
                    







                                                          
/* This file is part of SIS (SPARC/RISCV instruction simulator)

   Copyright (C) 2019 Free Software Foundation, Inc.
   Contributed by Jiri Gaisler, Sweden.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Very simple ELF program loader, only tested with RTEMS */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <winsock.h>
#else
#include <netinet/in.h>
#endif
#include <elf.h>
#include <ctype.h>
#include "sis.h"

struct elf_file
{
  FILE *fp;
  Elf32_Ehdr ehdr;
  char *strtab;
  int arch;
  int cpu;
  int bswap;
};

static struct elf_file efile;

static int
read_elf_header (FILE * fp)
{
  Elf32_Ehdr ehdr;

  fseek (fp, 0, SEEK_SET);
  if (fread (&ehdr, sizeof (ehdr), 1, fp) != 1)
    {
      return (-1);
    }
  efile.fp = fp;

  if ((ehdr.e_ident[EI_MAG0] != 0x7f) ||
      strncmp ((char *) &ehdr.e_ident[EI_MAG1], "ELF", 3) != 0)
    {
      return (-1);
    }
#ifdef HOST_LITTLE_ENDIAN
  if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
#else
  if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
#endif
    {
      efile.bswap = 1;
    }
  if (efile.bswap)
    {
      ehdr.e_entry = SWAP_UINT32 (ehdr.e_entry);
      ehdr.e_shoff = SWAP_UINT32 (ehdr.e_shoff);
      ehdr.e_phoff = SWAP_UINT32 (ehdr.e_phoff);
      ehdr.e_phnum = SWAP_UINT16 (ehdr.e_phnum);
      ehdr.e_shnum = SWAP_UINT16 (ehdr.e_shnum);
      ehdr.e_phentsize = SWAP_UINT16 (ehdr.e_phentsize);
      ehdr.e_shentsize = SWAP_UINT16 (ehdr.e_shentsize);
      ehdr.e_shstrndx = SWAP_UINT16 (ehdr.e_shstrndx);
      ehdr.e_machine = SWAP_UINT16 (ehdr.e_machine);
    }

  switch (ehdr.e_machine)
    {
    case EM_SPARC:
      if (sis_verbose)
	printf ("SPARC executable\n");
      efile.arch = CPU_SPARC;
      if (ehdr.e_entry == 0)
	efile.cpu = CPU_LEON4;
      else if (ehdr.e_entry == 0x40000000)
	efile.cpu = CPU_LEON3;
      else if (ehdr.e_entry == 0x2000000)
	efile.cpu = CPU_ERC32;
      break;
    case EM_RISCV:
      if (sis_verbose)
	printf ("RISCV executable\n");
      efile.arch = CPU_RISCV;
      if (ehdr.e_entry == 0x40000000)
	efile.cpu = CPU_LEON3;
      else if (ehdr.e_entry == 0x80000000)
	efile.cpu = CPU_RISCV;
      break;
    default:
      printf ("Unknown architecture (%d)\n", ehdr.e_machine);
      return (-1);
    }

  if (ehdr.e_ident[EI_CLASS] != 1)
    {
      printf ("Only 32-bit ELF supported!\n");
      return (-1);
    }
  efile.ehdr = ehdr;
  ebase.cpu = efile.cpu;
  ebase.arch = efile.arch;
  return (ehdr.e_entry);
}

static int
read_elf_body ()
{
  Elf32_Ehdr ehdr = efile.ehdr;
  Elf32_Shdr sh, ssh;
  Elf32_Phdr ph[16];
  char *strtab;
  char *mem;
  uint32 *memw, i, j, k, vaddr;
  int bswap = efile.bswap;
  FILE *fp = efile.fp;

  fseek (fp, ehdr.e_shoff + ((ehdr.e_shstrndx) * ehdr.e_shentsize), SEEK_SET);
  if ((!fp) || (fread (&ssh, sizeof (ssh), 1, fp) != 1))
    {
      return (-1);
    }

  /* endian swap if big-endian target */
  if (bswap)
    {
      ssh.sh_name = SWAP_UINT32 (ssh.sh_name);
      ssh.sh_type = SWAP_UINT32 (ssh.sh_type);
      ssh.sh_offset = SWAP_UINT32 (ssh.sh_offset);
      ssh.sh_size = SWAP_UINT32 (ssh.sh_size);
    }
  strtab = (char *) malloc (ssh.sh_size);
  fseek (fp, ssh.sh_offset, SEEK_SET);
  if (fread (strtab, ssh.sh_size, 1, fp) != 1)
    {
      return (-1);
    }

  for (i = 0; i < ehdr.e_phnum; i++)
    {
      fseek (fp, ehdr.e_phoff + (i * ehdr.e_phentsize), SEEK_SET);
      if (fread (&ph[i], ehdr.e_phentsize, 1, fp) != 1)
	{
	  return (-1);
	}
      if (bswap)
	{
	  ph[i].p_type = SWAP_UINT32 (ph[i].p_type);
	  ph[i].p_offset = SWAP_UINT32 (ph[i].p_offset);
	  ph[i].p_vaddr = SWAP_UINT32 (ph[i].p_vaddr);
	  ph[i].p_paddr = SWAP_UINT32 (ph[i].p_paddr);
	  ph[i].p_filesz = SWAP_UINT32 (ph[i].p_filesz);
	  ph[i].p_memsz = SWAP_UINT32 (ph[i].p_memsz);
	}
    }

  for (i = 1; i < ehdr.e_shnum; i++)
    {
      fseek (fp, ehdr.e_shoff + (i * ehdr.e_shentsize), SEEK_SET);
      if (fread (&sh, sizeof (sh), 1, fp) != 1)
	{
	  return (-1);
	}
      if (bswap)
	{
	  sh.sh_name = SWAP_UINT32 (sh.sh_name);
	  sh.sh_addr = SWAP_UINT32 (sh.sh_addr);
	  sh.sh_size = SWAP_UINT32 (sh.sh_size);
	  sh.sh_type = SWAP_UINT32 (sh.sh_type);
	  sh.sh_offset = SWAP_UINT32 (sh.sh_offset);
	  sh.sh_flags = SWAP_UINT32 (sh.sh_flags);
	}

      if ((sh.sh_type != SHT_NOBITS) && (sh.sh_size)
	  && (sh.sh_flags & SHF_ALLOC))
	{
	  for (k = 0; k < ehdr.e_phnum; k++)
	    {
	      if ((sh.sh_addr >= ph[k].p_vaddr) &&
		  ((sh.sh_addr + sh.sh_size) <=
		   (ph[k].p_vaddr + ph[k].p_filesz)))
		{
		  vaddr = sh.sh_addr;
		  sh.sh_addr = sh.sh_addr - ph[k].p_vaddr + ph[k].p_paddr;
		  break;
		}
	    }
	  if (sis_verbose)
	    printf ("section: %s at 0x%x, size %d bytes\n",
		    &strtab[sh.sh_name], sh.sh_addr, sh.sh_size);
	  mem = calloc (sh.sh_size / 4 + 1, 4);
	  if (mem != NULL)
	    {
	      if ((sh.sh_type == SHT_PROGBITS)
		  || (sh.sh_type == SHT_INIT_ARRAY)
		  || (sh.sh_type == SHT_FINI_ARRAY))
		{
		  fseek (fp, sh.sh_offset, SEEK_SET);
		  if (fread (mem, sh.sh_size, 1, fp) != 1)
		    {
		      return (-1);
		    }
		  memw = (unsigned int *) mem;
		  if (bswap)
		    for (j = 0; j < (sh.sh_size) / 4 + 1; j++)
		      memw[j] = SWAP_UINT32 (memw[j]);
		  ms->sis_memory_write (sh.sh_addr, mem,
					(sh.sh_size / 4 + 1) * 4);
		}
	    }
	  else
	    {
	      return (-1);
	    }
	  free (mem);
	}
    }

  free (strtab);
  return (ehdr.e_entry);
}

int
elf_load (char *fname, int load)
{
  FILE *fp;
  int res;

  if ((fp = fopen (fname, "rb")) == NULL)
    {
      printf ("file not found\n");
      return (-1);
    }


  res = read_elf_header (fp);
  if (res == -1)
    printf ("File read error\n");

  else if (load)
    {
      res = read_elf_body ();
      if (res == -1)
	printf ("File read error\n");
      else
	printf (" Loaded %s, entry 0x%08x\n", fname, res);
      fclose (efile.fp);
    }
  return res;

}