home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / stub / go32-v2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-12  |  15.0 KB  |  497 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. /* GO32V2 - A quick hack for loading non-stubbed images for V2.0
  4.    Charles Sandmann 6/95 NO WARRANTY - build with -ldbg
  5.    Eli Zaretskii    6/96 fix code which runs V1 EXE and COFF.
  6.    Eli Zaretskii    7/96 fix code which runs V2 COFF.
  7.    Eli Zaretskii    8/96 *really* fix code which runs V2 COFF;
  8.              support for V2.1 long command lines.
  9.    Bugs:  doesn't scan manifest for versions installed yet */
  10.  
  11. /* If you want to change this, remember to test it with all the
  12.    various ways `go32' can be called.  The following is the
  13.    list of different cases I know about:
  14.  
  15.      go32 is called from DOS prompt without any arguments
  16.      go32 is called from DOS prompt to run unstubbed COFF image of a v2 program
  17.      go32 is called from DOS prompt to debug a v1 COFF image (go32 -d)
  18.      v2's Make calls go32 to run unstubbed COFF image of a v2 program
  19.      v1's Make calls go32 to run unstubbed COFF image of a v2 program
  20.      16-bit Make calls go32 to run unstubbed COFF image of a v2 program
  21.      v2's Make calls go32 to run unstubbed COFF image of a v1 program
  22.      v1's Make calls go32 to run unstubbed COFF image of a v1 program
  23.      16-bit Make calls go32 to run unstubbed COFF image of a v1 program
  24.      v1 .EXE program is called from the DOS prompt
  25.      v1 symlink is called from the DOS prompt
  26.      v2's Make calls a v1 .EXE program
  27.      v1's Make calls a v1 .EXE program
  28.      16-bit Make calls a v1 .EXE program
  29.      when v2's Make calls go32, you need to test command lines which
  30.        are both shorter and longer than the DOS 126-character limit
  31.  
  32.    Note that there is nothing special about Make, it just serves as an
  33.    example of one program that calls another.  It is convenient to use
  34.    Make to test the above cases, because Make is available as both v2
  35.    and v1 executable and as a 16-bit program, and because it can be
  36.    easily used to call different programs with different command lines
  37.    by tweaking a Makefile.  */
  38.  
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <unistd.h>
  43. #include <ctype.h>
  44. #include <fcntl.h>
  45. #include <sys/stat.h>
  46. #include <process.h>
  47. #include <go32.h>
  48. #include <errno.h>
  49. #include <dpmi.h>
  50. #include <debug/v2load.h>
  51. #include <sys/exceptn.h>
  52. #include <sys/farptr.h>
  53.  
  54. /*======================================================================*/
  55.  
  56. #define IMG_ERROR    0
  57. #define IMG_EXE        1
  58. #define IMG_COFF    2
  59. #define IMG_V2        4
  60. #define IMG_UNKNOWN    8
  61.  
  62. #define IMG_PLAIN_EXE    (IMG_EXE | IMG_UNKNOWN)
  63. #define IMG_EXE_V1    (IMG_EXE | IMG_COFF)
  64. #define IMG_EXE_V2    (IMG_EXE | IMG_COFF | IMG_V2)
  65. #define IMG_COFF_V1    (IMG_COFF)
  66. #define IMG_COFF_V2    (IMG_COFF | IMG_V2)
  67.  
  68. /* Non-zero if debugging printout is required.  */
  69. static int verbose = 0;
  70.  
  71. /* Non-zero if we were called by the !proxy method.  */
  72. static int proxy_call = 0;
  73.  
  74. /* If we were called as `go32', holds the length of go32 full pathname.  */
  75. static int go32_len = 0;
  76.  
  77. /* Read the given file and determine what kind of program it is.
  78.    If IS_GO32 is non-zero, we *know* that IMAGE should be v1's go32.exe
  79.    (therefore it cannot be a v1 symlink).  */
  80.  
  81. static int
  82. check_image_version(char *image, int is_go32)
  83. {
  84.   int rv = 0;
  85.   unsigned short header[3];
  86.   unsigned char firstbytes[1];
  87.   unsigned long coffhdr[42];
  88.   int pf, i;
  89.   int coff_offset, text_foffset;
  90.  
  91.   if (verbose)
  92.     fprintf (stderr, "Hmmm... `%s\': ", image);
  93.  
  94.   errno = 0;
  95.   pf = open(image, O_RDONLY|O_BINARY);
  96.   if(pf < 0)
  97.   {
  98.     if (verbose)
  99.       fprintf (stderr, "(error: %s)\n", strerror(errno));
  100.     return IMG_ERROR;
  101.   }
  102.  
  103.   /* See if it has an EXE header */
  104.   read(pf, header, sizeof(header));
  105.   if (header[0] == 0x5a4d)    /* MZ exe signature, stubbed? */
  106.   {
  107.     if (verbose)
  108.       fprintf (stderr, "MZ, ");
  109.     coff_offset = (long)header[2]*512L;
  110.     if (header[1])
  111.       coff_offset += (long)header[1] - 512L;
  112.     lseek(pf, coff_offset, 0);
  113.     read(pf, header, sizeof(header));
  114.     rv |= IMG_EXE;
  115.   }
  116.   else
  117.     coff_offset = 0;
  118.  
  119.   /* See if it has a COFF header (maybe after exe) */
  120.   if (header[0] != 0x014c)    /* COFF? */
  121.   {
  122.     /* We need to pretend that v1 symlinks are v1 executables, to avoid
  123.        endless looping, unless we were called as "go32 !proxy...", in
  124.        which case we *know* it cannot be a symlink.  */
  125.     int symlink = proxy_call && !is_go32;
  126.  
  127.     close(pf);
  128.     if (verbose)
  129.       fprintf (stderr, "without COFF signature%s\n",
  130.            symlink ? ", V1.x symlink" : "");
  131.     if (symlink)
  132.       return rv | IMG_COFF;
  133.     else
  134.       return rv | IMG_UNKNOWN;
  135.   }
  136.   if (verbose)
  137.     fprintf (stderr, "COFF, ");
  138.   rv |= IMG_COFF;
  139.  
  140.   /* Read the COFF header */
  141.   errno = 0;
  142.   lseek(pf, coff_offset, 0);
  143.   i = read(pf, coffhdr, 0x0a8);    
  144.   if (i != 0x0a8)
  145.   {
  146.     close(pf);
  147.     if (verbose)
  148.       fprintf (stderr, "(error reading COFF header: %s)\n", strerror(errno));
  149.     return rv | IMG_UNKNOWN;
  150.   }
  151.  
  152.   /* See what the first opcode is */
  153.   text_foffset = coff_offset + coffhdr[12 + 5];    /* scnptr */
  154.   errno = 0;
  155.   lseek(pf, text_foffset, 0);
  156.   read(pf, firstbytes, 1);
  157.   if (errno)
  158.   {
  159.     if (verbose)
  160.       fprintf (stderr, "(error reading 1st opcode: %s)\n", strerror(errno));
  161.     return rv | IMG_UNKNOWN;
  162.   }
  163.   if (firstbytes[0] == 0xa3)    /* Opcode for movl %eax, 0x12345678 (V1) */
  164.   {
  165.     close(pf);
  166.     if (verbose)
  167.       fprintf (stderr, "V1.x\n");
  168.     return rv;
  169.   }
  170.   if (verbose)
  171.     fprintf (stderr, "V2\n");
  172.   return rv | IMG_V2;
  173. }
  174.  
  175. /*======================================================================*/
  176.  
  177. static int
  178. far_strlen(int selector, int linear_addr)
  179. {
  180.   int save=linear_addr;
  181.   _farsetsel(selector);
  182.   while (_farnspeekb(linear_addr))
  183.     linear_addr++;
  184.   return linear_addr - save;
  185. }
  186.  
  187. extern char **environ;
  188.  
  189. /* The original DOS command-line tail.  */
  190. char DosCmdLine[128];
  191.  
  192. /* Points to the first non-blank character of the DOS command-line tail. */
  193. char *argv1_start = &DosCmdLine[1];
  194.  
  195. char PROXY_STRING[] = "!proxy ";
  196.  
  197. /* We were called when the V1 go32 should have been called.  Call it
  198.    with the same arguments we were passed */
  199.  
  200. int
  201. run_v1_coff(int argc, char **argv)
  202. {
  203.   char *path = getenv("PATH");
  204.   char *tokbuf = alloca(strlen(path)+1);
  205.   char *dir;
  206.   char *cmdbuf = alloca(strlen(argv1_start)+sizeof(PROXY_STRING));
  207.  
  208.   strcpy(tokbuf, path);
  209.   if (strncmp(argv1_start, PROXY_STRING, sizeof(PROXY_STRING)-1) == 0)
  210.     strcpy(cmdbuf, argv1_start);
  211.   else
  212.   {
  213.     /* We should never get here, because v1 programs aren't run
  214.        with !proxy in the environment.  But let's be defensive...  */
  215.     strcpy(cmdbuf, PROXY_STRING);
  216.     strcpy(cmdbuf + sizeof(PROXY_STRING) - 1, argv1_start);
  217.   }
  218.  
  219.   /* we don't check "." because if v1's go32 was in "." we
  220.      would never get a chance to run. */
  221.   for (dir=strtok(tokbuf, ";"); dir; dir=strtok(0, ";"))
  222.   {
  223.     char *cp;
  224.     char tmp[300];
  225.     strcpy(tmp, dir);
  226.     cp = tmp + strlen(tmp) - 1;
  227.     if (*cp != ':' && *cp != '/' && *cp != '\\')
  228.       *++cp = '/';
  229.     strcpy(cp+1, "go32.exe");
  230.     if (check_image_version(tmp, 1) == IMG_PLAIN_EXE)
  231.     {
  232.       /* We will try to pass the v1's go32 the original command line
  233.      as we got it (before our startup code built the argv[] array).  */
  234.       if (!go32_len || !proxy_call)
  235.       {
  236.     if (verbose)
  237.       fprintf (stderr, "Exec: `%s %s\'\n", tmp, argv1_start);
  238.     return _dos_exec(tmp, argv1_start, environ);
  239.       }
  240.       else
  241.       {
  242.     int proxy_argc, proxy_seg, proxy_ofs, i;
  243.     unsigned short *rm_argv;
  244.     char **arglist;
  245.  
  246.     /* Now, for the tricky part.
  247.  
  248.        When a v2 program calls us, it sees that we are a v2 image,
  249.        and constructs the `!proxy' arguments accordingly.  This
  250.        means the arguments pointed to by `!proxy' include the
  251.        pathname of v2's go32.  We need to bump the SEG:OFF pointer
  252.        into the transfer buffer so that the v2's go32 pathname is
  253.        not seen by v1's go32.  How? see below.  */
  254.     if (sscanf (cmdbuf + 7, "%x %x %x",
  255.             &proxy_argc, &proxy_seg, &proxy_ofs) != 3)
  256.     {
  257.       fprintf (stderr, "go32/v2: malformed !proxy command line\n");
  258.       return -1;
  259.     }
  260.  
  261.     /* Pull in the full command line from the transfer buffer
  262.        (actually, only needed for verbose operation).  */
  263.     rm_argv=(unsigned short *)alloca(proxy_argc*sizeof(unsigned short));
  264.     arglist = (char **)alloca (proxy_argc * sizeof (char *));
  265.     movedata(_dos_ds, proxy_seg * 16 + proxy_ofs,
  266.          _my_ds(), (int)rm_argv, proxy_argc*sizeof(unsigned short));
  267.     
  268.     for (i = 0; i < proxy_argc; i++)
  269.     {
  270.       int al = far_strlen(_dos_ds, proxy_seg*16 + rm_argv[i]);
  271.       char *arg = (char *)alloca(al+1);
  272.       movedata(_dos_ds, proxy_seg*16 + rm_argv[i],
  273.            _my_ds(), (int)(arg), al+1);
  274.       arglist[i] = arg;
  275.       if (verbose)
  276.         fprintf (stderr, "%s ", arg);
  277.     }
  278.     if (verbose)
  279.       fprintf (stderr, "\n");
  280.  
  281.     /* `rm_argv[i]' is the offset into the transfer buffer of the
  282.        i'th argument from the command line.  We need to bump
  283.        `proxy_ofs' so that it points to `rm_argv[1]' instead of
  284.        `rm_argv[0]'.  This way, the first argument that will be
  285.        seen by v1's go32 will be the image to run, not the
  286.        pathname of v2's go32.  */
  287.     proxy_ofs += sizeof (rm_argv[0]);
  288.     sprintf (cmdbuf + 7, "%04x %04x %04x",
  289.          proxy_argc - 1, proxy_seg, proxy_ofs);
  290.     if (verbose)
  291.       fprintf (stderr, "Exec: `%s %s\'\n", tmp, cmdbuf);
  292.  
  293.     return _dos_exec(tmp, cmdbuf, environ);
  294.       }
  295.     }
  296.   }
  297.   fprintf(stderr, "go32/v2: cannot find v1's go32.exe\n");
  298.   return -1;
  299. }
  300.  
  301. /*======================================================================*/
  302.  
  303. /* It was an unstubbed V2 COFF file.  Use v2load to run it */
  304.  
  305. int
  306. run_v2_coff(int argc, char **argv)
  307. {
  308.   jmp_buf start_state;
  309.   int rm_la;
  310.   short *rm_argv;
  311.   char newcmdline[24];
  312.   char fullpath[FILENAME_MAX];
  313.   int i, sl, q, cmdlen;
  314.   char *argv0 = argv[0];
  315.   int tbuf, max_dos_mem;
  316.  
  317.   /* The actual command line might be longer than 126 characters, so
  318.      we cannot call `v2loadimage' with the full argv[] array as the
  319.      command line.  We also cannot call it with the original DOS
  320.      command line, because after expansion by the startup code of
  321.      the image we load it will have the image name as both argv[0] and
  322.      argv[1] and all other arguments after it.  Response file cannot
  323.      be used either, because the thread never returns to us after we
  324.      longjmp, so we don't get to delete the response file.
  325.  
  326.      The only way I know out of this mess is to pass the argv[] array
  327.      (sans argv[1]) via the !proxy method.  Sigh..  (The code was
  328.      shamelessly stolen from src/libc/dis/process/dosexec.c).  */
  329.  
  330.   /* Make argv[0] explicit (why not?).  */
  331.   _fixpath (argv[0], fullpath);
  332.   argv[0] = fullpath;
  333.  
  334.   /* Can't use the usual transfer buffer, because `v2loadimage' will
  335.      overwrite it.  Allocate our own buffer, for as much bytes as we need.  */
  336.   for (cmdlen = sizeof (short), i = 0; i < argc; i++)
  337.     cmdlen += strlen (argv[i]) + 1 + sizeof (short);
  338.  
  339.   tbuf = __dpmi_allocate_dos_memory ((cmdlen + 15) >> 4, &max_dos_mem);
  340.   if (tbuf == -1)
  341.   {
  342.     fprintf (stderr, "Not enough DOS memory to pass args to %s\n", argv[0]);
  343.     if (verbose)
  344.       fprintf (stderr, "(Need %d bytes, only %d available)\n",
  345.            (cmdlen + 15) & 0xfffffff0, max_dos_mem << 4);
  346.     argv[0] = argv0;
  347.     return -1;
  348.   }
  349.  
  350.   rm_la   = (tbuf << 4) & 0xfffff;
  351.   q      = rm_la + (argc + 1) * sizeof (short);
  352.   rm_argv = (short *)alloca ((argc + 1) * sizeof (short));
  353.   for (i = 0; i < argc; i++)
  354.   {
  355.     sl = strlen (argv[i]) + 1;
  356.     dosmemput (argv[i], sl, q);
  357.     rm_argv[i] = (q - rm_la) & 0xffff;
  358.     q += sl;
  359.   }
  360.   rm_argv[i] = 0;
  361.   dosmemput (rm_argv, (argc + 1) * sizeof (short), rm_la);
  362.   newcmdline[0] = 22;
  363.   sprintf (newcmdline + 1, " %s%04x %04x 0000", PROXY_STRING, argc, tbuf);
  364.   if (verbose)
  365.     fprintf (stderr, "V2Load %s%s\n", argv[0], newcmdline + 1);
  366.  
  367.   newcmdline[23] = '\n';
  368.   if(v2loadimage(argv0, newcmdline, start_state))
  369.   {
  370.     fprintf(stderr, "Load failed for image %s\n",argv[0]);
  371.     argv[0] = argv0;
  372.     return -1;
  373.   }
  374.   argv[0] = argv0;
  375.  
  376.   longjmp(start_state, 0);
  377.   return 0;
  378. }
  379.  
  380. /*======================================================================*/
  381.  
  382. /* Save on space, don't expand command-line wildcards.  */
  383. char **__crt0_glob_function(char *argument) { return 0; }
  384. void __crt0_load_environment_file(char *app) {}
  385.  
  386. int
  387. main(int argc, char **argv)
  388. {
  389.   int i;
  390.   char *tail;
  391.   char *proxy_ev = getenv(" !proxy");
  392.  
  393.   __djgpp_set_ctrl_c(0);
  394.  
  395.   /* Debugging printout, anyone?  */
  396.   if (getenv ("GO32_V2_DEBUG"))
  397.     verbose = 1;
  398.  
  399.   /* Get the original DOS command-line tail.  */
  400.   dosmemget(_go32_info_block.linear_address_of_original_psp+128, 128,
  401.         DosCmdLine);
  402.   DosCmdLine[1+DosCmdLine[0]] = 0;
  403.  
  404.   /* Get past any whitespace in DOS command line.  */
  405.   argv1_start = &DosCmdLine[1];
  406.   while (*argv1_start && isspace(*argv1_start))
  407.     argv1_start++;
  408.  
  409.   if (verbose)
  410.   {
  411.     fprintf (stderr, "Called as `%s\'\n", argv[0]);
  412.     fprintf (stderr, "DOS CmdTail: `%s\'\n", argv1_start);
  413.   }
  414.  
  415.   /* !proxy in the environment overrides the command line.  */
  416.   if (proxy_ev)
  417.   {
  418.     argv1_start = proxy_ev;
  419.     proxy_call  = 1;
  420.     if (verbose)
  421.     {
  422.       fprintf (stderr, "Environ CmdTail: `%s\'\n", argv1_start);
  423.     }
  424.     while (*argv1_start && isspace(*argv1_start))
  425.       argv1_start++;
  426.   }
  427.   else if (strncmp(argv1_start, PROXY_STRING, sizeof(PROXY_STRING)-1) == 0)
  428.   {
  429.     proxy_call = 1;
  430.   }
  431.  
  432.   /* Are we called as GO32?
  433.      If we are, then the *real* image is in `argv[1]'.  */
  434.   for (tail = argv[0] + strlen (argv[0]); tail > argv[0]; tail--)
  435.     if (*tail == '/' || *tail == '\\')
  436.     {
  437.       ++tail;
  438.       break;
  439.     }
  440.  
  441.   /* I don't want to rely too much on the way argv[0] looks like.
  442.      `go32', `go32.exe', `Go32.EXE'--I think all of these should be OK.  */
  443.   if (strlen(tail) >= 4 && strnicmp (tail, "go32", 4) == 0
  444.       && (tail[4] == '.' || tail[4] == '-' || tail[4] == '\0'))
  445.   {
  446.     go32_len = strlen (argv[0]);
  447.     ++argv;
  448.     --argc;
  449.   }
  450.  
  451.   /* `go32 -d' means the *real* image (the debugger) is after `-d'.  */
  452.   if (go32_len && argc > 0 && strcmp (argv[0], "-d") == 0)
  453.   {
  454.     ++argv;
  455.     --argc;
  456.   }
  457.  
  458.   if (argc < 1)
  459.   {
  460.     printf("go32/v2 version %s built %s %s\n","2.0",__DATE__,__TIME__);
  461.     printf("Usage: go32 coff-image [args]\n");
  462.     printf("Rename this to go32.exe only if you need a go32 that can run v2 binaries as\n"
  463.        " well as v1 binaries (old makefiles).  Put ahead of the old go32 in your PATH\n"
  464.        " but do not delete your old go32 - leave it in the PATH after this one.\n");
  465.     
  466.     printf("Set GO32_V2_DEBUG=y in the environment to get verbose output.\n\n");
  467.     /* Add the memory that we use for ourselves to the free amount.  */
  468.     i = (_go32_dpmi_remaining_physical_memory() + (int)sbrk(0))/1024;
  469.     printf("DPMI memory available: %d Kb\n",i);
  470.     i = _go32_dpmi_remaining_virtual_memory()/1024-i;
  471.     if(i < 0)
  472.       i = 0;
  473.     printf("DPMI swap space available: %d Kb\n", i);
  474.     return 1;
  475.   }
  476.  
  477.   switch (check_image_version(argv[0], 0))
  478.   {
  479.   case IMG_UNKNOWN:
  480.     fprintf(stderr, "go32/v2: Unknown file type: %s\n", argv[0]);
  481.     return -1;
  482.   case IMG_ERROR:
  483.     fprintf(stderr, "go32/v2: Error: %s: %s\n", argv[0], strerror(errno));
  484.     return -1;
  485.   case IMG_COFF_V2:
  486.     return run_v2_coff(argc, argv);
  487.   case IMG_COFF_V1:
  488.   case IMG_EXE_V1:
  489.     return run_v1_coff(argc, argv);
  490.   case IMG_PLAIN_EXE:
  491.   case IMG_EXE_V2:
  492.     return spawnv(P_WAIT, argv[0], argv);
  493.   }
  494.   return 1;
  495. }
  496.  
  497.