Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / dll / win32 / dbghelp / coff.c
diff --git a/dll/win32/dbghelp/coff.c b/dll/win32/dbghelp/coff.c
new file mode 100644 (file)
index 0000000..a9d983e
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * 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 "dbghelp_private.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;
+}