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