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