xref: /reactos/sdk/tools/rsym/rsym.c (revision 8a978a17)
1 /*
2  * Usage: rsym input-file output-file
3  *
4  * There are two sources of information: the .stab/.stabstr
5  * sections of the executable and the COFF symbol table. Most
6  * of the information is in the .stab/.stabstr sections.
7  * However, most of our asm files don't contain .stab directives,
8  * so routines implemented in assembler won't show up in the
9  * .stab section. They are present in the COFF symbol table.
10  * So, we mostly use the .stab/.stabstr sections, but we augment
11  * the info there with info from the COFF symbol table when
12  * possible.
13  *
14  * This is a tool and is compiled using the host compiler,
15  * i.e. on Linux gcc and not mingw-gcc (cross-compiler).
16  * Therefore we can't include SDK headers and we have to
17  * duplicate some definitions here.
18  * Also note that the internal functions are "old C-style",
19  * returning an int, where a return of 0 means success and
20  * non-zero is failure.
21  */
22 
23 #include "../../dll/win32/dbghelp/compat.h"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <wchar.h>
30 
31 #include "rsym.h"
32 
33 #define MAX_PATH 260
34 #define MAX_SYM_NAME 2000
35 
36 struct StringEntry
37 {
38     struct StringEntry *Next;
39     ULONG Offset;
40     char *String;
41 };
42 
43 struct StringHashTable
44 {
45     ULONG TableSize;
46     struct StringEntry **Table;
47 };
48 
49 /* This is the famous DJB hash */
50 static unsigned int
51 ComputeDJBHash(const char *name)
52 {
53     unsigned int val = 5381;
54     int i = 0;
55 
56     for (i = 0; name[i]; i++)
57     {
58         val = (33 * val) + name[i];
59     }
60 
61     return val;
62 }
63 
64 static void
65 AddStringToHash(struct StringHashTable *StringTable,
66                 unsigned int hash,
67                 ULONG Offset,
68                 char *StringPtr)
69 {
70     struct StringEntry *entry = calloc(1, sizeof(struct StringEntry));
71     entry->Offset = Offset;
72     entry->String = StringPtr;
73     entry->Next = StringTable->Table[hash];
74     StringTable->Table[hash] = entry;
75 }
76 
77 static void
78 StringHashTableInit(struct StringHashTable *StringTable,
79                     ULONG StringsLength,
80                     char *StringsBase)
81 {
82     char *Start = StringsBase;
83     char *End = StringsBase + StringsLength;
84     StringTable->TableSize = 1024;
85     StringTable->Table = calloc(1024, sizeof(struct StringEntry *));
86     while (Start < End)
87     {
88         AddStringToHash(StringTable,
89                         ComputeDJBHash(Start) % StringTable->TableSize,
90                         Start - StringsBase,
91                         Start);
92         Start += strlen(Start) + 1;
93     }
94 }
95 
96 static void
97 StringHashTableFree(struct StringHashTable *StringTable)
98 {
99     int i;
100     struct StringEntry *entry;
101     for (i = 0; i < StringTable->TableSize; i++)
102     {
103         while ((entry = StringTable->Table[i]))
104         {
105             entry = entry->Next;
106             free(StringTable->Table[i]);
107             StringTable->Table[i] = entry;
108         }
109     }
110     free(StringTable->Table);
111 }
112 
113 static int
114 CompareSymEntry(const PROSSYM_ENTRY SymEntry1, const PROSSYM_ENTRY SymEntry2)
115 {
116     if (SymEntry1->Address < SymEntry2->Address)
117     {
118         return -1;
119     }
120 
121     if (SymEntry2->Address < SymEntry1->Address)
122     {
123         return +1;
124     }
125 
126     if (SymEntry2->SourceLine == 0)
127     {
128         return -1;
129     }
130 
131     if (SymEntry1->SourceLine == 0)
132     {
133         return +1;
134     }
135 
136     return 0;
137 }
138 
139 static int
140 GetStabInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader,
141             PIMAGE_SECTION_HEADER PESectionHeaders,
142             ULONG *StabSymbolsLength, void **StabSymbolsBase,
143             ULONG *StabStringsLength, void **StabStringsBase)
144 {
145     ULONG Idx;
146 
147     /* Load .stab and .stabstr sections if available */
148     *StabSymbolsBase = NULL;
149     *StabSymbolsLength = 0;
150     *StabStringsBase = NULL;
151     *StabStringsLength = 0;
152 
153     for (Idx = 0; Idx < PEFileHeader->NumberOfSections; Idx++)
154     {
155         /* printf("section: '%.08s'\n", PESectionHeaders[Idx].Name); */
156         if ((strncmp((char *) PESectionHeaders[Idx].Name, ".stab", 5) == 0)
157             && (PESectionHeaders[Idx].Name[5] == 0))
158         {
159             /* printf(".stab section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */
160 
161             *StabSymbolsLength = PESectionHeaders[Idx].SizeOfRawData;
162             *StabSymbolsBase = (void *)((char *) FileData + PESectionHeaders[Idx].PointerToRawData);
163         }
164 
165         if (strncmp((char *) PESectionHeaders[Idx].Name, ".stabstr", 8) == 0)
166         {
167             /* printf(".stabstr section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */
168 
169             *StabStringsLength = PESectionHeaders[Idx].SizeOfRawData;
170             *StabStringsBase = (void *)((char *) FileData + PESectionHeaders[Idx].PointerToRawData);
171         }
172     }
173 
174     return 0;
175 }
176 
177 static int
178 GetCoffInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader,
179             PIMAGE_SECTION_HEADER PESectionHeaders,
180             ULONG *CoffSymbolsLength, void **CoffSymbolsBase,
181             ULONG *CoffStringsLength, void **CoffStringsBase)
182 {
183 
184     if (PEFileHeader->PointerToSymbolTable == 0 || PEFileHeader->NumberOfSymbols == 0)
185     {
186         /* No COFF symbol table */
187         *CoffSymbolsLength = 0;
188         *CoffStringsLength = 0;
189     }
190     else
191     {
192         *CoffSymbolsLength = PEFileHeader->NumberOfSymbols * sizeof(COFF_SYMENT);
193         *CoffSymbolsBase = (void *)((char *) FileData + PEFileHeader->PointerToSymbolTable);
194         *CoffStringsLength = *((ULONG *) ((char *) *CoffSymbolsBase + *CoffSymbolsLength));
195         *CoffStringsBase = (void *)((char *) *CoffSymbolsBase + *CoffSymbolsLength);
196     }
197 
198     return 0;
199 }
200 
201 static ULONG
202 FindOrAddString(struct StringHashTable *StringTable,
203                 char *StringToFind,
204                 ULONG *StringsLength,
205                 void *StringsBase)
206 {
207     unsigned int hash = ComputeDJBHash(StringToFind) % StringTable->TableSize;
208     struct StringEntry *entry = StringTable->Table[hash];
209 
210     while (entry && strcmp(entry->String, StringToFind))
211         entry = entry->Next;
212 
213     if (entry)
214     {
215         return entry->Offset;
216     }
217     else
218     {
219         char *End = (char *)StringsBase + *StringsLength;
220 
221         strcpy(End, StringToFind);
222         *StringsLength += strlen(StringToFind) + 1;
223 
224         AddStringToHash(StringTable, hash, End - (char *)StringsBase, End);
225 
226         return End - (char *)StringsBase;
227     }
228 }
229 
230 static int
231 ConvertStabs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase,
232              ULONG *StringsLength, void *StringsBase,
233              ULONG StabSymbolsLength, void *StabSymbolsBase,
234              ULONG StabStringsLength, void *StabStringsBase,
235              ULONG_PTR ImageBase, PIMAGE_FILE_HEADER PEFileHeader,
236              PIMAGE_SECTION_HEADER PESectionHeaders)
237 {
238     PSTAB_ENTRY StabEntry;
239     ULONG Count, i;
240     ULONG_PTR Address, LastFunctionAddress;
241     int First = 1;
242     char *Name;
243     ULONG NameLen;
244     char FuncName[256];
245     PROSSYM_ENTRY Current;
246     struct StringHashTable StringHash;
247 
248     StabEntry = StabSymbolsBase;
249     Count = StabSymbolsLength / sizeof(STAB_ENTRY);
250     *SymbolsCount = 0;
251 
252     if (Count == 0)
253     {
254         /* No symbol info */
255         *SymbolsBase = NULL;
256         return 0;
257     }
258 
259     *SymbolsBase = malloc(Count * sizeof(ROSSYM_ENTRY));
260     if (*SymbolsBase == NULL)
261     {
262         fprintf(stderr, "Failed to allocate memory for converted .stab symbols\n");
263         return 1;
264     }
265     Current = *SymbolsBase;
266     memset(Current, 0, sizeof(*Current));
267 
268     StringHashTableInit(&StringHash, *StringsLength, (char *)StringsBase);
269 
270     LastFunctionAddress = 0;
271     for (i = 0; i < Count; i++)
272     {
273         if (LastFunctionAddress == 0)
274         {
275             Address = StabEntry[i].n_value - ImageBase;
276         }
277         else
278         {
279             Address = LastFunctionAddress + StabEntry[i].n_value;
280         }
281         switch (StabEntry[i].n_type)
282         {
283             case N_SO:
284             case N_SOL:
285             case N_BINCL:
286                 Name = (char *) StabStringsBase + StabEntry[i].n_strx;
287                 if (StabStringsLength < StabEntry[i].n_strx
288                     || *Name == '\0' || Name[strlen(Name) - 1] == '/'
289                     || Name[strlen(Name) - 1] == '\\'
290                     || StabEntry[i].n_value < ImageBase)
291                 {
292                     continue;
293                 }
294                 if (First || Address != Current->Address)
295                 {
296                     if (!First)
297                     {
298                         memset(++Current, 0, sizeof(*Current));
299                         Current->FunctionOffset = Current[-1].FunctionOffset;
300                     }
301                     else
302                         First = 0;
303                     Current->Address = Address;
304                 }
305                 Current->FileOffset = FindOrAddString(&StringHash,
306                                                       (char *)StabStringsBase + StabEntry[i].n_strx,
307                                                       StringsLength,
308                                                       StringsBase);
309                 break;
310             case N_FUN:
311                 if (StabEntry[i].n_desc == 0 || StabEntry[i].n_value < ImageBase)
312                 {
313                     LastFunctionAddress = 0; /* line # 0 = end of function */
314                     continue;
315                 }
316                 if (First || Address != Current->Address)
317                 {
318                     if (!First)
319                         memset(++Current, 0, sizeof(*Current));
320                     else
321                         First = 0;
322                     Current->Address = Address;
323                     Current->FileOffset = Current[-1].FileOffset;
324                 }
325                 Name = (char *)StabStringsBase + StabEntry[i].n_strx;
326                 NameLen = strcspn(Name, ":");
327                 if (sizeof(FuncName) <= NameLen)
328                 {
329                     free(*SymbolsBase);
330                     fprintf(stderr, "Function name too long\n");
331                     return 1;
332                 }
333                 memcpy(FuncName, Name, NameLen);
334                 FuncName[NameLen] = '\0';
335                 Current->FunctionOffset = FindOrAddString(&StringHash,
336                                                           FuncName,
337                                                           StringsLength,
338                                                           StringsBase);
339                 Current->SourceLine = 0;
340                 LastFunctionAddress = Address;
341                 break;
342             case N_SLINE:
343                 if (First || Address != Current->Address)
344                 {
345                     if (!First)
346                     {
347                         memset(++Current, 0, sizeof(*Current));
348                         Current->FileOffset = Current[-1].FileOffset;
349                         Current->FunctionOffset = Current[-1].FunctionOffset;
350                     }
351                     else
352                         First = 0;
353                     Current->Address = Address;
354                 }
355                 Current->SourceLine = StabEntry[i].n_desc;
356                 break;
357             default:
358                 continue;
359         }
360     }
361     *SymbolsCount = (Current - *SymbolsBase + 1);
362 
363     qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry);
364 
365     StringHashTableFree(&StringHash);
366 
367     return 0;
368 }
369 
370 static int
371 ConvertCoffs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase,
372              ULONG *StringsLength, void *StringsBase,
373              ULONG CoffSymbolsLength, void *CoffSymbolsBase,
374              ULONG CoffStringsLength, void *CoffStringsBase,
375              ULONG_PTR ImageBase, PIMAGE_FILE_HEADER PEFileHeader,
376              PIMAGE_SECTION_HEADER PESectionHeaders)
377 {
378     ULONG Count, i;
379     PCOFF_SYMENT CoffEntry;
380     char FuncName[256], FileName[1024];
381     char *p;
382     PROSSYM_ENTRY Current;
383     struct StringHashTable StringHash;
384 
385     CoffEntry = (PCOFF_SYMENT) CoffSymbolsBase;
386     Count = CoffSymbolsLength / sizeof(COFF_SYMENT);
387 
388     *SymbolsBase = malloc(Count * sizeof(ROSSYM_ENTRY));
389     if (*SymbolsBase == NULL)
390     {
391         fprintf(stderr, "Unable to allocate memory for converted COFF symbols\n");
392         return 1;
393     }
394     *SymbolsCount = 0;
395     Current = *SymbolsBase;
396 
397     StringHashTableInit(&StringHash, *StringsLength, (char*)StringsBase);
398 
399     for (i = 0; i < Count; i++)
400     {
401         if (ISFCN(CoffEntry[i].e_type) || C_EXT == CoffEntry[i].e_sclass)
402         {
403             Current->Address = CoffEntry[i].e_value;
404             if (CoffEntry[i].e_scnum > 0)
405             {
406                 if (PEFileHeader->NumberOfSections < CoffEntry[i].e_scnum)
407                 {
408                     free(*SymbolsBase);
409                     fprintf(stderr,
410                             "Invalid section number %d in COFF symbols (only %d sections present)\n",
411                             CoffEntry[i].e_scnum,
412                             PEFileHeader->NumberOfSections);
413                     return 1;
414                 }
415                 Current->Address += PESectionHeaders[CoffEntry[i].e_scnum - 1].VirtualAddress;
416             }
417             Current->FileOffset = 0;
418             if (CoffEntry[i].e.e.e_zeroes == 0)
419             {
420                 if (sizeof(FuncName) <= strlen((char *) CoffStringsBase + CoffEntry[i].e.e.e_offset))
421                 {
422                     free(*SymbolsBase);
423                     fprintf(stderr, "Function name too long\n");
424                     StringHashTableFree(&StringHash);
425                     return 1;
426                 }
427                 strcpy(FuncName, (char *) CoffStringsBase + CoffEntry[i].e.e.e_offset);
428             }
429             else
430             {
431                 memcpy(FuncName, CoffEntry[i].e.e_name, E_SYMNMLEN);
432                 FuncName[E_SYMNMLEN] = '\0';
433             }
434 
435             /* Name demangling: stdcall */
436             p = strrchr(FuncName, '@');
437             if (p != NULL)
438             {
439                 *p = '\0';
440             }
441             p = ('_' == FuncName[0] || '@' == FuncName[0] ? FuncName + 1 : FuncName);
442             Current->FunctionOffset = FindOrAddString(&StringHash,
443                                                       p,
444                                                       StringsLength,
445                                                       StringsBase);
446             Current->SourceLine = 0;
447             memset(++Current, 0, sizeof(*Current));
448         }
449 
450         i += CoffEntry[i].e_numaux;
451     }
452 
453     *SymbolsCount = (Current - *SymbolsBase + 1);
454     qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry);
455 
456     StringHashTableFree(&StringHash);
457 
458     return 0;
459 }
460 
461 struct DbgHelpLineEntry {
462   ULONG vma;
463   ULONG fileId;
464   ULONG functionId;
465   ULONG line;
466 };
467 
468 struct DbgHelpStringTab {
469   ULONG Length;
470   ULONG Bytes;
471   char ***Table;
472   ULONG LineEntries, CurLineEntries;
473   struct DbgHelpLineEntry *LineEntryData;
474   void *process;
475   DWORD module_base;
476   char *PathChop;
477   char *SourcePath;
478   struct DbgHelpLineEntry *lastLineEntry;
479 };
480 
481 static struct DbgHelpLineEntry*
482 DbgHelpAddLineEntry(struct DbgHelpStringTab *tab)
483 {
484     if (tab->CurLineEntries == tab->LineEntries)
485     {
486         struct DbgHelpLineEntry *newEntries = realloc(tab->LineEntryData,
487                                                       tab->LineEntries * 2 * sizeof(struct DbgHelpLineEntry));
488 
489         if (!newEntries)
490             return 0;
491 
492         tab->LineEntryData = newEntries;
493 
494         memset(tab->LineEntryData + tab->LineEntries, 0, sizeof(struct DbgHelpLineEntry) * tab->LineEntries);
495         tab->LineEntries *= 2;
496     }
497 
498     return &tab->LineEntryData[tab->CurLineEntries++];
499 }
500 
501 static int
502 DbgHelpAddStringToTable(struct DbgHelpStringTab *tab, char *name)
503 {
504     unsigned int bucket = ComputeDJBHash(name) % tab->Length;
505     char **tabEnt = tab->Table[bucket];
506     int i;
507     char **newBucket;
508 
509     if (tabEnt)
510     {
511         for (i = 0; tabEnt[i] && strcmp(tabEnt[i], name); i++);
512         if (tabEnt[i])
513         {
514             free(name);
515             return (i << 10) | bucket;
516         }
517     }
518     else
519         i = 0;
520 
521     /* At this point, we need to insert */
522     tab->Bytes += strlen(name) + 1;
523 
524     newBucket = realloc(tab->Table[bucket], (i+2) * sizeof(char *));
525 
526     if (!newBucket)
527     {
528         fprintf(stderr, "realloc failed!\n");
529         return -1;
530     }
531 
532     tab->Table[bucket] = newBucket;
533     tab->Table[bucket][i+1] = 0;
534     tab->Table[bucket][i] = name;
535     return (i << 10) | bucket;
536 }
537 
538 const char*
539 DbgHelpGetString(struct DbgHelpStringTab *tab, int id)
540 {
541     int i = id >> 10;
542     int bucket = id & 0x3ff;
543     return tab->Table[bucket][i];
544 }
545 
546 /* Remove a prefix of PathChop if it exists and return a copy of the tail. */
547 static char *
548 StrDupShortenPath(char *PathChop, char *FilePath)
549 {
550     int pclen = strlen(PathChop);
551     if (!strncmp(FilePath, PathChop, pclen))
552     {
553         return strdup(FilePath+pclen);
554     }
555     else
556     {
557         return strdup(FilePath);
558     }
559 }
560 
561 static BOOL
562 DbgHelpAddLineNumber(PSRCCODEINFO LineInfo, void *UserContext)
563 {
564     struct DbgHelpStringTab *tab = (struct DbgHelpStringTab *)UserContext;
565     DWORD64 disp;
566     int fileId, functionId;
567     PSYMBOL_INFO pSymbol = malloc(FIELD_OFFSET(SYMBOL_INFO, Name[MAX_SYM_NAME]));
568     if (!pSymbol) return FALSE;
569     memset(pSymbol, 0, FIELD_OFFSET(SYMBOL_INFO, Name[MAX_SYM_NAME]));
570 
571     /* If any file can be opened by relative path up to a certain level, then
572        record that path. */
573     if (!tab->PathChop)
574     {
575         int i, endLen;
576         char *end = strrchr(LineInfo->FileName, '/');
577 
578         if (!end)
579             end = strrchr(LineInfo->FileName, '\\');
580 
581         if (end)
582         {
583             for (i = (end - LineInfo->FileName) - 1; i >= 0; i--)
584             {
585                 if (LineInfo->FileName[i] == '/' || LineInfo->FileName[i] == '\\')
586                 {
587                     char *synthname = malloc(strlen(tab->SourcePath) +
588                                              strlen(LineInfo->FileName + i + 1)
589                                              + 2);
590                     strcpy(synthname, tab->SourcePath);
591                     strcat(synthname, "/");
592                     strcat(synthname, LineInfo->FileName + i + 1);
593                     FILE *f = fopen(synthname, "r");
594                     free(synthname);
595                     if (f)
596                     {
597                         fclose(f);
598                         break;
599                     }
600                 }
601             }
602 
603             i++; /* Be in the string or past the next slash */
604             tab->PathChop = malloc(i + 1);
605             memcpy(tab->PathChop, LineInfo->FileName, i);
606             tab->PathChop[i] = 0;
607         }
608     }
609 
610     fileId = DbgHelpAddStringToTable(tab,
611                                      StrDupShortenPath(tab->PathChop,
612                                                        LineInfo->FileName));
613 
614     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
615     pSymbol->MaxNameLen = MAX_SYM_NAME;
616 
617     if (!SymFromAddr(tab->process, LineInfo->Address, &disp, pSymbol))
618     {
619         //fprintf(stderr, "SymFromAddr failed.\n");
620         free(pSymbol);
621         return FALSE;
622     }
623 
624     functionId = DbgHelpAddStringToTable(tab, strdup(pSymbol->Name));
625 
626     if (LineInfo->Address == 0)
627         fprintf(stderr, "Address is 0.\n");
628 
629     tab->lastLineEntry = DbgHelpAddLineEntry(tab);
630     tab->lastLineEntry->vma = LineInfo->Address - LineInfo->ModBase;
631     tab->lastLineEntry->functionId = functionId;
632     tab->lastLineEntry->fileId = fileId;
633     tab->lastLineEntry->line = LineInfo->LineNumber;
634 
635     free(pSymbol);
636     return TRUE;
637 }
638 
639 static int
640 ConvertDbgHelp(void *process, DWORD module_base, char *SourcePath,
641                ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase,
642                ULONG *StringsLength, void **StringsBase)
643 {
644     char *strings, *strings_copy;
645     int i, j, bucket, entry;
646     PROSSYM_ENTRY rossym;
647     struct DbgHelpStringTab strtab = { 0 };
648 
649     strtab.process = process;
650     strtab.module_base = module_base;
651     strtab.Bytes = 1;
652     strtab.Length = 1024;
653     strtab.Table = calloc(1024, sizeof(const char **));
654     strtab.Table[0] = calloc(2, sizeof(const char *));
655     strtab.Table[0][0] = strdup(""); // The zero string
656     strtab.CurLineEntries = 0;
657     strtab.LineEntries = 16384;
658     strtab.LineEntryData = calloc(strtab.LineEntries, sizeof(struct DbgHelpLineEntry));
659     strtab.PathChop = NULL;
660     strtab.SourcePath = SourcePath ? SourcePath : "";
661 
662     SymEnumLines(process, module_base, NULL, NULL, DbgHelpAddLineNumber, &strtab);
663 
664     /* Transcribe necessary strings */
665     *StringsLength = strtab.Bytes;
666     strings = strings_copy = ((char *)(*StringsBase = malloc(strtab.Bytes)));
667 
668     /* Copy in strings */
669     for (i = 0; i < strtab.Length; i++)
670     {
671         for (j = 0; strtab.Table[i] && strtab.Table[i][j]; j++)
672         {
673             /* Each entry is replaced by its corresponding entry in our string
674                section. We can substract the strings origin to get an offset. */
675             char *toFree = strtab.Table[i][j];
676             strtab.Table[i][j] = strcpy(strings_copy, strtab.Table[i][j]);
677             free(toFree);
678             strings_copy += strlen(strings_copy) + 1;
679         }
680     }
681 
682     assert(strings_copy == strings + strtab.Bytes);
683 
684     *SymbolsBase = calloc(strtab.CurLineEntries, sizeof(ROSSYM_ENTRY));
685     *SymbolsCount = strtab.CurLineEntries;
686 
687     /* Copy symbols into rossym entries */
688     for (i = 0; i < strtab.CurLineEntries; i++)
689     {
690         rossym = &(*SymbolsBase)[i];
691         rossym->Address = strtab.LineEntryData[i].vma;
692         bucket = strtab.LineEntryData[i].fileId & 0x3ff;
693         entry = strtab.LineEntryData[i].fileId >> 10;
694         rossym->FileOffset = strtab.Table[bucket][entry] - strings;
695         bucket = strtab.LineEntryData[i].functionId & 0x3ff;
696         entry = strtab.LineEntryData[i].functionId >> 10;
697         rossym->FunctionOffset = strtab.Table[bucket][entry] - strings;
698         rossym->SourceLine = strtab.LineEntryData[i].line;
699     }
700 
701     /* Free stringtab */
702     for (i = 0; i < strtab.Length; i++)
703     {
704         free(strtab.Table[i]);
705     }
706 
707     free(strtab.LineEntryData);
708     free(strtab.PathChop);
709 
710     qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *))CompareSymEntry);
711 
712     return 0;
713 }
714 
715 static int
716 MergeStabsAndCoffs(ULONG *MergedSymbolCount, PROSSYM_ENTRY *MergedSymbols,
717                    ULONG StabSymbolsCount, PROSSYM_ENTRY StabSymbols,
718                    ULONG CoffSymbolsCount, PROSSYM_ENTRY CoffSymbols)
719 {
720     ULONG StabIndex, j;
721     ULONG CoffIndex;
722     ULONG_PTR StabFunctionStartAddress;
723     ULONG StabFunctionStringOffset, NewStabFunctionStringOffset, CoffFunctionStringOffset;
724     PROSSYM_ENTRY CoffFunctionSymbol;
725 
726     *MergedSymbolCount = 0;
727     if (StabSymbolsCount == 0)
728     {
729         *MergedSymbols = NULL;
730         return 0;
731     }
732     *MergedSymbols = malloc((StabSymbolsCount + CoffSymbolsCount) * sizeof(ROSSYM_ENTRY));
733     if (*MergedSymbols == NULL)
734     {
735         fprintf(stderr, "Unable to allocate memory for merged symbols\n");
736         return 1;
737     }
738 
739     StabFunctionStartAddress = 0;
740     StabFunctionStringOffset = 0;
741     CoffFunctionStringOffset = 0;
742     CoffFunctionSymbol = NULL;
743     CoffIndex = 0;
744     for (StabIndex = 0; StabIndex < StabSymbolsCount; StabIndex++)
745     {
746         (*MergedSymbols)[*MergedSymbolCount] = StabSymbols[StabIndex];
747         for (j = StabIndex + 1;
748              j < StabSymbolsCount && StabSymbols[j].Address == StabSymbols[StabIndex].Address;
749              j++)
750         {
751             if (StabSymbols[j].FileOffset != 0 && (*MergedSymbols)[*MergedSymbolCount].FileOffset == 0)
752             {
753                 (*MergedSymbols)[*MergedSymbolCount].FileOffset = StabSymbols[j].FileOffset;
754             }
755             if (StabSymbols[j].FunctionOffset != 0 && (*MergedSymbols)[*MergedSymbolCount].FunctionOffset == 0)
756             {
757                 (*MergedSymbols)[*MergedSymbolCount].FunctionOffset = StabSymbols[j].FunctionOffset;
758             }
759             if (StabSymbols[j].SourceLine != 0 && (*MergedSymbols)[*MergedSymbolCount].SourceLine == 0)
760             {
761                 (*MergedSymbols)[*MergedSymbolCount].SourceLine = StabSymbols[j].SourceLine;
762             }
763         }
764         StabIndex = j - 1;
765 
766         while (CoffIndex < CoffSymbolsCount &&
767                CoffSymbols[CoffIndex].Address <= (*MergedSymbols)[*MergedSymbolCount].Address)
768         {
769             if (CoffSymbols[CoffIndex].FunctionOffset != 0)
770             {
771                 CoffFunctionSymbol = &CoffSymbols[CoffIndex];
772                 CoffFunctionStringOffset = CoffFunctionSymbol->FunctionOffset;
773             }
774             CoffIndex++;
775         }
776         NewStabFunctionStringOffset = (*MergedSymbols)[*MergedSymbolCount].FunctionOffset;
777         if (CoffFunctionSymbol &&
778             CoffFunctionSymbol->Address <= (*MergedSymbols)[*MergedSymbolCount].Address &&
779             StabFunctionStartAddress < CoffFunctionSymbol->Address)
780         {
781             (*MergedSymbols)[*MergedSymbolCount].FunctionOffset = CoffFunctionStringOffset;
782             CoffFunctionSymbol->FunctionOffset = 0;
783         }
784         if (StabFunctionStringOffset != NewStabFunctionStringOffset)
785         {
786             StabFunctionStartAddress = (*MergedSymbols)[*MergedSymbolCount].Address;
787         }
788         StabFunctionStringOffset = NewStabFunctionStringOffset;
789         (*MergedSymbolCount)++;
790     }
791     /* Handle functions that have no analog in the upstream data */
792     for (CoffIndex = 0; CoffIndex < CoffSymbolsCount; CoffIndex++)
793     {
794         if (CoffSymbols[CoffIndex].Address &&
795             CoffSymbols[CoffIndex].FunctionOffset)
796         {
797             (*MergedSymbols)[*MergedSymbolCount] = CoffSymbols[CoffIndex];
798             (*MergedSymbolCount)++;
799         }
800     }
801 
802     qsort(*MergedSymbols, *MergedSymbolCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry);
803 
804     return 0;
805 }
806 
807 static PIMAGE_SECTION_HEADER
808 FindSectionForRVA(DWORD RVA, unsigned NumberOfSections, PIMAGE_SECTION_HEADER SectionHeaders)
809 {
810     unsigned Section;
811 
812     for (Section = 0; Section < NumberOfSections; Section++)
813     {
814         if (SectionHeaders[Section].VirtualAddress <= RVA &&
815             RVA < SectionHeaders[Section].VirtualAddress + SectionHeaders[Section].Misc.VirtualSize)
816         {
817             return SectionHeaders + Section;
818         }
819     }
820 
821     return NULL;
822 }
823 
824 static int
825 ProcessRelocations(ULONG *ProcessedRelocsLength, void **ProcessedRelocs,
826                    void *RawData, PIMAGE_OPTIONAL_HEADER OptHeader,
827                    unsigned NumberOfSections, PIMAGE_SECTION_HEADER SectionHeaders)
828 {
829     PIMAGE_SECTION_HEADER RelocSectionHeader, TargetSectionHeader;
830     PIMAGE_BASE_RELOCATION BaseReloc, End, AcceptedRelocs;
831     int Found;
832 
833     if (OptHeader->NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC ||
834         OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0)
835     {
836         /* No relocation entries */
837         *ProcessedRelocsLength = 0;
838         *ProcessedRelocs = NULL;
839         return 0;
840     }
841 
842     RelocSectionHeader = FindSectionForRVA(OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,
843                                            NumberOfSections, SectionHeaders);
844     if (RelocSectionHeader == NULL)
845     {
846         fprintf(stderr, "Can't find section header for relocation data\n");
847         return 1;
848     }
849 
850     *ProcessedRelocs = malloc(RelocSectionHeader->SizeOfRawData);
851     if (*ProcessedRelocs == NULL)
852     {
853         fprintf(stderr,
854                 "Failed to allocate %u bytes for relocations\n",
855                 (unsigned int)RelocSectionHeader->SizeOfRawData);
856         return 1;
857     }
858     *ProcessedRelocsLength = 0;
859 
860     BaseReloc = (PIMAGE_BASE_RELOCATION) ((char *) RawData +
861                                           RelocSectionHeader->PointerToRawData +
862                                           (OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress -
863                                            RelocSectionHeader->VirtualAddress));
864     End = (PIMAGE_BASE_RELOCATION) ((char *) BaseReloc +
865                                     OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
866 
867     while (BaseReloc < End && BaseReloc->SizeOfBlock > 0)
868     {
869         TargetSectionHeader = FindSectionForRVA(BaseReloc->VirtualAddress,
870                                                 NumberOfSections,
871                                                 SectionHeaders);
872         if (TargetSectionHeader != NULL)
873         {
874             AcceptedRelocs = *ProcessedRelocs;
875             Found = 0;
876             while (AcceptedRelocs < (PIMAGE_BASE_RELOCATION) ((char *) *ProcessedRelocs +
877                                                               *ProcessedRelocsLength)
878                    && !Found)
879             {
880                 Found = BaseReloc->SizeOfBlock == AcceptedRelocs->SizeOfBlock &&
881                                                   memcmp(BaseReloc, AcceptedRelocs, AcceptedRelocs->SizeOfBlock) == 0;
882                 AcceptedRelocs = (PIMAGE_BASE_RELOCATION) ((char *) AcceptedRelocs +
883                                                            AcceptedRelocs->SizeOfBlock);
884             }
885             if (!Found)
886             {
887                 memcpy((char *) *ProcessedRelocs + *ProcessedRelocsLength,
888                        BaseReloc,
889                        BaseReloc->SizeOfBlock);
890                 *ProcessedRelocsLength += BaseReloc->SizeOfBlock;
891             }
892         }
893         BaseReloc = (PIMAGE_BASE_RELOCATION)((char *) BaseReloc + BaseReloc->SizeOfBlock);
894     }
895 
896     return 0;
897 }
898 
899 static const BYTE*
900 GetSectionName(void *StringsBase, const BYTE *SectionTitle)
901 {
902     if (SectionTitle[0] == '/')
903     {
904         int offset = atoi((char*)SectionTitle+1);
905         return ((BYTE *)StringsBase) + offset;
906     }
907     else
908         return SectionTitle;
909 }
910 
911 static int
912 CreateOutputFile(FILE *OutFile, void *InData,
913                  PIMAGE_DOS_HEADER InDosHeader, PIMAGE_FILE_HEADER InFileHeader,
914                  PIMAGE_OPTIONAL_HEADER InOptHeader, PIMAGE_SECTION_HEADER InSectionHeaders,
915                  ULONG RosSymLength, void *RosSymSection)
916 {
917     ULONG StartOfRawData;
918     unsigned Section;
919     void *OutHeader, *ProcessedRelocs, *PaddedRosSym, *Data;
920     unsigned char *PaddedStringTable;
921     PIMAGE_DOS_HEADER OutDosHeader;
922     PIMAGE_FILE_HEADER OutFileHeader;
923     PIMAGE_OPTIONAL_HEADER OutOptHeader;
924     PIMAGE_SECTION_HEADER OutSectionHeaders, CurrentSectionHeader;
925     DWORD CheckSum;
926     ULONG Length, i;
927     ULONG ProcessedRelocsLength;
928     ULONG RosSymOffset, RosSymFileLength;
929     ULONG PaddedStringTableLength;
930     int InRelocSectionIndex;
931     PIMAGE_SECTION_HEADER OutRelocSection;
932     /* Each coff symbol is 18 bytes and the string table follows */
933     char *StringTable = (char *)InData +
934         InFileHeader->PointerToSymbolTable + 18 * InFileHeader->NumberOfSymbols;
935     ULONG StringTableLength = 0;
936     ULONG StringTableLocation;
937 
938     StartOfRawData = 0;
939     for (Section = 0; Section < InFileHeader->NumberOfSections; Section++)
940     {
941         const BYTE *SectionName = GetSectionName(StringTable,
942                                                  InSectionHeaders[Section].Name);
943         if (InSectionHeaders[Section].Name[0] == '/')
944         {
945             StringTableLength = atoi((const char *)InSectionHeaders[Section].Name + 1) +
946                                 strlen((const char *)SectionName) + 1;
947         }
948         if ((StartOfRawData == 0 || InSectionHeaders[Section].PointerToRawData < StartOfRawData)
949             && InSectionHeaders[Section].PointerToRawData != 0
950             && (strncmp((char *) SectionName, ".stab", 5)) != 0
951             && (strncmp((char *) SectionName, ".debug_", 7)) != 0)
952         {
953             StartOfRawData = InSectionHeaders[Section].PointerToRawData;
954         }
955     }
956     OutHeader = malloc(StartOfRawData);
957     if (OutHeader == NULL)
958     {
959         fprintf(stderr,
960                 "Failed to allocate %u bytes for output file header\n",
961                 (unsigned int)StartOfRawData);
962         return 1;
963     }
964     memset(OutHeader, '\0', StartOfRawData);
965 
966     OutDosHeader = (PIMAGE_DOS_HEADER) OutHeader;
967     memcpy(OutDosHeader, InDosHeader, InDosHeader->e_lfanew + sizeof(ULONG));
968 
969     OutFileHeader = (PIMAGE_FILE_HEADER)((char *) OutHeader + OutDosHeader->e_lfanew + sizeof(ULONG));
970     memcpy(OutFileHeader, InFileHeader, sizeof(IMAGE_FILE_HEADER));
971     OutFileHeader->PointerToSymbolTable = 0;
972     OutFileHeader->NumberOfSymbols = 0;
973     OutFileHeader->Characteristics &= ~(IMAGE_FILE_LINE_NUMS_STRIPPED | IMAGE_FILE_LOCAL_SYMS_STRIPPED |
974                                         IMAGE_FILE_DEBUG_STRIPPED);
975 
976     OutOptHeader = (PIMAGE_OPTIONAL_HEADER)(OutFileHeader + 1);
977     memcpy(OutOptHeader, InOptHeader, sizeof(IMAGE_OPTIONAL_HEADER));
978     OutOptHeader->CheckSum = 0;
979 
980     OutSectionHeaders = (PIMAGE_SECTION_HEADER)((char *) OutOptHeader + OutFileHeader->SizeOfOptionalHeader);
981 
982     if (ProcessRelocations(&ProcessedRelocsLength,
983                            &ProcessedRelocs,
984                            InData,
985                            InOptHeader,
986                            InFileHeader->NumberOfSections,
987                            InSectionHeaders))
988     {
989         return 1;
990     }
991     if (InOptHeader->NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC ||
992         InOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0)
993     {
994         InRelocSectionIndex = -1;
995     }
996     else
997     {
998         InRelocSectionIndex = FindSectionForRVA(InOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,
999                                                 InFileHeader->NumberOfSections, InSectionHeaders) - InSectionHeaders;
1000     }
1001 
1002     OutFileHeader->NumberOfSections = 0;
1003     CurrentSectionHeader = OutSectionHeaders;
1004     OutOptHeader->SizeOfImage = 0;
1005     RosSymOffset = 0;
1006     OutRelocSection = NULL;
1007 
1008     StringTableLocation = StartOfRawData;
1009 
1010     for (Section = 0; Section < InFileHeader->NumberOfSections; Section++)
1011     {
1012         const BYTE *SectionName = GetSectionName(StringTable,
1013                                                  InSectionHeaders[Section].Name);
1014         if ((strncmp((char *) SectionName, ".stab", 5) != 0) &&
1015             (strncmp((char *) SectionName, ".debug_", 7)) != 0)
1016         {
1017             *CurrentSectionHeader = InSectionHeaders[Section];
1018             CurrentSectionHeader->PointerToLinenumbers = 0;
1019             CurrentSectionHeader->NumberOfLinenumbers = 0;
1020             if (OutOptHeader->SizeOfImage < CurrentSectionHeader->VirtualAddress +
1021                                             CurrentSectionHeader->Misc.VirtualSize)
1022             {
1023                 OutOptHeader->SizeOfImage = ROUND_UP(CurrentSectionHeader->VirtualAddress +
1024                                                      CurrentSectionHeader->Misc.VirtualSize,
1025                                                      OutOptHeader->SectionAlignment);
1026             }
1027             if (RosSymOffset < CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData)
1028             {
1029                 RosSymOffset = CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData;
1030             }
1031             if (Section == (ULONG)InRelocSectionIndex)
1032             {
1033                 OutRelocSection = CurrentSectionHeader;
1034             }
1035             StringTableLocation = CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData;
1036             OutFileHeader->NumberOfSections++;
1037             CurrentSectionHeader++;
1038         }
1039     }
1040 
1041     if (OutRelocSection == CurrentSectionHeader - 1)
1042     {
1043         OutOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = ProcessedRelocsLength;
1044         if (OutOptHeader->SizeOfImage == OutRelocSection->VirtualAddress +
1045                                          ROUND_UP(OutRelocSection->Misc.VirtualSize,
1046                                                   OutOptHeader->SectionAlignment))
1047         {
1048             OutOptHeader->SizeOfImage = OutRelocSection->VirtualAddress +
1049                                         ROUND_UP(ProcessedRelocsLength,
1050                                                  OutOptHeader->SectionAlignment);
1051         }
1052         OutRelocSection->Misc.VirtualSize = ProcessedRelocsLength;
1053         if (RosSymOffset == OutRelocSection->PointerToRawData +
1054                             OutRelocSection->SizeOfRawData)
1055         {
1056             RosSymOffset = OutRelocSection->PointerToRawData +
1057                            ROUND_UP(ProcessedRelocsLength,
1058                                     OutOptHeader->FileAlignment);
1059         }
1060         OutRelocSection->SizeOfRawData = ROUND_UP(ProcessedRelocsLength,
1061                                                   OutOptHeader->FileAlignment);
1062     }
1063 
1064     if (RosSymLength > 0)
1065     {
1066         RosSymFileLength = ROUND_UP(RosSymLength, OutOptHeader->FileAlignment);
1067         memcpy(CurrentSectionHeader->Name, ".rossym", 8); /* We're lucky: string is exactly 8 bytes long */
1068         CurrentSectionHeader->Misc.VirtualSize = RosSymLength;
1069         CurrentSectionHeader->VirtualAddress = OutOptHeader->SizeOfImage;
1070         CurrentSectionHeader->SizeOfRawData = RosSymFileLength;
1071         CurrentSectionHeader->PointerToRawData = RosSymOffset;
1072         CurrentSectionHeader->PointerToRelocations = 0;
1073         CurrentSectionHeader->PointerToLinenumbers = 0;
1074         CurrentSectionHeader->NumberOfRelocations = 0;
1075         CurrentSectionHeader->NumberOfLinenumbers = 0;
1076         CurrentSectionHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
1077                                                 | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_TYPE_NOLOAD;
1078         OutOptHeader->SizeOfImage = ROUND_UP(CurrentSectionHeader->VirtualAddress + CurrentSectionHeader->Misc.VirtualSize,
1079                                              OutOptHeader->SectionAlignment);
1080         OutFileHeader->NumberOfSections++;
1081 
1082         PaddedRosSym = malloc(RosSymFileLength);
1083         if (PaddedRosSym == NULL)
1084         {
1085             fprintf(stderr,
1086                     "Failed to allocate %u bytes for padded .rossym\n",
1087                     (unsigned int)RosSymFileLength);
1088             return 1;
1089         }
1090         memcpy(PaddedRosSym, RosSymSection, RosSymLength);
1091         memset((char *) PaddedRosSym + RosSymLength,
1092                '\0',
1093                RosSymFileLength - RosSymLength);
1094 
1095         /* Position the string table after our new section */
1096         StringTableLocation = RosSymOffset + RosSymFileLength;
1097     }
1098     else
1099     {
1100         PaddedRosSym = NULL;
1101     }
1102 
1103     /* Set the string table area in the header if we need it */
1104     if (StringTableLength)
1105     {
1106         OutFileHeader->PointerToSymbolTable = StringTableLocation;
1107         OutFileHeader->NumberOfSymbols = 0;
1108     }
1109 
1110     CheckSum = 0;
1111     for (i = 0; i < StartOfRawData / 2; i++)
1112     {
1113         CheckSum += ((unsigned short*) OutHeader)[i];
1114         CheckSum = 0xffff & (CheckSum + (CheckSum >> 16));
1115     }
1116     Length = StartOfRawData;
1117     for (Section = 0; Section < OutFileHeader->NumberOfSections; Section++)
1118     {
1119         DWORD SizeOfRawData;
1120         if (OutRelocSection == OutSectionHeaders + Section)
1121         {
1122             Data = (void *) ProcessedRelocs;
1123             SizeOfRawData = ProcessedRelocsLength;
1124         }
1125         else if (RosSymLength > 0 && Section + 1 == OutFileHeader->NumberOfSections)
1126         {
1127             Data = (void *) PaddedRosSym;
1128             SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData;
1129         }
1130         else
1131         {
1132             Data = (void *) ((char *) InData + OutSectionHeaders[Section].PointerToRawData);
1133             SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData;
1134         }
1135         for (i = 0; i < SizeOfRawData / 2; i++)
1136         {
1137             CheckSum += ((unsigned short*) Data)[i];
1138             CheckSum = 0xffff & (CheckSum + (CheckSum >> 16));
1139         }
1140         Length += OutSectionHeaders[Section].SizeOfRawData;
1141     }
1142 
1143     if (OutFileHeader->PointerToSymbolTable)
1144     {
1145         int PaddingFrom = (OutFileHeader->PointerToSymbolTable + StringTableLength) %
1146                           OutOptHeader->FileAlignment;
1147         int PaddingSize = PaddingFrom ? OutOptHeader->FileAlignment - PaddingFrom : 0;
1148 
1149         PaddedStringTableLength = StringTableLength + PaddingSize;
1150         PaddedStringTable = malloc(PaddedStringTableLength);
1151         /* COFF string section is preceeded by a length */
1152         assert(sizeof(StringTableLength) == 4);
1153         memcpy(PaddedStringTable, &StringTableLength, sizeof(StringTableLength));
1154         /* We just copy enough of the string table to contain the strings we want
1155            The string table length technically counts as part of the string table
1156            space itself. */
1157         memcpy(PaddedStringTable + 4, StringTable + 4, StringTableLength - 4);
1158         memset(PaddedStringTable + StringTableLength, 0, PaddingSize);
1159 
1160         assert(OutFileHeader->PointerToSymbolTable % 2 == 0);
1161         for (i = 0; i < PaddedStringTableLength / 2; i++)
1162         {
1163             CheckSum += ((unsigned short*)PaddedStringTable)[i];
1164             CheckSum = 0xffff & (CheckSum + (CheckSum >> 16));
1165         }
1166         Length += PaddedStringTableLength;
1167     }
1168     else
1169     {
1170         PaddedStringTable = NULL;
1171     }
1172 
1173     CheckSum += Length;
1174     OutOptHeader->CheckSum = CheckSum;
1175 
1176     if (fwrite(OutHeader, 1, StartOfRawData, OutFile) != StartOfRawData)
1177     {
1178         perror("Error writing output header\n");
1179         free(OutHeader);
1180         return 1;
1181     }
1182 
1183     for (Section = 0; Section < OutFileHeader->NumberOfSections; Section++)
1184     {
1185         if (OutSectionHeaders[Section].SizeOfRawData != 0)
1186         {
1187             DWORD SizeOfRawData;
1188             fseek(OutFile, OutSectionHeaders[Section].PointerToRawData, SEEK_SET);
1189             if (OutRelocSection == OutSectionHeaders + Section)
1190             {
1191                 Data = (void *) ProcessedRelocs;
1192                 SizeOfRawData = ProcessedRelocsLength;
1193             }
1194             else if (RosSymLength > 0 && Section + 1 == OutFileHeader->NumberOfSections)
1195             {
1196                 Data = (void *) PaddedRosSym;
1197                 SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData;
1198             }
1199             else
1200             {
1201                 Data = (void *) ((char *) InData + OutSectionHeaders[Section].PointerToRawData);
1202                 SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData;
1203             }
1204             if (fwrite(Data, 1, SizeOfRawData, OutFile) != SizeOfRawData)
1205             {
1206                 perror("Error writing section data\n");
1207                 free(PaddedRosSym);
1208                 free(OutHeader);
1209                 return 1;
1210             }
1211         }
1212     }
1213 
1214     if (PaddedStringTable)
1215     {
1216         fseek(OutFile, OutFileHeader->PointerToSymbolTable, SEEK_SET);
1217         fwrite(PaddedStringTable, 1, PaddedStringTableLength, OutFile);
1218         free(PaddedStringTable);
1219     }
1220 
1221     if (PaddedRosSym)
1222     {
1223         free(PaddedRosSym);
1224     }
1225     free(OutHeader);
1226 
1227     return 0;
1228 }
1229 
1230 int main(int argc, char* argv[])
1231 {
1232     PSYMBOLFILE_HEADER SymbolFileHeader;
1233     PIMAGE_DOS_HEADER PEDosHeader;
1234     PIMAGE_FILE_HEADER PEFileHeader;
1235     PIMAGE_OPTIONAL_HEADER PEOptHeader;
1236     PIMAGE_SECTION_HEADER PESectionHeaders;
1237     ULONG ImageBase;
1238     void *StabBase;
1239     ULONG StabsLength;
1240     void *StabStringBase;
1241     ULONG StabStringsLength;
1242     void *CoffBase = NULL;
1243     ULONG CoffsLength;
1244     void *CoffStringBase = NULL;
1245     ULONG CoffStringsLength;
1246     char* path1;
1247     char* path2;
1248     FILE* out;
1249     void *StringBase = NULL;
1250     ULONG StringsLength = 0;
1251     ULONG StabSymbolsCount = 0;
1252     PROSSYM_ENTRY StabSymbols = NULL;
1253     ULONG CoffSymbolsCount = 0;
1254     PROSSYM_ENTRY CoffSymbols = NULL;
1255     ULONG MergedSymbolsCount = 0;
1256     PROSSYM_ENTRY MergedSymbols = NULL;
1257     size_t FileSize;
1258     void *FileData;
1259     ULONG RosSymLength;
1260     void *RosSymSection;
1261     DWORD module_base;
1262     void *file;
1263     char elfhdr[4] = { '\177', 'E', 'L', 'F' };
1264     BOOLEAN UseDbgHelp = FALSE;
1265     int arg, argstate = 0;
1266     char *SourcePath = NULL;
1267 
1268     for (arg = 1; arg < argc; arg++)
1269     {
1270         switch (argstate)
1271         {
1272             default:
1273                 argstate = -1;
1274                 break;
1275 
1276             case 0:
1277                 if (!strcmp(argv[arg], "-s"))
1278                 {
1279                     argstate = 1;
1280                 }
1281                 else
1282                 {
1283                     argstate = 2;
1284                     path1 = convert_path(argv[arg]);
1285                 }
1286             break;
1287 
1288             case 1:
1289                 free(SourcePath);
1290                 SourcePath = strdup(argv[arg]);
1291                 argstate = 0;
1292                 break;
1293 
1294             case 2:
1295                 path2 = convert_path(argv[arg]);
1296                 argstate = 3;
1297                 break;
1298         }
1299     }
1300 
1301     if (argstate != 3)
1302     {
1303         fprintf(stderr, "Usage: rsym [-s <sources>] <input> <output>\n");
1304         exit(1);
1305     }
1306 
1307     FileData = load_file(path1, &FileSize);
1308     if (!FileData)
1309     {
1310         fprintf(stderr, "An error occured loading '%s'\n", path1);
1311         exit(1);
1312     }
1313 
1314     file = fopen(path1, "rb");
1315 
1316     /* Check if MZ header exists  */
1317     PEDosHeader = (PIMAGE_DOS_HEADER) FileData;
1318     if (PEDosHeader->e_magic != IMAGE_DOS_MAGIC ||
1319         PEDosHeader->e_lfanew == 0L)
1320     {
1321         /* Ignore elf */
1322         if (!memcmp(PEDosHeader, elfhdr, sizeof(elfhdr)))
1323             exit(0);
1324         perror("Input file is not a PE image.\n");
1325         free(FileData);
1326         exit(1);
1327     }
1328 
1329     /* Locate PE file header  */
1330     /* sizeof(ULONG) = sizeof(MAGIC) */
1331     PEFileHeader = (PIMAGE_FILE_HEADER)((char *) FileData + PEDosHeader->e_lfanew + sizeof(ULONG));
1332 
1333     /* Locate optional header */
1334     assert(sizeof(ULONG) == 4);
1335     PEOptHeader = (PIMAGE_OPTIONAL_HEADER)(PEFileHeader + 1);
1336     ImageBase = PEOptHeader->ImageBase;
1337 
1338     /* Locate PE section headers  */
1339     PESectionHeaders = (PIMAGE_SECTION_HEADER)((char *) PEOptHeader + PEFileHeader->SizeOfOptionalHeader);
1340 
1341     if (GetStabInfo(FileData,
1342                     PEFileHeader,
1343                     PESectionHeaders,
1344                     &StabsLength,
1345                     &StabBase,
1346                     &StabStringsLength,
1347                     &StabStringBase))
1348     {
1349         free(FileData);
1350         exit(1);
1351     }
1352 
1353     if (StabsLength == 0)
1354     {
1355         // SYMOPT_AUTO_PUBLICS
1356         // SYMOPT_FAVOR_COMPRESSED
1357         // SYMOPT_LOAD_ANYTHING
1358         // SYMOPT_LOAD_LINES
1359         SymSetOptions(0x10000 | 0x800000 | 0x40 | 0x10);
1360         SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, TRUE);
1361         SymInitialize(FileData, ".", 0);
1362 
1363         module_base = SymLoadModule(FileData, file, path1, path1, 0, FileSize) & 0xffffffff;
1364 
1365         if (ConvertDbgHelp(FileData,
1366                            module_base,
1367                            SourcePath,
1368                            &StabSymbolsCount,
1369                            &StabSymbols,
1370                            &StringsLength,
1371                            &StringBase))
1372         {
1373             free(FileData);
1374             exit(1);
1375         }
1376 
1377         UseDbgHelp = TRUE;
1378         SymUnloadModule(FileData, module_base);
1379         SymCleanup(FileData);
1380     }
1381 
1382     if (GetCoffInfo(FileData,
1383                     PEFileHeader,
1384                     PESectionHeaders,
1385                     &CoffsLength,
1386                     &CoffBase,
1387                     &CoffStringsLength,
1388                     &CoffStringBase))
1389     {
1390         free(FileData);
1391         exit(1);
1392     }
1393 
1394     if (!UseDbgHelp)
1395     {
1396         StringBase = malloc(1 + StringsLength + CoffStringsLength +
1397                             (CoffsLength / sizeof(ROSSYM_ENTRY)) * (E_SYMNMLEN + 1));
1398         if (StringBase == NULL)
1399         {
1400             free(FileData);
1401             fprintf(stderr, "Failed to allocate memory for strings table\n");
1402             exit(1);
1403         }
1404         /* Make offset 0 into an empty string */
1405         *((char *) StringBase) = '\0';
1406         StringsLength = 1;
1407 
1408         if (ConvertStabs(&StabSymbolsCount,
1409                          &StabSymbols,
1410                          &StringsLength,
1411                          StringBase,
1412                          StabsLength,
1413                          StabBase,
1414                          StabStringsLength,
1415                          StabStringBase,
1416                          ImageBase,
1417                          PEFileHeader,
1418                          PESectionHeaders))
1419         {
1420             free(StringBase);
1421             free(FileData);
1422             fprintf(stderr, "Failed to allocate memory for strings table\n");
1423             exit(1);
1424         }
1425     }
1426     else
1427     {
1428         StringBase = realloc(StringBase, StringsLength + CoffStringsLength);
1429         if (!StringBase)
1430         {
1431             free(FileData);
1432             fprintf(stderr, "Failed to allocate memory for strings table\n");
1433             exit(1);
1434         }
1435     }
1436 
1437     if (ConvertCoffs(&CoffSymbolsCount,
1438                      &CoffSymbols,
1439                      &StringsLength,
1440                      StringBase,
1441                      CoffsLength,
1442                      CoffBase,
1443                      CoffStringsLength,
1444                      CoffStringBase,
1445                      ImageBase,
1446                      PEFileHeader,
1447                      PESectionHeaders))
1448     {
1449         if (StabSymbols)
1450         {
1451             free(StabSymbols);
1452         }
1453         free(StringBase);
1454         free(FileData);
1455         exit(1);
1456     }
1457 
1458     if (MergeStabsAndCoffs(&MergedSymbolsCount,
1459                            &MergedSymbols,
1460                            StabSymbolsCount,
1461                            StabSymbols,
1462                            CoffSymbolsCount,
1463                            CoffSymbols))
1464     {
1465         if (CoffSymbols)
1466         {
1467             free(CoffSymbols);
1468         }
1469         if (StabSymbols)
1470         {
1471             free(StabSymbols);
1472         }
1473         free(StringBase);
1474         free(FileData);
1475         exit(1);
1476     }
1477 
1478     if (CoffSymbols)
1479     {
1480         free(CoffSymbols);
1481     }
1482     if (StabSymbols)
1483     {
1484         free(StabSymbols);
1485     }
1486     if (MergedSymbolsCount == 0)
1487     {
1488         RosSymLength = 0;
1489         RosSymSection = NULL;
1490     }
1491     else
1492     {
1493         RosSymLength = sizeof(SYMBOLFILE_HEADER) +
1494                        MergedSymbolsCount * sizeof(ROSSYM_ENTRY) +
1495                        StringsLength;
1496 
1497         RosSymSection = malloc(RosSymLength);
1498         if (RosSymSection == NULL)
1499         {
1500             free(MergedSymbols);
1501             free(StringBase);
1502             free(FileData);
1503             fprintf(stderr, "Unable to allocate memory for .rossym section\n");
1504             exit(1);
1505         }
1506         memset(RosSymSection, '\0', RosSymLength);
1507 
1508         SymbolFileHeader = (PSYMBOLFILE_HEADER)RosSymSection;
1509         SymbolFileHeader->SymbolsOffset = sizeof(SYMBOLFILE_HEADER);
1510         SymbolFileHeader->SymbolsLength = MergedSymbolsCount * sizeof(ROSSYM_ENTRY);
1511         SymbolFileHeader->StringsOffset = SymbolFileHeader->SymbolsOffset +
1512                                           SymbolFileHeader->SymbolsLength;
1513         SymbolFileHeader->StringsLength = StringsLength;
1514 
1515         memcpy((char *) RosSymSection + SymbolFileHeader->SymbolsOffset,
1516                MergedSymbols,
1517                SymbolFileHeader->SymbolsLength);
1518 
1519         memcpy((char *) RosSymSection + SymbolFileHeader->StringsOffset,
1520                StringBase,
1521                SymbolFileHeader->StringsLength);
1522 
1523         free(MergedSymbols);
1524     }
1525 
1526     free(StringBase);
1527     out = fopen(path2, "wb");
1528     if (out == NULL)
1529     {
1530         perror("Cannot open output file");
1531         free(RosSymSection);
1532         free(FileData);
1533         exit(1);
1534     }
1535 
1536     if (CreateOutputFile(out,
1537                          FileData,
1538                          PEDosHeader,
1539                          PEFileHeader,
1540                          PEOptHeader,
1541                          PESectionHeaders,
1542                          RosSymLength,
1543                          RosSymSection))
1544     {
1545         fclose(out);
1546         if (RosSymSection)
1547         {
1548             free(RosSymSection);
1549         }
1550         free(FileData);
1551         exit(1);
1552     }
1553 
1554     fclose(out);
1555     if (RosSymSection)
1556     {
1557         free(RosSymSection);
1558     }
1559     free(FileData);
1560 
1561     return 0;
1562 }
1563 
1564 /* EOF */
1565