#include <iostream>
#include <fstream>
#include <vector>

#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <libelf/libelf.h>

using namespace std;

void output(ostream &os, unsigned base_addr, size_t len,
            unsigned char *bytes)
{
  // no support for S2 or S3 records yet.
  assert(base_addr < 0x10000);
  assert(base_addr + len <= 0x10000);

  os << uppercase << hex << right;

  while (len > 0)
  {
    os << "S1";

    size_t line = min(len, (size_t)0x20);
    os.fill('0');
    os.width(2);
    os << line+3;

    os.fill('0');
    os.width(4);
    os << base_addr;

    unsigned char checksum = line + 3 +
                             (base_addr  & 0x00ff) +
                             ((base_addr & 0xff00) >> 8);
    base_addr += line;
    len -= line;

    while (line > 0)
    {
      os.fill('0');
      os.width(2);
      os << static_cast<unsigned>(*bytes);
      checksum += *bytes;
      bytes++;
      line--;
    }
    checksum = ~checksum;

    os.fill('0');
    os.width(2);
    os << static_cast<unsigned>(checksum) << "\n";
  }
}

void closing(ostream &os)
{
  os << "S9030000FC\n";
}

void usage(int argc, char **argv)
{
  cerr << "Usage: " << argv[0] << " infile outfile\n\n";
}

int main(int argc, char **argv)
{
  if (argc != 3)
  {
    usage(argc, argv);
    return 1;
  }

  char *fn = argv[1];

  ofstream ofs(argv[2]);

  if (elf_version(EV_CURRENT) == EV_NONE)
  {
    cerr << "libelf out of date. Please rebuild " << argv[0] << "\n";
    return -1;
  }

  int fd = open(fn, 0);
  if (!fd)
  {
    cerr << "failed to open " << fn << ".\n";
    return -1;
  }

  Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
  if (!elf)
  {
    cerr << fn << " not an elf.\n";
    return -1;
  }

  // emit main program code.
  Elf32_Ehdr *header = elf32_getehdr(elf);
  Elf32_Phdr *segment = elf32_getphdr(elf);
  for (int i = 0; i < header->e_phnum; ++i)
  {
    lseek(fd, segment[i].p_offset, SEEK_SET);
#if 0
    assert(segment[i].p_memsz >= segment[i].p_filesz);
    vector<unsigned char> bytes(segment[i].p_memsz, 0);
    read(fd, &bytes[0], segment[i].p_filesz);
#else
    /* objcopy works *this* way. */
    vector<unsigned char> bytes(segment[i].p_filesz, 0);
    read(fd, &bytes[0], segment[i].p_filesz);
#endif

    output(ofs, segment[i].p_vaddr, bytes.size(), &bytes[0]);
  }

  elf_end(elf);

  closing(ofs);

  return 0;
}

(download)