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