www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1996/06/20/13:23:16

Date: Thu, 20 Jun 1996 20:13:32 +0200 (IST)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
To: djgpp-workers AT delorie DOT com
Subject: Go32-v2, a bugfix
Message-Id: <Pine.SUN.3.91.960620200733.22002N-100000@is>
Mime-Version: 1.0

I've managed to get go32-v2 to work in all the cases where v1's go32 could
be called (at least those I know about, see the comments in the source
below).  I finally decided not to put into it the code that measures the
stack usage; maybe later.  The changes relative to the original version
are too extensive, so the diffs are inappropriate IMHO.  The full source
of src/stub/go32-v2.c is below. 

----------------------------------- cut here ---------------------------
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
/* GO32V2 - A quick hack for loading non-stubbed images for V2.0
   Charles Sandmann 6/95 NO WARRANTY - build with -ldbg
   Eli Zaretskii    6/96 fix code which runs V1 EXE, COFF and symlinks.
   Bugs:  doesn't scan manifest for versions installed yet.  */

/* If you want to change this, remember to test it with all the
   various ways `go32' can be called.  The following is the
   list of different cases I know about:

     go32 is called from DOS prompt without any arguments
     go32 is called from DOS prompt to run unstubbed COFF image of a v2 program
     go32 is called from DOS prompt to debug a v1 COFF image (go32 -d)
     v2's Make calls go32 to run unstubbed COFF image of a v2 program
     v1's Make calls go32 to run unstubbed COFF image of a v2 program
     16-bit Make calls go32 to run unstubbed COFF image of a v2 program
     v2's Make calls go32 to run unstubbed COFF image of a v1 program
     v1's Make calls go32 to run unstubbed COFF image of a v1 program
     16-bit Make calls go32 to run unstubbed COFF image of a v1 program
     v1 .EXE program is called from the DOS prompt
     v1 symlink is called from the DOS prompt
     v2's Make calls a v1 .EXE program
     v1's Make calls a v1 .EXE program
     16-bit Make calls a v1 .EXE program

   Note that there is nothing special about Make, it just serves as an
   example of one program that calls another.  It is convenient to use
   Make to test the above cases, because Make is available as both v2
   and v1 executable and as a 16-bit program, and because it can be
   easily used to call different programs with different command lines
   by tweaking a Makefile.  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <process.h>
#include <go32.h>
#include <errno.h>
#include <dpmi.h>
#include <debug/v2load.h>
#include <sys/exceptn.h>
#include <sys/farptr.h>

/*======================================================================*/

#define IMG_ERROR	0
#define IMG_EXE		1
#define IMG_COFF	2
#define IMG_V2		4
#define IMG_UNKNOWN	8

#define IMG_PLAIN_EXE	(IMG_EXE | IMG_UNKNOWN)
#define IMG_EXE_V1	(IMG_EXE | IMG_COFF)
#define IMG_EXE_V2	(IMG_EXE | IMG_COFF | IMG_V2)
#define IMG_COFF_V1	(IMG_COFF)
#define IMG_COFF_V2	(IMG_COFF | IMG_V2)

/* Non-zero if debugging printout is required.  */
static int verbose = 0;

/* Non-zero if we were called by the !proxy method.  */
static int proxy_call = 0;

/* If we were called as `go32', holds the length of go32 full pathname.  */
static int go32_len = 0;

/* Read the given file and determine what kind of program it is.
   If IS_GO32 is non-zero, we *know* that IMAGE should be v1's go32.exe
   (therefore it cannot be a v1 symlink).  */

