| /* |
| * 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. |
| */ |
| |
| #include <stdio.h> |
| |
| |
| #include <sys/mman.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <limits.h> |
| #include <strings.h> |
| #include <unistd.h> |
| #include <malloc.h> |
| |
| #include "win.h" |
| #include "pe_image.h" |
| #include "debugger.h" |
| #include "peexe.h" |
| #include "xmalloc.h" |
| |
| /* |
| * 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 int Linenum; |
| }; |
| |
| struct CoffFiles { |
| unsigned int startaddr; |
| unsigned int endaddr; |
| char * filename; |
| }; |
| |
| |
| 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; |
| |
| struct deferred_debug_info |
| { |
| struct deferred_debug_info * next; |
| char * load_addr; |
| char * dbg_info; |
| int dbg_size; |
| struct PE_Debug_dir * dbgdir; |
| struct pe_data * pe; |
| }; |
| |
| 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 |
| |
| /* |
| * 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(int fd, struct pe_data * pe, |
| int load_addr, u_long v_addr, u_long size) |
| { |
| int rtn = FALSE; |
| struct PE_Debug_dir * dbgptr; |
| struct deferred_debug_info * deefer; |
| |
| dbgptr = (struct PE_Debug_dir *) (load_addr + v_addr); |
| for(; size > 0; size -= sizeof(*dbgptr), dbgptr++ ) |
| { |
| switch(dbgptr->type) |
| { |
| case IMAGE_DEBUG_TYPE_COFF: |
| 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->pe_header->coff.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) == 0 ) |
| { |
| break; |
| } |
| |
| deefer = (struct deferred_debug_info *) xmalloc(sizeof(*deefer)); |
| deefer->pe = pe; |
| |
| deefer->dbg_info = NULL; |
| deefer->dbg_size = 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 = dbgptr->dbgsize; |
| deefer->dbg_info = (char *) xmalloc(dbgptr->dbgsize); |
| lseek(fd, dbgptr->dbgoff, SEEK_SET); |
| read(fd, deefer->dbg_info, deefer->dbg_size); |
| |
| deefer->load_addr = (char *) load_addr; |
| deefer->dbgdir = dbgptr; |
| deefer->next = dbglist; |
| dbglist = deefer; |
| break; |
| default: |
| } |
| } |
| |
| return (rtn); |
| |
| } |
| |
| /* |
| * Process COFF debugging information embedded in a Win32 application. |
| * |
| * FIXME - we need to process the source file information and the line |
| * numbers. |
| */ |
| static |
| int |
| DEBUG_ProcessCoff(struct deferred_debug_info * deefer) |
| { |
| struct CoffAuxSection * aux; |
| struct CoffDebug * coff; |
| struct CoffSymbol * coff_sym; |
| struct CoffSymbol * coff_symbol; |
| struct CoffLinenum * coff_linetab; |
| char * coff_strtab; |
| int i; |
| DBG_ADDR new_addr; |
| int rtn = FALSE; |
| int naux; |
| char namebuff[9]; |
| char * nampnt; |
| int nfiles = 0; |
| int nfiles_alloc = 0; |
| struct CoffFiles * coff_files = NULL; |
| struct CoffFiles * curr_file = NULL; |
| char * this_file; |
| int j; |
| |
| 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); |
| |
| 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; |
| } |
| |
| /* |
| * 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->SectionNumber == 1) ) |
| { |
| aux = (struct CoffAuxSection *) ((unsigned int) coff_sym + 18); |
| 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; |
| } |
| |
| } |
| |
| 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; |
| } |
| |
| new_addr.seg = 0; |
| new_addr.off = (int) (deefer->load_addr + coff_sym->Value); |
| DEBUG_AddSymbol( nampnt, &new_addr, curr_file->filename ); |
| } |
| |
| 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; |
| } |
| |
| 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); |
| #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; |
| } |
| } |
| DEBUG_AddSymbol( nampnt, &new_addr, this_file ); |
| } |
| |
| /* |
| * For now, skip past the aux entries. |
| */ |
| i += naux; |
| |
| } |
| |
| rtn = TRUE; |
| |
| if( coff_files != NULL ) |
| { |
| free(coff_files); |
| } |
| |
| return (rtn); |
| |
| } |
| |
| int |
| DEBUG_ProcessDeferredDebug() |
| { |
| struct deferred_debug_info * deefer; |
| struct CodeViewDebug * cvd; |
| struct MiscDebug * misc; |
| |
| for(deefer = dbglist; deefer; deefer = deefer->next) |
| { |
| 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 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. |
| */ |
| misc = (struct MiscDebug *) deefer->dbg_info; |
| #if 0 |
| fprintf(stderr, "Processing DBG file %s\n", misc->Data); |
| #endif |
| break; |
| default: |
| /* |
| * We should never get here... |
| */ |
| break; |
| } |
| } |
| return TRUE; |
| } |