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