xref: /reactos/dll/win32/dbghelp/coff.c (revision da5f10af)
1 /*
2  * Read VC++ debug information from COFF and eventually
3  * from PDB files.
4  *
5  * Copyright (C) 1996,      Eric Youngdale.
6  * Copyright (C) 1999-2000, Ulrich Weigand.
7  * Copyright (C) 2004,      Eric Pouech.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /*
25  * Note - this handles reading debug information for 32 bit applications
26  * that run under Windows-NT for example.  I doubt that this would work well
27  * for 16 bit applications, but I don't think it really matters since the
28  * file format is different, and we should never get in here in such cases.
29  *
30  * TODO:
31  *	Get 16 bit CV stuff working.
32  *	Add symbol size to internal symbol table.
33  */
34 
35 #include "config.h"
36 #include "wine/port.h"
37 
38 #include <assert.h>
39 #include <stdlib.h>
40 
41 #include <string.h>
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 
46 #include <stdarg.h>
47 #include "windef.h"
48 #include "winbase.h"
49 #include "winternl.h"
50 
51 #include "wine/exception.h"
52 #include "wine/debug.h"
53 #include "dbghelp_private.h"
54 #include "wine/mscvpdb.h"
55 
56 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_coff);
57 
58 /*========================================================================
59  * Process COFF debug information.
60  */
61 
62 struct CoffFile
63 {
64     unsigned int                startaddr;
65     unsigned int                endaddr;
66     struct symt_compiland*      compiland;
67     int                         linetab_offset;
68     int                         linecnt;
69     struct symt**               entries;
70     int		                neps;
71     int		                neps_alloc;
72 };
73 
74 struct CoffFileSet
75 {
76     struct CoffFile*    files;
77     int		        nfiles;
78     int		        nfiles_alloc;
79 };
80 
81 static const char*	coff_get_name(const IMAGE_SYMBOL* coff_sym,
82                                       const char* coff_strtab)
83 {
84     static	char	namebuff[9];
85     const char*		nampnt;
86 
87     if (coff_sym->N.Name.Short)
88     {
89         memcpy(namebuff, coff_sym->N.ShortName, 8);
90         namebuff[8] = '\0';
91         nampnt = &namebuff[0];
92     }
93     else
94     {
95         nampnt = coff_strtab + coff_sym->N.Name.Long;
96     }
97 
98     if (nampnt[0] == '_') nampnt++;
99     return nampnt;
100 }
101 
102 static int coff_add_file(struct CoffFileSet* coff_files, struct module* module,
103                          const char* filename)
104 {
105     struct CoffFile* file;
106 
107     if (coff_files->nfiles + 1 >= coff_files->nfiles_alloc)
108     {
109         if (coff_files->files)
110         {
111             coff_files->nfiles_alloc *= 2;
112             coff_files->files = HeapReAlloc(GetProcessHeap(), 0, coff_files->files,
113                                             coff_files->nfiles_alloc * sizeof(struct CoffFile));
114         }
115         else
116         {
117             coff_files->nfiles_alloc = 16;
118             coff_files->files = HeapAlloc(GetProcessHeap(), 0,
119                                           coff_files->nfiles_alloc * sizeof(struct CoffFile));
120         }
121     }
122     file = coff_files->files + coff_files->nfiles;
123     file->startaddr = 0xffffffff;
124     file->endaddr   = 0;
125     file->compiland = symt_new_compiland(module, 0,
126                                          source_new(module, NULL, filename));
127     file->linetab_offset = -1;
128     file->linecnt = 0;
129     file->entries = NULL;
130     file->neps = file->neps_alloc = 0;
131 
132     return coff_files->nfiles++;
133 }
134 
135 static void coff_add_symbol(struct CoffFile* coff_file, struct symt* sym)
136 {
137     if (coff_file->neps + 1 >= coff_file->neps_alloc)
138     {
139         if (coff_file->entries)
140         {
141             coff_file->neps_alloc *= 2;
142             coff_file->entries = HeapReAlloc(GetProcessHeap(), 0, coff_file->entries,
143                                              coff_file->neps_alloc * sizeof(struct symt*));
144         }
145         else
146         {
147             coff_file->neps_alloc = 32;
148             coff_file->entries = HeapAlloc(GetProcessHeap(), 0,
149                                            coff_file->neps_alloc * sizeof(struct symt*));
150         }
151     }
152     coff_file->entries[coff_file->neps++] = sym;
153 }
154 
155 DECLSPEC_HIDDEN BOOL coff_process_info(const struct msc_debug_info* msc_dbg)
156 {
157     const IMAGE_AUX_SYMBOL*		aux;
158     const IMAGE_COFF_SYMBOLS_HEADER*	coff;
159     const IMAGE_LINENUMBER*		coff_linetab;
160     const IMAGE_LINENUMBER*		linepnt;
161     const char*                         coff_strtab;
162     const IMAGE_SYMBOL* 		coff_sym;
163     const IMAGE_SYMBOL* 		coff_symbols;
164     struct CoffFileSet	                coff_files;
165     int				        curr_file_idx = -1;
166     unsigned int		        i;
167     int			       	        j;
168     int			       	        k;
169     int			       	        l;
170     int		       		        linetab_indx;
171     const char*                         nampnt;
172     int		       		        naux;
173     BOOL                                ret = FALSE;
174     ULONG64                             addr;
175 
176     TRACE("Processing COFF symbols...\n");
177 
178     assert(sizeof(IMAGE_SYMBOL) == IMAGE_SIZEOF_SYMBOL);
179     assert(sizeof(IMAGE_LINENUMBER) == IMAGE_SIZEOF_LINENUMBER);
180 
181     coff_files.files = NULL;
182     coff_files.nfiles = coff_files.nfiles_alloc = 0;
183 
184     coff = (const IMAGE_COFF_SYMBOLS_HEADER*)msc_dbg->root;
185 
186     coff_symbols = (const IMAGE_SYMBOL*)((const char *)coff + coff->LvaToFirstSymbol);
187     coff_linetab = (const IMAGE_LINENUMBER*)((const char *)coff + coff->LvaToFirstLinenumber);
188     coff_strtab = (const char*)(coff_symbols + coff->NumberOfSymbols);
189 
190     linetab_indx = 0;
191 
192     for (i = 0; i < coff->NumberOfSymbols; i++)
193     {
194         coff_sym = coff_symbols + i;
195         naux = coff_sym->NumberOfAuxSymbols;
196 
197         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_FILE)
198 	{
199             curr_file_idx = coff_add_file(&coff_files, msc_dbg->module,
200                                           (const char*)(coff_sym + 1));
201             TRACE("New file %s\n", (const char*)(coff_sym + 1));
202             i += naux;
203             continue;
204 	}
205 
206         if (curr_file_idx < 0)
207         {
208             assert(coff_files.nfiles == 0 && coff_files.nfiles_alloc == 0);
209             curr_file_idx = coff_add_file(&coff_files, msc_dbg->module, "<none>");
210             TRACE("New file <none>\n");
211         }
212 
213         /*
214          * This guy marks the size and location of the text section
215          * for the current file.  We need to keep track of this so
216          * we can figure out what file the different global functions
217          * go with.
218          */
219         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC &&
220             naux != 0 && coff_sym->Type == 0 && coff_sym->SectionNumber == 1)
221 	{
222             aux = (const IMAGE_AUX_SYMBOL*) (coff_sym + 1);
223 
224             if (coff_files.files[curr_file_idx].linetab_offset != -1)
225 	    {
226                 /*
227                  * Save this so we can still get the old name.
228                  */
229                 const char* fn;
230 
231                 fn = source_get(msc_dbg->module,
232                                 coff_files.files[curr_file_idx].compiland->source);
233 
234                 TRACE("Duplicating sect from %s: %x %x %x %d %d\n",
235                       fn, aux->Section.Length,
236                       aux->Section.NumberOfRelocations,
237                       aux->Section.NumberOfLinenumbers,
238                       aux->Section.Number, aux->Section.Selection);
239                 TRACE("More sect %d %s %08x %d %d %d\n",
240                       coff_sym->SectionNumber,
241                       coff_get_name(coff_sym, coff_strtab),
242                       coff_sym->Value, coff_sym->Type,
243                       coff_sym->StorageClass, coff_sym->NumberOfAuxSymbols);
244 
245                 /*
246                  * Duplicate the file entry.  We have no way to describe
247                  * multiple text sections in our current way of handling things.
248                  */
249                 coff_add_file(&coff_files, msc_dbg->module, fn);
250 	    }
251             else
252 	    {
253                 TRACE("New text sect from %s: %x %x %x %d %d\n",
254                       source_get(msc_dbg->module, coff_files.files[curr_file_idx].compiland->source),
255                       aux->Section.Length,
256                       aux->Section.NumberOfRelocations,
257                       aux->Section.NumberOfLinenumbers,
258                       aux->Section.Number, aux->Section.Selection);
259 	    }
260 
261             if (coff_files.files[curr_file_idx].startaddr > coff_sym->Value)
262 	    {
263                 coff_files.files[curr_file_idx].startaddr = coff_sym->Value;
264 	    }
265 
266             if (coff_files.files[curr_file_idx].endaddr < coff_sym->Value + aux->Section.Length)
267 	    {
268                 coff_files.files[curr_file_idx].endaddr = coff_sym->Value + aux->Section.Length;
269 	    }
270 
271             coff_files.files[curr_file_idx].linetab_offset = linetab_indx;
272             coff_files.files[curr_file_idx].linecnt = aux->Section.NumberOfLinenumbers;
273             linetab_indx += aux->Section.NumberOfLinenumbers;
274             i += naux;
275             continue;
276 	}
277 
278         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0 &&
279             coff_sym->SectionNumber == 1)
280 	{
281             DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
282             /*
283              * This is a normal static function when naux == 0.
284              * Just register it.  The current file is the correct
285              * one in this instance.
286              */
287             nampnt = coff_get_name(coff_sym, coff_strtab);
288 
289             TRACE("\tAdding static symbol %s\n", nampnt);
290 
291             /* FIXME: was adding symbol to this_file ??? */
292             coff_add_symbol(&coff_files.files[curr_file_idx],
293                             &symt_new_function(msc_dbg->module,
294                                                coff_files.files[curr_file_idx].compiland,
295                                                nampnt,
296                                                msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
297                                                0 /* FIXME */,
298                                                NULL /* FIXME */)->symt);
299             continue;
300 	}
301 
302         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
303             ISFCN(coff_sym->Type) && coff_sym->SectionNumber > 0)
304 	{
305             struct symt_compiland* compiland = NULL;
306             DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
307             nampnt = coff_get_name(coff_sym, coff_strtab);
308 
309             TRACE("%d: %s %s\n",
310                   i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
311                   nampnt);
312             TRACE("\tAdding global symbol %s (sect=%s)\n",
313                   nampnt, msc_dbg->sectp[coff_sym->SectionNumber - 1].Name);
314 
315             /*
316              * Now we need to figure out which file this guy belongs to.
317              */
318             for (j = 0; j < coff_files.nfiles; j++)
319 	    {
320                 if (coff_files.files[j].startaddr <= base + coff_sym->Value
321                      && coff_files.files[j].endaddr > base + coff_sym->Value)
322 		{
323                     compiland = coff_files.files[j].compiland;
324                     break;
325 		}
326 	    }
327             if (j < coff_files.nfiles)
328             {
329                 coff_add_symbol(&coff_files.files[j],
330                                 &symt_new_function(msc_dbg->module, compiland, nampnt,
331                                                    msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
332                                                    0 /* FIXME */, NULL /* FIXME */)->symt);
333             }
334             else
335             {
336                 symt_new_function(msc_dbg->module, NULL, nampnt,
337                                   msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
338                                   0 /* FIXME */, NULL /* FIXME */);
339             }
340             i += naux;
341             continue;
342 	}
343 
344         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
345             coff_sym->SectionNumber > 0)
346 	{
347             DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
348             struct location loc;
349 
350             /*
351              * Similar to above, but for the case of data symbols.
352              * These aren't treated as entrypoints.
353              */
354             nampnt = coff_get_name(coff_sym, coff_strtab);
355 
356             TRACE("%d: %s %s\n",
357                   i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
358                   nampnt);
359             TRACE("\tAdding global data symbol %s\n", nampnt);
360 
361             /*
362              * Now we need to figure out which file this guy belongs to.
363              */
364             loc.kind = loc_absolute;
365             loc.reg = 0;
366             loc.offset = msc_dbg->module->module.BaseOfImage + base + coff_sym->Value;
367             symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */,
368                                      loc, 0 /* FIXME */, NULL /* FIXME */);
369             i += naux;
370             continue;
371 	}
372 
373         if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0)
374 	{
375             /*
376              * Ignore these.  They don't have anything to do with
377              * reality.
378              */
379             continue;
380 	}
381 
382         TRACE("Skipping unknown entry '%s' %d %d %d\n",
383               coff_get_name(coff_sym, coff_strtab),
384               coff_sym->StorageClass, coff_sym->SectionNumber, naux);
385 
386         /*
387          * For now, skip past the aux entries.
388          */
389         i += naux;
390     }
391 
392     if (coff_files.files != NULL)
393     {
394         /*
395          * OK, we now should have a list of files, and we should have a list
396          * of entrypoints.  We need to sort the entrypoints so that we are
397          * able to tie the line numbers with the given functions within the
398          * file.
399          */
400         for (j = 0; j < coff_files.nfiles; j++)
401         {
402             if (coff_files.files[j].entries != NULL)
403             {
404                 qsort(coff_files.files[j].entries, coff_files.files[j].neps,
405                       sizeof(struct symt*), symt_cmp_addr);
406             }
407         }
408 
409         /*
410          * Now pick apart the line number tables, and attach the entries
411          * to the given functions.
412          */
413         for (j = 0; j < coff_files.nfiles; j++)
414         {
415             l = 0;
416             if (coff_files.files[j].neps != 0)
417             {
418                 for (k = 0; k < coff_files.files[j].linecnt; k++)
419                 {
420                     linepnt = coff_linetab + coff_files.files[j].linetab_offset + k;
421                     /*
422                      * If we have spilled onto the next entrypoint, then
423                      * bump the counter..
424                      */
425                     for (; l+1 < coff_files.files[j].neps; l++)
426                     {
427                         if (symt_get_address(coff_files.files[j].entries[l+1], &addr) &&
428                             msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress < addr)
429                         {
430                             if (coff_files.files[j].entries[l+1]->tag == SymTagFunction)
431                             {
432                                 /*
433                                  * Add the line number.  This is always relative to the
434                                  * start of the function, so we need to subtract that offset
435                                  * first.
436                                  */
437                                 symt_add_func_line(msc_dbg->module,
438                                                    (struct symt_function*)coff_files.files[j].entries[l+1],
439                                                    coff_files.files[j].compiland->source,
440                                                    linepnt->Linenumber,
441                                                    msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress - addr);
442                             }
443                             break;
444                         }
445                     }
446                 }
447             }
448         }
449 
450         for (j = 0; j < coff_files.nfiles; j++)
451 	{
452             HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries);
453 	}
454         HeapFree(GetProcessHeap(), 0, coff_files.files);
455         msc_dbg->module->module.SymType = SymCoff;
456         /* FIXME: we could have a finer grain here */
457         msc_dbg->module->module.LineNumbers = TRUE;
458         msc_dbg->module->module.GlobalSymbols = TRUE;
459         msc_dbg->module->module.TypeInfo = FALSE;
460         msc_dbg->module->module.SourceIndexed = TRUE;
461         msc_dbg->module->module.Publics = TRUE;
462         ret = TRUE;
463     }
464 
465     return ret;
466 }
467