determine gdb target ABI by parsing 'gdb --configuration'

Currently, the gdb target ABI is determined by running
`gdb -version`, which for recent gdb's does not reproduce
the target ABI string -- yet this string is searched in
the output.  This obviously fails, and qtcreator uses a
fallback behavior, that is not suitable when using gdb
for debugging on targets (like avr microcontrollers).
With this change, QtCreator calls `gdb --configuration`
if that is supported by gdb and extracts `<string> from
`--target=<string>` in the output.

For older versions of gdb (which do not support the
`--configuration` flag, but still have the target
information in the output of `--version`, the output of
`--version` is parsed.

If both methods fail, no ABI is set for gdb.

Change-Id: Ib406f6700b63e2cedb46bd4ec8cc0d215677ecdc
Reviewed-by: Michael Kopp <kopp.michael@yahoo.de>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Michael Kopp
2018-11-20 22:31:27 +01:00
parent be4bd1dc06
commit 7e45ccc99b
3 changed files with 63 additions and 16 deletions

View File

@@ -31,6 +31,7 @@
#include <projectexplorer/abi.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
@@ -61,6 +62,35 @@ const char DEBUGGER_INFORMATION_ABIS[] = "Abis";
const char DEBUGGER_INFORMATION_LASTMODIFIED[] = "LastModified";
const char DEBUGGER_INFORMATION_WORKINGDIRECTORY[] = "WorkingDirectory";
//! Return the configuration of gdb as a list of --key=value
//! \note That the list will also contain some output not in this format.
static QString getConfigurationOfGdbCommand(const QString &command)
{
// run gdb with the --configuration opion
Utils::SynchronousProcess gdbConfigurationCall;
Utils::SynchronousProcessResponse output =
gdbConfigurationCall.runBlocking(command, {QString("--configuration")});
return output.allOutput();
}
//! Extract the target ABI identifier from GDB output
//! \return QString() (aka Null) if unable to find something
static QString extractGdbTargetAbiStringFromGdbOutput(const QString &gdbOutput)
{
const auto outputLines = gdbOutput.split('\n');
const auto whitespaceSeparatedTokens = outputLines.join(' ').split(' ', QString::SkipEmptyParts);
const QString targetKey{"--target="};
const QString targetValue = Utils::findOrDefault(whitespaceSeparatedTokens,
[&targetKey](const QString &token) { return token.startsWith(targetKey); });
if (!targetValue.isEmpty())
return targetValue.mid(targetKey.size());
return {};
}
namespace Debugger {
// --------------------------------------------------------------------------
@@ -130,22 +160,6 @@ void DebuggerItem::reinitializeFromFile()
const QString output = response.allOutput().trimmed();
if (output.contains("gdb")) {
m_engineType = GdbEngineType;
const char needle[] = "This GDB was configured as \"";
// E.g. "--host=i686-pc-linux-gnu --target=arm-unknown-nto-qnx6.5.0".
// or "i686-linux-gnu"
int pos1 = output.indexOf(needle);
if (pos1 != -1) {
pos1 += int(strlen(needle));
int pos2 = output.indexOf('"', pos1 + 1);
QString target = output.mid(pos1, pos2 - pos1);
int pos3 = target.indexOf("--target=");
if (pos3 >= 0)
target = target.mid(pos3 + 9);
m_abis.append(Abi::abiFromTargetTriplet(target));
} else {
// Fallback.
m_abis = Abi::abisOfBinary(m_command); // FIXME: Wrong.
}
// Version
bool isMacGdb, isQnxGdb;
@@ -155,6 +169,33 @@ void DebuggerItem::reinitializeFromFile()
if (version)
m_version = QString::fromLatin1("%1.%2.%3")
.arg(version / 10000).arg((version / 100) % 100).arg(version % 100);
// ABI
const bool unableToFindAVersion = (0 == version);
const bool gdbSupportsConfigurationFlag = (version >= 70700);
if (gdbSupportsConfigurationFlag || unableToFindAVersion) {
const auto gdbConfiguration = getConfigurationOfGdbCommand(m_command.toString());
const auto gdbTargetAbiString =
extractGdbTargetAbiStringFromGdbOutput(gdbConfiguration);
if (!gdbTargetAbiString.isEmpty()) {
m_abis.append(Abi::abiFromTargetTriplet(gdbTargetAbiString));
return;
}
}
// ABI: legacy: the target was removed from the output of --version with
// https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=c61b06a19a34baab66e3809c7b41b0c31009ed9f
auto legacyGdbTargetAbiString = extractGdbTargetAbiStringFromGdbOutput(output);
if (!legacyGdbTargetAbiString.isEmpty()) {
// remove trailing "
legacyGdbTargetAbiString =
legacyGdbTargetAbiString.left(legacyGdbTargetAbiString.length() - 1);
m_abis.append(Abi::abiFromTargetTriplet(legacyGdbTargetAbiString));
return;
}
qWarning() << "Unable to determine gdb target ABI";
//! \note If unable to determine the GDB ABI, no ABI is appended to m_abis here.
return;
}
if (output.startsWith("lldb") || output.startsWith("LLDB")) {

View File

@@ -436,6 +436,11 @@ QString DebuggerResponse::toString() const
// Tested in tests/auto/debugger/tst_gdb.cpp
//! Extract the GDB version number from the output of 'gdb --version'.
//! \param[out] gdbVersion GDB version "hash" with major*10000 + minor*100 + patch
//! e.g. version GDB 3.7.14 will set this to 30714
//! \param[out] gdbBuildVersion distribution dependent value
//! \note See the file tests/auto/debugger/tst_gdb.cpp for example conversions.
void extractGdbVersion(const QString &msg,
int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb)
{

View File

@@ -958,6 +958,7 @@ Abi Abi::hostAbi()
return result;
}
//! Extract available ABIs from a binary using heuristics.
QList<Abi> Abi::abisOfBinary(const Utils::FileName &path)
{
QList<Abi> tmp;