// ****************************************************************************
// (c)ROBOSOFT 1999 pierre@robosoft.fr
// 
// elf2sdxbin - aims to convert an ELF binary executable file into a format 
// understandable by the downloader. 
// 
// SYNOPSIS
//   elf2sdxbin [input file] [output file] 
// 
// The result of the  convertion is a file named "[output file]". Its format 
// is described below:
// 
// * 4 prefix bytes: containing the size of this binary file minus the 4 bytes 
//                   of this heading. This value is written in BIG ENDIAN.
//
// * Sequence encoding ONE complete EXECUTIVE:
//   + One or more section blocks formatted as follow)
// 
//     - 4 bytes: containing the address of the section in memory. 
//                This value is written in BIG ENDIAN.
//     - 4 bytes: containing the size of the section. 
//                This value is written in BIG ENDIAN.
//     - The section code.
// 
//   + 4 bytes: containing the START address of the program. 
//              This value is written in BIG ENDIAN.
//              
//   + 4 bytes: containing only zeros aiming to indicate the end of the binary.
// 
// ****************************************************************************

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

int main(int argc, char **argv)
{
  int input, output;
  Elf *elf;
  Elf32_Ehdr *ehdr;
  Elf_Scn *scn;
  Elf_Data *data;
  Elf32_Shdr *shdr;
  Elf32_Phdr *phdr;
  unsigned long int value;
  ssize_t size = 0;
  unsigned char c;
  unsigned int i, j;

  if (argc != 3) {
    fprintf(stderr, "elf2sdxbin: bad arguments number usage:\
%s SOURCE DESTINATION\
\nSOURCE is expected to be a statically linked executable in ELF format.\
\nDESTINATION will be generated in SynDEx downloader format.\n", argv[0]);
  }
  
  if ((input = open(argv[1], O_RDONLY)) == -1)
    exit(1);

  if ((output = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC,
                     S_IRWXU | S_IRWXG | S_IRWXO)) == -1)
    exit(1);

  // ---------------------------------------------------------------------
  // Read the content of the ELF binary file and file the "elf" structure.
  // ---------------------------------------------------------------------
  elf_version(EV_CURRENT);
  if ((elf = elf_begin(input, ELF_C_READ, NULL)) == NULL)
    exit(1);

  // -----------------------------------------
  // Get ELF header, data  and section header.
  // -----------------------------------------
  if (((ehdr = elf32_getehdr(elf)) == NULL) ||
      ((phdr = elf32_getphdr(elf)) == NULL) ||
      ((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) ||
      ((data = elf_getdata(scn, NULL)) == NULL))
    exit(1);

  // -------------------------------------------------
  // Reserve the first 4 bytes to store the file size.
  // -------------------------------------------------
  lseek(output, 4, SEEK_SET);

  // ----------------------------------------------------------------
  // In file.ld used for linking the program, we can specify which
  // sections will be loaded in the memory of the target machine at
  // download time. 
  // Thus, for each section, we set its ELF Program Header to PT_LOAD 
  // or PT_NULL whether the section have to be loaded or ignored.
  //
  // Once the object file is linked, the ELF binary file contains one
  // segment per section and one header per segment containing info 
  // needed by the downloader (i.e.: segment address, segment location
  // in the binary file, file offset, etc...). 
  //
  // In the SynDEx binary format we compute, we are only interested
  // in getting the loadable segment (all of the others will be 
  // ignored as they won't be downloaded into the target memory).
  //
  // First, e_phnum will tell us the number of segments present in
  // the binary. Then for each of the e_phnum segments, phdr struct 
  // pointer will give us access to the info we need.
  // 
  // Note: switching to the next segment header is simply done by
  // incrementing the phdr pointer.
  // ----------------------------------------------------------------
  printf("\nPowerPC ELF32 binary to SynDEx download format converter\n\n");
  for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
    static unsigned char buf[256];
    static unsigned int buf_size;

    printf("Segment %2d:\n", i);
    printf("  file offset to segment : 0x%-9X\n", phdr->p_offset);
    printf("  segment physical addres: 0x%-9X\n", phdr->p_paddr);
    printf("  size of segment        : 0x%-9X\n", phdr->p_memsz);
    switch (phdr->p_type) {
      case 0: 
        printf("  type of segment        : NULL........... (ignored)\n"); 
        break;
      case 1: 
        printf("  type of segment        : LOAD");
	if(phdr->p_memsz == 0) {
	  printf(                              " size=0... (ignored)\n"); 
	  break;
	}
	printf(                                ".......... (added)\n"); 
        // -----------------------------------------------------------------
        // Write memory location and size of the section in the output file.
        // -----------------------------------------------------------------
        value = htonl(phdr->p_paddr);
        size += write(output, &value, sizeof(Elf32_Addr));
        value = htonl(phdr->p_memsz);
        size += write(output, &value, sizeof(Elf32_Word));
        // ---------------------------------------------
        // Move the input file pointer the section code.
        // ---------------------------------------------
        lseek(input, (off_t) (phdr->p_offset), SEEK_SET);
        // ---------------------------------------
        // Copy section code into the output file.
        // ---------------------------------------
        buf_size = read(input, buf, phdr->p_memsz % sizeof(buf));
        size += write(output, buf, buf_size);
        for (j = phdr->p_memsz / sizeof(buf); j; j--) {
          read(input, buf, sizeof(buf));
          size += write(output, buf, sizeof(buf));
        }
        break;
      case 2: 
        printf("  type of segment        : DYNAMIC........ (ignored)\n"); 
        break;
      case 3: 
        printf("  type of segment        : INTERP......... (ignored)\n"); 
        break;
      case 4: 
        printf("  type of segment        : NOTE........... (ignored)\n"); 
        break;
      case 5: 
        printf("  type of segment        : SHLIB.......... (ignored)\n"); 
        break;
      case 6: 
        printf("  type of segment        : PHDR........... (ignored)\n"); 
        break;
      case 7: 
        printf("  type of segment        : NUM............ (ignored)\n"); 
        break;
      case 0x70000000: 
        printf("  type of segment        : LOPROC......... (ignored)\n"); 
        break;
      case 0x7fffffff: 
        printf("  type of segment        : HIPROC......... (ignored)\n"); 
        break;
    }
    printf("\n");
  }

  // ---------------------------------------------------
  // Write the entry code address in the output file.
  //
  // Note: we use htonl to convert little-endian (Intel) 
  // into big-endian (Motorola) before writing on file.
  // ---------------------------------------------------
  value = htonl(ehdr->e_entry);
  size += write(output, &value, sizeof(Elf32_Addr));
  printf("Entry point: %-9X\n\n", ehdr->e_entry);
  // -------------------
  // Write 4 null bytes.
  // -------------------
  i = 0;
  size += write(output, &i, sizeof(int));
  // --------------------------------------------------------
  // Write the file size into the previously 4 bytes reserved 
  // space at the beginningi of the file.
  // --------------------------------------------------------
  lseek(output, 0, SEEK_SET);
  value = htonl(size);
  write(output, &value, sizeof(ssize_t));

  // ------------
  // Close files.
  // ------------
  close(input);
  close(output);
}
