/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org 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 Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ /* *@@sourcefile debug.c: * this file contains debugging functions for the * exception handlers in except.c. * * This code is capable of unwinding the stack from * a given address and trying to get function names * and source line numbers, either from the respective * module's debug code (if present) or from a SYM file, * which is searched for in the directory of the module * or in ?:\OS2\PDPSI\PMDF\WARP4. * * This file incorporates code from the following: * -- Marc Fiammante, John Currier, Kim Rasmussen, * Anthony Cruise (EXCEPT3.ZIP package for a generic * exception handling DLL, available at Hobbes). * * Usage: All OS/2 programs. * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@changed V0.9.0 [umoeller]: made some declarations C++-compatible *@@changed V0.9.1 (2000-01-30) [umoeller]: greatly cleaned up this file * *@@header "helpers\debug.h" */ /* * This file Copyright (C) 1992-99 Ulrich M�ller, * Kim Rasmussen, * Marc Fiammante, * John Currier, * Anthony Cruise. * This file is part of the "XWorkplace helpers" source package. * * 2009-06-15 published under LGPL3 with Ulrich M�ller permission. * */ //#define DEBUG_SYMDUMP // enable to dump sym file to log //YD commented, since we need unsigned char BYTE! //#define OS2EMX_PLAIN_CHAR //Also gcc char is signed, while most structures requires unsigned data! //Raised limits for all fields! // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSPROCESS #define INCL_DOSMODULEMGR #define INCL_DOSMISC #define INCL_DOSERRORS #include #include #include #include #define DONT_REPLACE_MALLOC #include "helpers\setup.h" // code generation and debugging options #include "helpers\debug.h" #include "helpers\dosh.h" #pragma hdrstop #include #ifdef __EMX__ #include // required for sys\stat.h; UM 99-10-22 #endif #include #include #include #ifndef DWORD #define DWORD unsigned long #endif #ifndef WORD #define WORD unsigned short #endif #pragma stack16(512) #define HF_STDERR 2 /* *@@category: Helpers\Control program helpers\Exceptions/debugging * See except.c and debug.c. */ /* ****************************************************************** * * Global variables * ********************************************************************/ // this specifies whether we're dealing with 32-bit code; // this gets changed whenever 16-bit count is detected static BOOL f32bit = TRUE; /* * Global variables for Read32PmDebug: * */ ULONG func_ofs; ULONG pubfunc_ofs; //YD 17/07/06 c++ namespace can generate really long //YD names, use a large buffer! char func_name[16*1024]; ULONG var_ofs = 0; struct { BYTE name[128]; ULONG stack_offset; USHORT type_idx; } autovar_def[1024]; #pragma pack(1) BYTE *type_name[] = { "8 bit signed ", "16 bit signed ", "32 bit signed ", "Unknown (0x83) ", "8 bit unsigned ", "16 bit unsigned ", "32 bit unsigned ", "Unknown (0x87) ", "32 bit real ", "64 bit real ", "80 bit real ", "Unknown (0x8B) ", "64 bit complex ", "128 bit complex ", "160 bit complex ", "Unknown (0x8F) ", "8 bit boolean ", "16 bit boolean ", "32 bit boolean ", "Unknown (0x93) ", "8 bit character ", "16 bit characters ", "32 bit characters ", "void ", "15 bit unsigned ", "24 bit unsigned ", "31 bit unsigned ", "Unknown (0x9B) ", "Unknown (0x9C) ", "Unknown (0x9D) ", "Unknown (0x9E) ", "Unknown (0x9F) ", "near pointer to 8 bit signed ", "near pointer to 16 bit signed ", "near pointer to 32 bit signed ", "Unknown (0xA3) ", "near pointer to 8 bit unsigned ", "near pointer to 16 bit unsigned ", "near pointer to 32 bit unsigned ", "Unknown (0xA7) ", "near pointer to 32 bit real ", "near pointer to 64 bit real ", "near pointer to 80 bit real ", "Unknown (0xAB) ", "near pointer to 64 bit complex ", "near pointer to 128 bit complex ", "near pointer to 160 bit complex ", "Unknown (0xAF) ", "near pointer to 8 bit boolean ", "near pointer to 16 bit boolean ", "near pointer to 32 bit boolean ", "Unknown (0xB3) ", "near pointer to 8 bit character ", "near pointer to 16 bit characters", "near pointer to 32 bit characters", "near pointer to void ", "near pointer to 15 bit unsigned ", "near pointer to 24 bit unsigned ", "near pointer to 31 bit unsigned ", "Unknown (0xBB) ", "Unknown (0xBC) ", "Unknown (0xBD) ", "Unknown (0xBE) ", "Unknown (0xBF) ", "far pointer to 8 bit signed ", "far pointer to 16 bit signed ", "far pointer to 32 bit signed ", "Unknown (0xC3) ", "far pointer to 8 bit unsigned ", "far pointer to 16 bit unsigned ", "far pointer to 32 bit unsigned ", "Unknown (0xC7) ", "far pointer to 32 bit real ", "far pointer to 64 bit real ", "far pointer to 80 bit real ", "Unknown (0xCB) ", "far pointer to 64 bit complex ", "far pointer to 128 bit complex ", "far pointer to 160 bit complex ", "Unknown (0xCF) ", "far pointer to 8 bit boolean ", "far pointer to 16 bit boolean ", "far pointer to 32 bit boolean ", "Unknown (0xD3) ", "far pointer to 8 bit character ", "far pointer to 16 bit characters ", "far pointer to 32 bit characters ", "far pointer to void ", "far pointer to 15 bit unsigned ", "far pointer to 24 bit unsigned ", "far pointer to 31 bit unsigned ", }; // Thanks to John Currier: // Do not call 16 bit code in myHandler function to prevent call // to __EDCThunkProlog and problems is guard page exception handling // Also reduce the stack size to 1K for true 16 bit calls. // 16 bit calls thunk will now only occur on fatal exceptions #pragma stack16(1024) // ------------------------------------------------------------------ // Last 8 bytes of 16:16 file when CODEVIEW debugging info is present #pragma pack(1) struct _eodbug { unsigned short dbug; // 'NB' signature unsigned short ver; // version unsigned long dfaBase; // size of codeview info } G_eodbug; #define DBUGSIG 0x424E #define SSTMODULES 0x0101 #define SSTPUBLICS 0x0102 #define SSTTYPES 0x0103 #define SSTSYMBOLS 0x0104 #define SSTSRCLINES 0x0105 #define SSTLIBRARIES 0x0106 #define SSTSRCLINES2 0x0109 #define SSTSRCLINES32 0x010B typedef struct _SYMBASE { unsigned short dbug; // 'NB' signature unsigned short ver; // version unsigned long lfoDir; // file offset to dir entries } SYMBASE; typedef struct _SSDIR { unsigned short sst; // SubSection Type unsigned short modindex; // Module index number unsigned long lfoStart; // Start of section unsigned short cb; // Size of section } SSDIR; typedef struct _SSDIR32 { unsigned short sst; // SubSection Type unsigned short modindex; // Module index number unsigned long lfoStart; // Start of section unsigned long cb; // Size of section } SSDIR32; typedef struct _SSMODULE { unsigned short csBase; // code segment base unsigned short csOff; // code segment offset unsigned short csLen; // code segment length unsigned short ovrNum; // overlay number unsigned short indxSS; // Index into sstLib or 0 unsigned short reserved; BYTE csize; // size of prefix string } SSMODULE; typedef struct _SSMOD32 { unsigned short csBase; // code segment base unsigned long csOff; // code segment offset unsigned long csLen; // code segment length unsigned long ovrNum; // overlay number unsigned short indxSS; // Index into sstLib or 0 unsigned long reserved; BYTE csize; // size of prefix string } SSMOD32; typedef struct _SSPUBLIC { unsigned short offset; unsigned short segment; unsigned short type; BYTE csize; } SSPUBLIC; typedef struct _SSPUBLIC32 { unsigned long offset; unsigned short segment; unsigned short type; BYTE csize; } SSPUBLIC32; typedef struct _SSLINEENTRY32 { unsigned short LineNum; unsigned short FileNum; unsigned long Offset; } SSLINEENTRY32; typedef struct _FIRSTLINEENTRY32 { unsigned short LineNum; unsigned char entry_type; unsigned char reserved; unsigned short numlines; unsigned short segnum; } FIRSTLINEENTRY32; typedef struct _SSFILENUM32 { unsigned long first_displayable; // Not used unsigned long number_displayable; // Not used unsigned long file_count; // number of source files } SSFILENUM32; /* *@@ XDEBUGINFO: * buffers for Read... funcs. * *@@added V0.9.4 (2000-06-15) [umoeller] */ typedef struct _XDEBUGINFO { char szNrFile[300]; // receives source file char szNrLine[300]; // receives line number //YD 17/07/06 c++ namespace can generate really long //YD names, use a large buffer! char szNrPub[16*1024]; // receives function name struct new_seg *pseg; struct o32_obj *pobj; // flat .EXE object table entry SYMBASE base; SSDIR *pDirTab; SSDIR32 *pDirTab32; unsigned char *pEntTab; unsigned long lfaBase; SSMOD32 ssmod32; SSPUBLIC32 sspub32; SSMODULE ssmod; SSPUBLIC sspub; } XDEBUGINFO, *PXDEBUGINFO; USHORT _THUNK_FUNCTION (Dos16SizeSeg) (); //APIRET16 APIENTRY16 DOS16SIZESEG(USHORT Seg, PULONG16 Size); USHORT DosSizeSeg (USHORT Seg, PULONG16 Size) { return ((USHORT) (_THUNK_PROLOG (2+4); _THUNK_SHORT (Seg); _THUNK_FLAT (Size); _THUNK_CALL (Dos16SizeSeg))); } #pragma pack() /* ****************************************************************** * * PART 1: ANALYZE DEBUG CODE * ********************************************************************/ static int Read16CodeView(FILE *LogFile, PXDEBUGINFO pxdi, int fh, int TrapSeg, int TrapOff, CHAR *FileName); static int Read32PmDebug(FILE *LogFile, PXDEBUGINFO pxdi, int fh, int TrapSeg, int TrapOff, CHAR *FileName); /* *@@ WriteAddressInfo: * formats and writes a line into the trap log * file. * * This gets called for each line from the * stack dump. At this point, the line in the * trap log already has: * + CS:EIP : 000109FF XMLVIEW :0 + ^^^ and we write here * After this call, we have. * + CS:EIP : 000109FF XMLVIEW :0 xxx.c 123 ConfirmCreate__Fv + ^^^ and we write here * *@@added V0.9.12 (2001-05-12) [umoeller] */ static VOID WriteDebugInfo(FILE *LogFile, // in: open log file PXDEBUGINFO pxdi) // in: debug info { fprintf(LogFile, "%s%s%s", pxdi->szNrFile, pxdi->szNrLine, pxdi->szNrPub); } /* *@@ dbgPrintDebugInfo: * this is the main entry point into analyzing debug * code. * * This analyzes a given address and tries to find * debug code descriptions for this address. If found, * the information is written to the given log file. * * Gets called from dbgPrintStack. * * This returns NO_ERROR if the could was successfully * analyzed or something != 0 if we failed. * * New with V0.84. */ APIRET dbgPrintDebugInfo(FILE *LogFile, // out: log file to write to CHAR *FileName, // in: EXE/DLL module file name ULONG Object, // in: trapping object (from DosQueryModFromEIP) ULONG TrapOffset) // in: trapping address (from DosQueryModFromEIP) { APIRET rc = 0; int ModuleFile = 0; static struct exe_hdr OldExeHeader; static struct new_exe NewExeHeader; ULONG ulSegment = Object + 1; // segment no. is object no. + 1 XDEBUGINFO xdi; memset(&xdi, 0, sizeof(xdi)); // open the module file for reading to analyze the code ModuleFile = sopen(FileName, O_RDONLY | O_BINARY, SH_DENYNO); if (ModuleFile != -1) { // file found: // read old Exe header if (read(ModuleFile, (void*)&OldExeHeader, 64) == -1L) { fprintf(LogFile, "errno %d reading old exe header\n", errno); close(ModuleFile); return 2; } // seek to new Exe header if (lseek(ModuleFile, (long)E_LFANEW(OldExeHeader), SEEK_SET) == -1L) { fprintf(LogFile, "errno %d seeking to new exe header\n", errno); close(ModuleFile); return 3; } if (read(ModuleFile, (void *)&NewExeHeader, 64) == -1L) { fprintf(LogFile, "errno %d reading new exe header\n", errno); close(ModuleFile); return 4; } // check EXE signature if (NE_MAGIC(NewExeHeader) == E32MAGIC) { /* * flat 32 executable: * */ // do analysis for 32-bit code if (!(rc = Read32PmDebug(LogFile, &xdi, // output ModuleFile, ulSegment, TrapOffset, FileName))) WriteDebugInfo(LogFile, &xdi); close(ModuleFile); // rc !=0 try with DBG file if (rc != 0) { strcpy(FileName + strlen(FileName) - 3, "DBG"); // Build DBG File name ModuleFile = sopen(FileName, O_RDONLY | O_BINARY, SH_DENYNO); if (ModuleFile != -1) { if (!(rc = Read32PmDebug(LogFile, &xdi, ModuleFile, ulSegment, TrapOffset, FileName))) WriteDebugInfo(LogFile, &xdi); close(ModuleFile); } } return rc; } else { if (NE_MAGIC(NewExeHeader) == NEMAGIC) { /* * 16:16 executable: * */ if ((xdi.pseg = (struct new_seg *)calloc(NE_CSEG(NewExeHeader), sizeof(struct new_seg))) == NULL) { fprintf(LogFile, "Out of memory!"); close(ModuleFile); return -1; } if ( lseek(ModuleFile, E_LFANEW(OldExeHeader) + NE_SEGTAB(NewExeHeader), SEEK_SET) == -1L) { fprintf(LogFile, "Error %u seeking segment table in %s\n", errno, FileName); free(xdi.pseg); close(ModuleFile); return 9; } if (read(ModuleFile, (void *)xdi.pseg, NE_CSEG(NewExeHeader) * sizeof(struct new_seg)) == -1) { fprintf(LogFile, "Error %u reading segment table from %s\n", errno, FileName); free(xdi.pseg); close(ModuleFile); return 10; } if (!(rc = Read16CodeView(LogFile, &xdi, ModuleFile, ulSegment, TrapOffset, FileName))) WriteDebugInfo(LogFile, &xdi); free(xdi.pseg); close(ModuleFile); // rc !=0 try with DBG file if (rc != 0) { strcpy(FileName + strlen(FileName) - 3, "DBG"); // Build DBG File name ModuleFile = sopen(FileName, O_RDONLY | O_BINARY, SH_DENYNO); if (ModuleFile != -1) { if (!(rc = Read16CodeView(LogFile, &xdi, ModuleFile, ulSegment, TrapOffset, FileName))) WriteDebugInfo(LogFile, &xdi); close(ModuleFile); } } return rc; } else { /* * Unknown executable: * */ fprintf(LogFile, "Error, could not find exe signature"); close(ModuleFile); return 11; } } } // end if (ModuleFile != -1) else { fprintf(LogFile, "Error %d opening module file %s", errno, FileName); return 1; } // endif // return 0; we never get here } char fname[256], ModName[80]; char ename[256], dummy[256]; #define MAX_USERDEFS 300 // raised from 150 V0.9.1 (2000-01-30) [umoeller] #define MAX_POINTERS 300 // raised from 150 V0.9.1 (2000-01-30) [umoeller] USHORT userdef_count; USHORT pointer_count; struct one_userdef_rec { USHORT idx; USHORT type_index; BYTE name[33]; } one_userdef[MAX_USERDEFS]; struct one_pointer_rec { USHORT idx; USHORT type_index; BYTE type_qual; BYTE name[33]; } one_pointer[MAX_POINTERS]; /* * Read32PmDebug: * parses 32-bit debug code. * Called from dbgPrintDebugInfo for 32-bit modules. */ static int Read32PmDebug(FILE *LogFile, // in: text log file to write to PXDEBUGINFO pxdi, int ModuleFile, // in: module file opened with sopen() int TrapSeg, int TrapOff, CHAR *FileName) { static unsigned int CurrSymSeg, NrSymbol, /* offset, */ NrPublic, NrFile, NrLine, /* NrEntry */ numdir, namelen, numlines /* , line */; static int ModIndex; static int bytesread, i, j; static SSLINEENTRY32 LineEntry; static SSFILENUM32 FileInfo; static FIRSTLINEENTRY32 FirstLine; static BYTE dump_vars = FALSE; static USHORT idx; static BOOL read_types; static LONG lSize; ModIndex = 0; // See if any CODEVIEW info if (lseek(ModuleFile, -8L, SEEK_END) == -1) { fprintf(LogFile, "Error %u seeking CodeView table in %s\n", errno, FileName); return (18); } if (read(ModuleFile, (void *)&G_eodbug, 8) == -1) { fprintf(LogFile, "Error %u reading debug info from %s\n", errno, FileName); return (19); } if (G_eodbug.dbug != DBUGSIG) { // fprintf(LogFile,"\nNo CodeView information stored.\n"); return (100); } if ( (pxdi->lfaBase = lseek(ModuleFile, -(LONG)G_eodbug.dfaBase, SEEK_END)) == -1L) { fprintf(LogFile, "Error %u seeking base codeview data in %s\n", errno, FileName); return (20); } if (read(ModuleFile, (void *)&pxdi->base, 8) == -1) { fprintf(LogFile, "Error %u reading base codeview data in %s\n", errno, FileName); return (21); } if (lseek(ModuleFile, pxdi->base.lfoDir - 8 + 4, SEEK_CUR) == -1) { fprintf(LogFile, "Error %u seeking dir codeview data in %s\n", errno, FileName); return (22); } if (read(ModuleFile, (void *)&numdir, 4) == -1) { fprintf(LogFile, "Error %u reading dir codeview data in %s\n", errno, FileName); return (23); } // Read dir table into buffer if ( (pxdi->pDirTab32 = (SSDIR32*)calloc(numdir, sizeof(SSDIR32))) == NULL) { fprintf(LogFile, "Out of memory!"); return (-1); } if (read(ModuleFile, (void*)pxdi->pDirTab32, numdir * sizeof(SSDIR32)) == -1) { fprintf(LogFile, "Error %u reading codeview dir table from %s\n", errno, FileName); free(pxdi->pDirTab32); return (24); } i = 0; while (i < numdir) { if (pxdi->pDirTab32[i].sst != SSTMODULES) { i++; continue; } NrPublic = 0x0; NrSymbol = 0; NrLine = 0x0; NrFile = 0x0; CurrSymSeg = 0; // point to subsection lseek(ModuleFile, pxdi->pDirTab32[i].lfoStart + pxdi->lfaBase, SEEK_SET); read(ModuleFile, (void*)&pxdi->ssmod32.csBase, sizeof(SSMOD32)); read(ModuleFile, (void*)ModName, (unsigned)pxdi->ssmod32.csize); ModIndex = pxdi->pDirTab32[i].modindex; ModName[pxdi->ssmod32.csize] = '\0'; i++; read_types = FALSE; while ( (pxdi->pDirTab32[i].modindex == ModIndex) && (i < numdir) ) { // point to subsection lseek(ModuleFile, pxdi->pDirTab32[i].lfoStart + pxdi->lfaBase, SEEK_SET); switch (pxdi->pDirTab32[i].sst) { case SSTPUBLICS: bytesread = 0; while (bytesread < pxdi->pDirTab32[i].cb) { bytesread += read(ModuleFile, (void *)&pxdi->sspub32.offset, sizeof(pxdi->sspub32)); bytesread += read(ModuleFile, (void*)ename, (unsigned)pxdi->sspub32.csize); ename[pxdi->sspub32.csize] = '\0'; if ( (pxdi->sspub32.segment == TrapSeg) && (pxdi->sspub32.offset <= TrapOff) && (pxdi->sspub32.offset >= NrPublic) ) { NrPublic = pubfunc_ofs = pxdi->sspub32.offset; read_types = TRUE; sprintf(pxdi->szNrPub, "%s %s (%s)\n", (pxdi->sspub32.type == 1) ? " Abs" : " ", ename, ModName ); // but continue, because there might be a // symbol that comes closer } } break; // Read symbols, so we can dump the variables on the stack case SSTSYMBOLS: if (TrapSeg != pxdi->ssmod32.csBase) break; bytesread = 0; while (bytesread < pxdi->pDirTab32[i].cb) { static USHORT usLength; static USHORT usLengthSym; static BYTE b1, b2; static BYTE bType; // *ptr; static ULONG ofs; // static ULONG last_addr = 0; //YD 17/07/06 c++ namespace can generate really long //YD names, use a large buffer! static BYTE str[16*1024]; static struct symseg_rec symseg; static struct symauto_rec symauto; static struct symproc_rec symproc; // Read the length of this subentry bytesread += read(ModuleFile, &b1, 1); if (b1 & 0x80) { bytesread += read(ModuleFile, &b2, 1); usLength = ((b1 & 0x7F) << 8) + b2; } else usLength = b1; ofs = tell(ModuleFile); bytesread += read(ModuleFile, &bType, 1); switch (bType) { case SYM_CHANGESEG: read(ModuleFile, &symseg, sizeof(symseg)); CurrSymSeg = symseg.seg_no; break; case SYM_PROC: case SYM_CPPPROC: read(ModuleFile, &symproc, sizeof(symproc)); if (symproc.name_len & 0x80) { read(ModuleFile, &b2, 1); usLengthSym = ((symproc.name_len & 0x7F) << 8) + b2; } else { usLengthSym = symproc.name_len; } read(ModuleFile, str, usLengthSym); str[usLengthSym] = 0; if ((CurrSymSeg == TrapSeg) && (symproc.offset <= TrapOff) && (symproc.offset >= NrSymbol)) { dump_vars = TRUE; var_ofs = 0; NrSymbol = symproc.offset; func_ofs = symproc.offset; strcpy(func_name, str); } else { dump_vars = FALSE; } break; case SYM_AUTO: if (!dump_vars) break; read(ModuleFile, &symauto, sizeof(symauto)); read(ModuleFile, str, symauto.name_len); if (symauto.name_len==0x80) printf("symauto.name_len==0x80\n"); str[symauto.name_len] = 0; strcpy(autovar_def[var_ofs].name, str); autovar_def[var_ofs].stack_offset = symauto.stack_offset; autovar_def[var_ofs].type_idx = symauto.type_idx; var_ofs++; break; } bytesread += usLength; lseek(ModuleFile, ofs + usLength, SEEK_SET); } break; case SSTTYPES: // if (ModIndex != TrapSeg) if (!read_types) break; bytesread = 0; idx = 0x200; userdef_count = 0; pointer_count = 0; while (bytesread < pxdi->pDirTab32[i].cb) { static struct type_rec type; static struct type_userdefrec udef; static struct type_pointerrec point; static ULONG ofs; static BYTE str[256]; // Read the length of this subentry ofs = tell(ModuleFile); read(ModuleFile, &type, sizeof(type)); bytesread += sizeof(type); switch (type.type) { case TYPE_USERDEF: if (userdef_count >= MAX_USERDEFS) break; read(ModuleFile, &udef, sizeof(udef)); read(ModuleFile, str, udef.name_len); str[udef.name_len] = 0; // Insert userdef in table one_userdef[userdef_count].idx = idx; one_userdef[userdef_count].type_index = udef.type_index; memcpy(one_userdef[userdef_count].name, str, _min(udef.name_len + 1, 32)); one_userdef[userdef_count].name[32] = 0; userdef_count++; break; case TYPE_POINTER: if (pointer_count >= MAX_POINTERS) break; read(ModuleFile, &point, sizeof(point)); read(ModuleFile, str, point.name_len); str[point.name_len] = 0; // Insert userdef in table one_pointer[pointer_count].idx = idx; one_pointer[pointer_count].type_index = point.type_index; memcpy(one_pointer[pointer_count].name, str, _min(point.name_len + 1, 32)); one_pointer[pointer_count].name[32] = 0; one_pointer[pointer_count].type_qual = type.type_qual; pointer_count++; break; } ++idx; bytesread += type.length; lseek(ModuleFile, ofs + type.length + 2, SEEK_SET); } break; case SSTSRCLINES32: if (TrapSeg != pxdi->ssmod32.csBase) break; // read first line do { read(ModuleFile, (void *)&FirstLine, sizeof(FirstLine)); if (FirstLine.LineNum != 0) { fprintf(LogFile, "Missing Line table information\n"); break; } // endif numlines = FirstLine.numlines; // Other type of data skip 4 more bytes if (FirstLine.entry_type < 4) { read(ModuleFile, (void *)&lSize, 4); if (FirstLine.entry_type == 3) lseek(ModuleFile, lSize, SEEK_CUR); } } while (FirstLine.entry_type == 3); for (j = 0; j < numlines; j++) { switch (FirstLine.entry_type) { case 0: read(ModuleFile, (void *)&LineEntry, sizeof(LineEntry)); // Changed by Kim Rasmussen 26/06 1996 to ignore linenumber 0 // if (LineEntry.Offset+ssmod32.csOff<=TrapOff && LineEntry.Offset+ssmod32.csOff>=NrLine) { if ( (LineEntry.LineNum) && (LineEntry.Offset + pxdi->ssmod32.csOff <= TrapOff) && (LineEntry.Offset + pxdi->ssmod32.csOff >= NrLine) ) { NrLine = LineEntry.Offset; NrFile = LineEntry.FileNum; /*pOffset =sprintf(szNrLine,"%04X:%08X line #%hu ", * ssmod32.csBase,LineEntry.Offset, * LineEntry.LineNum); */ sprintf(pxdi->szNrLine, "% 6hu", LineEntry.LineNum); } break; case 1: lseek(ModuleFile, sizeof(struct linlist_rec), SEEK_CUR); break; case 2: lseek(ModuleFile, sizeof(struct linsourcelist_rec), SEEK_CUR); break; case 3: lseek(ModuleFile, sizeof(struct filenam_rec), SEEK_CUR); break; case 4: lseek(ModuleFile, sizeof(struct pathtab_rec), SEEK_CUR); break; } } if (NrFile != 0) { // file found: read(ModuleFile, (void*)&FileInfo, sizeof(FileInfo)); namelen = 0; for (j = 1; j <= FileInfo.file_count; j++) { namelen = 0; read(ModuleFile, (void *)&namelen, 1); read(ModuleFile, (void *)ename, namelen); if (j == NrFile) break; } ename[namelen] = '\0'; // pOffset=sprintf(szNrLine+pOffset," (%s) (%s)\n",ename,ModName); sprintf(pxdi->szNrFile, "%11.11s ", ename); } else { // strcat(szNrLine,"\n"); avoid new line for empty name fill strcpy(pxdi->szNrFile, "file? "); } // endif break; } // end switch i++; } // end while modindex } // End While i < numdir free(pxdi->pDirTab32); return (0); } /* * Read16CodeView: * parses 16-bit debug code. * Called from dbgPrintDebugInfo for 16-bit modules. */ static int Read16CodeView(FILE *LogFile, // in: text log file to write to PXDEBUGINFO pxdi, int fh, int TrapSeg, int TrapOff, CHAR *FileName) { static unsigned short int offset, NrPublic, NrLine, numdir, namelen, numlines, line; static int ModIndex; static int bytesread, i, j; ModIndex = 0; // See if any CODEVIEW info if (lseek(fh, -8L, SEEK_END) == -1) { fprintf(LogFile, "Error %u seeking CodeView table in %s\n", errno, FileName); return (18); } if (read(fh, (void *)&G_eodbug, 8) == -1) { fprintf(LogFile, "Error %u reading debug info from %s\n", errno, FileName); return (19); } if (G_eodbug.dbug != DBUGSIG) { // fprintf(LogFile,"\nNo CodeView information stored.\n"); return (100); } if ((pxdi->lfaBase = lseek(fh, -(LONG)G_eodbug.dfaBase, SEEK_END)) == -1L) { fprintf(LogFile, "Error %u seeking base codeview data in %s\n", errno, FileName); return (20); } if (read(fh, (void *)&pxdi->base, 8) == -1) { fprintf(LogFile, "Error %u reading base codeview data in %s\n", errno, FileName); return (21); } if (lseek(fh, pxdi->base.lfoDir - 8, SEEK_CUR) == -1) { fprintf(LogFile, "Error %u seeking dir codeview data in %s\n", errno, FileName); return (22); } if (read(fh, (void *)&numdir, 2) == -1) { fprintf(LogFile, "Error %u reading dir codeview data in %s\n", errno, FileName); return (23); } // Read dir table into buffer if ((pxdi->pDirTab = (SSDIR*)calloc(numdir, sizeof(SSDIR))) == NULL) { fprintf(LogFile, "Out of memory!"); return (-1); } if (read(fh, (void*)pxdi->pDirTab, numdir * sizeof(SSDIR)) == -1) { fprintf(LogFile, "Error %u reading codeview dir table from %s\n", errno, FileName); free(pxdi->pDirTab); return (24); } i = 0; while (i < numdir) { if (pxdi->pDirTab[i].sst != SSTMODULES) { i++; continue; } NrPublic = 0x0; NrLine = 0x0; // point to subsection lseek(fh, pxdi->pDirTab[i].lfoStart + pxdi->lfaBase, SEEK_SET); read(fh, (void *)&pxdi->ssmod.csBase, sizeof(SSMODULE)); read(fh, (void *)ModName, (unsigned)pxdi->ssmod.csize); ModIndex = pxdi->pDirTab[i].modindex; ModName[pxdi->ssmod.csize] = '\0'; i++; while (pxdi->pDirTab[i].modindex == ModIndex && i < numdir) { // point to subsection lseek(fh, pxdi->pDirTab[i].lfoStart + pxdi->lfaBase, SEEK_SET); switch (pxdi->pDirTab[i].sst) { case SSTPUBLICS: bytesread = 0; while (bytesread < pxdi->pDirTab[i].cb) { bytesread += read(fh, (void *)&pxdi->sspub.offset, sizeof(pxdi->sspub)); bytesread += read(fh, (void *)ename, (unsigned)pxdi->sspub.csize); ename[pxdi->sspub.csize] = '\0'; if ((pxdi->sspub.segment == TrapSeg) && (pxdi->sspub.offset <= TrapOff) && (pxdi->sspub.offset >= NrPublic)) { NrPublic = pxdi->sspub.offset; sprintf(pxdi->szNrPub, "%s %s (%s) %04hX:%04hX\n", (pxdi->sspub.type == 1) ? " Abs" : " ", ename, ModName, // () pxdi->sspub.segment, pxdi->sspub.offset ); } } break; case SSTSRCLINES2: case SSTSRCLINES: if (TrapSeg != pxdi->ssmod.csBase) break; namelen = 0; read(fh, (void *)&namelen, 1); read(fh, (void *)ename, namelen); ename[namelen] = '\0'; // skip 2 zero bytes if (pxdi->pDirTab[i].sst == SSTSRCLINES2) read(fh, (void *)&numlines, 2); read(fh, (void *)&numlines, 2); for (j = 0; j < numlines; j++) { read(fh, (void *)&line, 2); read(fh, (void *)&offset, 2); if (offset <= TrapOff && offset >= NrLine) { NrLine = offset; sprintf(pxdi->szNrFile, "% 12.12s ", ename); sprintf(pxdi->szNrLine, "% 6hu", line); /*sprintf(szNrLine,"%04hX:%04hX line #%hu (%s) (%s)\n", * ssmod.csBase,offset,line,ModName,ename); */ } } break; } // end switch i++; } // end while modindex } // End While i < numdir free(pxdi->pDirTab); return (0); } /* ****************************************************************** * * PART 2: ANALYZE VARIABLES * ********************************************************************/ /* * var_value: * writes a description of a variable type to * the specified buffer, depending on "type". * *@@changed V0.9.1 (2000-01-30) [umoeller]: changed prototype to use external buffer */ static VOID var_value(void *varptr, // in: address of the variable on the stack char *pszBuf, // out: information BYTE type) // in: type; if >= 32, we'll call DosQueryMem { ULONG Size = 1, Attr = 0; if (DosQueryMem(varptr, &Size, &Attr) != NO_ERROR) { sprintf(pszBuf, "type %d, DosQueryMem failed", type); return; } if ((Attr & PAG_READ) == 0) { sprintf(pszBuf, "type %d, read-access to value denied", type); return; } if (type == 0) sprintf(pszBuf, "%hd", *(signed char*)varptr); else if (type == 1) sprintf(pszBuf, "%hd", *(signed short*)varptr); else if (type == 2) sprintf(pszBuf, "%ld", *(signed long*)varptr); else if (type == 4) sprintf(pszBuf, "%hu", *(BYTE*) varptr); else if (type == 5) sprintf(pszBuf, "%hu", *(USHORT*)varptr); else if (type == 6) sprintf(pszBuf, "0x%lX (%lu)", *((ULONG*)varptr), *((ULONG*)varptr)); else if (type == 8) sprintf(pszBuf, "%f", *(float*)varptr); else if (type == 9) sprintf(pszBuf, "%f", *(double*)varptr); else if (type == 10) sprintf(pszBuf, "%f", (double)(*(long double*)varptr)); else if (type == 16) sprintf(pszBuf, "%s", *(char*)varptr ? "TRUE" : "FALSE"); else if (type == 17) sprintf(pszBuf, "%s", *(short*)varptr ? "TRUE" : "FALSE"); else if (type == 18) sprintf(pszBuf, "%s", *(long*)varptr ? "TRUE" : "FALSE"); else if (type == 20) sprintf(pszBuf, "%c", *(char*)varptr); else if (type == 21) sprintf(pszBuf, "%hd", (*(short*)varptr)); else if (type == 22) sprintf(pszBuf, "%ld", *(long*)varptr); else if (type == 23) sprintf(pszBuf, "void"); else if (type >= 32) { sprintf(pszBuf, "0x%p", (void*)(*(ULONG*)varptr)); if (Attr & PAG_FREE) { strcat(pszBuf, " unallocated memory"); } else { if ((Attr & PAG_COMMIT) == 0x0U) { strcat(pszBuf, " uncommitted"); } // endif if ((Attr & PAG_WRITE) == 0x0U) { strcat(pszBuf, " unwritable"); } // endif if ((Attr & PAG_READ) == 0x0U) { strcat(pszBuf, " unreadable"); } // endif } // endif } // endif else sprintf(pszBuf, "Unknown type %d", type); } /* * search_userdefs: * searches the table of userdef's- * Return TRUE if found. */ static BOOL search_userdefs(FILE *LogFile, // in: text log file to write to ULONG stackofs, USHORT var_no) { USHORT pos; for (pos = 0; pos < userdef_count; pos++) { if (one_userdef[pos].idx == autovar_def[var_no].type_idx) { if ( (one_userdef[pos].type_index >= 0x80) // && (one_userdef[pos].type_index <= 0xDA) ) { static char sszVar3[500] = "complex"; if (one_userdef[pos].type_index <= 0xDA) var_value((void*)(stackofs + autovar_def[var_no].stack_offset), sszVar3, one_userdef[pos].type_index - 0x80); fprintf(LogFile, " %- 6ld %- 20.20s %- 33.33s %s (user)\n", autovar_def[var_no].stack_offset, // stack offset autovar_def[var_no].name, // identifier one_userdef[pos].name, // type name sszVar3 // composed by var_value ); return TRUE; } else return FALSE; } } return FALSE; } /* * search_pointers: * */ static BOOL search_pointers(FILE *LogFile, // in: text log file to write to ULONG stackofs, USHORT var_no) { USHORT pos, upos; static BYTE str[35]; static char sszVar[500]; // BYTE type_index; for (pos = 0; ( (pos < pointer_count) && (one_pointer[pos].idx != autovar_def[var_no].type_idx) ); pos++); if (pos < pointer_count) { if ( (one_pointer[pos].type_index >= 0x80) && (one_pointer[pos].type_index <= 0xDA) ) { strcpy(str, type_name[one_pointer[pos].type_index - 0x80]); strcat(str, " *"); var_value((void*)(stackofs + autovar_def[var_no].stack_offset), sszVar, 32); fprintf(LogFile, " %- 6ld %- 20.20s %- 33.33s %s (ptr1)\n", autovar_def[var_no].stack_offset, autovar_def[var_no].name, str, sszVar); return TRUE; } else { // If the result isn't a simple type, look for it in the other lists for (upos = 0; ( (upos < userdef_count) && (one_userdef[upos].idx != one_pointer[pos].type_index) ); upos++) ; if (upos < userdef_count) { strcpy(str, one_userdef[upos].name); strcat(str, " *"); var_value((void *)(stackofs + autovar_def[var_no].stack_offset), sszVar, 32); fprintf(LogFile, " %- 6ld %- 20.20s %- 33.33s %s (ptr2)\n", autovar_def[var_no].stack_offset, autovar_def[var_no].name, str, sszVar); return TRUE; } else { // if it isn't a userdef, for now give up and just print // as much as we know sprintf(str, "Pointer to type 0x%X", one_pointer[pos].type_index); var_value((void *)(stackofs + autovar_def[var_no].stack_offset), sszVar, 32); fprintf(LogFile, " %- 6ld %- 20.20s %- 33.33s %s (ptr3)\n", autovar_def[var_no].stack_offset, autovar_def[var_no].name, str, sszVar); return TRUE; } } } return FALSE; } /* *@@ dbgPrintVariables: * Dumps variables for the specified stack offset * to the specified log file. * * New with V0.84. */ void dbgPrintVariables(FILE *LogFile, // in: text log file to write to ULONG stackofs) { USHORT n; // , pos; BOOL AutoVarsFound = FALSE; if (/* 1 || */ func_ofs == pubfunc_ofs) { for (n = 0; n < var_ofs; n++) { if (AutoVarsFound == FALSE) { AutoVarsFound = TRUE; fprintf(LogFile, " List of auto variables at EBP %p in %s:\n", (PVOID)stackofs, func_name); fprintf(LogFile, " Offset Name Type Value \n"); fprintf(LogFile, " ������ �������������������� ��������������������������������� �����������������\n"); } // If it's one of the simple types if ( (autovar_def[n].type_idx >= 0x80) && (autovar_def[n].type_idx <= 0xDA) ) { static char sszVar2[500]; var_value((void *)(stackofs + autovar_def[n].stack_offset), sszVar2, autovar_def[n].type_idx - 0x80); fprintf(LogFile, " %- 6ld %- 20.20s %- 33.33s %s (simple)\n", autovar_def[n].stack_offset, autovar_def[n].name, type_name[autovar_def[n].type_idx - 0x80], sszVar2); } else { // Complex type, check if we know what it is if (!search_userdefs(LogFile, stackofs, n)) { if (!search_pointers(LogFile, stackofs, n)) { fprintf(LogFile, " %- 6ld %-20.20s 0x%X (unknown)\n", autovar_def[n].stack_offset, autovar_def[n].name, autovar_def[n].type_idx); } } } } /* if (AutoVarsFound == FALSE) { fprintf(LogFile, " No auto variables found in %s.\n", func_name); } */ fprintf(LogFile, "\n"); } } /* ****************************************************************** * * PART 3: ANALYZE SYMBOL (.SYM) FILE * ********************************************************************/ /* *@@ dbgPrintSYMInfo: * this gets called by dbgPrintStack if dbgPrintDebugInfo * failed (because no debug code was found) to check if * maybe a SYM file with the same filename exists and try * to get the info from there. * * This gets called for every line of the stack * walk, but only if getting the information from * the debug code failed, e.g. because no debug code * was available for an address. * * The file pointer is in the "Source file" column * every time this gets called. * * New with V0.84. * * Returns 0 if reading the SYM file was successful. * *@@changed V0.9.1 (2000-01-30) [umoeller]: added return code; this used to be VOID */ int dbgPrintSYMInfo(FILE *LogFile, // in: text log file to write to CHAR *SymFileName, // in: SYM file name (can be fully q'fied) ULONG Object, ULONG TrapOffset) { static FILE *SymFile; static MAPDEF MapDef; static SEGDEF SegDef; static SYMDEF32 SymDef32; static SYMDEF16 SymDef16; static char Buffer[256]; static int SegNum, SymNum, LastVal; static unsigned long int SegOffset, SymOffset, SymPtrOffset; // open .SYM file #ifdef DEBUG_SYMDUMP fprintf(LogFile,"Dump of '%s' for object %d\n",SymFileName,Object); #endif SymFile = fopen(SymFileName, "rb"); if (SymFile == 0) return (2); // read in first map definition fread(&MapDef, sizeof(MAPDEF), 1, SymFile); #ifdef DEBUG_SYMDUMP Buffer[0] = MapDef.achModName[0]; fread(&Buffer[1], 1, MapDef.cbModName-1, SymFile); Buffer[MapDef.cbModName] = 0x00; fprintf(LogFile,"Module name '%s'\n",Buffer); #endif SegOffset = SEGDEFOFFSET(MapDef); #ifdef DEBUG_SYMDUMP fprintf(LogFile,"SegOffset %0x\n",SegOffset); #endif // go thru all segments for (SegNum = 0; SegNum < MapDef.cSegs; SegNum++) { #ifdef DEBUG_SYMDUMP fprintf(LogFile,"Scanning segment #%d Offset %08X\n",SegNum,SegOffset); #endif if (fseek(SymFile, SegOffset, SEEK_SET)) // seek error return (3); // read in segment definition fread(&SegDef, sizeof(SEGDEF), 1, SymFile); #ifdef DEBUG_SYMDUMP Buffer[0] = 0x00; if (SegDef.cbSegName>0) { Buffer[0] = SegDef.achSegName[0]; fread(&Buffer[1], 1, SegDef.cbSegName-1, SymFile); Buffer[SegDef.cbSegName] = 0x00; } fprintf(LogFile,"Segment name '%s', number %d, flags %02x\n",Buffer,SegNum,SegDef.bFlags); #endif if (SegNum == Object) { // stack object found: Buffer[0] = 0x00; LastVal = 0; // go thru all symbols in this object #ifdef DEBUG_SYMDUMP fprintf(LogFile,"Scanning #%d symbols\n",SegDef.cSymbols); #endif for (SymNum = 0; SymNum < SegDef.cSymbols; SymNum++) { SymPtrOffset=SYMDEFOFFSET(SegOffset,SegDef,SymNum); fseek(SymFile,SymPtrOffset,SEEK_SET); fread(&SymOffset,sizeof(unsigned short int),1,SymFile); fseek(SymFile,SymOffset+SegOffset,SEEK_SET); if (SegDef.bFlags & 0x01) { // 32-bit symbol: fread(&SymDef32, sizeof(SYMDEF32), 1, SymFile); if (SymDef32.wSymVal > TrapOffset) { // symbol found fprintf(LogFile, "between %s + 0x%lX ", Buffer, TrapOffset - LastVal); /* fprintf(LogFile, "(ppLineDef: 0x%lX) ", LINEDEFOFFSET(SegDef) ); */ fprintf(LogFile, "\n"); } LastVal = SymDef32.wSymVal; Buffer[0] = SymDef32.achSymName[0]; fread(&Buffer[1], 1, SymDef32.cbSymName-1, SymFile); Buffer[SymDef32.cbSymName] = 0x00; #ifdef DEBUG_SYMDUMP fprintf(LogFile,"32 Bit Symbol Address %08p <%s> \n",SymDef32.wSymVal,Buffer); #endif if (SymDef32.wSymVal > TrapOffset) { // symbol found, as above fprintf(LogFile, " " "and %s - 0x%lX ", Buffer, LastVal - TrapOffset); fprintf(LogFile, "\n"); break; } } else { // 16-bit symbol: fread(&SymDef16, sizeof(SYMDEF16), 1, SymFile); if (SymDef16.wSymVal > TrapOffset) { fprintf(LogFile, "between %s + %lX\n", Buffer, TrapOffset - LastVal); } LastVal = SymDef16.wSymVal; Buffer[0] = SymDef16.achSymName[0]; fread(&Buffer[1], 1, SymDef16.cbSymName-1, SymFile); Buffer[SymDef16.cbSymName] = 0x00; if (SymDef16.wSymVal > TrapOffset) { fprintf(LogFile, " " "and %s - %lX\n", Buffer, LastVal - TrapOffset); break; } #ifdef DEBUG_SYMDUMP fprintf(LogFile,"16 Bit Symbol <%s> Address %p\n",Buffer,SymDef16.wSymVal); #endif } // endif } break; } // endif SegOffset = NEXTSEGDEFOFFSET(SegDef); } // endwhile fclose(SymFile); return (0); // no error } /* ****************************************************************** * * PART 4: dbgPrintStack * ********************************************************************/ /* *@@ dbgPrintStackFrame: * parses and dumps one stack frame. * Called from excPrintStackFrame. * * This calls dbgPrintDebugInfo and, if * that fails, dbgPrintSYMInfo. * *@@added V0.9.2 (2000-03-10) [umoeller] *@@changed V0.9.3 (2000-04-10) [umoeller]: added support for non-Warp 4 SYM files *@@changed V0.9.3 (2000-04-26) [umoeller]: this broke Warp 4 FP 13, fixed */ BOOL dbgPrintStackFrame(FILE *LogFile, PSZ pszModuleName, // in: module name (fully q'fied) ULONG ulObject, ULONG ulOffset) { APIRET arc = 0; // "Source file"... columns //YD do not use debug info #define ENABLE_DEBUG_INFO #ifdef ENABLE_DEBUG_INFO // first attempt to analyze the debug code arc = dbgPrintDebugInfo(LogFile, pszModuleName, ulObject, ulOffset); #else arc = 1; #endif // if no debug code is available, analyze // the SYM file instead if (arc != NO_ERROR) { CHAR szSymName[CCHMAXPATH]; strcpy(szSymName, pszModuleName); strcpy(szSymName + strlen(szSymName) - 3, "SYM"); arc = dbgPrintSYMInfo(LogFile, szSymName, ulObject, ulOffset); if (arc != 0) { // SYM file not found in current directory: // check the SYM files in the \OS2 directory, // depending on the OS/2 version level: CHAR szSymFile2[CCHMAXPATH]; PSZ pszFilename = strrchr(szSymName, '\\'); if (pszFilename) { PSZ pszVersionDir = "WARP4"; ULONG aulBuf[3]; DosQuerySysInfo(QSV_VERSION_MAJOR, // 11 QSV_VERSION_MINOR, // 12 &aulBuf, sizeof(aulBuf)); // Warp 3 is reported as 20.30 // Warp 4 is reported as 20.40 // Aurora is reported as 20.45 if (aulBuf[0] == 20) { if (aulBuf[1] == 30) // Warp 3: pszVersionDir = "WARP3"; else if (aulBuf[1] >= 40) // Warp 4 or higher: // (NOTE: Warp 4 FP 13 now returns 45 also, // but the SYM files are still in the WARP4 directory...) // V0.9.3 (2000-04-26) [umoeller] pszVersionDir = "WARP4"; } pszFilename++; sprintf(szSymFile2, "%c:\\OS2\\PDPSI\\PMDF\\%s\\%s", doshQueryBootDrive(), pszVersionDir, pszFilename); arc = dbgPrintSYMInfo(LogFile, szSymFile2, ulObject, ulOffset); // V0.9.3 (2000-04-26) [umoeller] if ( (arc != 0) // still not found && (aulBuf[1] == 45) // and running Aurora or Warp 4 FP13? ) { // Warp Server for e-Business (aka Warp 4.5): // we use the SYM files for the UNI kernel, // I have found no way to find out whether // we're running on an SMP kernel sprintf(szSymFile2, "%c:\\OS2\\PDPSI\\PMDF\\%s\\%s", doshQueryBootDrive(), "WARP45_U", pszFilename); arc = dbgPrintSYMInfo(LogFile, szSymFile2, ulObject, ulOffset); } } } if (arc == 2) // file not found fprintf(LogFile, "Cannot find symbol file %s\n", szSymName); else if (arc != 0) fprintf(LogFile, "Error %lu reading symbol file %s\n", arc, szSymName); } return (arc == NO_ERROR); } /* *@@ dbgPrintStack: * this takes stack data from the TIB and * context record data structures and tries * to analyse what the different stack frames * point to. * * For each stack frame, this calls dbgPrintDebugInfo, * and, if that fails, dbgPrintSYMInfo. * * New with V0.84. * *@@changed V0.9.2 (2000-03-08) [umoeller]: now searching OS2\PDPSI\PMDF for SYM files also */ VOID dbgPrintStack(FILE *LogFile, // in: text log file to write to PUSHORT StackBottom, PUSHORT StackTop, PUSHORT Ebp, PUSHORT ExceptionAddress) { PUSHORT RetAddr = 0; PUSHORT LastEbp = 0; APIRET rc = 0; ULONG Size = 0, Attr = 0; USHORT Cs = 0, Ip = 0, // Bp, Sp = 0; static char Name[CCHMAXPATH]; HMODULE hMod = 0; ULONG ObjNum = 0; ULONG Offset = 0; BOOL fExceptionAddress = TRUE; // Use Exception Addr 1st time thru // Note: we can't handle stacks bigger than 64K for now... Sp = (USHORT) (((ULONG) StackBottom) >> 16); // Bp = ; if (!f32bit) Ebp = (PUSHORT) MAKEULONG(((USHORT)(ULONG)Ebp), Sp); fprintf(LogFile, "\n\nCall stack:\n"); fprintf(LogFile, " Source Line Nearest\n"); fprintf(LogFile, " EBP Address Module Obj# File Numbr Public Symbol\n"); fprintf(LogFile, " �������� ��������- �������� ���� ������������ ����� ������������-\n"); do { Size = 10; rc = DosQueryMem((PVOID) (Ebp + 2), &Size, &Attr); if (rc != NO_ERROR) { fprintf(LogFile, "Invalid EBP %8.8lX (DosQueryMem returned %lu)\n", (ULONG)Ebp, rc); break; } if (!(Attr & PAG_COMMIT)) { fprintf(LogFile, "Invalid EBP %8.8lX (not committed)\n", (ULONG)Ebp); break; } if (Size < 10) { fprintf(LogFile, "Invalid EBP %8.8lX (mem block size < 10)\n", (ULONG)Ebp); break; } if (fExceptionAddress) RetAddr = ExceptionAddress; else RetAddr = (PUSHORT) (*((PULONG) (Ebp + 2))); if (RetAddr == (PUSHORT) 0x00000053) { // For some reason there's a "return address" of 0x53 following // EBP on the stack and we have to adjust EBP by 44 bytes to get // at the real return address. This has something to do with // thunking from 32bits to 16bits... // Serious kludge, and it's probably dependent on versions of C(++) // runtime or OS, but it works for now! Ebp += 22; RetAddr = (PUSHORT) (*((PULONG) (Ebp + 2))); } // Get the (possibly) 16bit CS and IP if (fExceptionAddress) { Cs = (USHORT) (((ULONG) ExceptionAddress) >> 16); Ip = (USHORT) (ULONG) ExceptionAddress; } else { Cs = *(Ebp + 2); Ip = *(Ebp + 1); } // if the return address points to the stack then it's really just // a pointer to the return address (UGH!). if ( (USHORT) (((ULONG) RetAddr) >> 16) == Sp ) RetAddr = (PUSHORT) (*((PULONG) RetAddr)); if (Ip == 0 && *Ebp == 0) { // End of the stack so these are both shifted by 2 bytes: Cs = *(Ebp + 3); Ip = *(Ebp + 2); } // 16bit programs have on the stack: // BP:IP:CS // where CS may be thunked // // in dump swapped // BP IP CS BP CS IP // 4677 53B5 F7D0 7746 D0F7 B553 // // 32bit programs have: // EBP:EIP // and you'd have something like this (with SP added) (not // accurate values) // // in dump swapped // EBP EIP EBP EIP // 4677 2900 53B5 F7D0 0029 7746 D0F7 B553 // // So the basic difference is that 32bit programs have a 32bit // EBP and we can attempt to determine whether we have a 32bit // EBP by checking to see if its 'selector' is the same as SP. // Note that this technique limits us to checking stacks < 64K. // // Soooo, if IP (which maps into the same USHORT as the swapped // stack page in EBP) doesn't point to the stack (i.e. it could // be a 16bit IP) then see if CS is valid (as is or thunked). // // Note that there's the possibility of a 16bit return address // that has an offset that's the same as SP so we'll think it's // a 32bit return address and won't be able to successfully resolve // its details. if (Ip != Sp) { if (DosSizeSeg(Cs, &Size) == NO_ERROR) { RetAddr = (USHORT * _Seg16) MAKEULONG(Ip, Cs); f32bit = FALSE; } else if (DosSizeSeg((Cs << 3) + 7, &Size) == NO_ERROR) { Cs = (Cs << 3) + 7; RetAddr = (USHORT * _Seg16) MAKEULONG(Ip, Cs); f32bit = FALSE; } else f32bit = TRUE; } else f32bit = TRUE; // "EBP" column if (fExceptionAddress) fprintf(LogFile, " Trap -> "); else fprintf(LogFile, " %8.8lX ", (ULONG)Ebp); // "Address" column if (f32bit) fprintf(LogFile, ":%8.8lX ", (ULONG)RetAddr); else fprintf(LogFile, "%04.04X:%04.04X ", Cs, Ip); // Version check omitted; the following requires // OS/2 2.10 or later (*UM) // if (Version[0] >= 20 && Version[1] >= 10) { // Make a 'tick' sound to let the user know we're still alive DosBeep(2000, 10); Size = 10; // Inserted by Kim Rasmussen 26/06 1996 to avoid error 87 when Size is 0 // "Module"/"Object" columns rc = DosQueryMem((PVOID) RetAddr, &Size, &Attr); if (rc != NO_ERROR || !(Attr & PAG_COMMIT)) { fprintf(LogFile, "Invalid RetAddr: %8.8lX\n", (ULONG)RetAddr); break; // avoid infinite loops } else { rc = DosQueryModFromEIP(&hMod, &ObjNum, sizeof(Name), Name, &Offset, (PVOID)RetAddr); if ( (rc == NO_ERROR) // && (ObjNum != -1) ) { // static char szJunk[_MAX_FNAME]; static char szName[_MAX_FNAME]; DosQueryModuleName(hMod, sizeof(Name), Name); // _splitpath(Name, szJunk, szJunk, szName, szJunk); // print module and object fprintf(LogFile, "%-8s %04lX ", szName, ObjNum + 1); if (strlen(Name) > 3) { dbgPrintStackFrame(LogFile, Name, ObjNum, Offset); } } else fprintf(LogFile, "DosQueryModFromEIP failed, returned %lu\n", rc); } } if ( ((*Ebp) == 0) && ((*Ebp + 1) == 0) ) { fprintf(LogFile, "End of call stack\n"); break; } if (!fExceptionAddress) { LastEbp = Ebp; #if 0 Ebp = (PUSHORT) MAKEULONG(Bp, Sp); #else // Inserted by Kim Rasmussen 26/06 1996 to allow big stacks if (f32bit) Ebp = (PUSHORT) *(PULONG) LastEbp; else Ebp = (PUSHORT) MAKEULONG((*Ebp), Sp); #endif if (f32bit) { dbgPrintVariables(LogFile, (ULONG) Ebp); } // endif if (Ebp < LastEbp) { fprintf(LogFile, "... lost stack chain - new EBP below previous\n"); break; } } else fExceptionAddress = FALSE; Size = 4; rc = DosQueryMem((PVOID) Ebp, &Size, &Attr); if ((rc != NO_ERROR) || (Size < 4)) { fprintf(LogFile, "... lost stack chain - invalid EBP: %8.8lX\n", (ULONG)Ebp); break; } } while (TRUE); fprintf(LogFile, "\n"); } /* *@@ doshQueryBootDrive: * returns the letter of the boot drive as a * single (capital) character, which is useful for * constructing file names using sprintf and such. * *@@changed V0.9.16 (2002-01-13) [umoeller]: optimized */ CHAR doshQueryBootDrive(VOID) { // this can never change, so query this only once // V0.9.16 (2002-01-13) [umoeller] static CHAR cBootDrive = '\0'; if (!cBootDrive) { ULONG ulBootDrive; DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive, sizeof(ulBootDrive)); cBootDrive = (CHAR)ulBootDrive + 'A' - 1; } return (cBootDrive); }