static int
check_image_version(char *image, int is_go32)
{
  int rv = 0;
  unsigned short header[3];
  unsigned char firstbytes[1];
  unsigned long coffhdr[42];
  int pf, i;
  int coff_offset, text_foffset;

  if (verbose)
    fprintf (stderr, "Hmmm... `%s\': ", image);

  errno = 0;
  pf = open(image, O_RDONLY|O_BINARY);
  if(pf < 0)
  {
    if (verbose)
      fprintf (stderr, "(error: %s)\n", strerror(errno));
    return IMG_ERROR;
  }

  /* See if it has an EXE header */
  read(pf, header, sizeof(header));
  if (header[0] == 0x5a4d)	/* MZ exe signature, stubbed? */
  {
    if (verbose)
      fprintf (stderr, "MZ, ");
    coff_offset = (long)header[2]*512L;
    if (header[1])
      coff_offset += (long)header[1] - 512L;
    lseek(pf, coff_offset, 0);
    read(pf, header, sizeof(header));
    rv |= IMG_EXE;
  }
  else
    coff_offset = 0;

  /* See if it has a COFF header (maybe after exe) */
  if (header[0] != 0x014c)	/* COFF? */
  {
    /* We need to pretend that v1 symlinks are v1 executables, to avoid
       endless looping, unless we were called as "go32 !proxy...", in
       which case we *know* it cannot be a symlink.  */
    int symlink = proxy_call && !is_go32;

    close(pf);
    if (verbose)
      fprintf (stderr, "without COFF signature%s\n",
	       symlink ? ", V1.x symlink" : "");
    if (symlink)
      return rv | IMG_COFF;
    else
      return rv | IMG_UNKNOWN;
  }
  if (verbose)
    fprintf (stderr, "COFF, ");
  rv |= IMG_COFF;

  /* Read the COFF header */
  errno = 0;
  lseek(pf, coff_offset, 0);
  i = read(pf, coffhdr, 0x0a8);    
  if (i != 0x0a8)
  {
    close(pf);
    if (verbose)
      fprintf (stderr, "(error reading COFF header: %s)\n", strerror(errno));
    return rv | IMG_UNKNOWN;
  }

  /* See what the first opcode is */
  text_foffset = coff_offset + coffhdr[12 + 5];	/* scnptr */
  errno = 0;
  lseek(pf, text_foffset, 0);
  read(pf, firstbytes, 1);
  if (errno)
    {
      if (verbose)
	fprintf (stderr, "(error reading 1st opcode: %s)\n", strerror(errno));
      return rv | IMG_UNKNOWN;
    }
  if (firstbytes[0] == 0xa3)	/* Opcode for movl %eax, 0x12345678 (V1) */
  {
    close(pf);
    if (verbose)
      fprintf (stderr, "V1.x\n");
    return rv;
  }
  if (verbose)
    fprintf (stderr, "V2\n");
  return rv | IMG_V2;
}

/*======================================================================*/

static int
far_strlen(int selector, int linear_addr)
{
  int save=linear_addr;
  _farsetsel(selector);
  while (_farnspeekb(linear_addr))
    linear_addr++;
  return linear_addr - save;
}

extern char **environ;

/* The original DOS command-line tail.  */
char DosCmdLine[128];

/* Points to the first non-blank character of the DOS command-line tail. */
char *argv1_start = &DosCmdLine[1];

/* We were called when the V1 go32 should have been called.  Call it
   with the same arguments we were passed */

int
run_v1_coff(int argc, char **argv)
{
  char *path = getenv("PATH");
  char *tokbuf = alloca(strlen(path)+1);
  char *dir;

  strcpy(tokbuf, path);

  /* we don't check "." because if v1's go32 was in "." we
     would never get a chance to run. */
  for (dir=strtok(tokbuf, ";"); dir; dir=strtok(0, ";"))
  {
    char *cp;
    char tmp[300];
    strcpy(tmp, dir);
    cp = tmp + strlen(tmp) - 1;
    if (*cp != ':' && *cp != '/' && *cp != '\\')
      *++cp = '/';
    strcpy(cp+1, "go32.exe");
    if (check_image_version(tmp, 1) == IMG_PLAIN_EXE)
    {
      /* We will try to pass the v1's go32 the original command line
	 as we got it (before our startup code built the argv[] array).  */
      if (!go32_len || !proxy_call)
	{
	  if (verbose)
	    fprintf (stderr, "Exec: `%s %s\'\n", tmp, argv1_start);
	  return _dos_exec(tmp, argv1_start, environ);
	}
      else
	{
	  int proxy_argc, proxy_seg, proxy_ofs, i;
	  unsigned short *rm_argv;
	  char **arglist;

	  /* Now, for the tricky part.

	     When a v2 program calls us, it sees that we are a v2 image,
	     and constructs the `!proxy' arguments accordingly.  Passing
	     that original `!proxy' command line to v1's go32 crashes it.
	     To solve this, we need to bump the SEG:OFF pointer into the
	     transfer buffer so that the v2's go32 pathname is not seen
	     by v1's go32.  */
	  if (sscanf (argv1_start + 7, "%x %x %x",
		      &proxy_argc, &proxy_seg, &proxy_ofs) != 3)
	    {
	      fprintf (stderr, "go32/v2: malformed !proxy command line\n");
	      return -1;
	    }

	  /* Pull in the full command line from the transfer buffer
	     (actually, only needed for verbose operation).  */
	  rm_argv=(unsigned short *)alloca(proxy_argc*sizeof(unsigned short));
	  arglist = (char **)alloca (proxy_argc * sizeof (char *));
	  movedata(_dos_ds, proxy_seg * 16 + proxy_ofs,
		   _my_ds(), (int)rm_argv, proxy_argc*sizeof(unsigned short));
    
	  for (i = 0; i < proxy_argc; i++)
	    {
	      int al = far_strlen(_dos_ds, proxy_seg*16 + rm_argv[i]);
	      char *arg = (char *)alloca(al+1);
	      movedata(_dos_ds, proxy_seg*16 + rm_argv[i],
		       _my_ds(), (int)(arg), al+1);
	      arglist[i] = arg;
	      if (verbose)
		fprintf (stderr, "%s ", arg);
	    }
	  if (verbose)
	    fprintf (stderr, "\n");

	  /* `rm_argv[i]' is the offset into the transfer buffer of the
	     i'th argument from the command line.  We need to bump
	     `proxy_ofs' so that it points to `rm_argv[1]' instead of
	     `rm_argv[0]'.  This way, the first argument that will be
	     seen by v1's go32 will be the image to run, not the
	     pathname of v2's go32.  */
	  proxy_ofs += sizeof (rm_argv[0]);
	  sprintf (argv1_start + 7, "%04x %04x %04x",
		   proxy_argc - 1, proxy_seg, proxy_ofs);
	  if (verbose)
	    fprintf (stderr, "Exec: `%s %s\'\n", tmp, argv1_start);

	  return _dos_exec(tmp, argv1_start, environ);
	}
    }
  }
  fprintf(stderr, "go32/v2: cannot find v1's go32.exe\n");
  return -1;
}

