;-----------------------------------------------------------------------; ; ; ; RIPAUX.ASM - ; ; ; ; Debugging Support Routines ; ; ; ;-----------------------------------------------------------------------; TITLE RIPAUX.ASM - Debugging Support Routines ;-----------------------------------------------------------------------; ; INCLUDES ; ;-----------------------------------------------------------------------; ?RIPAUX = 1 .xlist win3deb = 1 include kernel.inc include newexe.inc .list ;-----------------------------------------------------------------------; ; DATA SEGMENT DEFINITION ; ;-----------------------------------------------------------------------; DataBegin externD pDisableProc if ROM externD prevInt21Proc endif if KDEBUG externW fWinX externW hExeHead externB szDebugStr externW DebugOptions externW DebugFilter externB fKTraceOut ; Used by DebugWrite to ignore traces else externB szExitStr1 externB szExitStr2 externB szFatalExit endif DataEnd ifdef WOW externFP FatalExitC endif ;-----------------------------------------------------------------------; ; CODE SEGMENT DEFINITION ; ;-----------------------------------------------------------------------; sBegin CODE assumes CS,CODE assumes DS,NOTHING assumes ES,NOTHING ;-----------------------------------------------------------------------; ; EXTERNS ; ;-----------------------------------------------------------------------; externFP ExitKernel externFP FatalAppExit if KDEBUG externFP KOutDebugStr externNP DebugOutput2 externNP LogParamError2 else ife ROM externD prevInt21Proc endif externFP InternalDisableDOS endif externNP DebugLogError externNP DebugLogParamError ; SAVEREGS - Preserve all registers ; SAVEREGS macro if PMODE32 .386 push ds ; preserve all registers push es push fs push gs pushad .286p else push ds push es pusha endif; PMODE32 endm ; RESTOREREGS - Restore registeres preserved with SAVEREGS ; RESTOREREGS macro if PMODE32 .386 popad pop gs pop fs pop es pop ds .286p else popa pop es pop ds endif; PMODE32 endm ; In Win3.1, different code is used for FatalExit on retail ; and debug builds. WOW uses the code below derived from the ; debug wrapper for FatalExitC. FatalExitC is a thunk to ; WOW32's WK32FatalExit thunk. cProc FatalExit, parmW errCode cBegin nogen push bp mov bp, sp push ds pusha setkernelds push [bp+6] cCall FatalExitC popa pop ds INT3_DEBUG ; Stop here so WDEB386 can generate same stack trace. pop bp ret 2 cEnd nogen if KDEBUG BegData szCRLF db 13,10,0 EndData ; trashes ES - but caller saves it cProc KOutDSStr,, parmW string cBegin if PMODE32 .386p pushad ; save upper 16 bits of EAX, etc push fs push gs .286p endif pushf push cs push ds push es push ss pusha SetKernelDS mov si,string call KOutDebugStr ; expects pusha right before call popa add sp, 8 ; don't need to restore seg regs popf if PMODE32 .386p pop gs pop fs popad .286p endif cEnd cProc _krDebugTest,, ParmW flags ParmW psz LocalW axSave LocalW bxSave LocalW esSave LocalB fBreakPrint cBegin assumes ds,NOTHING mov axSave,ax ; save these regs before they're trashed mov bxSave,bx mov esSave,es ; ; If this .exe hasn't been relocated yet, just call FatalExit. ; mov ax,cs:MyCSDS cmp ax,1000h ;; DATA should be selector, not addr jc @F jmp kdtnobreak @@: mov es,ax assumes es,DATA ; ; First use DebugOptions to determine whether we should print anything ; and/or break with a fatalexit. ; errnz low(DBF_SEVMASK) errnz low(DBF_TRACE) errnz low(DBF_WARNING) errnz low(DBF_ERROR) errnz low(DBF_FATAL) mov bx,es:DebugOptions mov ax,flags and ah,high(DBF_SEVMASK) mov al,2 ; fBreak = FALSE, fPrint = TRUE cmp ah,high(DBF_TRACE) jnz notrace push ax mov es:fKTraceOut, 1 ; Set flag for DebugWrite mov ax,flags ; if (!((flags & DBF_FILTERMASK) & DebugFilter)) and ax,DBF_FILTERMASK test ax,es:DebugFilter pop ax jnz @F and al,not 2 ; fPrint = FALSE jmp short kdtnobreak @@: test bx,DBO_TRACEBREAK ; else if (DebugOptions & DBO_TRACEBREAK) jnz kdtbreak ; fBreak = TRUE jmp short kdtnobreak notrace: cmp ah,high(DBF_WARNING) jnz nowarn test bx,DBO_WARNINGBREAK jnz kdtbreak jmp short kdtnobreak nowarn: cmp ah,high(DBF_ERROR) jnz dofatal test bx,DBO_NOERRORBREAK jnz kdtnobreak jmp short kdtbreak dofatal: test bx,DBO_NOFATALBREAK jnz kdtnobreak errn$ kdtbreak kdtbreak: or al,1 ; fBreak = TRUE kdtnobreak: ; ; If DBO_SILENT, then fPrint = FALSE ; test bx,DBO_SILENT jz @F and al,not 2 @@: mov fBreakPrint,al ; ; if (fBreak || fPrint) ; print out the string ; or al,al ; if !(fBreak | fPrint) jz kdtnoprint assumes es,NOTHING mov es,esSave ; restore registers mov ax,axSave mov bx,bxSave push psz call KOutDSStr ; output the string ifndef WOW push offset szCRLF call KOutDSStr endif kdtnoprint: test fBreakPrint,1 ; if fBreak, then FatalExit jz kdtexit kdtdobreak: cCall FatalExit, kdtexit: SetKernelDS es mov es:fKTraceOut,0 ; Clear DebugWrite flag mov ax, axSave mov bx, bxSave mov es, esSave cEnd ifdef DISABLE flags equ word ptr [bp+6] msg equ word ptr [bp+8] appDS equ word ptr [bp-2] appAX equ word ptr [bp-4] ;myDS equ word ptr [bp-6] _krDebugTest proc far ;Per-component - check right flags public _krDebugTest push bp mov bp, sp push ds ; at BP-2 push ax ; at BP-4 mov ax, _DATA cmp ax, 1000h ;; DATA should be selector, not addr jnc skip mov ds, ax assume ds:_DATA mov ax, [flags] ; See if component enabled and ax, [_krInfoLevel] and al, byte ptr [_Win3InfoLevel] ; See if system enabled cmp ax, [flags] jnz skip push bx ; Print it, so format message test al, DEB_ERRORS mov bx, dataoffset STR_krError jnz @F test al, DEB_WARNS mov bx, dataoffset STR_krWarn jnz @F test al, DEB_TRACES mov bx, dataoffset STR_krTrace jz short deb_no_msg_type @@: push bx call KOutDSStr deb_no_msg_type: mov bx, dataoffset STR_krTable or ah, ah jz deb_show_it @@: add bx, 2 ; get next string table entry shr ah, 1 ; find which category jnz @B deb_show_it: push [bx] ;; push parameter call KOutDSStr pop bx ;; restore reg mov ax, [appAX] ; print message passed in push ds mov ds, appDS ; restore App ds for error strings push [msg] call KOutDSStr pop ds ; restore kernel DS mov ax, [flags] ; shall we have a breakpoint? and ax, [_krBreakLevel] and al, byte ptr _Win3BreakLevel cmp ax, [flags] jnz skip INT3_DEBUG skip: test byte ptr [flags], DEB_FERRORS jz @F push 0 push cs:MyCSDS push word ptr [bp+8] cCall FatalAppExit ;,<0,DGROUP,[bp+8]> @@: pop ax pop ds pop bp retf _krDebugTest endp endif; DISABLE endif; KDEBUG ife KDEBUG ; RETAIL ONLY SECTION STARTS HERE! ifndef WOW ; WOW uses only the "debug" version ; of FatalExit which calls FatalExitC. ; FatalExitC is thunked in WOW, the ; version in rip.c is disabled. ;-----------------------------------------------------------------------; ; ; ; FatalExit() - ; ; ; ;-----------------------------------------------------------------------; ; Retail version. The Debug version is in RIP.C. assumes ds, nothing assumes es, nothing cProc FatalExit, parmW errCode cBegin SetKernelDS push 0 push ds push dataOffset szFatalExit cCall IFatalAppExit ;,<0, ds, dataOffset szUndefDyn> cEnd cProc FatalExitDeath, parmW errCode localD pbuf localV buf,6 cBegin if 0 mov ax,4CFFh int 21h else SetKernelDS cCall TextMode ; Is USER started? ; cmp pDisableProc.sel,0 ; je fex1 ; cCall pDisableProc ; Yes, Call USER's DisableOEMLayer() ; ; to get to the text screen ; jmps fex1a ;fex1: ; mov ax, 6 ; set text mode ; int 10h ;fex1a: ; Is Int 21h hooked? cmp prevInt21Proc.sel,0 je fex2 cCall InternalDisableDOS ; Yes, disable Int 21h fex2: mov cx,errCode ; No output if error code is zero jcxz fe4 ; Write "FatalExit = " message to STDOUT. mov bx,1 mov dx,dataOffset szExitStr1 mov cx,20 mov ah,40h int 21h ; Error code of FFFF means Stack Overflow. mov dx,errCode inc dx jnz fe1 mov dx,dataOffset szExitStr2 ; "Stack Overflow" message mov cx,17 mov ah,40h int 21h jmps fe4 UnSetKernelDS fe1: ; Write out error code in Hex. dec dx push ss pop es lea di,buf mov ax,'x' shl 8 OR '0' stosw mov cx,4 rol dx,cl ; Rotate most significant into view fe2: mov ax,dx and ax,0Fh push cx mov cl,4 rol dx,cl ; Rotate next byte into view pop cx add al,'0' cmp al,'9' jbe fe3 add al,'A'-':' fe3: stosb loop fe2 mov ax,10 shl 8 OR 13 stosw xor ax,ax stosb lea dx,buf push ss pop ds mov cx,8 mov bx,1 mov ah,40h int 21h ; Write it out fe4: mov ax,errcode cCall ExitKernel, endif cEnd endif ; ifndef WOW else ; DEBUG ONLY SECTION STARTS HERE! ;-----------------------------------------------------------------------; ; ; ; GetSymFileName() - ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing ifdef WOW cProc GetSymFileName,, else cProc GetSymFileName,, endif ParmW hExe ParmD lpName cBegin cld les di,lpName SetKernelDS ;; cmp fWinX,0 ;; je slowboot ;; mov ax,hExe ;; cmp hExeHead,ax ;; mov ds,ax ;; UnSetKernelDS ;; je makename ;; mov si,ds:[ne_pfileinfo] ;; or si,si ;; jnz havename ;;makename: ;; mov si,ds:[ne_restab] ;; xor ax,ax ;; lodsb ;; mov cx,ax ;; rep movsb ;;appendext: ;; mov ax,'S.' ;; stosw ;; mov ax,'MY' ;; stosw ;; xor ax,ax ;; stosb ;; jmps namedone ;; ;;slowboot: mov ds,hExe mov si,ds:[ne_pfileinfo] havename: add si,opFile nameloop: lodsb cmp al,'.' je appendext stosb jmp nameloop appendext: mov ax,'S.' stosw mov ax,'MY' stosw xor ax,ax stosb les ax,lpName mov dx,es cEnd ;-----------------------------------------------------------------------; ; ; ; OpenSymFile() - ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing ifdef WOW cProc OpenSymFile,, else cProc OpenSymFile,, endif ParmD path LocalV Buffer,MaxFileLen cBegin lds dx,path ; get pointer to pathname mov di,3D40h ; open file function code (SHARE_DENY_NONE) ; We don't use open test for existance, rather we use get ; file attributes. This is because if we are using Novell ; netware, opening a file causes Novell to execute its ; own path searching logic, but we want our code to do it. mov ax,4300h ; Get file attributes int 21h ; Does the file exist? jc opnnov ; No, then don't do the operation mov ax,di ; Yes, open the file then int 21h jnc opn2 opnnov: mov ax, -1 opn2: mov bx,ax cEnd ;-----------------------------------------------------------------------; ; ; ; GetDebugString() - ; ; ; ;-----------------------------------------------------------------------; ; Finds the 'strIndex'-th string in 'szDebugStr'. The strings are defined ; in STRINGS.ASM. assumes ds, nothing assumes es, nothing ifdef WOW cProc GetDebugString,, else cProc GetDebugString,, endif parmW strIndex cBegin SetKernelDS es mov di,dataOffset szDebugStr mov bx,strIndex cld gds1: dec bx jl gdsx xor ax,ax mov cx,-1 repne scasb cmp es:[di],al jne gds1 xor di,di mov es,di gdsx: mov ax,di mov dx,es UnSetKernelDS es cEnd ;-----------------------------------------------------------------------; ; ; ; GetExeHead() - ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing ifdef WOW cProc GetExeHead, else cProc GetExeHead, endif cBegin nogen push ds SetKernelDS mov ax,[hExeHead] pop ds UnSetKernelDS ret cEnd nogen ;-----------------------------------------------------------------------; ; ; ; LSHL() - ; ; ; ;-----------------------------------------------------------------------; ifdef WOW if KDEBUG sEnd CODE sBegin MISCCODE assumes cs, MISCCODE endif endif assumes ds, nothing assumes es, nothing cProc LSHL, cBegin nogen pop bx pop cx pop ax xor dx,dx lshl1: shl ax,1 rcl dx,1 loop lshl1 jmp bx cEnd nogen ifdef WOW if KDEBUG sEnd MISCCODE sBegin CODE assumes CS,CODE endif endif ;-----------------------------------------------------------------------; ; ; ; FarKernelError() - ; ; ; ; 05-09-91 EarleH modified to save and restore all registers. ; ; ; ;-----------------------------------------------------------------------; ; Far entry point for KernelError(). Allows the multitude of calls to ; KernelError() be made as near calls. assumes ds, nothing assumes es, nothing cProc FarKernelError, cBegin nogen push bp mov bp,sp SAVEREGS mov ax, _DATA mov ds, ax ; push [bp+14] push [bp+12] push ds ; seg of string push [bp+10] push [bp+8] push [bp+6] mov bp,[bp] ifdef WOW cCall else call KernelError endif or ax, ax RESTOREREGS pop bp jz @F INT3_DEBUG @@: ret 10 cEnd nogen ;-----------------------------------------------------------------------; ; ; ; NearKernelError() - ; ; ; ; 05-09-91 EarleH modified to save and restore all registers. ; ; ; ;-----------------------------------------------------------------------; ifndef WOW cProc NearKernelError, cBegin nogen push bp mov bp,sp SAVEREGS mov ax, _DATA mov ds, ax ; push [bp+12] ; only pass 4 words now push [bp+10] ; err code push ds ; seg of string push [bp+8] ; offset of string push [bp+6] push [bp+4] mov bp,[bp] ; hide this stack frame call KernelError or ax, ax RESTOREREGS pop bp jz @F INT3_DEBUG @@: ret 8 cEnd nogen endif ;; ! WOW ;-----------------------------------------------------------------------; ; ; ; IsCodeSelector() - ; ; ; ;-----------------------------------------------------------------------; ifdef WOW cProc IsCodeSelector, else cProc IsCodeSelector, endif parmW Candidate cBegin mov ax,Candidate lar dx,ax jz lar_passed xor ax,ax jmp ics_ret lar_passed: xor ax,ax test dh,00001000b ; Executable segment descriptor? jz ics_ret ; No push cs ; Yes pop dx and dx,3 and Candidate,3 ; RPL matches that of CS? cmp dx,Candidate jne ics_ret ; No inc ax ; Yes ics_ret: cEnd endif; DEBUG ; DEBUG ONLY SECTION ENDS HERE ;-----------------------------------------------------------------------; ; ; ; DebugBreak() - ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing if KDEBUG db_tab dw db_t, db_w, db_e, db_f, db_k, db_3 db_len equ ($ - db_tab) / 2 endif cProc DebugBreak, cBegin nogen if KDEBUG cmp ax, 0DACh jnz db_3 cmp bx, db_len jae db_3 shl bx, 1 jmp db_tab[bx] db_t: krDebugOut DEB_TRACE, "Test trace" jmps db_end db_w: krDebugOut DEB_WARN, "Test warning" jmps db_end db_e: krDebugOut DEB_ERROR, "Test error" jmps db_end db_f: krDebugOut DEB_FERROR, "Test fatal error" jmps db_end db_k: kerror 0, "This is a kernel error" jmps db_end db_3: endif INT3_DEBUG ; Jump to debugger if installed db_end: ret cEnd nogen ;-----------------------------------------------------------------------; ; ; ; TextMode() - Enter text mode for debugging load failure ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing cProc TextMode,, cBegin SetKernelDS cmp word ptr [pDisableProc+2],0 je tm1 cCall [pDisableProc] jmps tm2 tm1: ifdef NEC_98 mov ah,41h ;graphic picture stop display int 18h mov ah,0Ch ;text picture start disply int 18h else ; NEC_98 mov ax, 3 int 10h endif ; NEC_98 tm2: cEnd ;-----------------------------------------------------------------------; ; ; ; DoAbort() - ; ; ; ;-----------------------------------------------------------------------; assumes ds, nothing assumes es, nothing cProc DoAbort, cBegin nogen SetKernelDS cCall TextMode ; cmp word ptr [pDisableProc+2],0 ; je doa1 ; cCall [pDisableProc] ;doa1: mov ax,1 cCall ExitKernel, UnSetKernelDS cEnd nogen ;-----------------------------------------------------------------------; ; ; HandleParamError ; ; This entry point is jumped to by the parameter validation code of ; USER and GDI (and KERNEL). Its job is to parse the error code ; and, if necessary, RIP or jmp to the error handler routine ; ; Stack on entry: Far return addr to validation code ; Saved error handler offset ; Saved BP ; API far ret addr ; ;-----------------------------------------------------------------------; ERR_WARNING equ 08000h ; from error.h/windows.h/layer.inc LabelFP push bp mov bp,sp push bx ; save err code push bx ; push err code push [bp+4] ; push err return addr as place where error occured push [bp+2] push cx ; push parameter push ax call far ptr LogParamError ; yell at the guy pop bx pop bp test bh,high(ERR_WARNING) ; warn or fail? errnz low(ERR_WARNING) jnz @F pop bx pop ax ; pop far return addr pop bx ; get error handler address pop bp ; restore BP and bp,not 1 ; Make even in case "inc bp" for ATM push ax push bx ; far return to handler xor ax,ax ; set dx:ax == 0 for default return value cwd xor cx,cx ; set cx == 0 too, for kernel error returns mov es,ax ; clear ES just in case it contains ; a Windows DLL's DS... @@: retf cProc DebugFillBuffer,, ParmD lpb ParmW cb cBegin if KDEBUG assumes ES,data mov ax,_DATA mov es,ax test es:DebugOptions,DBO_BUFFERFILL assumes ES,nothing jz dfbexit les di,lpb mov cx,cb mov ax,(DBGFILL_BUFFER or (DBGFILL_BUFFER shl 8)) cld shr cx,1 rep stosw rcl cx,1 rep stosb dfbexit: endif; KDEBUG cEnd ;======================================================================== ; ; void FAR _cdecl DebugOutput(UINT flags, LPCSTR lpszFmt, ...); ; ; NOTE: there is a CMACROS bug with C that causes the parameter offsets ; to be calculated incorrectly. Offsets calculated by hand here. ; cProc DebugOutput, flags equ <[bp+2+4]> ; parmW flags point past ret addr & saved bp lpszFmt equ <[bp+2+4+2]> ; parmD lpszFmt cBegin if KDEBUG push bp ; generate a stack frame mov bp,sp SAVEREGS ; save all registers push ds SetKernelDS lea ax,flags ; point at flags, lpszFmt, and rest of arguments push ss push ax call DebugOutput2 UnsetKernelDS pop ds or ax,ax ; test break flag RESTOREREGS pop bp jz @F ; break if needed INT3_DEBUG @@: endif cEnd ;======================================================================== ; ; void WINAPI LogError(UINT err, void FAR* lpInfo); ; cProc LogError, ParmD err ParmW lpInfo cBegin SAVEREGS assumes ds,NOTHING cCall DebugLogError, RESTOREREGS cEnd ;======================================================================== ; ; void WINAPI LogParamError(UINT err, FARPROC lpfn, void FAR* param); ; cProc LogParamError, ParmW err ParmD lpfn ParmD param cBegin assumes ds,NOTHING SAVEREGS ; Call debugger hook (note the reversed parameter order) ; cCall DebugLogParamError, if KDEBUG push ds SetKernelDS assumes ds,DATA mov bx, [bp+0] ; address of faulting code mov bx, ss:[bx+0] ; mov bx, ss:[bx+0] mov bx, ss:[bx+4] cCall LogParamError2, UnsetKernelDS pop ds assumes ds,NOTHING or ax,ax ; test break flag endif RESTOREREGS if KDEBUG jz @F ; break if needed INT3_DEBUG @@: endif cEnd ;----------------------------------------------------------------------- sEnd CODE if KDEBUG ifdef WOW sBegin MISCCODE assumes cs, MISCCODE assumes ds, nothing assumes es, nothing externFP KernelError ;-----------------------------------------------------------------------; ; allows KernelError to be called from _TEXT code segment cProc Far_KernelError, parmW errcode parmD lpmsg1 parmD lpmsg2 cBegin push [bp+14] push [bp+12] push [bp+10] push [bp+8] push [bp+6] call far ptr KernelError cEnd sEnd MISCCODE endif ;; WOW endif ;; KDEBUG end