forked from qt-creator/qt-creator
m.txt
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "consoleprocess.h"
|
||||
#include "winutils.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDir>
|
||||
@@ -122,7 +123,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
|
||||
delete m_tempFile;
|
||||
m_tempFile = 0;
|
||||
stubServerShutdown();
|
||||
emit processError(tr("The process could not be started!"));
|
||||
emit processError(tr("The process '%1' could not be started: %2").arg(cmdLine, winErrorMessage(GetLastError())));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -173,18 +174,6 @@ void ConsoleProcess::stubConnectionAvailable()
|
||||
connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
|
||||
}
|
||||
|
||||
static QString errorMsg(int code)
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
|
||||
int len = FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, (DWORD)code, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
QString ret = QString::fromUtf16((ushort *)lpMsgBuf, len);
|
||||
LocalFree(lpMsgBuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ConsoleProcess::readStubOutput()
|
||||
{
|
||||
while (m_stubSocket->canReadLine()) {
|
||||
@@ -192,10 +181,10 @@ void ConsoleProcess::readStubOutput()
|
||||
out.chop(2); // \r\n
|
||||
if (out.startsWith("err:chdir ")) {
|
||||
emit processError(tr("Cannot change to working directory %1: %2")
|
||||
.arg(workingDirectory(), errorMsg(out.mid(10).toInt())));
|
||||
.arg(workingDirectory(), winErrorMessage(out.mid(10).toInt())));
|
||||
} else if (out.startsWith("err:exec ")) {
|
||||
emit processError(tr("Cannot execute %1: %2")
|
||||
.arg(m_executable, errorMsg(out.mid(9).toInt())));
|
||||
.arg(m_executable, winErrorMessage(out.mid(9).toInt())));
|
||||
} else if (out.startsWith("pid ")) {
|
||||
// Will not need it any more
|
||||
delete m_tempFile;
|
||||
@@ -207,7 +196,7 @@ void ConsoleProcess::readStubOutput()
|
||||
FALSE, m_appPid);
|
||||
if (m_hInferior == NULL) {
|
||||
emit processError(tr("Cannot obtain a handle to the inferior: %1")
|
||||
.arg(errorMsg(GetLastError())));
|
||||
.arg(winErrorMessage(GetLastError())));
|
||||
// Uhm, and now what?
|
||||
continue;
|
||||
}
|
||||
@@ -237,7 +226,7 @@ void ConsoleProcess::inferiorExited()
|
||||
|
||||
if (!GetExitCodeProcess(m_hInferior, &chldStatus))
|
||||
emit processError(tr("Cannot obtain exit status from inferior: %1")
|
||||
.arg(errorMsg(GetLastError())));
|
||||
.arg(winErrorMessage(GetLastError())));
|
||||
cleanupInferior();
|
||||
m_appStatus = QProcess::NormalExit;
|
||||
m_appCode = chldStatus;
|
||||
|
||||
@@ -26,8 +26,13 @@ SOURCES += \
|
||||
submiteditorwidget.cpp \
|
||||
synchronousprocess.cpp
|
||||
|
||||
win32:SOURCES += consoleprocess_win.cpp
|
||||
else:SOURCES += consoleprocess_unix.cpp
|
||||
win32 {
|
||||
SOURCES += consoleprocess_win.cpp \
|
||||
winutils.cpp
|
||||
HEADERS += winutils.h
|
||||
} else {
|
||||
SOURCES += consoleprocess_unix.cpp
|
||||
}
|
||||
|
||||
HEADERS += \
|
||||
utils_global.h \
|
||||
|
||||
56
src/libs/utils/winutils.cpp
Normal file
56
src/libs/utils/winutils.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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 "winutils.h"
|
||||
#include <windows.h>
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace Core {
|
||||
namespace Utils {
|
||||
|
||||
QWORKBENCH_UTILS_EXPORT QString winErrorMessage(unsigned long error)
|
||||
{
|
||||
QString rc = QString::fromLatin1("#%1: ").arg(error);
|
||||
ushort *lpMsgBuf;
|
||||
|
||||
const int len = FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
if (len) {
|
||||
rc = QString::fromUtf16(lpMsgBuf, len);
|
||||
LocalFree(lpMsgBuf);
|
||||
} else {
|
||||
rc += QString::fromLatin1("<unknown error>");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace Core
|
||||
48
src/libs/utils/winutils.h
Normal file
48
src/libs/utils/winutils.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef WINUTILS_H
|
||||
#define WINUTILS_H
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core {
|
||||
namespace Utils {
|
||||
|
||||
// Helper to format a Windows error message, taking the
|
||||
// code as returned by the GetLastError()-API.
|
||||
QWORKBENCH_UTILS_EXPORT QString winErrorMessage(unsigned long error);
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace Core
|
||||
#endif // WINUTILS_H
|
||||
@@ -198,7 +198,7 @@ bool CdbDebugEngine::startDebugger()
|
||||
m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
|
||||
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
|
||||
|
||||
if (m_d->m_debuggerManager->startMode() == DebuggerManager::AttachExternal) {
|
||||
if (m_d->m_debuggerManager->startMode() == AttachExternal) {
|
||||
qWarning("CdbDebugEngine: attach to process not yet implemented!");
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -86,4 +86,7 @@ HEADERS += $$PWD/modeltest.h
|
||||
DEFINES += USE_MODEL_TEST=1
|
||||
}
|
||||
|
||||
win32 {
|
||||
include(win/win.pri)
|
||||
CONFIG(cdbdebugger):include(cdb\cdb.pri)
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ const char * const GDBRUNNING = "Gdb.Running";
|
||||
|
||||
const char * const PROPERTY_REGISTER_FORMAT = "Debugger.Property.RegisterFormat";
|
||||
|
||||
namespace Internal {
|
||||
enum { debug = 0 };
|
||||
}
|
||||
} // namespace Constants
|
||||
} // namespace Debugger
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@
|
||||
#include "watchhandler.h"
|
||||
|
||||
#include "debuggerdialogs.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# include "peutils.h"
|
||||
#endif
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
@@ -432,6 +434,8 @@ void DebuggerManager::init()
|
||||
winEngine = createWinEngine(this);
|
||||
scriptEngine = createScriptEngine(this);
|
||||
setDebuggerType(GdbDebugger);
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << gdbEngine << winEngine << scriptEngine;
|
||||
}
|
||||
|
||||
void DebuggerManager::setDebuggerType(DebuggerType type)
|
||||
@@ -549,7 +553,8 @@ void DebuggerManager::clearStatusMessage()
|
||||
void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
|
||||
{
|
||||
Q_UNUSED(timeout)
|
||||
//qDebug() << "STATUS: " << msg;
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << "STATUS MSG: " << msg;
|
||||
showDebuggerOutput("status:", msg);
|
||||
m_statusLabel->setText(" " + msg);
|
||||
if (timeout > 0) {
|
||||
@@ -594,8 +599,9 @@ void DebuggerManager::notifyInferiorExited()
|
||||
|
||||
void DebuggerManager::notifyInferiorPidChanged(int pid)
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << pid;
|
||||
//QMessageBox::warning(0, "PID", "PID: " + QString::number(pid));
|
||||
//qDebug() << "PID: " << pid;
|
||||
emit inferiorPidChanged(pid);
|
||||
}
|
||||
|
||||
@@ -606,11 +612,11 @@ void DebuggerManager::showApplicationOutput(const QString &str)
|
||||
|
||||
void DebuggerManager::shutdown()
|
||||
{
|
||||
//qDebug() << "DEBUGGER_MANAGER SHUTDOWN START";
|
||||
if (m_engine) {
|
||||
//qDebug() << "SHUTTING DOWN ENGINE" << m_engine;
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << m_engine;
|
||||
|
||||
if (m_engine)
|
||||
m_engine->shutdown();
|
||||
}
|
||||
m_engine = 0;
|
||||
|
||||
delete scriptEngine;
|
||||
@@ -673,6 +679,9 @@ void DebuggerManager::toggleBreakpoint()
|
||||
|
||||
void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||
|
||||
QTC_ASSERT(m_engine, return);
|
||||
QTC_ASSERT(m_breakHandler, return);
|
||||
if (status() != DebuggerInferiorRunning
|
||||
@@ -779,8 +788,43 @@ void DebuggerManager::attachCore()
|
||||
emit debuggingFinished();
|
||||
}
|
||||
|
||||
// Figure out the debugger type of an exexcutable
|
||||
static bool determineDebuggerType(const QString &executable,
|
||||
DebuggerManager::DebuggerType *dt,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (executable.endsWith(QLatin1String(".js"))) {
|
||||
*dt = DebuggerManager::ScriptDebugger;
|
||||
return true;
|
||||
}
|
||||
#ifndef Q_WS_WIN
|
||||
*dt = DebuggerManager::GdbDebugger;
|
||||
return true;
|
||||
#else
|
||||
// If a file has PDB files, it has been compiled by VS.
|
||||
QStringList pdbFiles;
|
||||
if (!getPDBFiles(executable, &pdbFiles, errorMessage))
|
||||
return false;
|
||||
if (pdbFiles.empty()) {
|
||||
*dt = DebuggerManager::GdbDebugger;
|
||||
return true;
|
||||
}
|
||||
// We need the CDB debugger in order to be able to debug VS
|
||||
// executables
|
||||
if (!winEngine) {
|
||||
*errorMessage = DebuggerManager::tr("Debugging VS executables is not supported.");
|
||||
return false;
|
||||
}
|
||||
*dt = DebuggerManager::WinDebugger;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DebuggerManager::startNewDebugger(DebuggerStartMode mode)
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << mode;
|
||||
|
||||
m_startMode = mode;
|
||||
// FIXME: Clean up
|
||||
|
||||
@@ -858,11 +902,18 @@ bool DebuggerManager::startNewDebugger(DebuggerStartMode mode)
|
||||
|
||||
emit debugModeRequested();
|
||||
|
||||
if (m_executable.endsWith(".js"))
|
||||
setDebuggerType(ScriptDebugger);
|
||||
else
|
||||
setDebuggerType(GdbDebugger);
|
||||
DebuggerType type;
|
||||
QString errorMessage;
|
||||
if (!determineDebuggerType(m_executable, &type, &errorMessage)) {
|
||||
QMessageBox::warning(mainWindow(), tr("Warning"),
|
||||
tr("Cannot debug '%1': %2").arg(m_executable, errorMessage));
|
||||
return false;
|
||||
|
||||
}
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << m_executable << type;
|
||||
|
||||
setDebuggerType(type);
|
||||
setStatus(DebuggerProcessStartingUp);
|
||||
if (!m_engine->startDebugger()) {
|
||||
setStatus(DebuggerProcessNotReady);
|
||||
@@ -886,7 +937,9 @@ void DebuggerManager::cleanupViews()
|
||||
|
||||
void DebuggerManager::exitDebugger()
|
||||
{
|
||||
//qDebug() << "DebuggerManager::exitDebugger";
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
if (m_engine)
|
||||
m_engine->exitDebugger();
|
||||
cleanupViews();
|
||||
@@ -962,6 +1015,9 @@ void DebuggerManager::nextIExec()
|
||||
|
||||
void DebuggerManager::executeDebuggerCommand(const QString &command)
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO <<command;
|
||||
|
||||
QTC_ASSERT(m_engine, return);
|
||||
m_engine->executeDebuggerCommand(command);
|
||||
}
|
||||
@@ -1030,6 +1086,9 @@ void DebuggerManager::watchExpression(const QString &expression)
|
||||
|
||||
void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber)
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||
|
||||
QTC_ASSERT(m_breakHandler, return);
|
||||
QTC_ASSERT(m_engine, return);
|
||||
m_breakHandler->setBreakpoint(fileName, lineNumber);
|
||||
@@ -1062,7 +1121,8 @@ void DebuggerManager::breakAtMain()
|
||||
|
||||
void DebuggerManager::setStatus(int status)
|
||||
{
|
||||
//qDebug() << "STATUS CHANGE: from" << m_status << "to" << status;
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << "STATUS CHANGE: from" << m_status << "to" << status;
|
||||
|
||||
if (status == m_status)
|
||||
return;
|
||||
@@ -1179,8 +1239,9 @@ void DebuggerManager::continueExec()
|
||||
|
||||
void DebuggerManager::interruptDebuggingRequest()
|
||||
{
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << status();
|
||||
QTC_ASSERT(m_engine, return);
|
||||
//qDebug() << "INTERRUPTING AT" << status();
|
||||
bool interruptIsExit = (status() != DebuggerInferiorRunning);
|
||||
if (interruptIsExit)
|
||||
exitDebugger();
|
||||
@@ -1197,9 +1258,12 @@ void DebuggerManager::runToLineExec()
|
||||
QString fileName;
|
||||
int lineNumber = -1;
|
||||
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
||||
if (!fileName.isEmpty())
|
||||
if (!fileName.isEmpty()) {
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||
m_engine->runToLineExec(fileName, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerManager::runToFunctionExec()
|
||||
{
|
||||
@@ -1228,7 +1292,9 @@ void DebuggerManager::runToFunctionExec()
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug() << "RUN TO FUNCTION " << functionName;
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << functionName;
|
||||
|
||||
if (!functionName.isEmpty())
|
||||
m_engine->runToFunctionExec(functionName);
|
||||
}
|
||||
@@ -1238,9 +1304,12 @@ void DebuggerManager::jumpToLineExec()
|
||||
QString fileName;
|
||||
int lineNumber = -1;
|
||||
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
||||
if (!fileName.isEmpty())
|
||||
if (!fileName.isEmpty()) {
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||
m_engine->jumpToLineExec(fileName, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerManager::resetLocation()
|
||||
{
|
||||
|
||||
279
src/plugins/debugger/win/peutils.cpp
Normal file
279
src/plugins/debugger/win/peutils.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
51
src/plugins/debugger/win/peutils.h
Normal file
51
src/plugins/debugger/win/peutils.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef PEUTILS_H
|
||||
#define PEUTILS_H
|
||||
|
||||
#include <QtCore/qnamespace.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStringList;
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
/* Helper functions to extract information from PE Win32 executable
|
||||
* files (cf dumpbin utility). */
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Return a list of Program-Database (*.pdb) files a PE executable refers to. */
|
||||
bool getPDBFiles(const QString &peExecutableFileName, QStringList *rc, QString *errorMessage);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PEUTILS_H
|
||||
3
src/plugins/debugger/win/win.pri
Normal file
3
src/plugins/debugger/win/win.pri
Normal file
@@ -0,0 +1,3 @@
|
||||
INCLUDEPATH+=$$PWD
|
||||
SOURCES += $$PWD/peutils.cpp
|
||||
HEADERS += $$PWD/peutils.h
|
||||
Reference in New Issue
Block a user