OSDN Git Service

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