/*

    ADDSYMS.C - add symbols from Coff to to Win32 file (rsxntdj)

    Copyright (C) 1996
	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld
	email: rainer@mathematik.uni-bielefeld.de

    All rights reserved

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include "port.h"
#include "ntbind.h"

#define ROUNDUP(x, size) (((x)+(size-1))&(~(size-1)))

struct go32 {
    FILEHDR	    file_hdr;
    AOUTHDR	    nt_hdr;
    SCNHDR	    scn_hdr[2];
};

struct pe {
    FILEHDR	    file_hdr;
    NTOPTHDR	    nt_hdr;
};

static int addsyms(char * go32_file, char * pe_file)
{
    struct exe_hdr  exehdr;
    DWORD	    new_off, signatur;
    int 	    pehandle, go32handle;
    struct go32     go32;
    struct pe	    pe;
    DEBUG_DESC	    debug_desc;
    COFFSYMS_DESC   coffsyms_desc;
    SCNHDR	    scndeb;
    void	    *lnno;
    void	    *syms;
    long	    lnno_size, syms_size;
    long	    file_hdr_pos, file_scnlast_pos, file_end_pos;
    int 	    i;
    DWORD	    next_vaddr;

    /* test pe file */

    if ((pehandle = open(pe_file, O_RDWR | O_BINARY)) == -1) {
	perror(pe_file);
	return -1;
    }
    read(pehandle, &exehdr, sizeof(struct exe_hdr));
    if (exehdr.signatur != 0x5a4d) {
	printf("%s is not a win32 file (PE)\n", pe_file);
	return -1;
    }
    lseek(pehandle, 0x3c, 0);
    read(pehandle, &new_off, 4);
    lseek(pehandle, new_off, 0);
    read(pehandle, &signatur, 4);

    if (signatur != 0x00004550) {
	printf("%s is not a win32 file (PE)\n", pe_file);
	return -1;
    }

    file_hdr_pos = tell(pehandle);

    read(pehandle, &pe, sizeof(struct pe));

    for (i = 0; i < pe.file_hdr.f_nscns; i++) {
	read(pehandle, &scndeb, sizeof(SCNHDR));
	if (strncmp(scndeb.s_name, ".rdata", 6) == 0) {
	    puts("symbols already in pe file");
	    return -1;
	}
    }
    next_vaddr = ROUNDUP(scndeb.s_vaddr + scndeb.s_size, pe.nt_hdr.SectionAlignment);

    file_scnlast_pos = tell(pehandle);
    file_end_pos = filelength(pehandle);

    /* test go32 file */

    if ((go32handle = open(go32_file, O_RDONLY | O_BINARY)) == -1) {
	perror(go32_file);
	return -1;
    }
    read(go32handle, &go32, sizeof(struct go32));
    if (go32.file_hdr.f_magic != 0x14C) {    /* COFF header */
	printf("%s is not a coff/g32 file\n", go32_file);
	return -1;
    }

    lnno_size = go32.scn_hdr[0].s_nlnno * sizeof(LINENUMBER);
    syms_size = filelength(go32handle) - go32.file_hdr.f_symptr;

    lnno = malloc (lnno_size);
    syms = malloc (syms_size);

    if (!lnno || !syms) {
	puts("out of memory");
	return -1;
    }

    lseek(go32handle, go32.scn_hdr[0].s_lnnoptr, SEEK_SET);
    if (read(go32handle, lnno, lnno_size) != lnno_size) {
	puts("error reading line numbers");
	return -1;
    }

    lseek(go32handle, go32.file_hdr.f_symptr, SEEK_SET);
    if (read(go32handle, syms, syms_size) != syms_size) {
	puts("error reading symbols");
	return -1;
    }

    /* modify pe file / add syms */

    debug_desc.Characteristics = 0;
    debug_desc.TimeDateStamp = time(NULL) + _timezone;
    debug_desc.MajorVersion = 0;
    debug_desc.MinorVersion = 0;
    debug_desc.Type = 1;
    debug_desc.SizeOfData = syms_size;
    debug_desc.AddressOfRawData = 0;
    debug_desc.PointerOfRawData = file_end_pos + sizeof(DEBUG_DESC);

    memcpy(scndeb.s_name, ".rdata\0\0", 8);
    scndeb.s_paddr = syms_size + sizeof(DEBUG_DESC) + sizeof(COFFSYMS_DESC);
    scndeb.s_vaddr = next_vaddr;
    scndeb.s_size = ROUNDUP(scndeb.s_paddr, pe.nt_hdr.FileAlignment);
    scndeb.s_scnptr = file_end_pos;
    scndeb.s_relptr = 0;
    scndeb.s_lnnoptr = 0;
    scndeb.s_nreloc = 0;
    scndeb.s_nlnno = 0;
    scndeb.s_flags = 0x40000040L;   /* read; data */

    pe.file_hdr.f_flags &= ~4;	    /* lnno */
    pe.file_hdr.f_flags &= ~8;	    /* syms */
    pe.file_hdr.f_nscns ++;
    pe.file_hdr.f_symptr = file_end_pos + sizeof(DEBUG_DESC)
			    + sizeof(COFFSYMS_DESC) + lnno_size;
    pe.file_hdr.f_nsyms = go32.file_hdr.f_nsyms;

    /* pe.scn_hdr[0].s_lnnoptr = file_end_pos + sizeof(DEBUG_DESC); */
    /* pe.scn_hdr[0].s_nlnno = go32.scn_hdr[0].s_nlnno; */

    pe.nt_hdr.DataDirectory[DIR_DEBUG].VirtualAddress = next_vaddr;
    pe.nt_hdr.DataDirectory[DIR_DEBUG].Size = sizeof(DEBUG_DESC);
    pe.nt_hdr.SizeOfImage += ROUNDUP(scndeb.s_size, pe.nt_hdr.SectionAlignment);

    coffsyms_desc.NumberOfSymbols = pe.file_hdr.f_nsyms;
    coffsyms_desc.LvaToFirstSymbol = sizeof(COFFSYMS_DESC) + lnno_size;
    coffsyms_desc.NumberOfLineNumbers = go32.scn_hdr[0].s_nlnno;
    coffsyms_desc.LvaToFirstLineNumber = sizeof(COFFSYMS_DESC);
    coffsyms_desc.RvaToFirstByteOfCode = 0;
    coffsyms_desc.RvaToLastByteOfCode = 0;
    coffsyms_desc.RvaToFirstByteOfData = 0;
    coffsyms_desc.RvaToLastByteOfData = 0;

    lseek(pehandle, file_end_pos, SEEK_SET);
    write(pehandle, &debug_desc, sizeof(DEBUG_DESC));
    write(pehandle, &coffsyms_desc, sizeof(COFFSYMS_DESC));
    write(pehandle, lnno, lnno_size);
    write(pehandle, syms, syms_size);

    lseek(pehandle, file_hdr_pos, SEEK_SET);
    write(pehandle, &pe, sizeof(struct pe));

    lseek(pehandle, file_scnlast_pos, SEEK_SET);
    write(pehandle, &scndeb, sizeof(SCNHDR));

    close(pehandle);
    close(go32handle);
    return 0;
}

int main(int argc, char **argv)
{
    static char usage_text[] =
	"usage:\n"
	" addsym go32-file pe-file\n";

    if (argc != 3) {
	puts(usage_text);
	return -1;
    }

    return addsyms(argv[1], argv[2]);
}
