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