static bool MakeEnv(char * name,Bit16u * segment) { /* If segment to copy environment is 0 copy the caller's environment */ DOS_PSP psp(dos.psp()); PhysPt envread,envwrite; Bit16u envsize=1; bool parentenv=true; if (*segment==0) { if (!psp.GetEnvironment()) parentenv=false; //environment seg=0 envread=PhysMake(psp.GetEnvironment(),0); } else { if (!*segment) parentenv=false; //environment seg=0 envread=PhysMake(*segment,0); } if (parentenv) { for (envsize=0; ;envsize++) { if (envsize>=MAXENV - ENV_KEEPFREE) { DOS_SetError(DOSERR_ENVIRONMENT_INVALID); return false; } if (mem_readw(envread+envsize)==0) break; } envsize += 2; /* account for trailing \0\0 */ } Bit16u size=long2para(envsize+ENV_KEEPFREE); if (!DOS_AllocateMemory(segment,&size)) return false; envwrite=PhysMake(*segment,0); if (parentenv) { MEM_BlockCopy(envwrite,envread,envsize); // mem_memcpy(envwrite,envread,envsize); envwrite+=envsize; } else { mem_writeb(envwrite++,0); } mem_writew(envwrite,1); envwrite+=2; char namebuf[DOS_PATHLENGTH]; if (DOS_Canonicalize(name,namebuf)) { MEM_BlockWrite(envwrite,namebuf,strlen(namebuf)+1); return true; } else return false; }
bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { EXE_Header head;Bitu i; Bit16u fhandle;Bit16u len;Bit32u pos; Bit16u pspseg,envseg,loadseg,memsize,readsize; PhysPt loadaddress;RealPt relocpt; Bitu headersize,imagesize; DOS_ParamBlock block(block_pt); block.LoadData(); if (flags!=LOADNGO && flags!=OVERLAY && flags!=LOAD) { E_Exit("DOS:Not supported execute mode %d for file %s",flags,name); } /* Check for EXE or COM File */ bool iscom=false; if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) return false; len=sizeof(EXE_Header); if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) { DOS_CloseFile(fhandle); return false; } if (len<sizeof(EXE_Header)) { if (len==0) { /* Prevent executing zero byte files */ DOS_SetError(DOSERR_ACCESS_DENIED); DOS_CloseFile(fhandle); return false; } /* Otherwise must be a .com file */ iscom=true; } else { /* Convert the header to correct endian, i hope this works */ HostPt endian=(HostPt)&head; for (i=0;i<sizeof(EXE_Header)/2;i++) { *((Bit16u *)endian)=host_readw(endian); endian+=2; } if ((head.signature!=MAGIC1) && (head.signature!=MAGIC2)) iscom=true; else { if(head.pages & ~0x07ff) /* 1 MB dos maximum address limit. Fixes TC3 IDE (kippesoep) */ LOG(LOG_EXEC,LOG_NORMAL)("Weird header: head.pages > 1 MB"); head.pages&=0x07ff; headersize = head.headersize*16; imagesize = head.pages*512-headersize; if (imagesize+headersize<512) imagesize = 512-headersize; } } Bit8u * loadbuf=(Bit8u *)new Bit8u[0x10000]; if (flags!=OVERLAY) { /* Create an environment block */ envseg=block.exec.envseg; if (!MakeEnv(name,&envseg)) { DOS_CloseFile(fhandle); return false; } /* Get Memory */ Bit16u minsize,maxsize;Bit16u maxfree=0xffff;DOS_AllocateMemory(&pspseg,&maxfree); if (iscom) { minsize=0x1000;maxsize=0xffff; if (machine==MCH_PCJR) { /* try to load file into memory below 96k */ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); Bit16u dataread=0x1800; DOS_ReadFile(fhandle,loadbuf,&dataread); if (dataread<0x1800) maxsize=dataread; if (minsize>maxsize) minsize=maxsize; } } else { /* Exe size calculated from header */ minsize=long2para(imagesize+(head.minmemory<<4)+256); if (head.maxmemory!=0) maxsize=long2para(imagesize+(head.maxmemory<<4)+256); else maxsize=0xffff; } if (maxfree<minsize) { DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); DOS_FreeMemory(envseg); return false; } if (maxfree<maxsize) memsize=maxfree; else memsize=maxsize; if (!DOS_AllocateMemory(&pspseg,&memsize)) E_Exit("DOS:Exec error in memory"); if (iscom && (machine==MCH_PCJR) && (pspseg<0x2000)) { maxsize=0xffff; /* resize to full extent of memory block */ DOS_ResizeMemory(pspseg,&maxsize); /* now try to lock out memory above segment 0x2000 */ if ((real_readb(0x2000,0)==0x5a) && (real_readw(0x2000,1)==0) && (real_readw(0x2000,3)==0x7ffe)) { /* MCB after PCJr graphics memory region is still free */ if (pspseg+maxsize==0x17ff) { DOS_MCB cmcb((Bit16u)(pspseg-1)); cmcb.SetType(0x5a); // last block } } } loadseg=pspseg+16; if (!iscom) { /* Check if requested to load program into upper part of allocated memory */ if ((head.minmemory == 0) && (head.maxmemory == 0)) loadseg = ((pspseg+memsize)*0x10-imagesize)/0x10; } } else loadseg=block.overlay.loadseg; /* Load the executable */ loadaddress=PhysMake(loadseg,0); if (iscom) { /* COM Load 64k - 256 bytes max */ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); readsize=0xffff-256; DOS_ReadFile(fhandle,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,readsize); } else { /* EXE Load in 32kb blocks and then relocate */ pos=headersize;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); while (imagesize>0x7FFF) { readsize=0x8000;DOS_ReadFile(fhandle,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,readsize); // if (readsize!=0x8000) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header"); loadaddress+=0x8000;imagesize-=0x8000; } if (imagesize>0) { readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,readsize); // if (readsize!=imagesize) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header"); } /* Relocate the exe image */ Bit16u relocate; if (flags==OVERLAY) relocate=block.overlay.relocation; else relocate=loadseg; pos=head.reloctable;DOS_SeekFile(fhandle,&pos,0); for (i=0;i<head.relocations;i++) { readsize=4;DOS_ReadFile(fhandle,(Bit8u *)&relocpt,&readsize); relocpt=host_readd((HostPt)&relocpt); //Endianize PhysPt address=PhysMake(RealSeg(relocpt)+loadseg,RealOff(relocpt)); mem_writew(address,mem_readw(address)+relocate); } } delete[] loadbuf; DOS_CloseFile(fhandle); /* Setup a psp */ if (flags!=OVERLAY) { // Create psp after closing exe, to avoid dead file handle of exe in copied psp SetupPSP(pspseg,memsize,envseg); SetupCMDLine(pspseg,block); }; CALLBACK_SCF(false); /* Carry flag cleared for caller if successfull */ if (flags==OVERLAY) return true; /* Everything done for overlays */ RealPt csip,sssp; if (iscom) { csip=RealMake(pspseg,0x100); sssp=RealMake(pspseg,0xfffe); mem_writew(PhysMake(pspseg,0xfffe),0); } else { csip=RealMake(loadseg+head.initCS,head.initIP); sssp=RealMake(loadseg+head.initSS,head.initSP); } if (flags==LOAD) { DOS_PSP callpsp(dos.psp()); /* Save the SS:SP on the PSP of calling program */ callpsp.SetStack(RealMakeSeg(ss,reg_sp)); /* Switch the psp's */ dos.psp(pspseg); DOS_PSP newpsp(dos.psp()); dos.dta(RealMake(newpsp.GetSegment(),0x80)); block.exec.initsssp = sssp; block.exec.initcsip = csip; block.SaveData(); return true; } if (flags==LOADNGO) { /* Get Caller's program CS:IP of the stack and set termination address to that */ RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp))); SaveRegisters(); DOS_PSP callpsp(dos.psp()); /* Save the SS:SP on the PSP of calling program */ callpsp.SetStack(RealMakeSeg(ss,reg_sp)); /* Switch the psp's and set new DTA */ dos.psp(pspseg); DOS_PSP newpsp(dos.psp()); dos.dta(RealMake(newpsp.GetSegment(),0x80)); /* save vectors */ newpsp.SaveVectors(); /* copy fcbs */ newpsp.SetFCB1(block.exec.fcb1); newpsp.SetFCB2(block.exec.fcb2); /* Set the stack for new program */ SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp); /* Add some flags and CS:IP on the stack for the IRET */ reg_sp-=6; mem_writew(SegPhys(ss)+reg_sp+0,RealOff(csip)); mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(csip)); /* DOS starts programs with a RETF, so our IRET should not modify critical flags (IOPL in v86 mode); interrupt flag is set explicitly, test flags cleared */ mem_writew(SegPhys(ss)+reg_sp+4,(reg_flags&(~FMASK_TEST))|FLAG_IF); /* Setup the rest of the registers */ reg_ax=reg_bx=0;reg_cx=0xff; reg_dx=pspseg; reg_si=RealOff(csip); reg_di=RealOff(sssp); reg_bp=0x91c; /* DOS internal stack begin relict */ SegSet16(ds,pspseg);SegSet16(es,pspseg); #if C_DEBUG /* Started from debug.com, then set breakpoint at start */ DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); #endif /* Add the filename to PSP and environment MCB's */ char stripname[8];Bitu index=0; while (char chr=*name++) { switch (chr) { case ':':case '\\':case '/':index=0;break; default:if (index<8) stripname[index++]=toupper(chr); } } index=0; while (index<8) { if (stripname[index]=='.') break; if (!stripname[index]) break; index++; } memset(&stripname[index],0,8-index); DOS_MCB pspmcb(dos.psp()-1); pspmcb.SetFileName(stripname); DOS_UpdatePSPName(); return true; } return false; }
COUNT DosExeLoader(BYTE FAR * namep, exec_blk FAR * exp, COUNT mode) { COUNT rc, /*err, */ /*env_size,*/ i; UCOUNT nBytesRead; UWORD mem, env, asize, start_seg; ULONG image_size; ULONG image_offset; BYTE FAR *sp; psp FAR *p; psp FAR *q = MK_FP(cu_psp, 0); mcb FAR *mp; iregs FAR *irp; UWORD reloc[2]; seg FAR *spot; LONG exe_size; int ModeLoadHigh = mode & 0x80; UBYTE UMBstate = uppermem_link; mode &= 0x7f; /* Clone the environement and create a memory arena */ if (mode != OVERLAY) { if ((rc = ChildEnv(exp, &env, namep)) != SUCCESS) return rc; } else mem = exp->load.load_seg; /* compute image offset from the header */ asize = 16; image_offset = (ULONG)header.exHeaderSize * asize; /* compute image size by removing the offset from the */ /* number pages scaled to bytes plus the remainder and */ /* the psp */ /* First scale the size */ asize = 512; image_size = (ULONG)header.exPages * asize; /* remove the offset */ image_size -= image_offset; /* and finally add in the psp size */ if (mode != OVERLAY) image_size += sizeof(psp); /*TE 03/20/01*/ if (mode != OVERLAY) { if ( ModeLoadHigh && uppermem_root) { DosUmbLink(1); /* link in UMB's */ mem_access_mode |= ModeLoadHigh; } /* Now find out how many paragraphs are available */ if ((rc = DosMemLargest((seg FAR *) & asize)) != SUCCESS) { DosMemFree(env); return rc; } exe_size = (LONG) long2para(image_size) + header.exMinAlloc; /* + long2para((LONG) sizeof(psp)); ?? see above image_size += sizeof(psp) -- 1999/04/21 ska */ if (exe_size > asize && (mem_access_mode & 0x80)) { /* First try low memory */ mem_access_mode &= ~0x80; rc = DosMemLargest((seg FAR *) & asize); mem_access_mode |= 0x80; if (rc != SUCCESS) { DosMemFree(env); return rc; } } if (exe_size > asize) { DosMemFree(env); return DE_NOMEM; } exe_size = (LONG) long2para(image_size) + header.exMaxAlloc; /* + long2para((LONG) sizeof(psp)); ?? -- 1999/04/21 ska */ if (exe_size > asize) exe_size = asize; /* TE if header.exMinAlloc == header.exMaxAlloc == 0, DOS will allocate the largest possible memory area and load the image as high as possible into it. discovered (and after that found in RBIL), when testing NET */ if ((header.exMinAlloc | header.exMaxAlloc ) == 0) exe_size = asize; /* /// Removed closing curly brace. We should not attempt to allocate memory if we are overlaying the current process, because the new process will simply re-use the block we already have allocated. This was causing execl() to fail in applications which use it to overlay (replace) the current exe file with a new one. Jun 11, 2000 - rbc } */ /* Allocate our memory and pass back any errors */ /* We can still get an error on first fit if the above */ /* returned size was a bet fit case */ /* ModeLoadHigh = 80 = try high, then low */ if ((rc = DosMemAlloc((seg) exe_size, mem_access_mode | ModeLoadHigh, (seg FAR *) & mem ,(UWORD FAR *) & asize)) < 0) { if (rc == DE_NOMEM) { if ((rc = DosMemAlloc(0, LARGEST, (seg FAR *) & mem ,(UWORD FAR *) & asize)) < 0) { DosMemFree(env); return rc; } /* This should never happen, but ... */ if (asize < exe_size) { DosMemFree(mem); DosMemFree(env); return rc; } } else { DosMemFree(env); return rc; } } else /* with no error, we got exactly what we asked for */ asize = exe_size; #ifdef DEBUG printf("loading '%S' at %04x\n", namep, mem); #endif /* /// Added open curly brace and "else" clause. We should not attempt to allocate memory if we are overlaying the current process, because the new process will simply re-use the block we already have allocated. This was causing execl() to fail in applications which use it to overlay (replace) the current exe file with a new one. Jun 11, 2000 - rbc */ } else asize = exe_size; /* /// End of additions. Jun 11, 2000 - rbc */ if ( ModeLoadHigh && uppermem_root) { mem_access_mode &= ~ModeLoadHigh; /* restore old situation */ DosUmbLink(UMBstate); /* restore link state */ } if (mode != OVERLAY) { /* memory found large enough - continue processing */ mp = MK_FP(mem, 0); ++mem; } else mem = exp->load.load_seg; /* create the start seg for later computations */ if (mode == OVERLAY) start_seg = mem; else { start_seg = mem + long2para((LONG) sizeof(psp)); } /* Now load the executable */ /* If file not found - error */ /* NOTE - this is fatal because we lost it in transit */ /* from DosExec! */ if ((rc = DosOpen(namep, 0)) < 0) { fatal("(DosExeLoader) exe file lost in transit"); } /* offset to start of image */ if (doslseek(rc, image_offset, 0) != image_offset) { if (mode != OVERLAY) { DosMemFree(--mem); DosMemFree(env); } return DE_INVLDDATA; } /* read in the image in 32K chunks */ if (mode != OVERLAY) { exe_size = image_size - sizeof(psp); } else exe_size = image_size; if (exe_size > 0) { if (mode != OVERLAY) { if ((header.exMinAlloc == 0) && (header.exMaxAlloc == 0)) { /* then the image should be placed as high as possible */ start_seg = start_seg + mp->m_size - (image_size + 15) / 16; } } sp = MK_FP(start_seg, 0x0); do { nBytesRead = DosRead((COUNT) rc, (COUNT) (exe_size < CHUNK ? exe_size : CHUNK), (VOID FAR *) sp, &UnusedRetVal); sp = add_far((VOID FAR *) sp, (ULONG) nBytesRead); exe_size -= nBytesRead; } while (nBytesRead && exe_size > 0); } /* relocate the image for new segment */ doslseek(rc, (LONG) header.exRelocTable, 0); for (i = 0; i < header.exRelocItems; i++) { if (DosRead(rc, sizeof(reloc), (VOID FAR *) & reloc[0], &UnusedRetVal) != sizeof(reloc)) { return DE_INVLDDATA; } if (mode == OVERLAY) { spot = MK_FP(reloc[1] + mem, reloc[0]); *spot += exp->load.reloc; } else { /* spot = MK_FP(reloc[1] + mem + 0x10, reloc[0]); */ spot = MK_FP(reloc[1] + start_seg, reloc[0]); *spot += start_seg; } } /* and finally close the file */ DosClose(rc); /* exit here for overlay */ if (mode == OVERLAY) return SUCCESS; /* point to the PSP so we can build it */ p = MK_FP(mem, 0); setvec(0x22, (VOID(INRPT FAR *) (VOID)) MK_FP(user_r->CS, user_r->IP)); new_psp(p, mem + asize); asize = patchPSP(mem - 1, env, exp, namep); /* asize = fcbcode */ /* Transfer control to the executable */ p->ps_parent = cu_psp; p->ps_prevpsp = (BYTE FAR *) MK_FP(cu_psp, 0); q->ps_stack = (BYTE FAR *) user_r; user_r->FLAGS &= ~FLG_CARRY; switch (mode) { case LOADNGO: /* build the user area on the stack */ irp = MK_FP(header.exInitSS + start_seg, ((header.exInitSP - sizeof(iregs)) & 0xffff)); /* start allocating REGs */ /* Note: must match es & ds memory segment */ irp->ES = irp->DS = mem; irp->CS = header.exInitCS + start_seg; irp->IP = header.exInitIP; irp->AX = asize; /* asize = fcbcode */ irp->BX = irp->CX = irp->DX = irp->SI = irp->DI = irp->BP = 0; irp->FLAGS = 0x200; cu_psp = mem; dta = p->ps_dta; if (InDOS) --InDOS; exec_user(irp); /* We should never be here */ fatal("KERNEL RETURNED!!!"); break; case LOAD: cu_psp = mem; exp->exec.stack = MK_FP(header.exInitSS + start_seg, header.exInitSP); *((UWORD FAR *) exp->exec.stack) = asize; /* fcbcode */ exp->exec.start_addr = MK_FP(header.exInitCS + start_seg, header.exInitIP); return SUCCESS; } return DE_INVLDFMT; }
COUNT ChildEnv(exec_blk FAR * exp, UWORD * pChildEnvSeg, char far * pathname) { BYTE FAR *pSrc; BYTE FAR *pDest; UWORD nEnvSize; COUNT RetCode; /* UWORD MaxEnvSize; not used -- 1999/04/21 ska */ psp FAR *ppsp = MK_FP(cu_psp, 0); /* create a new environment for the process */ /* copy parent's environment if exec.env_seg == 0 */ pSrc = exp->exec.env_seg ? MK_FP(exp->exec.env_seg, 0) : MK_FP(ppsp->ps_environ, 0); #if 0 /* Every process requires an environment because of argv[0] -- 1999/04/21 ska */ */ if (!pSrc) /* no environment to copy */ { *pChildEnvSeg = 0; return SUCCESS; } #endif nEnvSize = 1; /* This loop had not counted the very last '\0' -- 1999/04/21 ska */ if (pSrc) { /* if no environment is available, one byte is required */ for (nEnvSize = 0; ; nEnvSize++) { /* Test env size and abort if greater than max */ if (nEnvSize >= MAXENV - ENV_KEEPFREE) return DE_INVLDENV; if (*(UWORD FAR *)(pSrc+nEnvSize) == 0) break; } nEnvSize += 2; /* account for trailing \0\0 */ } /* allocate enough space for env + path */ if ((RetCode = DosMemAlloc(long2para(nEnvSize + ENV_KEEPFREE), mem_access_mode, (seg FAR *) pChildEnvSeg, NULL /*(UWORD FAR *) MaxEnvSize ska */ )) < 0) return RetCode; pDest = MK_FP(*pChildEnvSeg + 1, 0); /* fill the new env and inform the process of its */ /* location throught the psp */ /* copy the environment */ if (pSrc) { fbcopy(pSrc, pDest, nEnvSize); pDest += nEnvSize; } else *pDest++ = '\0'; /* create an empty environment */ /* initialize 'extra strings' count */ *((UWORD FAR *) pDest)++ = 1; /* copy complete pathname */ if ((RetCode = truename(pathname, pDest, TRUE)) != SUCCESS) { return RetCode; } /* Theoretically one could either: + resize the already allocated block to best-fit behind the pathname, or + generate the filename into a temporary buffer to allocate only the minimum required environment -- 1999/04/21 ska */ return SUCCESS; }