summaryrefslogtreecommitdiff
path: root/src/plugins/debugger/shared/peutils.cpp
diff options
context:
space:
mode:
authorhjk <qtc-committer@nokia.com>2009-05-19 15:45:58 +0200
committerhjk <qtc-committer@nokia.com>2009-05-19 15:45:58 +0200
commitcce1c7f78e29a498db034cdb2854ae2aba5e3387 (patch)
tree52239ecb0cb555e84899026faa087f702560fe5f /src/plugins/debugger/shared/peutils.cpp
parent420cd301c7b9279c57c2c9716fda06358b15e153 (diff)
downloadqt-creator-cce1c7f78e29a498db034cdb2854ae2aba5e3387.tar.gz
debugger: rename 'win' subdir to 'shared'
Diffstat (limited to 'src/plugins/debugger/shared/peutils.cpp')
-rw-r--r--src/plugins/debugger/shared/peutils.cpp279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/plugins/debugger/shared/peutils.cpp b/src/plugins/debugger/shared/peutils.cpp
new file mode 100644
index 0000000000..084e511f46
--- /dev/null
+++ b/src/plugins/debugger/shared/peutils.cpp
@@ -0,0 +1,279 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+**************************************************************************/
+
+#include "peutils.h"
+
+#include <utils/winutils.h>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+#include <windows.h>
+
+using Core::Utils::winErrorMessage;
+
+// Create a pointer from base and offset when rummaging around in
+// a memory mapped file
+
+template <class Ptr>
+inline Ptr *makePtr(void *base, ptrdiff_t offset)
+{
+ return reinterpret_cast<Ptr*>(static_cast<char*>(base) + offset);
+}
+
+// CodeView header
+struct CV_HEADER
+{
+ DWORD CvSignature; // NBxx
+ LONG Offset; // Always 0 for NB10
+};
+
+// CodeView NB10 debug information of a PDB 2.00 file (VS 6)
+struct CV_INFO_PDB20
+{
+ CV_HEADER Header;
+ DWORD Signature;
+ DWORD Age;
+ BYTE PdbFileName[1];
+};
+
+// CodeView RSDS debug information of a PDB 7.00 file
+struct CV_INFO_PDB70
+{
+ DWORD CvSignature;
+ GUID Signature;
+ DWORD Age;
+ BYTE PdbFileName[1];
+};
+
+// Retrieve the NT image header of an executable via the legacy DOS header.
+static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
+{
+ IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
+ // Check DOS header consistency
+ if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
+ || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
+ *errorMessage = QString::fromLatin1("DOS header check failed.");
+ return 0;
+ }
+ // Retrieve NT header
+ IMAGE_NT_HEADERS *ntHeaders = makePtr<IMAGE_NT_HEADERS>(dosHeader, dosHeader->e_lfanew);
+ // check NT header consistency
+ if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
+ || ntHeaders->Signature != IMAGE_NT_SIGNATURE
+ || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header check failed.");
+ return 0;
+ }
+ // Check magic
+ const WORD magic = ntHeaders->OptionalHeader.Magic;
+#ifdef __GNUC__ // MinGW does not have complete 64bit definitions.
+ if (magic != 0x10b) {
+ *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is not that of a 32-bit executable.").
+ arg(magic);
+ return 0;
+ }
+#else
+ if (magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is none of %2, %3.").
+ arg(magic).arg(IMAGE_NT_OPTIONAL_HDR32_MAGIC).arg(IMAGE_NT_OPTIONAL_HDR64_MAGIC);
+ return 0;
+ }
+#endif
+ // Check section headers
+ IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
+ if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
+ *errorMessage = QString::fromLatin1("NT header section header check failed.");
+ return 0;
+ }
+ return ntHeaders;
+}
+
+// Find the COFF section an RVA belongs to and convert to file offset
+static bool getFileOffsetFromRVA(IMAGE_NT_HEADERS *ntHeaders, DWORD rva, DWORD* fileOffset)
+{
+ IMAGE_SECTION_HEADER *sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
+ for( int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++ ) {
+ const DWORD sectionSize = sectionHeader->Misc.VirtualSize ?
+ sectionHeader->Misc.VirtualSize : sectionHeader->SizeOfRawData;
+ if ((rva >= sectionHeader->VirtualAddress) && (rva < sectionHeader->VirtualAddress + sectionSize)) {
+ const DWORD diff = sectionHeader->VirtualAddress - sectionHeader->PointerToRawData;
+ *fileOffset = rva - diff;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Retrieve debug directory and number of entries
+static bool getDebugDirectory(IMAGE_NT_HEADERS *ntHeaders,
+ void *fileMemory,
+ IMAGE_DEBUG_DIRECTORY **debugDir,
+ int *count,
+ QString *errorMessage)
+{
+ DWORD debugDirRva = 0;
+ DWORD debugDirSize;
+ *debugDir = 0;
+ *count = 0;
+#ifdef __GNUC__ // MinGW does not have complete 64bit definitions.
+ typedef IMAGE_OPTIONAL_HEADER IMAGE_OPTIONAL_HEADER32;
+#else
+ // Find the virtual address
+ const bool is64Bit = ntHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
+ if (is64Bit) {
+ IMAGE_OPTIONAL_HEADER64 *optionalHeader64 = reinterpret_cast<IMAGE_OPTIONAL_HEADER64*>(&(ntHeaders->OptionalHeader));
+ debugDirRva = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ debugDirSize = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ } else {
+#endif
+ IMAGE_OPTIONAL_HEADER32 *optionalHeader32 = reinterpret_cast<IMAGE_OPTIONAL_HEADER32*>(&(ntHeaders->OptionalHeader));
+ debugDirRva = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ debugDirSize = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+#ifndef __GNUC__
+ }
+#endif
+ // Empty. This is the case for MinGW binaries
+ if (debugDirSize == 0)
+ return true;
+ // Look up in file
+ DWORD debugDirOffset;
+ if (!getFileOffsetFromRVA(ntHeaders, debugDirRva, &debugDirOffset)) {
+ *errorMessage = QString::fromLatin1("Unable to locate debug dir RVA %1/%2.").arg(debugDirRva).arg(debugDirSize);
+ return false;
+ }
+ *debugDir = makePtr<IMAGE_DEBUG_DIRECTORY>(fileMemory, debugDirOffset);
+ // Check
+ if (IsBadReadPtr(*debugDir, debugDirSize) || debugDirSize < sizeof(IMAGE_DEBUG_DIRECTORY)) {
+ *errorMessage = QString::fromLatin1("Debug directory corrupted.");
+ return 0;
+ }
+
+ *count = debugDirSize / sizeof(IMAGE_DEBUG_DIRECTORY);
+ return debugDir;
+}
+
+// Return the PDB file of a Code View debug section
+static QString getPDBFileOfCodeViewSection(void *debugInfo, DWORD size)
+{
+ static const DWORD CV_SIGNATURE_NB10 = 0x3031424e; // '01BN';
+ static const DWORD CV_SIGNATURE_RSDS = 0x53445352; // 'SDSR';
+ if (IsBadReadPtr(debugInfo, size) || size < sizeof(DWORD))
+ return QString();
+
+ const DWORD cvSignature = *static_cast<DWORD*>(debugInfo);
+ if (cvSignature == CV_SIGNATURE_NB10) {
+ CV_INFO_PDB20* cvInfo = static_cast<CV_INFO_PDB20*>(debugInfo);
+ if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB20)))
+ return QString();
+ CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
+ if (IsBadStringPtrA(pdbFileName, UINT_MAX))
+ return QString();
+ return QString::fromLocal8Bit(pdbFileName);
+ }
+ if (cvSignature == CV_SIGNATURE_RSDS) {
+ CV_INFO_PDB70* cvInfo = static_cast<CV_INFO_PDB70*>(debugInfo);
+ if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB70)))
+ return QString();
+ CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
+ if (IsBadStringPtrA(pdbFileName, UINT_MAX))
+ return QString();
+ return QString::fromLocal8Bit(pdbFileName);
+ }
+ return QString();
+}
+
+// Collect all PDB files of all debug sections
+static void collectPDBfiles(void *fileMemory, IMAGE_DEBUG_DIRECTORY *directoryBase, int count, QStringList *pdbFiles)
+{
+ for (int i = 0; i < count; i++, directoryBase++)
+ if (directoryBase->Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
+ const QString pdb = getPDBFileOfCodeViewSection(static_cast<char*>(fileMemory) + directoryBase->PointerToRawData, directoryBase->SizeOfData);
+ if (!pdb.isEmpty())
+ pdbFiles->push_back(pdb);
+ }
+}
+
+namespace Debugger {
+namespace Internal {
+
+bool getPDBFiles(const QString &peExecutableFileName, QStringList *rc, QString *errorMessage)
+{
+ HANDLE hFile = NULL;
+ HANDLE hFileMap = NULL;
+ void *fileMemory = 0;
+ bool success = false;
+
+ rc->clear();
+ do {
+ // Create a memory mapping of the file
+ hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hFileMap == NULL) {
+ *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
+ if(!fileMemory) {
+ *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
+ break;
+ }
+
+ IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
+ if (!ntHeaders)
+ break;
+
+ int debugSectionCount;
+ IMAGE_DEBUG_DIRECTORY *debugDir;
+ if (!getDebugDirectory(ntHeaders, fileMemory, &debugDir, &debugSectionCount, errorMessage))
+ return false;
+ if (debugSectionCount)
+ collectPDBfiles(fileMemory, debugDir, debugSectionCount, rc);
+ success = true;
+ } while(false);
+
+ if (fileMemory)
+ UnmapViewOfFile(fileMemory);
+
+ if (hFileMap != NULL)
+ CloseHandle(hFileMap);
+
+ if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ return success;
+}
+
+} // namespace Internal
+} // namespace Debugger