/*======================================================================*/

/* It was an unstubbed V2 COFF file.  Use v2load to run it */

int
run_v2_coff(int argc, char **argv)
{
  jmp_buf start_state;

  /* Pass the original DOS command line, because the actual command
     line might be longer than 126 characters.  (The startup code of
     the image we load will expand it.)  */
  if(v2loadimage(argv[0], DosCmdLine, start_state))
  {
    fprintf(stderr, "Load failed for image %s\n",argv[0]);
    return -1;
  }

  longjmp(start_state, 0);
  return 0;
}

/*======================================================================*/

/* Save on space, don't expand command-line wildcards.  */
char **__crt0_glob_function(char *argument) { return 0; }
void __crt0_load_environment_file(char *app) {}

int
main(int argc, char **argv)
{
  int i;
  char *tail;

  __djgpp_set_ctrl_c(0);

  /* Debugging printout, anyone?  */
  if (getenv ("GO32_V2_DEBUG"))
    verbose = 1;

  /* Get the original DOS command-line tail.  */
  dosmemget(_go32_info_block.linear_address_of_original_psp+128, 128,
	    DosCmdLine);
  DosCmdLine[1+DosCmdLine[0]] = 0;

  /* Get past any whitespace in DOS command line.  */
  argv1_start = &DosCmdLine[1];
  while (*argv1_start && isspace(*argv1_start))
    argv1_start++;

  if (verbose)
    {
      fprintf (stderr, "Called as `%s\'\n", argv[0]);
      fprintf (stderr, "DOS CmdTail: `%s\'\n", argv1_start);
    }

  /* Are we called as GO32?
     If we are, then the *real* image is in `argv[1]'.  */
  for (tail = argv[0] + strlen (argv[0]); tail > argv[0]; tail--)
    if (*tail == '/' || *tail == '\\')
      {
	++tail;
	break;
      }

  /* I don't want to rely too much on the way argv[0] looks like.
     `go32', `go32.exe', `Go32.EXE'--I think all of these should be OK.  */
  if (strnicmp (tail, "go32", 4) == 0)
    {
      go32_len = strlen (argv[0]);
      ++argv;
      --argc;
    }

  /* `go32 -d' means the *real* image (the debugger) is after `-d'.  */
  if (go32_len && argc > 0 && strcmp (argv[0], "-d") == 0)
    {
      ++argv;
      --argc;
    }

  if (argc < 1)
  {
    printf("go32/v2 version %s built %s %s\n","2.0",__DATE__,__TIME__);
    printf("Usage: go32 coff-image [args]\n");
    printf("Rename this to go32.exe only if you need a go32 that can run v2 binaries as\n"
	   " well as v1 binaries (old makefiles).  Put ahead of the old go32 in your PATH.\n");
    printf("Set GO32_V2_DEBUG=y in the environment to get verbose output.\n\n");
    /* Add the memory that we use for ourselves to the free amount.  */
    i = (_go32_dpmi_remaining_physical_memory() + (int)sbrk(0))/1024;
    printf("DPMI memory available: %d Kb\n",i);
    i = _go32_dpmi_remaining_virtual_memory()/1024-i;
    if(i < 0)
      i = 0;
    printf("DPMI swap space available: %d Kb\n", i);
    return 1;
  }
  
  if (strncmp(argv1_start, "!proxy ", 7) == 0)
    proxy_call = 1;

  switch (check_image_version(argv[0], 0))
  {
  case IMG_UNKNOWN:
    fprintf(stderr, "go32/v2: Unknown file type: %s\n", argv[0]);
    return -1;
  case IMG_ERROR:
    fprintf(stderr, "go32/v2: Error: %s: %s\n", argv[0], strerror(errno));
    return -1;
  case IMG_COFF_V2:
    return run_v2_coff(argc, argv);
  case IMG_COFF_V1:
  case IMG_EXE_V1:
    return run_v1_coff(argc, argv);
  case IMG_PLAIN_EXE:
  case IMG_EXE_V2:
    return spawnv(P_WAIT, argv[0], argv);
  }
  return 1;
}

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019