more serialization bugfixes/cleaning, etc
[dyninst.git] / symtabAPI / src / emitWin.C
1 /*\r
2  * Copyright (c) 1996-2007 Barton P. Miller\r
3  *\r
4  * We provide the Paradyn Parallel Performance Tools (below\r
5  * described as "Paradyn") on an AS IS basis, and do not warrant its\r
6  * validity or performance.  We reserve the right to update, modify,\r
7  * or discontinue this software at any time.  We shall have no\r
8  * obligation to supply such updates or modifications or any other\r
9  * form of support to you.\r
10  *\r
11  * By your use of Paradyn, you understand and agree that we (or any\r
12  * other person or entity with proprietary rights in Paradyn) are\r
13  * under no obligation to provide either maintenance services,\r
14  * update services, notices of latent defects, or correction of\r
15  * defects for Paradyn.\r
16  *\r
17  * This library is free software; you can redistribute it and/or\r
18  * modify it under the terms of the GNU Lesser General Public\r
19  * License as published by the Free Software Foundation; either\r
20  * version 2.1 of the License, or (at your option) any later version.\r
21  *\r
22  * This library is distributed in the hope that it will be useful,\r
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
25  * Lesser General Public License for more details.\r
26  *\r
27  * You should have received a copy of the GNU Lesser General Public\r
28  * License along with this library; if not, write to the Free Software\r
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
30  */\r
31 \r
32 #include <algorithm>\r
33 #include "../common/h/parseauxv.h"\r
34 #include "emitWin.h"\r
35 #include "Symtab.h"\r
36 #include <iostream>\r
37 #include <dbghelp.h>\r
38 \r
39 //extern void symtab_log_perror(const char *msg);\r
40 \r
41 using namespace Dyninst;\r
42 using namespace Dyninst::SymtabAPI;\r
43 using namespace std;\r
44 \r
45 \r
46 emitWin::emitWin(PCHAR baseaddress, Object *o_nt, void (*err_func)(const char *)){\r
47     base_addr =  baseaddress;\r
48     obj_nt = o_nt;\r
49         err_func_ = err_func;\r
50 }\r
51 \r
52 PIMAGE_SECTION_HEADER emitWin::CreateSecHeader(unsigned int size, PIMAGE_SECTION_HEADER preSecHdr){\r
53     DWORD mem_offset, mem_size, disk_offset, disk_size;\r
54     PIMAGE_NT_HEADERS pNTHeader = obj_nt -> GetImageHeader();\r
55    \r
56     disk_size=PEAlign(size,pNTHeader -> OptionalHeader.FileAlignment);\r
57     mem_size=PEAlign(disk_size, pNTHeader->OptionalHeader.SectionAlignment);\r
58 \r
59     disk_offset=PEAlign(preSecHdr->PointerToRawData+preSecHdr->SizeOfRawData,\r
60                 pNTHeader->OptionalHeader.FileAlignment);\r
61 \r
62     mem_offset=PEAlign(preSecHdr->VirtualAddress+preSecHdr->Misc.VirtualSize,\r
63                 pNTHeader->OptionalHeader.SectionAlignment);\r
64     PIMAGE_SECTION_HEADER newSecHdr=new IMAGE_SECTION_HEADER;  \r
65     memset(newSecHdr,0,(size_t)sizeof(IMAGE_SECTION_HEADER));\r
66   \r
67     newSecHdr->PointerToRawData=disk_offset;\r
68     newSecHdr->VirtualAddress=mem_offset;\r
69 \r
70     newSecHdr->SizeOfRawData=disk_size;\r
71     newSecHdr->Misc.VirtualSize=mem_size;\r
72     newSecHdr->Characteristics=0xC0000040;\r
73     return newSecHdr;\r
74 }\r
75 \r
76 bool emitWin::AlignSection(PIMAGE_SECTION_HEADER p){\r
77     return true;\r
78 }\r
79 \r
80 bool emitWin::driver(Symtab *obj, std::string fName){\r
81         Offset MoveAheadOffset=0;\r
82     //get the number of new added sections\r
83     std::vector<Region *> newregs;\r
84     if(!obj -> getAllNewRegions(newregs)){\r
85                 log_winerror(err_func_, "No new section added, no need to drive");\r
86         printf("No new section added, no need to drive\n");\r
87         return false;\r
88     }\r
89     //printf("number of new added sections is %d\n",newregs.size());\r
90 \r
91     unsigned int n1 = NumOfAllowedSecInSectionTable();\r
92         \r
93     if(newregs.size() > n1){\r
94                 //no enough space in section table for new sections\r
95                 //need to use space in Dos Stub Area\r
96                 unsigned int n2= NumOfAllowedSecInDosHeader();\r
97                 if(newregs.size() > n1+n2){\r
98                         //can not accommandate all new sections even if steal all space in Dos Stub.\r
99                         log_winerror(err_func_, "no way to insert all new added sections, abort.\n");\r
100             printf("no way to insert all new added sections, abort.\n");\r
101             return false;\r
102                 }\r
103                 //need to move into Dos Stub Area\r
104                 isMoveAhead = true;\r
105                 //put n1 new section headers in section table, rest in dos stub\r
106                 MoveAheadOffset = SizeOfSecHeader*(newregs.size() - n1);\r
107                 //printf("move ahead in %lu bytes\n", MoveAheadOffset);\r
108 \r
109     }\r
110     //there is enough space in section header table for new added sections\r
111     //no need to steal space in Dos Stub Area\r
112     else \r
113                 isMoveAhead = false;\r
114 \r
115     //get the set of regions which include new added sections.\r
116     std::vector<Region *> regs;\r
117     if(!(obj -> getAllRegions(regs))) {\r
118         printf("Failed to get regions.\n");\r
119         log_winerror(err_func_, "Failed to get regions.\n");\r
120         return false;\r
121     }\r
122 \r
123     IMAGE_DOS_HEADER DosHeader;\r
124     memcpy(&DosHeader, base_addr, sizeof(IMAGE_DOS_HEADER));\r
125     DWORD peHdrOffset = DosHeader.e_lfanew;\r
126     DWORD dwDosStufOffset = sizeof(IMAGE_DOS_HEADER);\r
127     PIMAGE_NT_HEADERS NTHeader=new IMAGE_NT_HEADERS;\r
128 \r
129     memcpy(NTHeader, base_addr + DosHeader.e_lfanew, sizeof(IMAGE_NT_HEADERS));\r
130     assert(NTHeader->Signature == IMAGE_NT_SIGNATURE);\r
131    \r
132     //vector of section headers\r
133     //including the new added sections\r
134     std::vector<PIMAGE_SECTION_HEADER> secHdrs;\r
135     PIMAGE_NT_HEADERS pehdr = obj_nt->GetImageHeader();\r
136    \r
137     //pointer of 1st section header\r
138     PIMAGE_SECTION_HEADER pScnHdr = (PIMAGE_SECTION_HEADER)(((char*)pehdr) +\r
139                                  sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) +\r
140                                  pehdr->FileHeader.SizeOfOptionalHeader);\r
141 \r
142     //first, the original sections\r
143     for(unsigned int i=0; i<NTHeader->FileHeader.NumberOfSections; i++){\r
144         PIMAGE_SECTION_HEADER p=new IMAGE_SECTION_HEADER;\r
145         memcpy(p,pScnHdr, sizeof(IMAGE_SECTION_HEADER));\r
146         //printf("%d, name: %s, disk_off:%x\n",i, (const char*)p->Name, p->PointerToRawData);\r
147         secHdrs.push_back(p);\r
148         //AlignSection(p);\r
149         pScnHdr++;\r
150     }\r
151     //then, create section header for new added sections\r
152     PIMAGE_SECTION_HEADER pre = secHdrs[secHdrs.size()-1];\r
153     for(unsigned int i=0; i<newregs.size(); i++){\r
154         PIMAGE_SECTION_HEADER p = CreateSecHeader(newregs[i]->getDiskSize(), pre);\r
155         if((size_t)strlen(newregs[i]->getRegionName().c_str())> 8)\r
156             memcpy(p->Name, newregs[i]->getRegionName().c_str(),8);\r
157         else\r
158             memcpy(p->Name, newregs[i]->getRegionName().c_str(),\r
159             (size_t)strlen(newregs[i]->getRegionName().c_str()));\r
160         secHdrs.push_back(p);\r
161         pre = p;\r
162     }\r
163     //printf("size of sec headers:%d\n", secHdrs.size());\r
164     for(unsigned int i=0; i<secHdrs.size(); i++){\r
165         printf("%s, diskOff=%x, diskSize=%x, memoff=%x, memsize=%x\n", (const char*)secHdrs[i]->Name,secHdrs[i]->PointerToRawData, secHdrs[i]->SizeOfRawData,\r
166             secHdrs[i]->VirtualAddress, secHdrs[i]->Misc.VirtualSize);\r
167     }\r
168 \r
169     //calculate size of file\r
170     DWORD dwFileSize = secHdrs[secHdrs.size()-1]->PointerToRawData + secHdrs[secHdrs.size()-1]->SizeOfRawData;\r
171     //printf("size of file=%x", dwFileSize);\r
172 \r
173 \r
174     //open a file to write the image to disk\r
175     HANDLE hFile = CreateFileA(fName.c_str(),GENERIC_WRITE,\r
176                                FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,\r
177                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
178 \r
179     if(hFile == INVALID_HANDLE_VALUE){\r
180                 log_winerror(err_func_, "Failed to open a file to write.\n");\r
181         printf("Failed to open a file to write.\n");\r
182         return false;\r
183     }\r
184 \r
185     PCHAR pMem = (char*)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,dwFileSize);\r
186     if(pMem == NULL){\r
187                 log_winerror(err_func_, "Failed to allocate memory space for written file\n");\r
188         printf("Failed to allocate memory space for written file\n");\r
189         return false;\r
190     }\r
191 \r
192     // start to wrtie the file\r
193         Offset writeOffset = 0;\r
194     //retrieve the dos header from exe image\r
195     memcpy(pMem,&DosHeader,sizeof(IMAGE_DOS_HEADER));\r
196         writeOffset += sizeof(IMAGE_DOS_HEADER);\r
197 \r
198         //if move ahead, do adjustment for the pe pointer\r
199         if(isMoveAhead)\r
200                 ((PIMAGE_DOS_HEADER)pMem)->e_lfanew -= MoveAheadOffset;\r
201 \r
202     //write the dos stub\r
203     PCHAR dosStub = base_addr + sizeof(IMAGE_DOS_HEADER);\r
204         if(isMoveAhead){\r
205                 memcpy(pMem+writeOffset, dosStub, peHdrOffset-sizeof(IMAGE_DOS_HEADER)-MoveAheadOffset);\r
206                 writeOffset+=peHdrOffset-sizeof(IMAGE_DOS_HEADER)-MoveAheadOffset;\r
207         }\r
208         else{\r
209         memcpy(pMem+writeOffset, dosStub, peHdrOffset-sizeof(IMAGE_DOS_HEADER));\r
210                 writeOffset += peHdrOffset-sizeof(IMAGE_DOS_HEADER);\r
211         }\r
212    \r
213     //write the PE file header\r
214    \r
215     //write NT header\r
216     NTHeader->FileHeader.NumberOfSections = regs.size();\r
217     //update SizeOfImage\r
218     NTHeader->OptionalHeader.SizeOfImage = secHdrs[secHdrs.size()-1]->VirtualAddress + \r
219         secHdrs[secHdrs.size()-1]->Misc.VirtualSize;\r
220 \r
221     memcpy(pMem+writeOffset, NTHeader,sizeof(IMAGE_NT_HEADERS));\r
222     //if bound import table is not empty, update its virtual address\r
223     if(bit_addr != 0){\r
224         PIMAGE_NT_HEADERS newpeHdr = (PIMAGE_NT_HEADERS)((void *)(pMem+writeOffset));\r
225         PIMAGE_OPTIONAL_HEADER newoptHdr = (PIMAGE_OPTIONAL_HEADER)&newpeHdr -> OptionalHeader;\r
226         printf("old address: 0x%x\n", newoptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress);\r
227         newoptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress+=sizeof(IMAGE_SECTION_HEADER);\r
228         printf("new address: 0x%x\n", newoptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress);\r
229     }\r
230         writeOffset += sizeof(IMAGE_NT_HEADERS);\r
231 \r
232     //write section header table and section\r
233     //DWORD dwFirstSectionHeaderOffset = peHdrOffset + sizeof(IMAGE_NT_HEADERS);\r
234         DWORD dwFirstSectionHeaderOffset = writeOffset;\r
235 \r
236     for(unsigned int i=0; i<secHdrs.size(); i++){\r
237         memcpy(pMem+dwFirstSectionHeaderOffset + i*sizeof(IMAGE_SECTION_HEADER),secHdrs[i],\r
238             sizeof(IMAGE_SECTION_HEADER));\r
239         memcpy(pMem+secHdrs[i]->PointerToRawData, regs[i]->getPtrToRawData(),regs[i]->getDiskSize());\r
240 \r
241     }\r
242 \r
243     //write bound import table info\r
244     if(bit_addr != 0){\r
245         memcpy(pMem+dwFirstSectionHeaderOffset + secHdrs.size()*sizeof(IMAGE_SECTION_HEADER),\r
246             (char*)(base_addr+bit_addr), bit_size);\r
247     }\r
248 \r
249 \r
250         //WriteFile(hFile, secHdrs[i],sizeof(IMAGE_SECTION_HEADER), &dwByteWritten, NULL);\r
251         //printf("section header%d:%lu, %lu\n", i,sizeof(IMAGE_SECTION_HEADER), dwByteWritten);\r
252     //}   \r
253 \r
254     //write sections\r
255     //for(unsigned int i=0; i<secHdrs.size(); i++){\r
256     //    SetFilePointer(hFile,secHdrs[0]->PointerToRawData,NULL,FILE_BEGIN);\r
257     //    WriteFile(hFile, regs[i]->getPtrToRawData(),regs[i]->getDiskSize(),&dwByteWritten, NULL);\r
258     //    printf("section %d:%lu, %lu\n", i,regs[i]->getDiskSize(), dwByteWritten);\r
259     //}\r
260    \r
261 \r
262     //move the file pointer to beginning to write\r
263     DWORD dwByteWritten =0;\r
264     SetFilePointer(hFile, 0, NULL, FILE_BEGIN);\r
265     WriteFile(hFile, pMem, dwFileSize,&dwByteWritten, NULL);\r
266     // ------ FORCE CALCULATED FILE SIZE ------\r
267     SetFilePointer(hFile,dwFileSize,NULL,FILE_BEGIN);\r
268     SetEndOfFile(hFile);\r
269     CloseHandle(hFile);\r
270 \r
271     GlobalFree(pMem);\r
272 \r
273     return true;\r
274 }\r
275 \r
276 /*\r
277 void emitWin::AlignSections(){\r
278     PIMAGE_NT_HEADERS pNTHeader = obj_nt -> GetImageHeader();\r
279     std::vector<PIMAGE_SECTION_HEADER> sectionHeader = obj_nt ->getSectionHeaders();\r
280     std::vector<PIMAGE_SECTION_HEADER>::iterator iter;\r
281    \r
282     for(iter = sectionHeader.begin(); iter != sectionHeader.end(); iter++)\r
283     {\r
284         iter->VirtualAddress = PEAlign(iter->VirtualAddress,\r
285             pNTHeader->OptionalHeader.SectionAlignment);\r
286 \r
287         iter->Misc.VirtualSize=PEAlign(iter->Misc.VirtualSize,\r
288             pNTHeader->OptionalHeader.SectionAlignment);\r
289 \r
290         iter->PointerToRawData=PEAlign(iter->PointerToRawData,\r
291             pNTHeader->OptionalHeader.FileAlignment);\r
292 \r
293         iter->SizeOfRawData=PEAlign(iter->SizeOfRawData,\r
294             pNTHeader->OptionalHeader.FileAlignment);\r
295     }\r
296     pNTHeader->OptionalHeader.SizeOfImage=sectionHeader.end()->VirtualAddress+\r
297         sectionHeader.end()->Misc.VirtualSize;\r
298 }\r
299 */\r
300 \r
301 \r
302 /*In windows PE file, there are two alignments:\r
303  1. SectionAlignment: the alignment of section in memory\r
304  2. FileAlignment: the alignment of section in disk\r
305  */\r
306 //this function is used to calculated the aligned address given dwAddr.\r
307 //which is applicable for either case.\r
308 Offset emitWin::PEAlign(Offset dwAddr,Offset dwAlign)\r
309 {   \r
310     return(((dwAddr + dwAlign - 1) / dwAlign) * dwAlign);\r
311 }\r
312 \r
313 //This function is used to calculate the maximum number of sections allowed to insert\r
314 //for the section header table, the usable space includes the usused space plus the dos stub area if any.\r
315 unsigned int emitWin::NumOfTotalAllowedSec(){\r
316     unsigned int unusedSpaces = 0;\r
317    \r
318     //get the PE header pointer;\r
319     PIMAGE_NT_HEADERS peHdrs = obj_nt -> GetImageHeader();\r
320     //if no phdr, the\r
321     if(peHdrs){\r
322    \r
323     }\r
324 \r
325     return unusedSpaces;\r
326 }\r
327 \r
328 //to calculate the number of entries that can be inserted into section header table\r
329 //because of the fileAlignment, there could be unused space in section header table\r
330 unsigned int emitWin::NumOfAllowedSecInSectionTable(){\r
331 \r
332     //get base address of mapped file\r
333     PIMAGE_NT_HEADERS peHdr = obj_nt -> GetImageHeader();\r
334     IMAGE_OPTIONAL_HEADER optHdr = peHdr -> OptionalHeader;\r
335 \r
336     unsigned int NumOfDataDir = optHdr.NumberOfRvaAndSizes;\r
337     //printf("NumOfDataDir: %lu\n", NumOfDataDir);\r
338     assert( NumOfDataDir == 16);\r
339 \r
340     //retrieve information on bound import table\r
341     //which is 12th entry (IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) in data directory\r
342     //Note: bound import table is right after section header table and before sections\r
343     IMAGE_DATA_DIRECTORY bit = optHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT];\r
344     bit_addr = bit.VirtualAddress;\r
345     bit_size = bit.Size;\r
346 \r
347     //printf("bound import table:0x%x, 0x%x\n", bit_addr, bit_size);\r
348 \r
349     //calculate the size of space between section header table and section\r
350     PIMAGE_SECTION_HEADER pScnHdr = (PIMAGE_SECTION_HEADER)(((char*)peHdr) +\r
351                                  sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) +\r
352                                  peHdr->FileHeader.SizeOfOptionalHeader); //pointer of 1st section header\r
353     unsigned int nSections = peHdr->FileHeader.NumberOfSections;\r
354 \r
355     DWORD dwFirstSectionHeaderOffset = ((PIMAGE_DOS_HEADER)base_addr)->e_lfanew + sizeof(IMAGE_NT_HEADERS);\r
356 \r
357     DWORD dwStuffSize = pScnHdr ->PointerToRawData - (dwFirstSectionHeaderOffset + nSections*sizeof(IMAGE_SECTION_HEADER));\r
358 \r
359     unsigned int ret = (dwStuffSize - bit_size)/SizeOfSecHeader;\r
360     //printf("extra section headers: %lu\n", ret);\r
361     return ret;\r
362 \r
363 }\r
364 \r
365 //to calculate the number of entries that can be inserted into Dos Stub area\r
366 //we don't care the Dos Stub, so just steal the space in that area for the additional section header\r
367 unsigned int emitWin::NumOfAllowedSecInDosHeader(){\r
368          //get base address of mapped file\r
369     PIMAGE_NT_HEADERS peHdr = obj_nt -> GetImageHeader();\r
370         //size of dos stub area\r
371         unsigned int size = (char*)peHdr - (base_addr+sizeof(IMAGE_DOS_HEADER));\r
372         unsigned int ret = size/SizeOfSecHeader;\r
373         //printf("extra section headers in dos stub: %lu\n", ret);\r
374         return ret;\r
375 }\r
376 \r
377 void emitWin::log_winerror(void (*err_func)(const char *), const char* msg) {
378     err_func(msg);
379 }