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