/* * File msc.c - read VC++ debug information from COFF and eventually * from PDB files. * * Copyright (C) 1996, Eric Youngdale. * * Note - this handles reading debug information for 32 bit applications * that run under Windows-NT for example. I doubt that this would work well * for 16 bit applications, but I don't think it really matters since the * file format is different, and we should never get in here in such cases. * * TODO: * Get 16 bit CV stuff working. * Add symbol size to internal symbol table. */ #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX _MAX_PATH #endif #include "debugger.h" #include "neexe.h" #include "peexe.h" #include "xmalloc.h" #include "file.h" /* *dbg_filename must be at least MAX_PATHNAME_LEN bytes in size */ static void LocateDebugInfoFile(char *filename, char *dbg_filename) { char *str1 = xmalloc(MAX_PATHNAME_LEN*10); char *str2 = xmalloc(MAX_PATHNAME_LEN); char *file; char *name_part; DOS_FULL_NAME fullname; file = strrchr(filename, '\\'); if( file == NULL ) file = filename; else file++; if (GetEnvironmentVariable32A("_NT_SYMBOL_PATH", str1, sizeof(str1))) if (SearchPath32A(str1, file, NULL, sizeof(str2), str2, &name_part)) goto ok; if (GetEnvironmentVariable32A("_NT_ALT_SYMBOL_PATH", str1, sizeof(str1))) if (SearchPath32A(str1, file, NULL, sizeof(str2), str2, &name_part)) goto ok; if (SearchPath32A(NULL, file, NULL, sizeof(str2), str2, &name_part)) goto ok; else { quit: memcpy(dbg_filename, filename, MAX_PATHNAME_LEN); free(str1); free(str2); return; } ok: if (DOSFS_GetFullName(str2, TRUE, &fullname)) memcpy(dbg_filename, fullname.long_name, MAX_PATHNAME_LEN); else goto quit; free(str1); free(str2); return; } /* * This is an index we use to keep track of the debug information * when we have multiple sources. We use the same database to also * allow us to do an 'info shared' type of deal, and we use the index * to eliminate duplicates. */ static int DEBUG_next_index = 0; union any_size { char * c; short * s; int * i; unsigned int * ui; }; /* * This is a convenience structure used to map portions of the * line number table. */ struct startend { unsigned int start; unsigned int end; }; /* * This is how we reference the various record types. */ union codeview_symbol { struct { short int len; short int id; } generic; struct { short int len; short int id; unsigned int offset; unsigned short seg; unsigned short symtype; unsigned char namelen; unsigned char name[1]; } data; struct { short int len; short int id; unsigned int pparent; unsigned int pend; unsigned int next; unsigned int offset; unsigned short segment; unsigned short thunk_len; unsigned char thtype; unsigned char namelen; unsigned char name[1]; } thunk; struct { short int len; short int id; unsigned int pparent; unsigned int pend; unsigned int next; unsigned int proc_len; unsigned int debug_start; unsigned int debug_end; unsigned int offset; unsigned short segment; unsigned short proctype; unsigned char flags; unsigned char namelen; unsigned char name[1]; } proc; struct { short int len; /* Total length of this entry */ short int id; /* Always S_BPREL32 */ unsigned int offset; /* Stack offset relative to BP */ unsigned short symtype; unsigned char namelen; unsigned char name[1]; } stack; }; union codeview_type { struct { short int len; short int id; } generic; struct { short int len; short int id; short int attribute; short int datatype; unsigned char variant[1]; } pointer; struct { short int len; short int id; unsigned char nbits; unsigned char bitoff; unsigned short type; } bitfield; struct { short int len; short int id; short int elemtype; short int idxtype; unsigned char arrlen; unsigned char namelen; unsigned char name[1]; } array; struct { short int len; short int id; short int n_element; short int fieldlist; short int property; short int derived; short int vshape; unsigned short structlen; unsigned char namelen; unsigned char name[1]; } structure; struct { short int len; short int id; short int count; short int field; short int property; unsigned short un_len; unsigned char namelen; unsigned char name[1]; } t_union; struct { short int len; short int id; short int count; short int type; short int field; short int property; unsigned char namelen; unsigned char name[1]; } enumeration; struct { short int id; short int attribute; unsigned short int value; unsigned char namelen; unsigned char name[1]; } enumerate; struct { short int id; short int type; short int attribute; unsigned short int offset; unsigned char namelen; unsigned char name[1]; } member; struct { short int len; short int id; short int count; short int type; short int field; short int property; unsigned char namelen; unsigned char name[1]; } fieldlist; }; #define S_BPREL32 0x200 #define S_LDATA32 0x201 #define S_GDATA32 0x202 #define S_PUB32 0x203 #define S_LPROC32 0x204 #define S_GPROC32 0x205 #define S_THUNK32 0x206 #define S_BLOCK32 0x207 #define S_WITH32 0x208 #define S_LABEL32 0x209 #define S_PROCREF 0x400 #define S_DATAREF 0x401 #define S_ALIGN 0x402 #define S_UNKNOWN 0x403 /* * This covers the basic datatypes that VC++ seems to be using these days. * 32 bit mode only. There are additional numbers for the pointers in 16 * bit mode. There are many other types listed in the documents, but these * are apparently not used by the compiler, or represent pointer types * that are not used. */ #define T_NOTYPE 0x0000 /* Notype */ #define T_ABS 0x0001 /* Abs */ #define T_VOID 0x0003 /* Void */ #define T_CHAR 0x0010 /* signed char */ #define T_SHORT 0x0011 /* short */ #define T_LONG 0x0012 /* long */ #define T_QUAD 0x0013 /* long long */ #define T_UCHAR 0x0020 /* unsigned char */ #define T_USHORT 0x0021 /* unsigned short */ #define T_ULONG 0x0022 /* unsigned long */ #define T_UQUAD 0x0023 /* unsigned long long */ #define T_REAL32 0x0040 /* float */ #define T_REAL64 0x0041 /* double */ #define T_RCHAR 0x0070 /* real char */ #define T_WCHAR 0x0071 /* wide char */ #define T_INT4 0x0074 /* int */ #define T_UINT4 0x0075 /* unsigned int */ #define T_32PVOID 0x0403 /* 32 bit near pointer to void */ #define T_32PCHAR 0x0410 /* 16:32 near pointer to signed char */ #define T_32PSHORT 0x0411 /* 16:32 near pointer to short */ #define T_32PLONG 0x0412 /* 16:32 near pointer to int */ #define T_32PQUAD 0x0413 /* 16:32 near pointer to long long */ #define T_32PUCHAR 0x0420 /* 16:32 near pointer to unsigned char */ #define T_32PUSHORT 0x0421 /* 16:32 near pointer to unsigned short */ #define T_32PULONG 0x0422 /* 16:32 near pointer to unsigned int */ #define T_32PUQUAD 0x0423 /* 16:32 near pointer to long long */ #define T_32PREAL32 0x0440 /* 16:32 near pointer to float */ #define T_32PREAL64 0x0441 /* 16:32 near pointer to float */ #define T_32PRCHAR 0x0470 /* 16:32 near pointer to real char */ #define T_32PWCHAR 0x0471 /* 16:32 near pointer to real char */ #define T_32PINT4 0x0474 /* 16:32 near pointer to int */ #define T_32PUINT4 0x0475 /* 16:32 near pointer to unsigned int */ #define LF_MODIFIER 0x1 #define LF_POINTER 0x2 #define LF_ARRAY 0x3 #define LF_CLASS 0x4 #define LF_STRUCTURE 0x5 #define LF_UNION 0x6 #define LF_ENUMERATION 0x7 #define LF_PROCEDURE 0x8 #define LF_MFUNCTION 0x9 #define LF_VTSHAPE 0xa #define LF_BARRAY 0xd #define LF_DIMARRAY 0x11 #define LF_VFTPATH 0x12 #define LF_SKIP 0x200 #define LF_ARGLIST 0x201 #define LF_FIELDLIST 0x204 #define LF_DERIVED 0x205 #define LF_BITFIELD 0x206 #define LF_BCLASS 0x400 #define LF_VBCLASS 0x401 #define LF_IVBCLASS 0x402 #define LF_ENUMERATE 0x403 #define LF_FRIENDFCN 0x404 #define LF_INDEX 0x405 #define LF_MEMBER 0x406 #define LF_STMEMBER 0x407 #define LF_METHOD 0x408 #define LF_NESTEDTYPE 0x409 #define LF_VFUNCTAB 0x40a #define LF_FRIENDCLS 0x40b #define LF_ONEMETHOD 0x40c #define LF_FUNCOFF 0x40d #define MAX_BUILTIN_TYPES 0x480 static struct datatype * cv_basic_types[MAX_BUILTIN_TYPES]; static int num_cv_defined_types = 0; static struct datatype **cv_defined_types = NULL; /* * For the type CODEVIEW debug directory entries, the debug directory * points to a structure like this. The cv_name field is the name * of an external .PDB file. */ struct CodeViewDebug { char cv_nbtype[8]; unsigned int cv_timestamp; char cv_unknown[4]; char cv_name[1]; }; struct MiscDebug { unsigned int DataType; unsigned int Length; char Unicode; char Reserved[3]; char Data[1]; }; /* * This is the header that the COFF variety of debug header points to. */ struct CoffDebug { unsigned int N_Sym; unsigned int SymbolOffset; unsigned int N_Linenum; unsigned int LinenumberOffset; unsigned int Unused[4]; }; struct CoffLinenum { unsigned int VirtualAddr; unsigned short int Linenum; }; struct CoffFiles { unsigned int startaddr; unsigned int endaddr; char * filename; int linetab_offset; int linecnt; struct name_hash **entries; int neps; int neps_alloc; }; struct CoffSymbol { union { char ShortName[8]; struct { unsigned int NotLong; unsigned int StrTaboff; } Name; } N; unsigned int Value; short SectionNumber; short Type; char StorageClass; unsigned char NumberOfAuxSymbols; }; struct CoffAuxSection{ unsigned int Length; unsigned short NumberOfRelocations; unsigned short NumberOfLinenumbers; unsigned int CheckSum; short Number; char Selection; } Section; /* * These two structures are used in the directory within a .DBG file * to locate the individual important bits that we might want to see. */ struct CV4_DirHead { short unsigned int dhsize; short unsigned int desize; unsigned int ndir; unsigned int next_offset; unsigned int flags; }; struct CV4_DirEnt { short unsigned int subsect_number; short unsigned int module_number; unsigned int offset; unsigned int size; }; /* * These are the values of interest that the subsect_number field takes. */ #define sstAlignSym 0x125 #define sstSrcModule 0x127 struct codeview_linetab_hdr { unsigned int nline; unsigned int segno; unsigned int start; unsigned int end; char * sourcefile; unsigned short * linetab; unsigned int * offtab; }; struct codeview_pdb_hdr { char ident[44]; unsigned int blocksize; /* Extent size */ unsigned short loc_freelist; /* freelist. */ unsigned short alloc_filesize; /* # extents allocated. */ unsigned int toc_len; unsigned int unknown; unsigned short toc_ext[1]; /* array of extent #'s for toc. */ }; /* * This is our own structure that we use to keep track of the contents * of a PDB file. */ struct file_list { int record_len; int nextents; short int * extent_list; unsigned int linetab_offset; unsigned int linetab_len; }; /* * These are the structures that represent how the file table is set up * within the PDB file. */ struct filetab_hdr { unsigned short tab1_file; unsigned short tab2_file; unsigned short gsym_file; unsigned short padding; unsigned int ftab_len; unsigned int fofftab_len; unsigned int hash_len; unsigned int strtab_len; }; struct file_ent { unsigned int reserved1; unsigned short datasect_segment; unsigned short reserved2; unsigned int datasect_offset; unsigned int datasect_size; unsigned int datasect_flags; unsigned short reserved3; unsigned short index; unsigned short num6a; unsigned short file_number; unsigned int linetab_offset; unsigned int linetab_len; unsigned int num9; unsigned int num10; unsigned int num11; unsigned char filename[1]; }; /* ******************************************************************** */ struct deferred_debug_info { struct deferred_debug_info * next; char * load_addr; char * module_name; char * dbg_info; int dbg_size; HMODULE32 module; PIMAGE_DEBUG_DIRECTORY dbgdir; PIMAGE_SECTION_HEADER sectp; int nsect; short int dbg_index; char loaded; }; struct deferred_debug_info * dbglist = NULL; /* * A simple macro that tells us whether a given COFF symbol is a * function or not. */ #define N_TMASK 0x0030 #define IMAGE_SYM_DTYPE_FUNCTION 2 #define N_BTSHFT 4 #define ISFCN(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT)) /* * This is what we are looking for in the COFF symbols. */ #define IMAGE_SYM_CLASS_EXTERNAL 0x2 #define IMAGE_SYM_CLASS_STATIC 0x3 #define IMAGE_SYM_CLASS_FILE 0x67 static struct datatype * DEBUG_GetCVType(int typeno) { struct datatype * dt = NULL; /* * Convert Codeview type numbers into something we can grok internally. * Numbers < 0x1000 are all fixed builtin types. Numbers from 0x1000 and * up are all user defined (structs, etc). */ if( typeno < 0x1000 ) { if( typeno < MAX_BUILTIN_TYPES ) { dt = cv_basic_types[typeno]; } } else { if( typeno - 0x1000 < num_cv_defined_types ) { dt = cv_defined_types[typeno - 0x1000]; } } return dt; } static int DEBUG_ParseTypeTable(char * table, int len) { int arr_max; int curr_type; enum debug_type fieldtype; int elem_size; union any_size ptr; union any_size ptr2; struct datatype * subtype; char symname[256]; union codeview_type * type; union codeview_type * type2; struct datatype * typeptr; curr_type = 0x1000; ptr = (union any_size) (table + 16); while( ptr.c - table < len ) { type = (union codeview_type *) ptr.c; if( curr_type - 0x1000 >= num_cv_defined_types ) { num_cv_defined_types += 0x100; cv_defined_types = (struct datatype **) realloc(cv_defined_types, num_cv_defined_types * sizeof(struct datatype *)); memset(cv_defined_types + num_cv_defined_types - 0x100, 0, 0x100 * sizeof(struct datatype *)); if( cv_defined_types == NULL ) { return FALSE; } } switch(type->generic.id) { case LF_POINTER: cv_defined_types[curr_type - 0x1000] = DEBUG_FindOrMakePointerType(DEBUG_GetCVType(type->pointer.datatype)); break; case LF_ARRAY: if( type->array.arrlen >= 0x8000 ) { /* * This is a numeric leaf, I am too lazy to handle this right * now. */ fprintf(stderr, "Ignoring large numberic leaf.\n"); break; } if( type->array.namelen != 0 ) { memset(symname, 0, sizeof(symname)); memcpy(symname, type->array.name, type->array.namelen); typeptr = DEBUG_NewDataType(DT_ARRAY, symname); } else { typeptr = DEBUG_NewDataType(DT_ARRAY, NULL); } cv_defined_types[curr_type - 0x1000] = typeptr; subtype = DEBUG_GetCVType(type->array.elemtype); if( (subtype == NULL) || (elem_size = DEBUG_GetObjectSize(subtype)) == 0 ) { arr_max = 0; } else { arr_max = type->array.arrlen / DEBUG_GetObjectSize(subtype); } DEBUG_SetArrayParams(typeptr, 0, arr_max, subtype); break; case LF_FIELDLIST: /* * This is where the basic list of fields is defined for * structures and classes. * * First, we need to look ahead and see whether we are building * a fieldlist for an enum or a struct. */ ptr2.i = ptr.i + 1; type2 = (union codeview_type *) ptr2.c; if( type2->member.id == LF_MEMBER ) { typeptr = DEBUG_NewDataType(DT_STRUCT, NULL); fieldtype = DT_STRUCT; } else if( type2->member.id == LF_ENUMERATE ) { typeptr = DEBUG_NewDataType(DT_ENUM, NULL); fieldtype = DT_ENUM; } else { break; } cv_defined_types[curr_type - 0x1000] = typeptr; while( ptr2.c < (ptr.c + ((type->generic.len + 3) & ~3)) ) { type2 = (union codeview_type *) ptr2.c; if( type2->member.id == LF_MEMBER && fieldtype == DT_STRUCT ) { memset(symname, 0, sizeof(symname)); memcpy(symname, type2->member.name, type2->member.namelen); subtype = DEBUG_GetCVType(type2->member.type); elem_size = 0; if( subtype != NULL ) { elem_size = DEBUG_GetObjectSize(subtype); } if( type2->member.offset >= 0x8000 ) { /* * This is a numeric leaf, I am too lazy to handle this right * now. */ fprintf(stderr, "Ignoring large numberic leaf.\n"); } else { DEBUG_AddStructElement(typeptr, symname, subtype, type2->member.offset << 3, elem_size << 3); } } else if( type2->member.id == LF_ENUMERATE && fieldtype == DT_ENUM ) { memset(symname, 0, sizeof(symname)); memcpy(symname, type2->enumerate.name, type2->enumerate.namelen); if( type2->enumerate.value >= 0x8000 ) { /* * This is a numeric leaf, I am too lazy to handle this right * now. */ fprintf(stderr, "Ignoring large numberic leaf.\n"); } else { DEBUG_AddStructElement(typeptr, symname, NULL, type2->enumerate.value, 0); } } else { /* * Something else I have never seen before. Either wrong type of * object in the fieldlist, or some other problem which I wouldn't * really know how to handle until it came up. */ fprintf(stderr, "Unexpected entry in fieldlist\n"); break; } ptr2.c += ((type2->member.namelen + 9 + 3) & ~3); } break; case LF_STRUCTURE: case LF_CLASS: if( type->structure.structlen >= 0x8000 ) { /* * This is a numeric leaf, I am too lazy to handle this right * now. */ fprintf(stderr, "Ignoring large numberic leaf.\n"); break; } memset(symname, 0, sizeof(symname)); memcpy(symname, type->structure.name, type->structure.namelen); if( strcmp(symname, "__unnamed") == 0 ) { typeptr = DEBUG_NewDataType(DT_STRUCT, NULL); } else { typeptr = DEBUG_NewDataType(DT_STRUCT, symname); } cv_defined_types[curr_type - 0x1000] = typeptr; /* * Now copy the relevant bits from the fieldlist that we specified. */ subtype = DEBUG_GetCVType(type->structure.fieldlist); if( subtype != NULL ) { DEBUG_SetStructSize(typeptr, type->structure.structlen); DEBUG_CopyFieldlist(typeptr, subtype); } break; case LF_UNION: if( type->t_union.un_len >= 0x8000 ) { /* * This is a numeric leaf, I am too lazy to handle this right * now. */ fprintf(stderr, "Ignoring large numberic leaf.\n"); break; } memset(symname, 0, sizeof(symname)); memcpy(symname, type->t_union.name, type->t_union.namelen); if( strcmp(symname, "__unnamed") == 0 ) { typeptr = DEBUG_NewDataType(DT_STRUCT, NULL); } else { typeptr = DEBUG_NewDataType(DT_STRUCT, symname); } cv_defined_types[curr_type - 0x1000] = typeptr; /* * Now copy the relevant bits from the fieldlist that we specified. */ subtype = DEBUG_GetCVType(type->t_union.field); if( subtype != NULL ) { DEBUG_SetStructSize(typeptr, type->t_union.un_len); DEBUG_CopyFieldlist(typeptr, subtype); } break; case LF_BITFIELD: typeptr = DEBUG_NewDataType(DT_BITFIELD, NULL); cv_defined_types[curr_type - 0x1000] = typeptr; DEBUG_SetBitfieldParams(typeptr, type->bitfield.bitoff, type->bitfield.nbits, DEBUG_GetCVType(type->bitfield.type)); break; case LF_ENUMERATION: memset(symname, 0, sizeof(symname)); memcpy(symname, type->enumeration.name, type->enumeration.namelen); typeptr = DEBUG_NewDataType(DT_ENUM, symname); cv_defined_types[curr_type - 0x1000] = typeptr; /* * Now copy the relevant bits from the fieldlist that we specified. */ subtype = DEBUG_GetCVType(type->enumeration.field); if( subtype != NULL ) { DEBUG_CopyFieldlist(typeptr, subtype); } break; case LF_DIMARRAY: default: break; } curr_type++; ptr.c += (type->generic.len + 3) & ~3; } return TRUE; } void DEBUG_InitCVDataTypes() { /* * These are the common builtin types that are used by VC++. */ cv_basic_types[T_NOTYPE] = NULL; cv_basic_types[T_ABS] = NULL; cv_basic_types[T_VOID] = DEBUG_NewDataType(DT_BASIC, "void"); cv_basic_types[T_CHAR] = DEBUG_NewDataType(DT_BASIC, "char"); cv_basic_types[T_SHORT] = DEBUG_NewDataType(DT_BASIC, "short int"); cv_basic_types[T_LONG] = DEBUG_NewDataType(DT_BASIC, "long int"); cv_basic_types[T_QUAD] = DEBUG_NewDataType(DT_BASIC, "long long int"); cv_basic_types[T_UCHAR] = DEBUG_NewDataType(DT_BASIC, "unsigned char"); cv_basic_types[T_USHORT] = DEBUG_NewDataType(DT_BASIC, "short unsigned int"); cv_basic_types[T_ULONG] = DEBUG_NewDataType(DT_BASIC, "long unsigned int"); cv_basic_types[T_UQUAD] = DEBUG_NewDataType(DT_BASIC, "long long unsigned int"); cv_basic_types[T_REAL32] = DEBUG_NewDataType(DT_BASIC, "float"); cv_basic_types[T_REAL64] = DEBUG_NewDataType(DT_BASIC, "double"); cv_basic_types[T_RCHAR] = DEBUG_NewDataType(DT_BASIC, "char"); cv_basic_types[T_WCHAR] = DEBUG_NewDataType(DT_BASIC, "short"); cv_basic_types[T_INT4] = DEBUG_NewDataType(DT_BASIC, "int"); cv_basic_types[T_UINT4] = DEBUG_NewDataType(DT_BASIC, "unsigned int"); cv_basic_types[T_32PVOID] = DEBUG_FindOrMakePointerType(cv_basic_types[T_VOID]); cv_basic_types[T_32PCHAR] = DEBUG_FindOrMakePointerType(cv_basic_types[T_CHAR]); cv_basic_types[T_32PSHORT] = DEBUG_FindOrMakePointerType(cv_basic_types[T_SHORT]); cv_basic_types[T_32PLONG] = DEBUG_FindOrMakePointerType(cv_basic_types[T_LONG]); cv_basic_types[T_32PQUAD] = DEBUG_FindOrMakePointerType(cv_basic_types[T_QUAD]); cv_basic_types[T_32PUCHAR] = DEBUG_FindOrMakePointerType(cv_basic_types[T_UCHAR]); cv_basic_types[T_32PUSHORT] = DEBUG_FindOrMakePointerType(cv_basic_types[T_USHORT]); cv_basic_types[T_32PULONG] = DEBUG_FindOrMakePointerType(cv_basic_types[T_ULONG]); cv_basic_types[T_32PUQUAD] = DEBUG_FindOrMakePointerType(cv_basic_types[T_UQUAD]); cv_basic_types[T_32PREAL32] = DEBUG_FindOrMakePointerType(cv_basic_types[T_REAL32]); cv_basic_types[T_32PREAL64] = DEBUG_FindOrMakePointerType(cv_basic_types[T_REAL64]); cv_basic_types[T_32PRCHAR] = DEBUG_FindOrMakePointerType(cv_basic_types[T_RCHAR]); cv_basic_types[T_32PWCHAR] = DEBUG_FindOrMakePointerType(cv_basic_types[T_WCHAR]); cv_basic_types[T_32PINT4] = DEBUG_FindOrMakePointerType(cv_basic_types[T_INT4]); cv_basic_types[T_32PUINT4] = DEBUG_FindOrMakePointerType(cv_basic_types[T_UINT4]); } /* * In this function, we keep track of deferred debugging information * that we may need later if we were to need to use the internal debugger. * We don't fully process it here for performance reasons. */ int DEBUG_RegisterDebugInfo( HMODULE32 hModule, const char *module_name) { int has_codeview = FALSE; int rtn = FALSE; int orig_size; PIMAGE_DEBUG_DIRECTORY dbgptr; u_long v_addr, size; PIMAGE_NT_HEADERS nth = PE_HEADER(hModule); size = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; if (size) { v_addr = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; dbgptr = (PIMAGE_DEBUG_DIRECTORY) (hModule + v_addr); orig_size = size; for(; size >= sizeof(*dbgptr); size -= sizeof(*dbgptr), dbgptr++ ) { switch(dbgptr->Type) { case IMAGE_DEBUG_TYPE_CODEVIEW: case IMAGE_DEBUG_TYPE_MISC: has_codeview = TRUE; break; } } size = orig_size; dbgptr = (PIMAGE_DEBUG_DIRECTORY) (hModule + v_addr); for(; size >= sizeof(*dbgptr); size -= sizeof(*dbgptr), dbgptr++ ) { switch(dbgptr->Type) { case IMAGE_DEBUG_TYPE_COFF: /* * If we have both codeview and COFF debug info, ignore the * coff debug info as it would just confuse us, and it is * less complete. * * FIXME - this is broken - if we cannot find the PDB file, then * we end up with no debugging info at all. In this case, we * should use the COFF info as a backup. */ if( has_codeview ) { break; } case IMAGE_DEBUG_TYPE_CODEVIEW: case IMAGE_DEBUG_TYPE_MISC: /* * This is usually an indirection to a .DBG file. * This is similar to (but a slightly older format) from the * PDB file. * * First check to see if the image was 'stripped'. If so, it * means that this entry points to a .DBG file. Otherwise, * it just points to itself, and we can ignore this. */ if( (dbgptr->Type != IMAGE_DEBUG_TYPE_MISC) || (PE_HEADER(hModule)->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) != 0 ) { char fn[PATH_MAX]; int fd = -1; DOS_FULL_NAME full_name; struct deferred_debug_info* deefer = (struct deferred_debug_info *) xmalloc(sizeof(*deefer)); deefer->module = hModule; deefer->load_addr = (char *)hModule; /* * Read the important bits. What we do after this depends * upon the type, but this is always enough so we are able * to proceed if we know what we need to do next. */ /* basically, the PE loader maps all sections (data, resources...), but doesn't map * the DataDirectory array's content. One its entry contains the *beloved* * debug information. (Note the DataDirectory is mapped, not its content) */ if (GetModuleFileName32A(hModule, fn, sizeof(fn)) > 0 && DOSFS_GetFullName(fn, TRUE, &full_name) && (fd = open(full_name.long_name, O_RDONLY)) > 0) { deefer->dbg_info = mmap(NULL, dbgptr->SizeOfData, PROT_READ, MAP_PRIVATE, fd, dbgptr->PointerToRawData); close(fd); if( deefer->dbg_info == (char *) 0xffffffff ) { free(deefer); break; } } else { free(deefer); fprintf(stderr, " (not mapped: fn=%s, lfn=%s, fd=%d)", fn, full_name.long_name, fd); break; } deefer->dbg_size = dbgptr->SizeOfData; deefer->dbgdir = dbgptr; deefer->next = dbglist; deefer->loaded = FALSE; deefer->dbg_index = DEBUG_next_index; deefer->module_name = xstrdup(module_name); deefer->sectp = PE_SECTIONS(hModule); deefer->nsect = PE_HEADER(hModule)->FileHeader.NumberOfSections; dbglist = deefer; } break; default: } } DEBUG_next_index++; } /* look for .stabs/.stabstr sections */ { PIMAGE_SECTION_HEADER pe_seg = PE_SECTIONS(hModule); int i,stabsize=0,stabstrsize=0; unsigned int stabs=0,stabstr=0; for (i=0;iFileHeader.NumberOfSections;i++) { if (!strcasecmp(pe_seg[i].Name,".stab")) { stabs = pe_seg[i].VirtualAddress; stabsize = pe_seg[i].SizeOfRawData; } if (!strncasecmp(pe_seg[i].Name,".stabstr",8)) { stabstr = pe_seg[i].VirtualAddress; stabstrsize = pe_seg[i].SizeOfRawData; } } if (stabstrsize && stabsize) { #ifdef _WE_SUPPORT_THE_STAB_TYPES_USED_BY_MINGW_TOO /* Won't work currently, since MINGW32 uses some special typedefs * which we do not handle yet. Support for them is a bit difficult. */ DEBUG_ParseStabs(hModule,0,stabs,stabsize,stabstr,stabstrsize); #endif fprintf(stderr,"(stabs not loaded)"); } } return (rtn); } /* * ELF modules are also entered into the list - this is so that we * can make 'info shared' types of displays possible. */ int DEBUG_RegisterELFDebugInfo(int load_addr, u_long size, char * name) { struct deferred_debug_info * deefer; deefer = (struct deferred_debug_info *) xmalloc(sizeof(*deefer)); deefer->module = 0; /* * Read the important bits. What we do after this depends * upon the type, but this is always enough so we are able * to proceed if we know what we need to do next. */ deefer->dbg_size = size; deefer->dbg_info = (char *) NULL; deefer->load_addr = (char *) load_addr; deefer->dbgdir = NULL; deefer->next = dbglist; deefer->loaded = TRUE; deefer->dbg_index = DEBUG_next_index; deefer->module_name = xstrdup(name); dbglist = deefer; DEBUG_next_index++; return (TRUE); } /* * Process COFF debugging information embedded in a Win32 application. * */ static int DEBUG_ProcessCoff(struct deferred_debug_info * deefer) { struct CoffAuxSection * aux; struct CoffDebug * coff; struct CoffFiles * coff_files = NULL; struct CoffLinenum * coff_linetab; char * coff_strtab; struct CoffSymbol * coff_sym; struct CoffSymbol * coff_symbol; struct CoffFiles * curr_file = NULL; int i; int j; int k; struct CoffLinenum * linepnt; int linetab_indx; char namebuff[9]; char * nampnt; int naux; DBG_ADDR new_addr; int nfiles = 0; int nfiles_alloc = 0; struct CoffFiles orig_file; int rtn = FALSE; char * this_file = NULL; coff = (struct CoffDebug *) deefer->dbg_info; coff_symbol = (struct CoffSymbol *) ((unsigned int) coff + coff->SymbolOffset); coff_linetab = (struct CoffLinenum *) ((unsigned int) coff + coff->LinenumberOffset); coff_strtab = (char *) ((unsigned int) coff_symbol + 18*coff->N_Sym); linetab_indx = 0; for(i=0; i < coff->N_Sym; i++ ) { /* * We do this because some compilers (i.e. gcc) incorrectly * pad the structure up to a 4 byte boundary. The structure * is really only 18 bytes long, so we have to manually make sure * we get it right. * * FIXME - there must be a way to have autoconf figure out the * correct compiler option for this. If it is always gcc, that * makes life simpler, but I don't want to force this. */ coff_sym = (struct CoffSymbol *) ((unsigned int) coff_symbol + 18*i); naux = coff_sym->NumberOfAuxSymbols; if( coff_sym->StorageClass == IMAGE_SYM_CLASS_FILE ) { if( nfiles + 1 >= nfiles_alloc ) { nfiles_alloc += 10; coff_files = (struct CoffFiles *) realloc( coff_files, nfiles_alloc * sizeof(struct CoffFiles)); } curr_file = coff_files + nfiles; nfiles++; curr_file->startaddr = 0xffffffff; curr_file->endaddr = 0; curr_file->filename = ((char *) coff_sym) + 18; curr_file->linetab_offset = -1; curr_file->linecnt = 0; curr_file->entries = NULL; curr_file->neps = curr_file->neps_alloc = 0; #if 0 fprintf(stderr,"New file %s\n", curr_file->filename); #endif i += naux; continue; } /* * This guy marks the size and location of the text section * for the current file. We need to keep track of this so * we can figure out what file the different global functions * go with. */ if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC) && (naux != 0) && (coff_sym->Type == 0) && (coff_sym->SectionNumber == 1) ) { aux = (struct CoffAuxSection *) ((unsigned int) coff_sym + 18); if( curr_file->linetab_offset != -1 ) { #if 0 fprintf(stderr, "Duplicating sect from %s: %x %x %x %d %d\n", curr_file->filename, aux->Length, aux->NumberOfRelocations, aux->NumberOfLinenumbers, aux->Number, aux->Selection); fprintf(stderr, "More sect %d %x %d %d %d\n", coff_sym->SectionNumber, coff_sym->Value, coff_sym->Type, coff_sym->StorageClass, coff_sym->NumberOfAuxSymbols); #endif /* * Save this so we can copy bits from it. */ orig_file = *curr_file; /* * Duplicate the file entry. We have no way to describe * multiple text sections in our current way of handling things. */ if( nfiles + 1 >= nfiles_alloc ) { nfiles_alloc += 10; coff_files = (struct CoffFiles *) realloc( coff_files, nfiles_alloc * sizeof(struct CoffFiles)); } curr_file = coff_files + nfiles; nfiles++; curr_file->startaddr = 0xffffffff; curr_file->endaddr = 0; curr_file->filename = orig_file.filename; curr_file->linetab_offset = -1; curr_file->linecnt = 0; curr_file->entries = NULL; curr_file->neps = curr_file->neps_alloc = 0; } #if 0 else { fprintf(stderr, "New text sect from %s: %x %x %x %d %d\n", curr_file->filename, aux->Length, aux->NumberOfRelocations, aux->NumberOfLinenumbers, aux->Number, aux->Selection); } #endif if( curr_file->startaddr > coff_sym->Value ) { curr_file->startaddr = coff_sym->Value; } if( curr_file->startaddr > coff_sym->Value ) { curr_file->startaddr = coff_sym->Value; } if( curr_file->endaddr < coff_sym->Value + aux->Length ) { curr_file->endaddr = coff_sym->Value + aux->Length; } curr_file->linetab_offset = linetab_indx; curr_file->linecnt = aux->NumberOfLinenumbers; linetab_indx += aux->NumberOfLinenumbers; i += naux; continue; } if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC) && (naux == 0) && (coff_sym->SectionNumber == 1) ) { /* * This is a normal static function when naux == 0. * Just register it. The current file is the correct * one in this instance. */ if( coff_sym->N.Name.NotLong ) { memcpy(namebuff, coff_sym->N.ShortName, 8); namebuff[8] = '\0'; nampnt = &namebuff[0]; } else { nampnt = coff_strtab + coff_sym->N.Name.StrTaboff; } if( nampnt[0] == '_' ) { nampnt++; } new_addr.seg = 0; new_addr.off = (int) (deefer->load_addr + coff_sym->Value); if( curr_file->neps + 1 >= curr_file->neps_alloc ) { curr_file->neps_alloc += 10; curr_file->entries = (struct name_hash **) realloc( curr_file->entries, curr_file->neps_alloc * sizeof(struct name_hash *)); } #if 0 fprintf(stderr,"\tAdding static symbol %s\n", nampnt); #endif curr_file->entries[curr_file->neps++] = DEBUG_AddSymbol( nampnt, &new_addr, this_file, SYM_WIN32 ); i += naux; continue; } if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) && ISFCN(coff_sym->Type) && (coff_sym->SectionNumber > 0) ) { if( coff_sym->N.Name.NotLong ) { memcpy(namebuff, coff_sym->N.ShortName, 8); namebuff[8] = '\0'; nampnt = &namebuff[0]; } else { nampnt = coff_strtab + coff_sym->N.Name.StrTaboff; } if( nampnt[0] == '_' ) { nampnt++; } new_addr.seg = 0; new_addr.off = (int) (deefer->load_addr + coff_sym->Value); #if 0 fprintf(stderr, "%d: %x %s\n", i, new_addr.off, nampnt); fprintf(stderr,"\tAdding global symbol %s\n", nampnt); #endif /* * Now we need to figure out which file this guy belongs to. */ this_file = NULL; for(j=0; j < nfiles; j++) { if( coff_files[j].startaddr <= coff_sym->Value && coff_files[j].endaddr > coff_sym->Value ) { this_file = coff_files[j].filename; break; } } if( coff_files[j].neps + 1 >= coff_files[j].neps_alloc ) { coff_files[j].neps_alloc += 10; coff_files[j].entries = (struct name_hash **) realloc( coff_files[j].entries, coff_files[j].neps_alloc * sizeof(struct name_hash *)); } coff_files[j].entries[coff_files[j].neps++] = DEBUG_AddSymbol( nampnt, &new_addr, this_file, SYM_WIN32 ); i += naux; continue; } if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) && (coff_sym->SectionNumber > 0) ) { /* * Similar to above, but for the case of data symbols. * These aren't treated as entrypoints. */ if( coff_sym->N.Name.NotLong ) { memcpy(namebuff, coff_sym->N.ShortName, 8); namebuff[8] = '\0'; nampnt = &namebuff[0]; } else { nampnt = coff_strtab + coff_sym->N.Name.StrTaboff; } if( nampnt[0] == '_' ) { nampnt++; } new_addr.seg = 0; new_addr.off = (int) (deefer->load_addr + coff_sym->Value); #if 0 fprintf(stderr, "%d: %x %s\n", i, new_addr.off, nampnt); fprintf(stderr,"\tAdding global data symbol %s\n", nampnt); #endif /* * Now we need to figure out which file this guy belongs to. */ DEBUG_AddSymbol( nampnt, &new_addr, NULL, SYM_WIN32 ); i += naux; continue; } if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC) && (naux == 0) ) { /* * Ignore these. They don't have anything to do with * reality. */ i += naux; continue; } #if 0 fprintf(stderr,"Skipping unknown entry %d %d %d\n", coff_sym->StorageClass, coff_sym->SectionNumber, naux); #endif /* * For now, skip past the aux entries. */ i += naux; } /* * OK, we now should have a list of files, and we should have a list * of entrypoints. We need to sort the entrypoints so that we are * able to tie the line numbers with the given functions within the * file. */ if( coff_files != NULL ) { for(j=0; j < nfiles; j++) { if( coff_files[j].entries != NULL ) { qsort(coff_files[j].entries, coff_files[j].neps, sizeof(struct name_hash *), DEBUG_cmp_sym); } } /* * Now pick apart the line number tables, and attach the entries * to the given functions. */ for(j=0; j < nfiles; j++) { i = 0; if( coff_files[j].neps != 0 ) for(k=0; k < coff_files[j].linecnt; k++) { /* * Another monstrosity caused by the fact that we are using * a 6 byte structure, and gcc wants to pad structures to 4 byte * boundaries. Otherwise we could just index into an array. */ linepnt = (struct CoffLinenum *) ((unsigned int) coff_linetab + 6*(coff_files[j].linetab_offset + k)); /* * If we have spilled onto the next entrypoint, then * bump the counter.. */ while(TRUE) { if (i+1 >= coff_files[j].neps) break; DEBUG_GetSymbolAddr(coff_files[j].entries[i+1], &new_addr); if( (((unsigned int)deefer->load_addr + linepnt->VirtualAddr) >= new_addr.off) ) { i++; } else break; } /* * Add the line number. This is always relative to the * start of the function, so we need to subtract that offset * first. */ DEBUG_GetSymbolAddr(coff_files[j].entries[i], &new_addr); DEBUG_AddLineNumber(coff_files[j].entries[i], linepnt->Linenum, (unsigned int) deefer->load_addr + linepnt->VirtualAddr - new_addr.off); } } } rtn = TRUE; if( coff_files != NULL ) { for(j=0; j < nfiles; j++) { if( coff_files[j].entries != NULL ) { free(coff_files[j].entries); } } free(coff_files); } return (rtn); } /* * Process a codeview line number table. Digestify the thing so that * we can easily reference the thing when we process the rest of * the information. */ static struct codeview_linetab_hdr * DEBUG_SnarfLinetab(char * linetab, int size) { int file_segcount; char filename[PATH_MAX]; unsigned int * filetab; char * fn; int i; int k; struct codeview_linetab_hdr * lt_hdr; unsigned int * lt_ptr; int nfile; int nseg; union any_size pnt; union any_size pnt2; struct startend * start; int this_seg; /* * Now get the important bits. */ pnt = (union any_size) linetab; nfile = *pnt.s++; nseg = *pnt.s++; filetab = (unsigned int *) pnt.c; /* * Now count up the number of segments in the file. */ nseg = 0; for(i=0; insect; sectp = deefer->sectp; /* * Skip over the first word. Don't really know what it means, but * it is useless. */ ptr.ui++; /* * Loop over the different types of records and whenever we * find something we are interested in, record it and move on. */ while( ptr.c - cv_data < size ) { sym = (union codeview_symbol *) ptr.c; if( sym->generic.len - sizeof(int) == (ptr.c - cv_data) ) { /* * This happens when we have indirect symbols that VC++ 4.2 * sometimes uses when there isn't a line number table. * We ignore it - we will process and enter all of the * symbols in the global symbol table anyways, so there * isn't much point in keeping track of all of this crap. */ break; } memset(symname, 0, sizeof(symname)); switch(sym->generic.id) { case S_GDATA32: case S_LDATA32: case S_PUB32: /* * First, a couple of sanity checks. */ if( sym->data.namelen == 0 ) { break; } if( sym->data.seg == 0 || sym->data.seg > nsect ) { break; } /* * Global and local data symbols. We don't associate these * with any given source file. */ memcpy(symname, sym->data.name, sym->data.namelen); new_addr.seg = 0; new_addr.type = DEBUG_GetCVType(sym->data.symtype); new_addr.off = (unsigned int) deefer->load_addr + sectp[sym->data.seg - 1].VirtualAddress + sym->data.offset; DEBUG_AddSymbol( symname, &new_addr, NULL, SYM_WIN32 | SYM_DATA ); break; case S_THUNK32: /* * Sort of like a global function, but it just points * to a thunk, which is a stupid name for what amounts to * a PLT slot in the normal jargon that everyone else uses. */ memcpy(symname, sym->thunk.name, sym->thunk.namelen); new_addr.seg = 0; new_addr.type = NULL; new_addr.off = (unsigned int) deefer->load_addr + sectp[sym->thunk.segment - 1].VirtualAddress + sym->thunk.offset; thunk_sym = DEBUG_AddSymbol( symname, &new_addr, NULL, SYM_WIN32 | SYM_FUNC); DEBUG_SetSymbolSize(thunk_sym, sym->thunk.thunk_len); break; case S_GPROC32: case S_LPROC32: /* * Global and static functions. */ memcpy(symname, sym->proc.name, sym->proc.namelen); new_addr.seg = 0; new_addr.type = DEBUG_GetCVType(sym->proc.proctype); new_addr.off = (unsigned int) deefer->load_addr + sectp[sym->proc.segment - 1].VirtualAddress + sym->proc.offset; /* * See if we can find a segment that this goes with. If so, * it means that we also may have line number information * for this function. */ for(i=0; linetab[i].linetab != NULL; i++) { if( ((unsigned int) deefer->load_addr + sectp[linetab[i].segno - 1].VirtualAddress + linetab[i].start <= new_addr.off) && ((unsigned int) deefer->load_addr + sectp[linetab[i].segno - 1].VirtualAddress + linetab[i].end > new_addr.off) ) { break; } } DEBUG_Normalize(curr_func); if( linetab[i].linetab == NULL ) { curr_func = DEBUG_AddSymbol( symname, &new_addr, NULL, SYM_WIN32 | SYM_FUNC); } else { /* * First, create the entry. Then dig through the linetab * and add whatever line numbers are appropriate for this * function. */ curr_func = DEBUG_AddSymbol( symname, &new_addr, linetab[i].sourcefile, SYM_WIN32 | SYM_FUNC); for(j=0; j < linetab[i].nline; j++) { if( linetab[i].offtab[j] >= sym->proc.offset && linetab[i].offtab[j] < sym->proc.offset + sym->proc.proc_len ) { DEBUG_AddLineNumber(curr_func, linetab[i].linetab[j], linetab[i].offtab[j] - sym->proc.offset); } } } /* * Add information about where we should set breakpoints * in this function. */ DEBUG_SetSymbolBPOff(curr_func, sym->proc.debug_start); DEBUG_SetSymbolSize(curr_func, sym->proc.proc_len); break; case S_BPREL32: /* * Function parameters and stack variables. */ memcpy(symname, sym->stack.name, sym->stack.namelen); curr_sym = DEBUG_AddLocal(curr_func, 0, sym->stack.offset, 0, 0, symname); DEBUG_SetLocalSymbolType(curr_sym, DEBUG_GetCVType(sym->stack.symtype)); break; default: break; } /* * Adjust pointer to point to next entry, rounding up to a word * boundary. MS preserving alignment? Stranger things have * happened. */ if( sym->generic.id == S_PROCREF || sym->generic.id == S_DATAREF || sym->generic.id == S_UNKNOWN ) { len = (sym->generic.len + 3) & ~3; len += ptr.c[16] + 1; ptr.c += (len + 3) & ~3; } else { ptr.c += (sym->generic.len + 3) & ~3; } } if( linetab != NULL ) { free(linetab); } return TRUE; } /* * Process PDB file which contains debug information. * * These are really weird beasts. They are intended to be incrementally * updated by the incremental linker, and this means that you need to * be able to remove and add information. Thus the PDB file is sort of * like a block structured device, with a freelist and lists of extent numbers * that are used to get the relevant pieces. In all cases seen so far, the * blocksize is always 0x400 bytes. The header has a field which apparently * holds the blocksize, so if it ever changes we are safe. * * In general, every time we need to extract something from the pdb file, * it is easier to copy it into another buffer so we have the information * in one contiguous block rather than attempt to try and keep track of when * we need to grab another extent from the pdb file. * * The thing that is a real pain about some MS stuff is that they choose * data structures which are not representable in C. Thus we have to * hack around and diddle pointers. */ /* static */ int DEBUG_ProcessPDBFile(struct deferred_debug_info * deefer, char * full_filename) { char * addr = (char *) 0xffffffff; unsigned int blocksize; unsigned int bufflen = 0; char * buffer = NULL; unsigned short * extent_table; int fd = -1; struct file_ent * fent; char filename[MAX_PATHNAME_LEN]; struct file_list * filelist = NULL; unsigned int gsym_record = 0; char * gsymtab = NULL; struct filetab_hdr * hd; int i; int j; unsigned int last_extent; struct codeview_linetab_hdr * linetab; unsigned int nblocks; unsigned int npair; unsigned int offset; struct codeview_pdb_hdr * pdbhdr; unsigned int * pnt; struct stat statbuf; int status; unsigned short * table; char * toc; unsigned int toc_blocks; LocateDebugInfoFile(full_filename, filename); status = stat(filename, &statbuf); if( status == -1 ) { fprintf(stderr, "-Unable to open .PDB file %s\n", filename); goto leave; } /* * Now open the file, so that we can mmap() it. */ fd = open(filename, O_RDONLY); if( fd == -1 ) { fprintf(stderr, "-Unable to open .DBG file %s\n", filename); goto leave; } /* * Now mmap() the file. */ addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if( addr == (char *) 0xffffffff ) { fprintf(stderr, "-Unable to mmap .DBG file %s\n", filename); goto leave; } /* * Now that we have the formalities over and done with, we need * to find the table of contents for the PDB file. */ pdbhdr = (struct codeview_pdb_hdr *) addr; blocksize = pdbhdr->blocksize; last_extent = (statbuf.st_size + blocksize - 1) / blocksize; /* * The TOC itself isn't always contiguous, so we need to extract a few * extents from the file to form the TOC. */ toc_blocks = (pdbhdr->toc_len + blocksize - 1) / blocksize; toc = (char *) xmalloc(toc_blocks * blocksize); table = pdbhdr->toc_ext; for(i=0; i < toc_blocks; i++) { memcpy(toc + blocksize*i, addr + table[i]*blocksize, blocksize); } /* * Next build our own table which will have the size and extent block * list for each record in the PDB file. * * The TOC starts out with the number of files. Then it is followed by * (npair * 2*sizeof(int)) bytes of information, which are pairs of ints. * The first one is the size of the record (in bytes), and the second one * is something else which I haven't figured out yet. */ pnt = (unsigned int *) toc; npair = *pnt++; extent_table = (unsigned short *) ((unsigned int) toc + npair * 2 * sizeof(int) + sizeof(int)); /* * Sanity check. */ if( sizeof(int) + 2*sizeof(int)*npair > pdbhdr->toc_len ) { goto leave; } filelist = (struct file_list *) xmalloc(npair * sizeof(*filelist)); if( filelist == NULL ) { goto leave; } memset(filelist, 0, npair * sizeof(*filelist)); nblocks = 0; for(i=0; i < npair; i++) { filelist[i].record_len = pnt[i*2]; filelist[i].nextents = (filelist[i].record_len + blocksize - 1) / blocksize; filelist[i].extent_list = extent_table + nblocks; nblocks += filelist[i].nextents; /* * These get filled in later when we parse one of the records. */ filelist[i].linetab_offset = 0; filelist[i].linetab_len = 0; } /* * OK, now walk through the various records and pick out the bits we * really want to see. Some of the records are extra special, and * we need to handle these a little bit differently. */ for(i=0; i < npair; i++) { if( filelist[i].record_len == 0xffffffff ) { continue; } /* * Make sure our buffer is large enough to hold the record. */ if( bufflen < filelist[i].nextents * blocksize ) { bufflen = filelist[i].nextents * blocksize; buffer = (char *) realloc(buffer, bufflen); } /* * Do this just for completeness. It makes debugging easier * if we have a clean indication of where the record ends. */ memset(buffer, 0, filelist[i].nextents * blocksize); /* * Next, build the record using the extent list. */ for(j=0; j < filelist[i].nextents; j++) { memcpy(buffer + j * blocksize, addr + filelist[i].extent_list[j] * blocksize, blocksize); } pnt = (unsigned int *) buffer; /* * OK, now figure out what to do with it. */ /* * Always ignore the first entry. It seems to contain a backup copy * of the TOC (the last time the file was modified??) */ if( i == 0 ) { continue; } /* * The second entry as a id block. It contains a magic number * to identify the compiler, plus it also contains the timestamp * which must match the timestamp in the executable. */ if( i == 1 ) { if( ((*pnt != 19950623) && (*pnt != 19950814)) || (filelist[i].record_len != 0x24) || (pnt[1] != ((struct CodeViewDebug *)(deefer->dbg_info))->cv_timestamp) ) { goto leave; } } /* * The third entry contains pointers to the global symbol table, * plus it also contains additional information about each record * in the PDB file. */ if( i == 3 ) { hd = (struct filetab_hdr *) buffer; gsym_record = hd->gsym_file; gsymtab = (char *) xmalloc( filelist[gsym_record].nextents * blocksize); memset(gsymtab, 0, filelist[gsym_record].nextents * blocksize); for(j=0; j < filelist[gsym_record].nextents; j++) { memcpy(gsymtab + j * blocksize, addr + filelist[gsym_record].extent_list[j] * blocksize, blocksize); } /* * This record also contains information about where in the * remaining records we will be able to find the start of the * line number table. We could locate that bit using heuristics, * but since we have the info handy, we might as well use it. */ offset = sizeof(*hd); while(1==1) { fent = (struct file_ent *) (buffer + offset); if( offset > hd->ftab_len ) { break; } if( fent->file_number == 0 || fent->file_number >= npair ) { break; } filelist[fent->file_number].linetab_offset = fent->linetab_offset; filelist[fent->file_number].linetab_len = fent->linetab_len; /* * Figure out the offset of the next entry. * There is a fixed part of the record and a variable * length filename which we must also skip past. */ offset += ((unsigned int) &fent->filename - (unsigned int) fent) + strlen(fent->filename) + 1; offset += strlen(buffer+offset) + 1; offset = (offset + 3) & ~3; } } /* * Two different magic numbers used as dates. * These indicate the 'type' table. */ if( *pnt == 19950410 || *pnt == 19951122 ) { DEBUG_ParseTypeTable(buffer, filelist[i].record_len); continue; } /* * This is something we really want to look at, since it contains * real debug info. Anything that doesn't match this can be * ignored for now. */ if( *pnt == 1 ) { /* * First, snag the line table, if we have one. This always * occurs at the end of the record, so we take the linetab * offset as the end of the normal part of the record. */ linetab = NULL; if( filelist[i].linetab_len != 0 ) { linetab = DEBUG_SnarfLinetab(buffer + filelist[i].linetab_offset, filelist[i].linetab_len); DEBUG_SnarfCodeView(deefer, buffer, filelist[i].linetab_offset, linetab); } else { DEBUG_SnarfCodeView(deefer, buffer, filelist[i].record_len, linetab); } continue; } } /* * Finally, process the global symbol table itself. There isn't * a line number component to this, so we just toss everything * into the mix and it all should work out. */ if( gsym_record != 0 ) { DEBUG_SnarfCodeView(deefer, gsymtab - sizeof(int), filelist[gsym_record].record_len, NULL); } leave: if( gsymtab != NULL ) { free(gsymtab); gsymtab = NULL; } if( buffer != NULL ) { free(buffer); } if( filelist != NULL ) { free(filelist); } if( addr != (char *) 0xffffffff ) { munmap(addr, statbuf.st_size); } if( fd != -1 ) { close(fd); } return TRUE; } /* * Process DBG file which contains debug information. */ /* static */ int DEBUG_ProcessDBGFile(struct deferred_debug_info * deefer, char * filename) { char * addr = (char *) 0xffffffff; char * codeview; struct CV4_DirHead * codeview_dir; struct CV4_DirEnt * codeview_dent; PIMAGE_DEBUG_DIRECTORY dbghdr; struct deferred_debug_info deefer2; int fd = -1; int i; int j; struct codeview_linetab_hdr * linetab; int nsect; PIMAGE_SEPARATE_DEBUG_HEADER pdbg = NULL; IMAGE_SECTION_HEADER * sectp; struct stat statbuf; int status; char dbg_file[MAX_PATHNAME_LEN]; LocateDebugInfoFile(filename, dbg_file); status = stat(dbg_file, &statbuf); if( status == -1 ) { fprintf(stderr, "-Unable to open .DBG file %s\n", dbg_file); goto leave; } /* * Now open the file, so that we can mmap() it. */ fd = open(dbg_file, O_RDONLY); if( fd == -1 ) { fprintf(stderr, "Unable to open .DBG file %s\n", dbg_file); goto leave; } /* * Now mmap() the file. */ addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if( addr == (char *) 0xffffffff ) { fprintf(stderr, "Unable to mmap .DBG file %s\n", dbg_file); goto leave; } pdbg = (PIMAGE_SEPARATE_DEBUG_HEADER) addr; if( pdbg->TimeDateStamp != deefer->dbgdir->TimeDateStamp ) { fprintf(stderr, "Warning - %s has incorrect internal timestamp\n", dbg_file); /* goto leave; */ /* Well, sometimes this happens to DBG files which ARE REALLY the right .DBG files but nonetheless this check fails. Anyway, WINDBG (debugger for Windows by Microsoft) loads debug symbols which have incorrect timestamps. */ } fprintf(stderr, "Processing symbols from %s...\n", dbg_file); dbghdr = (PIMAGE_DEBUG_DIRECTORY) ( addr + sizeof(*pdbg) + pdbg->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + pdbg->ExportedNamesSize); sectp = (PIMAGE_SECTION_HEADER) ((char *) pdbg + sizeof(*pdbg)); nsect = pdbg->NumberOfSections; for( i=0; i < pdbg->DebugDirectorySize / sizeof(*pdbg); i++, dbghdr++ ) { switch(dbghdr->Type) { case IMAGE_DEBUG_TYPE_COFF: /* * Dummy up a deferred debug header to handle the * COFF stuff embedded within the DBG file. */ memset((char *) &deefer2, 0, sizeof(deefer2)); deefer2.dbg_info = (addr + dbghdr->PointerToRawData); deefer2.dbg_size = dbghdr->SizeOfData; deefer2.load_addr = deefer->load_addr; DEBUG_ProcessCoff(&deefer2); break; case IMAGE_DEBUG_TYPE_CODEVIEW: /* * This is the older format by which codeview stuff is * stored, known as the 'NB09' format. Newer executables * and dlls created by VC++ use PDB files instead, which * have lots of internal similarities, but the overall * format and structure is quite different. */ codeview = (addr + dbghdr->PointerToRawData); /* * The first thing in the codeview section should be * an 'NB09' identifier. As a sanity check, make sure * it is there. */ if( *((unsigned int*) codeview) != 0x3930424e ) { break; } /* * Next we need to find the directory. This is easy too. */ codeview_dir = (struct CV4_DirHead *) (codeview + ((unsigned int*) codeview)[1]); /* * Some more sanity checks. Make sure that everything * is as we expect it. */ if( codeview_dir->next_offset != 0 || codeview_dir->dhsize != sizeof(*codeview_dir) || codeview_dir->desize != sizeof(*codeview_dent) ) { break; } codeview_dent = (struct CV4_DirEnt *) (codeview_dir + 1); for(j=0; j < codeview_dir->ndir; j++, codeview_dent++) { if( codeview_dent->subsect_number == sstAlignSym ) { /* * Check the previous entry. If it is a * sstSrcModule, it contains the line number * info for this file. */ linetab = NULL; if( codeview_dent[1].module_number == codeview_dent[0].module_number && codeview_dent[1].subsect_number == sstSrcModule ) { linetab = DEBUG_SnarfLinetab( codeview + codeview_dent[1].offset, codeview_dent[1].size); } if( codeview_dent[-1].module_number == codeview_dent[0].module_number && codeview_dent[-1].subsect_number == sstSrcModule ) { linetab = DEBUG_SnarfLinetab( codeview + codeview_dent[-1].offset, codeview_dent[-1].size); } /* * Now process the CV stuff. */ DEBUG_SnarfCodeView(deefer, codeview + codeview_dent->offset, codeview_dent->size, linetab); } } break; default: break; } } leave: if( addr != (char *) 0xffffffff ) { munmap(addr, statbuf.st_size); } if( fd != -1 ) { close(fd); } return TRUE; } int DEBUG_ProcessDeferredDebug() { struct deferred_debug_info * deefer; struct CodeViewDebug * cvd; struct MiscDebug * misc; char * filename; int last_proc = -1; int need_print =0; DEBUG_InitCVDataTypes(); for(deefer = dbglist; deefer; deefer = deefer->next) { if( deefer->loaded ) { continue; } if( last_proc != deefer->dbg_index ) { if (!need_print) { fprintf(stderr, "DeferredDebug for:"); need_print=1; } fprintf(stderr, " %s",deefer->module_name); last_proc = deefer->dbg_index; } switch(deefer->dbgdir->Type) { case IMAGE_DEBUG_TYPE_COFF: /* * Standard COFF debug information that VC++ adds when you * use /debugtype:both with the linker. */ #if 0 fprintf(stderr, "Processing COFF symbols...\n"); #endif DEBUG_ProcessCoff(deefer); break; case IMAGE_DEBUG_TYPE_CODEVIEW: /* * This is a pointer to a PDB file of some sort. */ cvd = (struct CodeViewDebug *) deefer->dbg_info; if( strcmp(cvd->cv_nbtype, "NB10") != 0 ) { /* * Whatever this is, we don't know how to deal with * it yet. */ break; } DEBUG_ProcessPDBFile(deefer, cvd->cv_name); #if 0 fprintf(stderr, "Processing PDB file %s\n", cvd->cv_name); #endif break; case IMAGE_DEBUG_TYPE_MISC: /* * A pointer to a .DBG file of some sort. These files * can contain either CV4 or COFF information. Open * the file, and try to do the right thing with it. */ misc = (struct MiscDebug *) deefer->dbg_info; filename = strrchr((char *) &misc->Data, '.'); /* * Ignore the file if it doesn't have a .DBG extension. */ if( (filename == NULL) || ( (strcmp(filename, ".dbg") != 0) && (strcmp(filename, ".DBG") != 0)) ) { break; } filename = (char *) &misc->Data; /* * Do the dirty deed... */ DEBUG_ProcessDBGFile(deefer, filename); break; default: /* * We should never get here... */ break; } } if(need_print) fprintf(stderr, "\n"); return TRUE; } /*********************************************************************** * DEBUG_InfoShare * * Display shared libarary information. */ void DEBUG_InfoShare(void) { struct deferred_debug_info * deefer; fprintf(stderr,"Address\t\tModule\tName\n"); for(deefer = dbglist; deefer; deefer = deefer->next) { fprintf(stderr,"0x%8.8x\t(%s)\t%s\n", (unsigned int) deefer->load_addr, deefer->module ? "Win32" : "ELF", deefer->module_name); } }