/* File:      dynaout_xsb_i.h
** Author(s): Jiyang Xu, Kostis Sagonas, Steve Dawson
** Contact:   xsb-contact@cs.sunysb.edu
** 
** Copyright (C) The Research Foundation of SUNY, 1986, 1993-1998
** Copyright (C) ECRC, Germany, 1990
** 
** XSB is free software; you can redistribute it and/or modify it under the
** terms of the GNU Library General Public License as published by the Free
** Software Foundation; either version 2 of the License, or (at your option)
** any later version.
** 
** XSB 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 Library General Public License for
** more details.
** 
** You should have received a copy of the GNU Library General Public License
** along with XSB; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** $Id: dynaout_xsb_i.h,v 1.6 2000/05/20 06:55:58 kifer Exp $
** 
*/


#include <a.out.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
/* wind2unix.h must be included after sys/stat.h */
#include "wind2unix.h"
#include <errno.h>
#include <stdio.h>

#include "auxlry.h"
#include "cell_xsb.h"
#include "memory_xsb.h"
#include "inst_xsb.h"
#include "psc_xsb.h"
#include "flags_xsb.h"
#include "error_xsb.h"
#include "io_builtins_xsb.h"

#define BUFFEXTRA 1024

char tfile[128];	/* uniquely-named tmp file for "ld" */

/*----------------------------------------------------------------------*/

xsbBool dummy()
{
    xsb_error("Trying to use an undefined foreign procedure");
    return FALSE;
}

/*----------------------------------------------------------------------*/

static void dyn_link_all(char *symtab, Psc cur_mod)
{
  int count, i;
  char *ptr, *strtab;
  struct nlist *sym_entry;
  char *name;
  Pair search_ptr;

  count = *(int *)(symtab+4);
  symtab += 8;
  strtab = symtab + count*sizeof(struct nlist);
  search_ptr = (Pair)get_data(cur_mod);
  while (search_ptr) {
    name = get_name(search_ptr->psc_ptr);
/* Jiyang changed it to the form ``module_pred'':
    sprintf(name, "%s_%s", get_name(cur_mod), get_name(search_ptr->psc_ptr));
 */
    if (get_type(search_ptr->psc_ptr) == T_FORN) {
      for (i=0; i<count; i++) {
	sym_entry = (struct nlist *)(symtab + i * sizeof(struct nlist));
	ptr = strtab + sym_entry->n_un.n_strx;
	if (*ptr++ == '_' && strcmp(name, ptr)==0) { 
	  set_forn(search_ptr->psc_ptr, (byte *)(sym_entry->n_value));
	  break;
	}
      }
      if (i>= count) {          /* does not find the name */
	  xsb_warn("Cannot find foreign procedure %s", name);
	  set_forn(search_ptr->psc_ptr, (byte *)(dummy));
      }
    }
    search_ptr = search_ptr->next;
  }
}

/*----------------------------------------------------------------------*/

static byte *load_obj_dyn(char *pofilename, Psc cur_mod, char *ld_option)
{
  int buffsize, fd, loadsize;
  byte *start;	/* Changed from int -- Kostis.	*/
  int *loc;	/* Changed from int -- Kostis.	*/
  struct exec header;
  char buff[3*MAXPATHLEN], subfile[MAXPATHLEN];
  struct stat statbuff;
  
  sprintf(tfile, "/tmp/xsb-dyn.%d", (int)getpid());
  
  /* first step: get the header entries of the *.o file, in order	*/
  /* to obtain the size of the object code and then allocate space	*/
  /* for it.							*/
  if (strlen(pofilename) >= 127) return 0;
  strcpy(subfile, pofilename);
  subfile[strlen(subfile)-1] = 'o';	/* change *.O to *.o */
  fd = open(subfile, O_RDONLY, 0);
  if (fd < 0) {
    xsb_error("Cannot find the C object file: %s", subfile);
    return 0;
  }
  read(fd, &header, sizeof(struct exec));
  close(fd);
  
  /* second step: run incremental ld and generate a temporary 	*/
  /* object file (including orginal *.o and libraries) ready to be	*/
  /* read in.							*/
  buffsize = header.a_text + header.a_data + header.a_bss;
  start = mem_alloc(buffsize);
  /* The "-T hex" option of ld starts the text segment at location	*/
  /* hex. Specifying -T is the same as using the -Ttext option.	*/
  sprintf(buff, "/usr/bin/ld -N -A %s -T %x -o %s %s %s -lc",
	  executable, (int)start, tfile, subfile, ld_option);
  system(buff);
  
  /* third step: check if the size of the buffer just allocated is	*/
  /* big enough to load the object (when the object code uses other	*/
  /* libraries, the buffer may not be big enough). If this is the	*/
  /* case, redo the second step with a bigger buffer.		*/
  fd = open(tfile, O_RDONLY, 0);
  if (fd < 0) {
    xsb_error("The file is not generated by the loader");
    return 0;
  }
  read(fd, &header, sizeof(struct exec));
  loadsize = header.a_text + header.a_data + header.a_bss;
  if (loadsize > buffsize) {
    close(fd);			/* need to reallocate buffer */
    mem_dealloc(start, buffsize);
    start = mem_alloc(loadsize+BUFFEXTRA);
    sprintf(buff, "/usr/bin/ld -N -A %s -T %x -o %s %s %s -lc",
	    executable, (int)start, tfile, subfile, ld_option);
    system(buff);
    fd = open(tfile, O_RDONLY, 0);
    read(fd, &header, sizeof(struct exec));
  }
  
  /* fourth step: read in the intermediate object files.		*/
  /* load text and data segment */
  loadsize = header.a_text + header.a_data;
  lseek(fd, N_TXTOFF(header), 0);
  read(fd, start, loadsize);
  /* load symbol table and string table */
  fstat(fd, &statbuff);
  loadsize = statbuff.st_size-N_SYMOFF(header);
  loc = (int *)mem_alloc(loadsize+8);
  *loc = loadsize+8;
  *(loc+1) = header.a_syms/sizeof(struct nlist);
  lseek(fd, N_SYMOFF(header), 0);
  read(fd, loc+2, loadsize);
  close(fd);
  
  /* fifth step: link C procedure names with Prolog names.		*/
  dyn_link_all((char *)loc, cur_mod);
  mem_dealloc((byte *)loc, loadsize+8);
  return (byte *)4;
}
