/* * Read VC++ debug information from COFF and eventually * from PDB files. * * Copyright (C) 1996, Eric Youngdale. * Copyright (C) 1999-2000, Ulrich Weigand. * Copyright (C) 2004, Eric Pouech. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /* * 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 "config.h" #include "wine/port.h" #include <assert.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <stdarg.h> #include "windef.h" #include "winbase.h" #include "winternl.h" #include "wine/exception.h" #include "wine/debug.h" #include "dbghelp_private.h" #include "wine/mscvpdb.h" WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_coff); /*======================================================================== * Process COFF debug information. */ struct CoffFile { unsigned int startaddr; unsigned int endaddr; struct symt_compiland* compiland; int linetab_offset; int linecnt; struct symt** entries; int neps; int neps_alloc; }; struct CoffFileSet { struct CoffFile* files; int nfiles; int nfiles_alloc; }; static const char* coff_get_name(const IMAGE_SYMBOL* coff_sym, const char* coff_strtab) { static char namebuff[9]; const char* nampnt; if (coff_sym->N.Name.Short) { memcpy(namebuff, coff_sym->N.ShortName, 8); namebuff[8] = '\0'; nampnt = &namebuff[0]; } else { nampnt = coff_strtab + coff_sym->N.Name.Long; } if (nampnt[0] == '_') nampnt++; return nampnt; } static int coff_add_file(struct CoffFileSet* coff_files, struct module* module, const char* filename) { struct CoffFile* file; if (coff_files->nfiles + 1 >= coff_files->nfiles_alloc) { if (coff_files->files) { coff_files->nfiles_alloc *= 2; coff_files->files = HeapReAlloc(GetProcessHeap(), 0, coff_files->files, coff_files->nfiles_alloc * sizeof(struct CoffFile)); } else { coff_files->nfiles_alloc = 16; coff_files->files = HeapAlloc(GetProcessHeap(), 0, coff_files->nfiles_alloc * sizeof(struct CoffFile)); } } file = coff_files->files + coff_files->nfiles; file->startaddr = 0xffffffff; file->endaddr = 0; file->compiland = symt_new_compiland(module, 0, source_new(module, NULL, filename)); file->linetab_offset = -1; file->linecnt = 0; file->entries = NULL; file->neps = file->neps_alloc = 0; return coff_files->nfiles++; } static void coff_add_symbol(struct CoffFile* coff_file, struct symt* sym) { if (coff_file->neps + 1 >= coff_file->neps_alloc) { if (coff_file->entries) { coff_file->neps_alloc *= 2; coff_file->entries = HeapReAlloc(GetProcessHeap(), 0, coff_file->entries, coff_file->neps_alloc * sizeof(struct symt*)); } else { coff_file->neps_alloc = 32; coff_file->entries = HeapAlloc(GetProcessHeap(), 0, coff_file->neps_alloc * sizeof(struct symt*)); } } coff_file->entries[coff_file->neps++] = sym; } DECLSPEC_HIDDEN BOOL coff_process_info(const struct msc_debug_info* msc_dbg) { const IMAGE_AUX_SYMBOL* aux; const IMAGE_COFF_SYMBOLS_HEADER* coff; const IMAGE_LINENUMBER* coff_linetab; const IMAGE_LINENUMBER* linepnt; const char* coff_strtab; const IMAGE_SYMBOL* coff_sym; const IMAGE_SYMBOL* coff_symbols; struct CoffFileSet coff_files; int curr_file_idx = -1; unsigned int i; int j; int k; int l; int linetab_indx; const char* nampnt; int naux; BOOL ret = FALSE; ULONG64 addr; TRACE("Processing COFF symbols...\n"); assert(sizeof(IMAGE_SYMBOL) == IMAGE_SIZEOF_SYMBOL); assert(sizeof(IMAGE_LINENUMBER) == IMAGE_SIZEOF_LINENUMBER); coff_files.files = NULL; coff_files.nfiles = coff_files.nfiles_alloc = 0; coff = (const IMAGE_COFF_SYMBOLS_HEADER*)msc_dbg->root; coff_symbols = (const IMAGE_SYMBOL*)((const char *)coff + coff->LvaToFirstSymbol); coff_linetab = (const IMAGE_LINENUMBER*)((const char *)coff + coff->LvaToFirstLinenumber); coff_strtab = (const char*)(coff_symbols + coff->NumberOfSymbols); linetab_indx = 0; for (i = 0; i < coff->NumberOfSymbols; i++) { coff_sym = coff_symbols + i; naux = coff_sym->NumberOfAuxSymbols; if (coff_sym->StorageClass == IMAGE_SYM_CLASS_FILE) { curr_file_idx = coff_add_file(&coff_files, msc_dbg->module, (const char*)(coff_sym + 1)); TRACE("New file %s\n", (const char*)(coff_sym + 1)); i += naux; continue; } if (curr_file_idx < 0) { assert(coff_files.nfiles == 0 && coff_files.nfiles_alloc == 0); curr_file_idx = coff_add_file(&coff_files, msc_dbg->module, "<none>"); TRACE("New file <none>\n"); } /* * 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 = (const IMAGE_AUX_SYMBOL*) (coff_sym + 1); if (coff_files.files[curr_file_idx].linetab_offset != -1) { /* * Save this so we can still get the old name. */ const char* fn; fn = source_get(msc_dbg->module, coff_files.files[curr_file_idx].compiland->source); TRACE("Duplicating sect from %s: %x %x %x %d %d\n", fn, aux->Section.Length, aux->Section.NumberOfRelocations, aux->Section.NumberOfLinenumbers, aux->Section.Number, aux->Section.Selection); TRACE("More sect %d %s %08x %d %d %d\n", coff_sym->SectionNumber, coff_get_name(coff_sym, coff_strtab), coff_sym->Value, coff_sym->Type, coff_sym->StorageClass, coff_sym->NumberOfAuxSymbols); /* * Duplicate the file entry. We have no way to describe * multiple text sections in our current way of handling things. */ coff_add_file(&coff_files, msc_dbg->module, fn); } else { TRACE("New text sect from %s: %x %x %x %d %d\n", source_get(msc_dbg->module, coff_files.files[curr_file_idx].compiland->source), aux->Section.Length, aux->Section.NumberOfRelocations, aux->Section.NumberOfLinenumbers, aux->Section.Number, aux->Section.Selection); } if (coff_files.files[curr_file_idx].startaddr > coff_sym->Value) { coff_files.files[curr_file_idx].startaddr = coff_sym->Value; } if (coff_files.files[curr_file_idx].endaddr < coff_sym->Value + aux->Section.Length) { coff_files.files[curr_file_idx].endaddr = coff_sym->Value + aux->Section.Length; } coff_files.files[curr_file_idx].linetab_offset = linetab_indx; coff_files.files[curr_file_idx].linecnt = aux->Section.NumberOfLinenumbers; linetab_indx += aux->Section.NumberOfLinenumbers; i += naux; continue; } if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0 && coff_sym->SectionNumber == 1) { DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress; /* * This is a normal static function when naux == 0. * Just register it. The current file is the correct * one in this instance. */ nampnt = coff_get_name(coff_sym, coff_strtab); TRACE("\tAdding static symbol %s\n", nampnt); /* FIXME: was adding symbol to this_file ??? */ coff_add_symbol(&coff_files.files[curr_file_idx], &symt_new_function(msc_dbg->module, coff_files.files[curr_file_idx].compiland, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, 0 /* FIXME */, NULL /* FIXME */)->symt); continue; } if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL && ISFCN(coff_sym->Type) && coff_sym->SectionNumber > 0) { struct symt_compiland* compiland = NULL; DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress; nampnt = coff_get_name(coff_sym, coff_strtab); TRACE("%d: %s %s\n", i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value), nampnt); TRACE("\tAdding global symbol %s (sect=%s)\n", nampnt, msc_dbg->sectp[coff_sym->SectionNumber - 1].Name); /* * Now we need to figure out which file this guy belongs to. */ for (j = 0; j < coff_files.nfiles; j++) { if (coff_files.files[j].startaddr <= base + coff_sym->Value && coff_files.files[j].endaddr > base + coff_sym->Value) { compiland = coff_files.files[j].compiland; break; } } if (j < coff_files.nfiles) { coff_add_symbol(&coff_files.files[j], &symt_new_function(msc_dbg->module, compiland, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, 0 /* FIXME */, NULL /* FIXME */)->symt); } else { symt_new_function(msc_dbg->module, NULL, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, 0 /* FIXME */, NULL /* FIXME */); } i += naux; continue; } if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL && coff_sym->SectionNumber > 0) { DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress; struct location loc; /* * Similar to above, but for the case of data symbols. * These aren't treated as entrypoints. */ nampnt = coff_get_name(coff_sym, coff_strtab); TRACE("%d: %s %s\n", i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value), nampnt); TRACE("\tAdding global data symbol %s\n", nampnt); /* * Now we need to figure out which file this guy belongs to. */ loc.kind = loc_absolute; loc.reg = 0; loc.offset = msc_dbg->module->module.BaseOfImage + base + coff_sym->Value; symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */, loc, 0 /* FIXME */, NULL /* FIXME */); i += naux; continue; } if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0) { /* * Ignore these. They don't have anything to do with * reality. */ continue; } TRACE("Skipping unknown entry '%s' %d %d %d\n", coff_get_name(coff_sym, coff_strtab), coff_sym->StorageClass, coff_sym->SectionNumber, naux); /* * For now, skip past the aux entries. */ i += naux; } if (coff_files.files != NULL) { /* * 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. */ for (j = 0; j < coff_files.nfiles; j++) { if (coff_files.files[j].entries != NULL) { qsort(coff_files.files[j].entries, coff_files.files[j].neps, sizeof(struct symt*), symt_cmp_addr); } } /* * Now pick apart the line number tables, and attach the entries * to the given functions. */ for (j = 0; j < coff_files.nfiles; j++) { l = 0; if (coff_files.files[j].neps != 0) { for (k = 0; k < coff_files.files[j].linecnt; k++) { linepnt = coff_linetab + coff_files.files[j].linetab_offset + k; /* * If we have spilled onto the next entrypoint, then * bump the counter.. */ for (; l+1 < coff_files.files[j].neps; l++) { if (symt_get_address(coff_files.files[j].entries[l+1], &addr) && msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress < addr) { if (coff_files.files[j].entries[l+1]->tag == SymTagFunction) { /* * Add the line number. This is always relative to the * start of the function, so we need to subtract that offset * first. */ symt_add_func_line(msc_dbg->module, (struct symt_function*)coff_files.files[j].entries[l+1], coff_files.files[j].compiland->source, linepnt->Linenumber, msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress - addr); } break; } } } } } for (j = 0; j < coff_files.nfiles; j++) { HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries); } HeapFree(GetProcessHeap(), 0, coff_files.files); msc_dbg->module->module.SymType = SymCoff; /* FIXME: we could have a finer grain here */ msc_dbg->module->module.LineNumbers = TRUE; msc_dbg->module->module.GlobalSymbols = TRUE; msc_dbg->module->module.TypeInfo = FALSE; msc_dbg->module->module.SourceIndexed = TRUE; msc_dbg->module->module.Publics = TRUE; ret = TRUE; } return ret; }