/*
** makeinit.c -- This program reads in an a.out file and writes out an
**               object file which contains the text and data of the a.out
**               file as data, and two other variables, the size of the text
**               and data, and the size of the bss of the a.out file.
**
**               The text and data are described as:
**                        unsigned char initdata[];
**
**               The other two variables are:
**                        unsigned long initsize;
**                        unsigned long initbss;
**
**
** Copyright 1993 by Hamish Macdonald.
**
** This file is subject to the terms and conditions of the GNU General Public
** License.  See the file README.legal in the main directory of this archive
** for more details.
**
*/

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <a.out.h>
#include <unistd.h>

void usage(void)
{
    fprintf (stderr, "Usage:\n"
	     "\tmakeinit a.outfile [outputfilename]\n");
    exit (EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    int aoutfd, objfd;
    struct exec inex, outex;
    char *aoutname;
    char *objname = "init.o";
    unsigned long initsize, initbss;
    unsigned char *buf;
    struct nlist sym[3];
    unsigned long strsize;

    /* print the greet message */
    puts("Amiga Linux makeinit version 1.0");
    puts("Copyright 1993 by Hamish Macdonald\n");

    if (argc < 2 || argc > 3) {
	usage();
	exit (EXIT_FAILURE);
    }

    aoutname = argv[1];
    if (argc > 2)
	objname = argv[2];

    /* open a.out file and read exec header */
    if ((aoutfd = open (aoutname, O_RDONLY)) == -1) {
	fprintf (stderr, "Unable to open a.out file %s\n", aoutname);
	exit (EXIT_FAILURE);
    }

    if (read (aoutfd, (void *)&inex, sizeof(inex)) != sizeof(inex)) {
	fprintf (stderr, "Unable to read exec header from %s\n",
		 aoutname);
	exit (EXIT_FAILURE);
    }

    /* we can only use OMAGIC executables for the Linux init */
    if (inex.a_magic != OMAGIC) {
	fprintf (stderr, "a.out file has wrong magic number\n");
	exit (EXIT_FAILURE);
    }
	
    /* record sizes of "init" text/data and bss */
    initsize = inex.a_text + inex.a_data;
    initbss = inex.a_bss;

    /* setup exec header for object file */
    outex.a_magic  = OMAGIC;
    outex.a_text   = 0;
    outex.a_data   = initsize + 2 * sizeof (unsigned long);
    outex.a_bss    = 0;
    outex.a_syms   = 3 * sizeof (struct nlist);
    outex.a_entry  = 0;
    outex.a_trsize = 0;
    outex.a_drsize = 0;

    /* open the output file */
    if ((objfd = open (objname, O_WRONLY | O_CREAT | O_TRUNC)) == -1) {
	fprintf (stderr, "Unable to open objectfile %s\n", objname);
	exit (EXIT_FAILURE);
    }

    /* write exec header to object file */
    if (write (objfd, (void *)&outex, sizeof(outex)) != sizeof(outex)) {
	fprintf (stderr, "Unable to write exec header to %s\n",
		 objname);
	exit (EXIT_FAILURE);
    }

    /* allocate buffer to hold text and data */
    if (!(buf = malloc (initsize))) {
	fprintf (stderr, "Failed to allocate memory for buffer\n");
	exit (EXIT_FAILURE);
    }

    /* seek to text in a.out file */
    if (lseek (aoutfd, N_TXTOFF(inex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to text in a.out file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* read text from a.out file */
    if (read (aoutfd, buf, inex.a_text) != inex.a_text) {
	fprintf (stderr, "Failed to read text from a.out file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* seek to data in a.out file */
    if (lseek (aoutfd, N_DATOFF(inex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to data in a.out file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* read data from a.out file */
    if (read (aoutfd, buf + inex.a_text, inex.a_data) != inex.a_data) {
	fprintf (stderr, "Failed to read data from a.out file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* close the a.out file */
    close (aoutfd);

    /* seek to data offset in object file */
    if (lseek (objfd, N_DATOFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to data in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write the text/data */
    if (write (objfd, buf, initsize) != initsize) {
	fprintf (stderr, "Failed to write text/data to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write the initsize and initbss variable values */
    if (write (objfd, &initsize, sizeof(initsize)) != sizeof(initsize)) {
	fprintf (stderr, "Failed to write initsize to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }
    if (write (objfd, &initbss, sizeof(initbss)) != sizeof(initbss)) {
	fprintf (stderr, "Failed to write initbss to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /*
     * setup symbol table
     */

    /* sym entry for initdata */
    sym[0].n_un.n_strx = sizeof(strsize);
    sym[0].n_type      = N_DATA | N_EXT; 
    sym[0].n_other     = 0;
    sym[0].n_desc      = 0;
    sym[0].n_value     = 0;  

    /* sym entry for initsize */
    sym[1].n_un.n_strx = sizeof(strsize) + strlen("_initdata") + 1;
    sym[1].n_type      = N_DATA | N_EXT; 
    sym[1].n_other     = 0;
    sym[1].n_desc      = 0;
    sym[1].n_value     = initsize;  

    /* sym entry for initbss */
    sym[2].n_un.n_strx = sizeof(strsize) + strlen("_initdata") + 1 +
	strlen ("_initsize") + 1;
    sym[2].n_type      = N_DATA | N_EXT; 
    sym[2].n_other     = 0;
    sym[2].n_desc      = 0;
    sym[2].n_value     = initsize + sizeof (initsize);  

    /* seek to symbol offset in object file */
    if (lseek (objfd, N_SYMOFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to symbols in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write symbols */
    if (write (objfd, sym, sizeof(sym)) != sizeof(sym)) {
	fprintf (stderr, "Failed to write symbols to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* calculate string table size */
    strsize = sizeof(strsize) + strlen("_initdata") + 1 +
	strlen("_initsize") + 1 + strlen("_initbss") + 1;

    /* seek to string table offset in object file */
    if (lseek (objfd, N_STROFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to symbols in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    if (write (objfd, &strsize, sizeof(strsize)) != sizeof(strsize)) {
	fprintf (stderr, "Failed to write str table size to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write strings to the string table */
    write (objfd, "_initdata", sizeof("_initdata"));
    write (objfd, "_initsize", sizeof("_initsize"));
    write (objfd, "_initbss", sizeof("_initbss"));

    /* close the object file */
    close (objfd);

    /* free the buffer */
    free (buf);

    printf( "Converted a.out file %s to object file %s\n", aoutname,
	   objname);

    exit (EXIT_SUCCESS);
}
