xref: /reactos/sdk/tools/rsym/rsym64.c (revision 3a49e26f)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #include "rsym.h"
6 #include "rsym64.h"
7 #include "dwarf2.h"
8 
9 char DoPrint = 0;
10 ULONG g_ehframep;
11 
12 #define DPRINT if(DoPrint) printf
13 
14 struct {char *name; char regnt;} regs[] =
15 { {"rax", REG_RAX}, {"rdx", REG_RDX}, {"rcx", REG_RCX}, {"rbx", REG_RBX},
16   {"rsi", REG_RSI}, {"rdi", REG_RDI}, {"rbp", REG_RBP}, {"rsp", REG_RSP},
17   {"r8",  REG_R8},  {"r9",  REG_R9},  {"r10", REG_R10}, {"r11", REG_R11},
18   {"r12", REG_R12}, {"r13", REG_R13}, {"r14", REG_R14}, {"r15", REG_R15},
19   {"xmm0", REG_XMM0}, {"xmm1", REG_XMM1}, {"xmm2", REG_XMM2}, {"xmm3", REG_XMM3},
20   {"xmm4", REG_XMM4}, {"xmm5", REG_XMM5}, {"xmm6", REG_XMM6}, {"xmm7", REG_XMM7},
21   {"xmm8", REG_XMM8}, {"xmm9", REG_XMM9}, {"xmm10",REG_XMM10},{"xmm11",REG_XMM11},
22   {"xmm12",REG_XMM12},{"xmm13",REG_XMM13},{"xmm14",REG_XMM14},{"xmm15",REG_XMM15},
23 //      "st0", "st1", "st2", "st3",
24 //      "st4", "st5", "st6", "st7",
25 //      "mm0", "mm1", "mm2", "mm3",
26 //      "mm4", "mm5", "mm6", "mm7"
27 };
28 
29 /** Functions for DWARF2 ******************************************************/
30 
31 unsigned long
32 DwDecodeUleb128(unsigned long *pResult, char *pc)
33 {
34 	unsigned long ulResult = 0;
35 	unsigned long ulShift = 0;
36 	unsigned char current;
37 	unsigned long ulSize = 0;
38 
39 	do
40 	{
41 		current = pc[ulSize];
42 		ulSize++;
43 		ulResult |= (current & 0x7f) << ulShift;
44 		ulShift += 7;
45 	}
46 	while (current & 0x80);
47 
48     *pResult = ulResult;
49 	return ulSize;
50 }
51 
52 unsigned long
53 DwDecodeSleb128(long *pResult, char *pc)
54 {
55 	long lResult = 0;
56 	unsigned long ulShift = 0;
57 	unsigned char current;
58 	unsigned long ulSize = 0;
59 
60 	do
61 	{
62 		current = pc[ulSize];
63 		ulSize++;
64 		lResult |= (current & 0x7f) << ulShift;
65 		ulShift += 7;
66 	}
67 	while (current & 0x80);
68 
69 	if (current & 0x40)
70 		lResult |= - (1 << (ulShift));
71 
72     *pResult = lResult;
73 
74 	return ulSize;
75 }
76 
77 unsigned long
78 DwDecodeCie(PDW2CIE Cie, char *pc)
79 {
80     Cie->Length = *(ULONG*)pc;
81     Cie->Next = pc + 4 + Cie->Length;
82     Cie->CieId = *(ULONG*)(pc + 4);
83     Cie->Version = pc[8];
84     Cie->AugString = pc + 9;
85     Cie->AugStringLength = strlen(Cie->AugString);
86     pc = Cie->AugString + Cie->AugStringLength + 1;
87     pc += DwDecodeUleb128(&Cie->CodeAlign, pc);
88     pc += DwDecodeSleb128(&Cie->DataAlign, pc);
89     pc += DwDecodeUleb128(&Cie->ReturnAddressRegister, pc);
90     pc += DwDecodeUleb128(&Cie->AugLength, pc);
91     Cie->AugData = pc;
92     pc += Cie->AugLength;
93     Cie->Instructions = pc;
94 
95     return Cie->Length + 4;
96 }
97 
98 unsigned long
99 DwDecodeFde(PDW2FDE Fde, char *pc)
100 {
101     Fde->Length = *(ULONG*)pc;
102     Fde->Next = pc + 4 + Fde->Length;
103     Fde->CiePointer = pc + 4 - *(ULONG*)(pc + 4);
104     Fde->PcBegin = *(ULONG*)(pc + 8);
105     Fde->PcRange = *(ULONG*)(pc + 12);
106     pc += 16;
107     pc += DwDecodeUleb128(&Fde->AugLength, pc);
108     Fde->AugData = pc;
109     Fde->Instructions = Fde->AugData + Fde->AugLength;
110 
111     return Fde->Length + 4;
112 }
113 
114 unsigned long
115 DwExecIntruction(PDW2CFSTATE State, char *pc)
116 {
117     unsigned char Code;
118     unsigned long Length;
119     unsigned long PrevFramePtr = State->FramePtr;
120 
121     State->Scope = 0;
122     State->IsUwop = 0;
123     State->Code = Code = *pc;
124     Length = 1;
125     if ((Code & 0xc0) == DW_CFA_advance_loc)
126     {
127         State->Code = DW_CFA_advance_loc;
128         State->Location += Code & 0x3f;
129     }
130     else if ((Code & 0xc0) == DW_CFA_offset)
131     {
132         State->Code = DW_CFA_offset;
133         State->Reg = Code & 0x3f;
134         Length += DwDecodeUleb128((unsigned long*)&State->Offset, pc + 1);
135         State->Offset *= 8; // fixme data alignment
136         State->IsUwop = 1;
137     }
138     else if ((Code & 0xc0) == DW_CFA_restore)
139     {
140         State->Code = DW_CFA_restore;
141         State->Reg = Code & 0x3f;
142     }
143     else switch (Code)
144     {
145         case DW_CFA_nop:
146             break;
147         case DW_CFA_set_loc:
148             Length = 9; // address
149             State->Location = *(DWORD*)(pc + 1);
150             break;
151         case DW_CFA_advance_loc1:
152             Length = 2;
153             State->Location += pc[1];
154             break;
155         case DW_CFA_advance_loc2:
156             Length = 3;
157 //            printf("Found a DW_CFA_advance_loc2 : 0x%lx ->", *(WORD*)(pc + 1));
158             State->Location += *(WORD*)(pc + 1);
159 //            printf(" 0x%lx\n", State->Location);
160             break;
161         case DW_CFA_advance_loc4:
162             Length = 5;
163 //            printf("Found a DW_CFA_advance_loc4 : 0x%lx ->", *(DWORD*)(pc + 1));
164             State->Location += *(DWORD*)(pc + 1);
165 //            printf(" 0x%lx\n", State->Location);
166             break;
167         case DW_CFA_offset_extended:
168             Length += DwDecodeUleb128(&State->Reg, pc + Length);
169             Length += DwDecodeUleb128((unsigned long*)&State->Offset, pc + Length);
170             State->IsUwop = 1;
171             break;
172         case DW_CFA_offset_extended_sf:
173             Length += DwDecodeUleb128(&State->Reg, pc + Length);
174             Length += DwDecodeSleb128(&State->Offset, pc + Length);
175             State->IsUwop = 1;
176             break;
177         case DW_CFA_restore_extended:
178             Length += DwDecodeUleb128(&State->Reg, pc + Length);
179             break;
180         case DW_CFA_undefined:
181             Length += DwDecodeUleb128(&State->Reg, pc + Length);
182             break;
183         case DW_CFA_same_value:
184             Length += DwDecodeUleb128(&State->Reg, pc + Length);
185             break;
186         case DW_CFA_register:
187             Length += DwDecodeUleb128(&State->Reg, pc + Length);
188             Length += DwDecodeUleb128(&State->Reg2, pc + Length);
189             break;
190         case DW_CFA_remember_state:
191             break;
192         case DW_CFA_restore_state:
193             break;
194         case DW_CFA_def_cfa:
195             Length += DwDecodeUleb128(&State->Reg, pc + Length);
196             Length += DwDecodeUleb128((unsigned long*)&State->FramePtr, pc + Length);
197             State->IsUwop = 1;
198             break;
199         case DW_CFA_def_cfa_register:
200             Length += DwDecodeUleb128(&State->Reg, pc + Length);
201             break;
202         case DW_CFA_def_cfa_offset:
203             Length += DwDecodeUleb128((unsigned long*)&State->FramePtr, pc + Length);
204             State->IsUwop = 1;
205             break;
206         case DW_CFA_def_cfa_sf:
207             Length += DwDecodeUleb128(&State->Reg, pc + Length);
208             Length += DwDecodeSleb128(&State->FramePtr, pc + Length);
209             State->FramePtr *= 8; // data alignment
210             State->IsUwop = 1;
211             break;
212         case DW_CFA_GNU_args_size:
213         {
214             unsigned long argsize;
215             printf("Warning, DW_CFA_GNU_args_size is unimplemented\n");
216             Length += DwDecodeUleb128(&argsize, pc + Length);
217             break;
218         }
219         /* PSEH */
220         case 0x21:
221         {
222             unsigned long SehType;
223 
224 //            printf("found 0x21 at %lx\n", State->Location);
225             Length += DwDecodeUleb128(&SehType, pc + Length);
226             switch (SehType)
227             {
228                 case 1: /* Begin Try */
229                     State->TryLevel++;
230                     if (State->TryLevel >= 20)
231                     {
232                         printf("WTF? Trylevel of 20 exceeded...\n");
233                         exit(1);
234                     }
235                     State->SehBlock[State->TryLevel-1].BeginTry = State->Location;
236 //                    printf("Found begintry at 0x%lx\n", State->Location);
237                     State->Scope = 1;
238                     break;
239 
240                 case 2: /* End Try */
241                     State->SehBlock[State->TryLevel-1].EndTry = State->Location;
242                     State->Scope = 2;
243                     break;
244 
245                 case 3: /* Jump target */
246                     State->SehBlock[State->TryLevel-1].Target = State->Location;
247                     State->Scope = 3;
248                     break;
249 
250                 case 4: /* SEH End */
251                     if (State->TryLevel == 20)
252                     {
253                         printf("Ooops, end of SEH with trylevel at 0!\n");
254                         exit(1);
255                     }
256                     State->SehBlock[State->TryLevel-1].End = State->Location;
257                     State->TryLevel--;
258                     State->cScopes++;
259                     State->Scope = 0;
260                     break;
261 
262                 case 5: /* Constant filter */
263                 {
264                     unsigned long value;
265                     Length += DwDecodeUleb128(&value, pc + Length);
266                     State->SehBlock[State->TryLevel-1].Handler = value;
267 //                     printf("Found a constant filter at 0x%lx\n", State->Location);
268                     break;
269                 }
270 
271                /* These work differently. We are in a new function.
272                  * We have to parse a lea opcode to find the address of
273                  * the jump target. This is the reference to find the
274                  * appropriate C_SCOPE_TABLE. */
275                 case 6: /* Filter func */
276 //                    printf("Found a filter func at 0x%lx\n", State->Location);
277                     break;
278 
279                 case 7: /* Finally func */
280                 {
281 //                     printf("Found a finally func at 0x%lx\n", State->Location);
282                     break;
283                 }
284 
285                 default:
286                     printf("Found unknow PSEH code 0x%lx\n", SehType);
287                     exit(1);
288             }
289             break;
290         }
291         default:
292             fprintf(stderr, "unknown instruction 0x%x at 0x%p\n", Code, pc);
293             exit(1);
294     }
295 
296     State->FramePtrDiff = State->FramePtr - PrevFramePtr;
297     DPRINT("@%p: code=%x, Loc=%lx, offset=%lx, reg=0x%lx:%s\n",
298         (void*)((ULONG)pc - g_ehframep), Code, State->Location, State->Offset, State->Reg, regs[State->Reg].name);
299     return Length;
300 }
301 
302 /** Windows unwind data functions *********************************************/
303 
304 ULONG
305 StoreUnwindCodes(PUNWIND_INFO Info, PDW2CFSTATE State, ULONG FunctionStart)
306 {
307     ULONG cCodes = 0;
308     ULONG AllocSize;
309     UNWIND_CODE Code[3];
310     int i;
311 
312     Code[0].CodeOffset = State->Location - FunctionStart;
313 
314     switch (State->Code)
315     {
316         case DW_CFA_offset:
317         case DW_CFA_offset_extended:
318             // save register at offset
319             Code[0].OpInfo = regs[State->Reg].regnt;
320             if (State->Offset <= 0x7FFF8)
321             {
322                 Code[0].UnwindOp = UWOP_SAVE_NONVOL;
323                 Code[1].FrameOffset = State->Offset / 8;
324                 cCodes = 2;
325             }
326             else
327             {
328                 Code[0].UnwindOp = UWOP_SAVE_NONVOL_FAR;
329                 Code[1].FrameOffset = (State->Offset / 8);
330                 Code[2].FrameOffset = (State->Offset / 8) >> 16;
331                 cCodes = 3;
332             }
333             break;
334 
335         case DW_CFA_def_cfa:
336         //case DW_CFA_def_cfa_register:
337         case DW_CFA_def_cfa_offset:
338         case DW_CFA_def_cfa_sf:
339             AllocSize = State->FramePtrDiff;
340             if (AllocSize <= 128)
341             {
342                 Code[0].UnwindOp = UWOP_ALLOC_SMALL;
343                 Code[0].OpInfo = (AllocSize / 8) - 1;
344                 cCodes = 1;
345             }
346             else if (AllocSize <= 0x7FFF8)
347             {
348                 Code[0].UnwindOp = UWOP_ALLOC_LARGE;
349                 Code[0].OpInfo = 0;
350                 Code[1].FrameOffset = AllocSize / 8;
351                 cCodes = 2;
352             }
353             else // if (AllocSize > 0x7FFF8)
354             {
355                 Code[0].UnwindOp = UWOP_ALLOC_LARGE;
356                 Code[0].OpInfo = 1;
357                 Code[1].FrameOffset = (USHORT)AllocSize;
358                 Code[2].FrameOffset = (USHORT)(AllocSize >> 16);
359                 cCodes = 3;
360             }
361             break;
362     }
363 
364     if (Info)
365     {
366         /* Move old codes */
367         for (i = Info->CountOfCodes - 1; i >= 0; i--)
368         {
369             Info->UnwindCode[i + cCodes] = Info->UnwindCode[i];
370         }
371 
372         /* Copy new codes */
373         for (i = 0; i < cCodes; i++)
374         {
375             Info->UnwindCode[i] = Code[i];
376         }
377 
378         Info->CountOfCodes += cCodes;
379     }
380 
381     return cCodes;
382 }
383 
384 #define GetxdataSize(cFuncs, cUWOP, cScopes) \
385     ( cFuncs * (sizeof(UNWIND_INFO) + 2 + 4 + 4) \
386     + cUWOP * sizeof(UNWIND_CODE) \
387     + cScopes * sizeof(C_SCOPE_TABLE_ENTRY) )
388 
389 ULONG
390 StoreUnwindInfo(PUNWIND_INFO Info, PDW2FDE pFde, ULONG FunctionStart)
391 {
392     ULONG cbSize;
393     DW2CFSTATE State;
394     char *pInst;
395     ULONG c;
396     DW2CIE Cie;
397 
398     cbSize = 4; // sizeof(UNWIND_INFO);
399     Info->Version = 1;
400     Info->Flags = 0;
401     Info->SizeOfProlog = 0;
402     Info->CountOfCodes = 0;
403     Info->FrameRegister = 0;
404     Info->FrameOffset = 0;
405 
406     /* Decode the CIE */
407     DwDecodeCie(&Cie, pFde->CiePointer);
408 
409     /* Initialize state */
410     State.Location = FunctionStart;
411     State.FramePtr = 0;
412     State.TryLevel = 0;
413     State.cScopes = 0;
414 
415     /* Parse the CIE's initial instructions */
416     pInst = Cie.Instructions;
417     while (pInst < Cie.Next)
418     {
419         pInst += DwExecIntruction(&State, pInst);
420     }
421 
422     /* Parse the FDE instructions */
423     pInst = pFde->Instructions;
424     while (pInst < pFde->Next)
425     {
426         pInst += DwExecIntruction(&State, pInst);
427 
428         if (State.IsUwop)
429         {
430             c = StoreUnwindCodes(Info, &State, FunctionStart);
431             cbSize += c * sizeof(UNWIND_CODE);
432             Info->SizeOfProlog = State.Location - FunctionStart;
433         }
434     }
435     cbSize = ROUND_UP(cbSize, 4);
436 
437     /* Do we have scope table to write? */
438     if (State.cScopes > 0)
439     {
440         unsigned long i;
441         ULONG *pExceptionHandler;
442         PC_SCOPE_TABLE pScopeTable;
443 
444         /* Set flag for exception handler */
445         Info->Flags |= UNW_FLAG_EHANDLER;
446 
447         /* Store address of handler and number of scope tables */
448         pExceptionHandler = (ULONG*)((char*)Info + cbSize);
449         // HACK for testing purpose
450         *pExceptionHandler = FunctionStart; // _C_specific_handler
451 
452         pScopeTable = (PC_SCOPE_TABLE)(pExceptionHandler + 1);
453         pScopeTable->NumEntries = State.cScopes;
454 
455         /* Store the scope table entries */
456         for (i = 0; i < State.cScopes; i++)
457         {
458             pScopeTable->Entry[i].Begin = State.SehBlock[i].BeginTry;
459             pScopeTable->Entry[i].End = State.SehBlock[i].EndTry;
460             pScopeTable->Entry[i].Handler = 1;//State.SehBlock[i].Handler;
461             pScopeTable->Entry[i].Target = State.SehBlock[i].Target;
462         }
463 
464         /* Update size */
465         cbSize += 8 + State.cScopes * sizeof(C_SCOPE_TABLE_ENTRY);
466     }
467 
468     return cbSize;
469 }
470 
471 void
472 CountUnwindData(PFILE_INFO File)
473 {
474     DW2CIEFDE *p;
475     DW2FDE Fde;
476     char *pInst, *pmax;
477     DW2CFSTATE State;
478 
479     File->cFuncs = 0;
480     File->cScopes = 0;
481     File->cUWOP = 0;
482     State.FramePtr = 0;
483     State.TryLevel = 0;
484 
485     p = File->eh_frame.p;
486     pmax = (char*)p + File->eh_frame.psh->Misc.VirtualSize;
487     for (; p->Length && (char*)p < pmax; p = NextCIE(p))
488     {
489         /* Is this an FDE? */
490         if (p->CiePointer != 0)
491         {
492             File->cFuncs++;
493             DwDecodeFde(&Fde, (char*)p);
494 
495             pInst = Fde.Instructions;
496             while (pInst < Fde.Next)
497             {
498                 pInst += DwExecIntruction(&State, pInst);
499                 File->cUWOP += StoreUnwindCodes(NULL, &State, 0);
500                 File->cScopes += State.Scope ? 1 : 0;
501             }
502         }
503     }
504 
505     return;
506 }
507 
508 int CompFunc(const void *p1, const void *p2)
509 {
510     PRUNTIME_FUNCTION prf1 = (void*)p1, prf2 = (void*)p2;
511     return (prf1->FunctionStart > prf2->FunctionStart ? 1 : -1);
512 }
513 
514 void
515 GeneratePData(PFILE_INFO File)
516 {
517     DW2CIEFDE *p;
518     DW2FDE Fde;
519     PIMAGE_DATA_DIRECTORY Dir;
520     ULONG i, Offset;
521     void * eh_frame;
522     PRUNTIME_FUNCTION pdata;
523     ULONG xdata_va;
524     char *xdata_p;
525     ULONG cbSize;
526     PIMAGE_SECTION_HEADER pshp, pshx;
527     ULONG FileAlignment;
528     char *pmax;
529 
530     FileAlignment = File->OptionalHeader->FileAlignment;
531 
532     /* Get pointer to eh_frame section */
533     eh_frame = File->eh_frame.p;
534     g_ehframep = (ULONG)eh_frame;
535 
536     /* Get sizes */
537     CountUnwindData(File);
538 //    printf("cFuncs = %ld, cUWOPS = %ld, cScopes = %ld\n",
539 //        File->cFuncs, File->cUWOP, File->cScopes);
540 
541     /* Initialize section header for .pdata */
542     i = File->pdata.idx = File->UsedSections;
543     pshp = File->pdata.psh = &File->NewSectionHeaders[i];
544     memcpy(pshp->Name, ".pdata", 7);
545     pshp->Misc.VirtualSize = (File->cFuncs + 1) * sizeof(RUNTIME_FUNCTION);
546     pshp->VirtualAddress = File->NewSectionHeaders[i - 1].VirtualAddress +
547                            File->NewSectionHeaders[i - 1].SizeOfRawData;
548     pshp->SizeOfRawData = ROUND_UP(pshp->Misc.VirtualSize, FileAlignment);
549     pshp->PointerToRawData = File->NewSectionHeaders[i - 1].PointerToRawData +
550                            File->NewSectionHeaders[i - 1].SizeOfRawData;
551     pshp->PointerToRelocations = 0;
552     pshp->PointerToLinenumbers = 0;
553     pshp->NumberOfRelocations = 0;
554     pshp->NumberOfLinenumbers = 0;
555     pshp->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_NOT_PAGED |
556                             IMAGE_SCN_CNT_INITIALIZED_DATA;
557 
558     /* Allocate .pdata buffer */
559     pdata = File->pdata.p = malloc(pshp->SizeOfRawData);
560     memset(File->pdata.p, 0, pshp->SizeOfRawData);
561 
562     /* Init exception data dir */
563     Dir = &File->OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
564     Dir->VirtualAddress = pshp->VirtualAddress;
565     Dir->Size = pshp->Misc.VirtualSize;
566 
567     /* Initialize section header for .xdata */
568     File->xdata.idx = File->pdata.idx + 1;
569     pshx = File->xdata.psh = &File->NewSectionHeaders[File->xdata.idx];
570     memcpy(pshx->Name, ".xdata", 7);
571     pshx->Misc.VirtualSize = GetxdataSize(File->cFuncs, File->cUWOP, File->cScopes);
572     pshx->VirtualAddress = pshp->VirtualAddress + pshp->SizeOfRawData;
573     pshx->SizeOfRawData = ROUND_UP(pshx->Misc.VirtualSize, FileAlignment);
574     pshx->PointerToRawData = pshp->PointerToRawData + pshp->SizeOfRawData;
575     pshx->PointerToRelocations = 0;
576     pshx->PointerToLinenumbers = 0;
577     pshx->NumberOfRelocations = 0;
578     pshx->NumberOfLinenumbers = 0;
579     pshx->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_NOT_PAGED |
580                             IMAGE_SCN_CNT_INITIALIZED_DATA;
581 
582     /* Allocate .xdata buffer */
583     File->xdata.p = malloc(pshx->SizeOfRawData);
584     memset(File->xdata.p, 0, pshx->SizeOfRawData);
585 
586     i = 0;
587     Offset = File->eh_frame.psh->VirtualAddress;
588     xdata_va = pshx->VirtualAddress;
589     xdata_p = File->xdata.p;
590     pmax = (char*)eh_frame + File->eh_frame.psh->Misc.VirtualSize - 100;
591 
592     for (p = eh_frame; p->Length && (char*)p < pmax; p = NextCIE(p))
593     {
594         /* Is this an FDE? */
595         if (p->CiePointer != 0)
596         {
597             DwDecodeFde(&Fde, (char*)p);
598             pdata[i].FunctionStart = Offset + 8 + Fde.PcBegin;
599             pdata[i].FunctionEnd = pdata[i].FunctionStart + Fde.PcRange;
600             pdata[i].UnwindInfo = xdata_va;
601 
602 //            printf("%ld: RUNTIME_FUNCTION: {0x%lx, 0x%lx, 0x%lx}\n", i, pdata[i].FunctionStart, pdata[i].FunctionEnd, pdata[i].UnwindInfo);
603 
604             cbSize = StoreUnwindInfo((void*)xdata_p, &Fde, pdata[i].FunctionStart);
605             xdata_va += cbSize;
606             xdata_p += cbSize;
607             i++;
608         }
609         Offset += 4 + p->Length;
610     }
611 
612     /* Sort the RUNTIME_FUNCTIONS */
613     qsort(pdata, i, sizeof(RUNTIME_FUNCTION), CompFunc);
614 
615 }
616 
617 /** Functions for COFF ********************************************************/
618 
619 
620 WORD
621 CalculateChecksum(DWORD Start, void *pFile, ULONG cbSize)
622 {
623     WORD *Ptr = pFile;
624     DWORD i;
625     DWORD checksum = Start;
626 
627     for (i = 0; i < (cbSize + 1) / sizeof(WORD); i++)
628     {
629         checksum += Ptr[i];
630         checksum = (checksum + (checksum >> 16)) & 0xffff;
631     }
632 
633     return checksum ;
634 }
635 
636 void
637 WriteOutFile(FILE *handle, PFILE_INFO File)
638 {
639     int ret, Size, Pos = 0;
640     DWORD CheckSum;
641     ULONG i, Alignment;
642 
643     Alignment = File->OptionalHeader->FileAlignment;
644 
645     /* Update section count */
646     File->FileHeader->NumberOfSections = File->UsedSections + 2; // FIXME!!!
647 
648     /* Update SizeOfImage */
649     Size = File->xdata.psh->VirtualAddress
650            + File->xdata.psh->SizeOfRawData;
651     File->OptionalHeader->SizeOfImage = Size;
652 
653     /* Recalculate checksum */
654     CheckSum = CalculateChecksum(0, File->FilePtr, File->HeaderSize);
655     for (i = 0; i < File->AllSections; i++)
656     {
657         if (File->UseSection[i])
658         {
659             Size = File->SectionHeaders[i].SizeOfRawData;
660             if (Size)
661             {
662                 void *p;
663                 p = File->FilePtr + File->SectionHeaders[i].PointerToRawData;
664                 CheckSum = CalculateChecksum(CheckSum, p, Size);
665             }
666         }
667     }
668     Size = File->pdata.psh->Misc.VirtualSize;
669     CheckSum = CalculateChecksum(CheckSum, File->pdata.p, Size);
670     Size = File->xdata.psh->Misc.VirtualSize;
671     CheckSum = CalculateChecksum(CheckSum, File->xdata.p, Size);
672     CheckSum += File->HeaderSize;
673     CheckSum += File->pdata.psh->Misc.VirtualSize;
674     CheckSum += File->xdata.psh->Misc.VirtualSize;
675     File->OptionalHeader->CheckSum = CheckSum;
676 
677     /* Write file header */
678     Size = File->HeaderSize;
679     ret = fwrite(File->DosHeader, 1, Size, handle);
680     Pos = Size;
681 
682     /* Write Section headers */
683     Size = File->NewSectionHeaderSize;
684     ret = fwrite(File->NewSectionHeaders, 1, Size, handle);
685     Pos += Size;
686 
687     /* Fill up to next alignement */
688     Size = ROUND_UP(Pos, Alignment) - Pos;
689     ret = fwrite(File->AlignBuf, 1, Size, handle);
690     Pos += Size;
691 
692     /* Write sections */
693     for (i = 0; i < File->AllSections; i++)
694     {
695         if (File->UseSection[i])
696         {
697             void *p;
698             Size = File->SectionHeaders[i].SizeOfRawData;
699             if (Size)
700             {
701                 p = File->FilePtr + File->SectionHeaders[i].PointerToRawData;
702                 ret = fwrite(p, 1, Size, handle);
703                 Pos += Size;
704             }
705         }
706     }
707 
708     /* Write .pdata section */
709     Size = File->pdata.psh->SizeOfRawData;
710     ret = fwrite(File->pdata.p, 1, Size, handle);
711     Pos += Size;
712 
713     /* Write .xdata section */
714     Size = File->xdata.psh->SizeOfRawData;
715     ret = fwrite(File->xdata.p, 1, Size, handle);
716     Pos += Size;
717 
718 }
719 
720 
721 int
722 ParsePEHeaders(PFILE_INFO File)
723 {
724     DWORD OldChecksum, Checksum;
725     ULONG Alignment, CurrentPos;
726     int i, j;
727 
728     /* Check if MZ header exists  */
729     File->DosHeader = (PIMAGE_DOS_HEADER)File->FilePtr;
730     if ((File->DosHeader->e_magic != IMAGE_DOS_MAGIC) ||
731         (File->DosHeader->e_lfanew == 0L))
732     {
733         perror("Input file is not a PE image.\n");
734         return -1;
735     }
736 
737     /* Locate PE file header  */
738     File->FileHeader = (PIMAGE_FILE_HEADER)(File->FilePtr +
739                                File->DosHeader->e_lfanew + sizeof(ULONG));
740 
741     /* Check for x64 image */
742     if (File->FileHeader->Machine != IMAGE_FILE_MACHINE_AMD64)
743     {
744         perror("Input file is not an x64 image.\n");
745         return -1;
746     }
747 
748     /* Locate optional header */
749     File->OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)(File->FileHeader + 1);
750 
751     /* Check if checksum is correct */
752     OldChecksum = File->OptionalHeader->CheckSum;
753     File->OptionalHeader->CheckSum = 0;
754     Checksum = CalculateChecksum(0, File->FilePtr, File->cbInFileSize);
755     Checksum += File->cbInFileSize;
756     if ((Checksum & 0xffff) != (OldChecksum & 0xffff))
757     {
758         fprintf(stderr, "Input file has incorrect PE checksum: 0x%lx (calculated: 0x%lx)\n",
759             OldChecksum, Checksum);
760 //        return 0;
761     }
762 
763     /* Locate PE section headers  */
764     File->SectionHeaders = (PIMAGE_SECTION_HEADER)((char*)File->OptionalHeader
765                            + File->FileHeader->SizeOfOptionalHeader);
766 
767     File->HeaderSize = File->DosHeader->e_lfanew
768                        + sizeof(ULONG)
769                        + sizeof(IMAGE_FILE_HEADER)
770                        + File->FileHeader->SizeOfOptionalHeader;
771 
772     /* Create some shortcuts */
773     File->ImageBase = File->OptionalHeader->ImageBase;
774     File->Symbols = File->FilePtr + File->FileHeader->PointerToSymbolTable;
775     File->Strings = (char*)File->Symbols + File->FileHeader->NumberOfSymbols * 18;
776 
777     /* Check section names */
778     File->AllSections = File->FileHeader->NumberOfSections;
779     Alignment = File->OptionalHeader->FileAlignment;
780     File->NewSectionHeaders = malloc((File->AllSections+2) * sizeof(IMAGE_SECTION_HEADER));
781     File->UsedSections = 0;
782     File->eh_frame.idx = -1;
783 
784     /* Allocate array of chars, specifying whether to copy the section */
785     File->UseSection = malloc(File->AllSections);
786 
787     for (i = 0; i < File->AllSections; i++)
788     {
789         char *pName = (char*)File->SectionHeaders[i].Name;
790         File->UseSection[i] = 1;
791 
792         /* Check for long name */
793         if (pName[0] == '/')
794         {
795             unsigned long index = strtoul(pName+1, 0, 10);
796             pName = File->Strings + index;
797 
798             // Hack, simply remove all sections with long names
799             File->UseSection[i] = 0;
800         }
801 
802         /* Chek if we have the eh_frame section */
803         if (strcmp(pName, ".eh_frame") == 0)
804         {
805             File->eh_frame.psh = &File->SectionHeaders[i];
806             File->eh_frame.idx = i;
807             File->eh_frame.p = File->FilePtr + File->eh_frame.psh->PointerToRawData;
808         }
809 
810         /* Increase number of used sections */
811         if (File->UseSection[i])
812             File->UsedSections = i+1;
813 
814     }
815 
816     /* This is the actual size of the new section headers */
817     File->NewSectionHeaderSize =
818         (File->UsedSections+2) * sizeof(IMAGE_SECTION_HEADER);
819 
820     /* Calculate the position to start writing the sections to */
821     CurrentPos = File->HeaderSize + File->NewSectionHeaderSize;
822     CurrentPos = ROUND_UP(CurrentPos, Alignment);
823 
824     /* Create new section headers */
825     for (i = 0, j = 0; i < File->UsedSections; i++)
826     {
827         /* Copy section header */
828         File->NewSectionHeaders[j] = File->SectionHeaders[i];
829 
830         /* Shall we strip the section? */
831         if (File->UseSection[i] == 0)
832         {
833             /* Make it a bss section */
834             File->NewSectionHeaders[j].PointerToRawData = 0;
835             File->NewSectionHeaders[j].SizeOfRawData = 0;
836             File->NewSectionHeaders[j].Characteristics = 0xC0500080;
837         }
838 
839         /* Fix Offset into File */
840         File->NewSectionHeaders[j].PointerToRawData =
841               File->NewSectionHeaders[j].PointerToRawData ? CurrentPos : 0;
842         CurrentPos += File->NewSectionHeaders[j].SizeOfRawData;
843         j++;
844     }
845 
846     if (File->eh_frame.idx == -1)
847     {
848         //fprintf(stderr, "No .eh_frame section found\n");
849         return 0;
850     }
851 
852     return 1;
853 }
854 
855 int main(int argc, char* argv[])
856 {
857     char* pszInFile;
858     char* pszOutFile;
859     FILE_INFO File;
860     FILE* outfile;
861     int ret;
862     int arg, argstate = 0;
863     char *SourcePath = NULL;
864 
865     for (arg = 1; arg < argc; arg++)
866     {
867         switch (argstate)
868         {
869             default:
870                 argstate = -1;
871                 break;
872 
873             case 0:
874                 if (!strcmp(argv[arg], "-s"))
875                 {
876                     argstate = 1;
877                 }
878                 else
879                 {
880                     argstate = 2;
881                     pszInFile = convert_path(argv[arg]);
882                 }
883             break;
884 
885             case 1:
886                 free(SourcePath);
887                 SourcePath = strdup(argv[arg]);
888                 argstate = 0;
889                 break;
890 
891             case 2:
892                 pszOutFile = convert_path(argv[arg]);
893                 argstate = 3;
894                 break;
895         }
896     }
897 
898     if (argstate != 3)
899     {
900         fprintf(stderr, "Usage: rsym [-s <sources>] <input> <output>\n");
901         exit(1);
902     }
903 
904     File.FilePtr = load_file(pszInFile, &File.cbInFileSize);
905     if (!File.FilePtr)
906     {
907         fprintf(stderr, "An error occured loading '%s'\n", pszInFile);
908         exit(1);
909     }
910 
911     ret = ParsePEHeaders(&File);
912     if (ret != 1)
913     {
914         free(File.FilePtr);
915         exit(ret == -1 ? 1 : 0);
916     }
917 
918     File.AlignBuf = malloc(File.OptionalHeader->FileAlignment);
919     memset(File.AlignBuf, 0, File.OptionalHeader->FileAlignment);
920 
921     GeneratePData(&File);
922 
923     outfile = fopen(pszOutFile, "wb");
924     if (outfile == NULL)
925     {
926         perror("Cannot open output file");
927         free(File.FilePtr);
928         exit(1);
929     }
930 
931     WriteOutFile(outfile, &File);
932 
933     fclose(outfile);
934 
935     return 0;
936 }
937