| /* |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 |
| #ifndef PATH_MAX |
| #define PATH_MAX MAX_PATH |
| #endif |
| #include <stdarg.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "winternl.h" |
| |
| #include "wine/exception.h" |
| #include "wine/debug.h" |
| #include "excpt.h" |
| #include "dbghelp_private.h" |
| #include "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) |
| { |
| coff_files->nfiles_alloc += 10; |
| coff_files->files = (coff_files->files) ? |
| HeapReAlloc(GetProcessHeap(), 0, coff_files->files, |
| coff_files->nfiles_alloc * sizeof(struct CoffFile)) : |
| 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, 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) |
| { |
| coff_file->neps_alloc += 10; |
| coff_file->entries = (coff_file->entries) ? |
| HeapReAlloc(GetProcessHeap(), 0, coff_file->entries, |
| coff_file->neps_alloc * sizeof(struct symt*)) : |
| HeapAlloc(GetProcessHeap(), 0, |
| coff_file->neps_alloc * sizeof(struct symt*)); |
| } |
| coff_file->entries[coff_file->neps++] = sym; |
| } |
| |
| 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; |
| DWORD 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: %lx %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 %08lx %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: %lx %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); |
| i += naux; |
| 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: %lx %s\n", |
| i, 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; |
| /* |
| * 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: %lx %s\n", |
| i, 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. |
| */ |
| symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */, |
| msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, |
| 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. |
| */ |
| i += naux; |
| 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 (;;) |
| { |
| if (l+1 >= coff_files.files[j].neps) break; |
| symt_get_info(coff_files.files[j].entries[l+1], TI_GET_ADDRESS, &addr); |
| if (((msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress) < addr)) |
| break; |
| l++; |
| } |
| |
| 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_get_info(coff_files.files[j].entries[l+1], TI_GET_ADDRESS, &addr); |
| 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); |
| } |
| } |
| } |
| } |
| |
| 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; |
| ret = TRUE; |
| } |
| |
| return ret; |
| } |