OSDN Git Service

86032c995df1fe493cb46083d456335d02be934f
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / shared / peutils.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "peutils.h"
35
36 #include <utils/winutils.h>
37 #include <QtCore/QStringList>
38 #include <QtCore/QDebug>
39 #include <climits>
40 #include <windows.h>
41
42 using Utils::winErrorMessage;
43
44 // Create a pointer from base and offset when rummaging around in
45 // a memory mapped file
46
47 template <class Ptr>
48 inline Ptr *makePtr(void *base, ptrdiff_t offset)
49 {
50     return reinterpret_cast<Ptr*>(static_cast<char*>(base) + offset);
51 }
52
53 // CodeView header
54 struct CV_HEADER
55 {
56     DWORD CvSignature; // NBxx
57     LONG  Offset;      // Always 0 for NB10
58 };
59
60 // CodeView NB10 debug information of a PDB 2.00 file (VS 6)
61 struct CV_INFO_PDB20
62 {
63     CV_HEADER  Header;
64     DWORD      Signature;
65     DWORD      Age;
66     BYTE       PdbFileName[1];
67 };
68
69 // CodeView RSDS debug information of a PDB 7.00 file
70 struct CV_INFO_PDB70
71 {
72     DWORD      CvSignature;
73     GUID       Signature;
74     DWORD      Age;
75     BYTE       PdbFileName[1];
76 };
77
78 // Retrieve the NT image header of an executable via the legacy DOS header.
79 static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
80 {
81     IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
82     // Check DOS header consistency
83     if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
84         || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
85         *errorMessage = QString::fromLatin1("DOS header check failed.");
86         return 0;
87     }
88     // Retrieve NT header
89     IMAGE_NT_HEADERS *ntHeaders = makePtr<IMAGE_NT_HEADERS>(dosHeader, dosHeader->e_lfanew);
90     // check NT header consistency
91     if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
92         || ntHeaders->Signature != IMAGE_NT_SIGNATURE
93         || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
94         *errorMessage = QString::fromLatin1("NT header check failed.");
95         return 0;
96     }
97     // Check magic
98     const WORD magic = ntHeaders->OptionalHeader.Magic;
99 #ifdef  __GNUC__ // MinGW does not have complete 64bit definitions.
100     if (magic != 0x10b) {
101         *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is not that of a 32-bit executable.").
102                         arg(magic);
103         return 0;
104     }
105 #else
106     if (magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
107         *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is none of %2, %3.").
108                         arg(magic).arg(IMAGE_NT_OPTIONAL_HDR32_MAGIC).arg(IMAGE_NT_OPTIONAL_HDR64_MAGIC);
109         return 0;
110     }
111 #endif
112     // Check section headers
113     IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
114     if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
115         *errorMessage = QString::fromLatin1("NT header section header check failed.");
116         return 0;
117     }
118     return ntHeaders;
119 }
120
121 // Find the COFF section an RVA belongs to and convert to file offset
122 static bool getFileOffsetFromRVA(IMAGE_NT_HEADERS *ntHeaders, DWORD rva, DWORD* fileOffset)
123 {
124     IMAGE_SECTION_HEADER *sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
125     for( int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++ ) {
126         const DWORD sectionSize = sectionHeader->Misc.VirtualSize ?
127                                   sectionHeader->Misc.VirtualSize : sectionHeader->SizeOfRawData;
128         if ((rva >= sectionHeader->VirtualAddress) && (rva < sectionHeader->VirtualAddress + sectionSize)) {
129             const DWORD diff = sectionHeader->VirtualAddress - sectionHeader->PointerToRawData;
130             *fileOffset = rva - diff;
131             return true;
132         }
133     }
134     return false;
135 }
136
137 // Retrieve debug directory and number of entries
138 static bool getDebugDirectory(IMAGE_NT_HEADERS *ntHeaders,
139                               void *fileMemory,
140                               IMAGE_DEBUG_DIRECTORY **debugDir,
141                               int *count,
142                               QString *errorMessage)
143 {
144     DWORD debugDirRva = 0;
145     DWORD debugDirSize;
146     *debugDir = 0;
147     *count = 0;
148 #ifdef  __GNUC__ // MinGW does not have complete 64bit definitions.
149     typedef IMAGE_OPTIONAL_HEADER IMAGE_OPTIONAL_HEADER32;
150 #else
151     // Find the virtual address
152     const bool is64Bit = ntHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
153     if (is64Bit) {
154         IMAGE_OPTIONAL_HEADER64 *optionalHeader64 = reinterpret_cast<IMAGE_OPTIONAL_HEADER64*>(&(ntHeaders->OptionalHeader));
155         debugDirRva = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
156         debugDirSize = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
157     } else {
158 #endif
159         IMAGE_OPTIONAL_HEADER32 *optionalHeader32 = reinterpret_cast<IMAGE_OPTIONAL_HEADER32*>(&(ntHeaders->OptionalHeader));
160         debugDirRva = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
161         debugDirSize = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
162 #ifndef  __GNUC__
163     }
164 #endif
165     // Empty. This is the case for MinGW binaries
166     if (debugDirSize == 0)
167         return true;
168     // Look up in  file
169     DWORD debugDirOffset;
170     if (!getFileOffsetFromRVA(ntHeaders, debugDirRva, &debugDirOffset)) {
171         *errorMessage = QString::fromLatin1("Unable to locate debug dir RVA %1/%2.").arg(debugDirRva).arg(debugDirSize);
172         return false;
173     }
174     *debugDir = makePtr<IMAGE_DEBUG_DIRECTORY>(fileMemory, debugDirOffset);
175     // Check
176     if (IsBadReadPtr(*debugDir, debugDirSize) || debugDirSize < sizeof(IMAGE_DEBUG_DIRECTORY)) {
177         *errorMessage = QString::fromLatin1("Debug directory corrupted.");
178         return 0;
179     }
180
181     *count = debugDirSize / sizeof(IMAGE_DEBUG_DIRECTORY);
182     return debugDir;
183 }
184
185 // Return the PDB file of a Code View debug section
186 static QString getPDBFileOfCodeViewSection(void *debugInfo, DWORD size)
187 {
188     static const DWORD CV_SIGNATURE_NB10 = 0x3031424e; // '01BN';
189     static const DWORD CV_SIGNATURE_RSDS = 0x53445352; // 'SDSR';
190     if (IsBadReadPtr(debugInfo, size) || size < sizeof(DWORD))
191         return QString();
192
193     const DWORD cvSignature = *static_cast<DWORD*>(debugInfo);
194     if (cvSignature == CV_SIGNATURE_NB10) {
195         CV_INFO_PDB20* cvInfo = static_cast<CV_INFO_PDB20*>(debugInfo);
196         if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB20)))
197             return QString();
198         CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
199         if (IsBadStringPtrA(pdbFileName, UINT_MAX))
200             return QString();
201         return QString::fromLocal8Bit(pdbFileName);
202     }
203     if (cvSignature == CV_SIGNATURE_RSDS) {
204         CV_INFO_PDB70* cvInfo = static_cast<CV_INFO_PDB70*>(debugInfo);
205         if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB70)))
206             return QString();
207         CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
208         if (IsBadStringPtrA(pdbFileName, UINT_MAX))
209             return QString();
210         return QString::fromLocal8Bit(pdbFileName);
211     }
212     return QString();
213 }
214
215 // Collect all PDB files of all debug sections
216 static void collectPDBfiles(void *fileMemory, IMAGE_DEBUG_DIRECTORY *directoryBase, int count, QStringList *pdbFiles)
217 {
218     for (int i = 0; i < count; i++, directoryBase++)
219         if (directoryBase->Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
220            const QString pdb = getPDBFileOfCodeViewSection(static_cast<char*>(fileMemory) + directoryBase->PointerToRawData, directoryBase->SizeOfData);
221            if (!pdb.isEmpty())
222                pdbFiles->push_back(pdb);
223        }
224 }
225
226 namespace Debugger {
227 namespace Internal {
228
229 bool getPDBFiles(const QString &peExecutableFileName, QStringList *rc, QString *errorMessage)
230 {
231     HANDLE hFile = NULL;
232     HANDLE hFileMap = NULL;
233     void *fileMemory = 0;
234     bool success = false;
235
236     rc->clear();
237     do {
238         // Create a memory mapping of the file
239         hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
240                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
241         if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
242             *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
243             break;
244         }
245
246         hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
247         if (hFileMap == NULL) {
248             *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
249             break;
250         }
251
252         fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
253         if(!fileMemory) {
254             *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
255             break;
256         }
257
258         IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
259         if (!ntHeaders)
260             break;
261
262         int debugSectionCount;
263         IMAGE_DEBUG_DIRECTORY *debugDir;
264         if (!getDebugDirectory(ntHeaders, fileMemory, &debugDir, &debugSectionCount, errorMessage))
265             return false;
266         if (debugSectionCount)
267             collectPDBfiles(fileMemory, debugDir, debugSectionCount, rc);
268         success = true;
269     }  while(false);
270
271     if (fileMemory)
272         UnmapViewOfFile(fileMemory);
273
274     if (hFileMap != NULL)
275         CloseHandle(hFileMap);
276
277     if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
278         CloseHandle(hFile);
279
280     return success;
281 }
282
283 } // namespace Internal
284 } // namespace Debugger