Sync to trunk revision 61757.
[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 "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 i += naux;
281 continue;
282 }
283
284 if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
285 ISFCN(coff_sym->Type) && coff_sym->SectionNumber > 0)
286 {
287 struct symt_compiland* compiland = NULL;
288 DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
289 nampnt = coff_get_name(coff_sym, coff_strtab);
290
291 TRACE("%d: %s %s\n",
292 i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
293 nampnt);
294 TRACE("\tAdding global symbol %s (sect=%s)\n",
295 nampnt, msc_dbg->sectp[coff_sym->SectionNumber - 1].Name);
296
297 /*
298 * Now we need to figure out which file this guy belongs to.
299 */
300 for (j = 0; j < coff_files.nfiles; j++)
301 {
302 if (coff_files.files[j].startaddr <= base + coff_sym->Value
303 && coff_files.files[j].endaddr > base + coff_sym->Value)
304 {
305 compiland = coff_files.files[j].compiland;
306 break;
307 }
308 }
309 if (j < coff_files.nfiles)
310 {
311 coff_add_symbol(&coff_files.files[j],
312 &symt_new_function(msc_dbg->module, compiland, nampnt,
313 msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
314 0 /* FIXME */, NULL /* FIXME */)->symt);
315 }
316 else
317 {
318 symt_new_function(msc_dbg->module, NULL, nampnt,
319 msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
320 0 /* FIXME */, NULL /* FIXME */);
321 }
322 i += naux;
323 continue;
324 }
325
326 if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
327 coff_sym->SectionNumber > 0)
328 {
329 DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
330 struct location loc;
331
332 /*
333 * Similar to above, but for the case of data symbols.
334 * These aren't treated as entrypoints.
335 */
336 nampnt = coff_get_name(coff_sym, coff_strtab);
337
338 TRACE("%d: %s %s\n",
339 i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
340 nampnt);
341 TRACE("\tAdding global data symbol %s\n", nampnt);
342
343 /*
344 * Now we need to figure out which file this guy belongs to.
345 */
346 loc.kind = loc_absolute;
347 loc.reg = 0;
348 loc.offset = msc_dbg->module->module.BaseOfImage + base + coff_sym->Value;
349 symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */,
350 loc, 0 /* FIXME */, NULL /* FIXME */);
351 i += naux;
352 continue;
353 }
354
355 if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0)
356 {
357 /*
358 * Ignore these. They don't have anything to do with
359 * reality.
360 */
361 i += naux;
362 continue;
363 }
364
365 TRACE("Skipping unknown entry '%s' %d %d %d\n",
366 coff_get_name(coff_sym, coff_strtab),
367 coff_sym->StorageClass, coff_sym->SectionNumber, naux);
368
369 /*
370 * For now, skip past the aux entries.
371 */
372 i += naux;
373 }
374
375 if (coff_files.files != NULL)
376 {
377 /*
378 * OK, we now should have a list of files, and we should have a list
379 * of entrypoints. We need to sort the entrypoints so that we are
380 * able to tie the line numbers with the given functions within the
381 * file.
382 */
383 for (j = 0; j < coff_files.nfiles; j++)
384 {
385 if (coff_files.files[j].entries != NULL)
386 {
387 qsort(coff_files.files[j].entries, coff_files.files[j].neps,
388 sizeof(struct symt*), symt_cmp_addr);
389 }
390 }
391
392 /*
393 * Now pick apart the line number tables, and attach the entries
394 * to the given functions.
395 */
396 for (j = 0; j < coff_files.nfiles; j++)
397 {
398 l = 0;
399 if (coff_files.files[j].neps != 0)
400 {
401 for (k = 0; k < coff_files.files[j].linecnt; k++)
402 {
403 linepnt = coff_linetab + coff_files.files[j].linetab_offset + k;
404 /*
405 * If we have spilled onto the next entrypoint, then
406 * bump the counter..
407 */
408 for (; l+1 < coff_files.files[j].neps; l++)
409 {
410 if (symt_get_address(coff_files.files[j].entries[l+1], &addr) &&
411 msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress < addr)
412 {
413 if (coff_files.files[j].entries[l+1]->tag == SymTagFunction)
414 {
415 /*
416 * Add the line number. This is always relative to the
417 * start of the function, so we need to subtract that offset
418 * first.
419 */
420 symt_add_func_line(msc_dbg->module,
421 (struct symt_function*)coff_files.files[j].entries[l+1],
422 coff_files.files[j].compiland->source,
423 linepnt->Linenumber,
424 msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress - addr);
425 }
426 break;
427 }
428 }
429 }
430 }
431 }
432
433 for (j = 0; j < coff_files.nfiles; j++)
434 {
435 HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries);
436 }
437 HeapFree(GetProcessHeap(), 0, coff_files.files);
438 msc_dbg->module->module.SymType = SymCoff;
439 /* FIXME: we could have a finer grain here */
440 msc_dbg->module->module.LineNumbers = TRUE;
441 msc_dbg->module->module.GlobalSymbols = TRUE;
442 msc_dbg->module->module.TypeInfo = FALSE;
443 msc_dbg->module->module.SourceIndexed = TRUE;
444 msc_dbg->module->module.Publics = TRUE;
445 ret = TRUE;
446 }
447
448 return ret;
449 }