abi.cpp 47.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25 26 27

#include "abi.h"

28 29
#include <utils/fileutils.h>

30 31 32 33 34 35
#include <QDebug>
#include <QtEndian>
#include <QFile>
#include <QString>
#include <QStringList>
#include <QSysInfo>
36

37 38 39
/*!
    \class ProjectExplorer::Abi

40 41
    \brief The Abi class represents the Application Binary Interface (ABI) of
    a target platform.
42 43 44 45

    \sa ProjectExplorer::ToolChain
*/

46 47
namespace ProjectExplorer {

48 49 50 51
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------

Tobias Hunger's avatar
Tobias Hunger committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
static quint8 getUint8(const QByteArray &data, int pos)
{
    return static_cast<quint8>(data.at(pos));
}

static quint32 getLEUint32(const QByteArray &ba, int pos)
{
    Q_ASSERT(ba.size() >= pos + 3);
    return (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 3))) << 24)
            + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2)) << 16))
            + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 8)
            + static_cast<quint8>(ba.at(pos));
}

static quint32 getBEUint32(const QByteArray &ba, int pos)
{
    Q_ASSERT(ba.size() >= pos + 3);
    return (static_cast<quint32>(static_cast<quint8>(ba.at(pos))) << 24)
            + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 16)
            + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2))) << 8)
            + static_cast<quint8>(ba.at(pos + 3));
}

static quint32 getLEUint16(const QByteArray &ba, int pos)
{
    Q_ASSERT(ba.size() >= pos + 1);
    return (static_cast<quint16>(static_cast<quint8>(ba.at(pos + 1))) << 8) + static_cast<quint8>(ba.at(pos));
}

static quint32 getBEUint16(const QByteArray &ba, int pos)
{
    Q_ASSERT(ba.size() >= pos + 1);
    return (static_cast<quint16>(static_cast<quint8>(ba.at(pos))) << 8) + static_cast<quint8>(ba.at(pos + 1));
}

87 88 89
static Abi macAbiForCpu(quint32 type) {
    switch (type) {
    case 7: // CPU_TYPE_X86, CPU_TYPE_I386
90
        return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 32);
91
    case 0x01000000 +  7: // CPU_TYPE_X86_64
92
        return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 64);
93
    case 18: // CPU_TYPE_POWERPC
94
        return Abi(Abi::PowerPCArchitecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 32);
95
    case 0x01000000 + 18: // CPU_TYPE_POWERPC64
96
        return Abi(Abi::PowerPCArchitecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 32);
97
    case 12: // CPU_TYPE_ARM
98
        return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 32);
99
    case 0x01000000 + 12: // CPU_TYPE_ARM64
100
        return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericDarwinFlavor, Abi::MachOFormat, 64);
101 102 103 104 105
    default:
        return Abi();
    }
}

Tobias Hunger's avatar
Tobias Hunger committed
106 107 108 109 110 111 112 113 114 115 116
static QList<Abi> parseCoffHeader(const QByteArray &data)
{
    QList<Abi> result;
    if (data.size() < 20)
        return result;

    Abi::Architecture arch = Abi::UnknownArchitecture;
    Abi::OSFlavor flavor = Abi::UnknownFlavor;
    int width = 0;

    // Get machine field from COFF file header
Tobias Hunger's avatar
Tobias Hunger committed
117
    quint16 machine = getLEUint16(data, 0);
Tobias Hunger's avatar
Tobias Hunger committed
118
    switch (machine) {
119 120 121 122 123 124
    case 0x01c0: // ARM LE
    case 0x01c2: // ARM or thumb
    case 0x01c4: // ARMv7 thumb
        arch = Abi::ArmArchitecture;
        width = 32;
        break;
Tobias Hunger's avatar
Tobias Hunger committed
125 126 127 128 129 130 131 132
    case 0x8664: // x86_64
        arch = Abi::X86Architecture;
        width = 64;
        break;
    case 0x014c: // i386
        arch = Abi::X86Architecture;
        width = 32;
        break;
133
    case 0x0166: // MIPS, little endian
Tobias Hunger's avatar
Tobias Hunger committed
134
        arch = Abi::MipsArchitecture;
135 136
        width = 32;
        break;
Tobias Hunger's avatar
Tobias Hunger committed
137 138 139 140 141 142
    case 0x0200: // ia64
        arch = Abi::ItaniumArchitecture;
        width = 64;
        break;
    }

143
    if (data.size() >= 24) {
Tobias Hunger's avatar
Tobias Hunger committed
144
        // Get Major and Minor Image Version from optional header fields
145 146 147 148
        quint8 minorLinker = data.at(23);
        switch (data.at(22)) {
        case 2:
        case 3: // not yet reached:-)
Tobias Hunger's avatar
Tobias Hunger committed
149
            flavor = Abi::WindowsMSysFlavor;
150 151 152 153 154 155 156 157 158 159 160 161 162
            break;
        case 8:
            flavor = Abi::WindowsMsvc2005Flavor;
            break;
        case 9:
            flavor = Abi::WindowsMsvc2008Flavor;
            break;
        case 10:
            flavor = Abi::WindowsMsvc2010Flavor;
            break;
        case 11:
            flavor = Abi::WindowsMsvc2012Flavor;
            break;
Yuchen Deng's avatar
Yuchen Deng committed
163 164 165
        case 12:
            flavor = Abi::WindowsMsvc2013Flavor;
            break;
Joerg Bornemann's avatar
Joerg Bornemann committed
166
        case 14:
167 168 169
            flavor = minorLinker >= quint8(10)
                ? Abi::WindowsMsvc2017Flavor // MSVC2017 RC
                : Abi::WindowsMsvc2015Flavor;
170
            break;
171 172
        case 15:
            flavor = Abi::WindowsMsvc2017Flavor;
Joerg Bornemann's avatar
Joerg Bornemann committed
173
            break;
174 175 176 177
        default: // Keep unknown flavor
            if (minorLinker != 0)
                flavor = Abi::WindowsMSysFlavor; // MSVC seems to avoid using minor numbers
            else
Tobias Hunger's avatar
Tobias Hunger committed
178
                qWarning("%s: Unknown MSVC flavour encountered.", Q_FUNC_INFO);
179
            break;
180
        }
Tobias Hunger's avatar
Tobias Hunger committed
181 182 183 184 185 186 187 188
    }

    if (arch != Abi::UnknownArchitecture && width != 0)
        result.append(Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, width));

    return result;
}

189 190 191
static QList<Abi> abiOf(const QByteArray &data)
{
    QList<Abi> result;
Tobias Hunger's avatar
Tobias Hunger committed
192 193
    if (data.size() <= 8)
        return result;
194 195

    if (data.size() >= 20
Tobias Hunger's avatar
Tobias Hunger committed
196 197
            && getUint8(data, 0) == 0x7f && getUint8(data, 1) == 'E' && getUint8(data, 2) == 'L'
            && getUint8(data, 3) == 'F') {
198
        // ELF format:
Tobias Hunger's avatar
Tobias Hunger committed
199 200 201
        bool isLE = (getUint8(data, 5) == 1);
        quint16 machine = isLE ? getLEUint16(data, 18) : getBEUint16(data, 18);
        quint8 osAbi = getUint8(data, 7);
202 203 204 205 206

        Abi::OS os = Abi::UnixOS;
        Abi::OSFlavor flavor = Abi::GenericUnixFlavor;
        // http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
        switch (osAbi) {
207 208
#if defined(Q_OS_NETBSD)
        case 0: // NetBSD: ELFOSABI_NETBSD  2, however, NetBSD uses 0
209 210 211
            os = Abi::BsdOS;
            flavor = Abi::NetBsdFlavor;
            break;
212 213 214 215 216 217
#elif defined(Q_OS_OPENBSD)
        case 0: // OpenBSD: ELFOSABI_OPENBSD 12, however, OpenBSD uses 0
            os = Abi::BsdOS;
            flavor = Abi::OpenBsdFlavor;
            break;
#else
218
        case 0: // no extra info available: Default to Linux:
219 220
#endif
        case 3: // Linux:
221
        case 97: // ARM, also linux most of the time.
222 223 224 225 226 227 228 229 230 231 232 233
            os = Abi::LinuxOS;
            flavor = Abi::GenericLinuxFlavor;
            break;
        case 6: // Solaris:
            os = Abi::UnixOS;
            flavor = Abi::SolarisUnixFlavor;
            break;
        case 9: // FreeBSD:
            os = Abi::BsdOS;
            flavor = Abi::FreeBsdFlavor;
            break;
        }
234

235 236
        switch (machine) {
        case 3: // EM_386
237
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
238 239
            break;
        case 8: // EM_MIPS
Tobias Hunger's avatar
Tobias Hunger committed
240
            result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
241 242
            break;
        case 20: // EM_PPC
243
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
244 245
            break;
        case 21: // EM_PPC64
246
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
247
            break;
248
        case 40: // EM_ARM
249
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
250
            break;
Daniel Teske's avatar
Daniel Teske committed
251 252 253
        case 183: // EM_AARCH64
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 64));
            break;
254
        case 62: // EM_X86_64
255
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
256
            break;
Tobias Hunger's avatar
Tobias Hunger committed
257 258 259
        case 42: // EM_SH
            result.append(Abi(Abi::ShArchitecture, os, flavor, Abi::ElfFormat, 32));
            break;
260
        case 50: // EM_IA_64
261
            result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
262 263
            break;
        default:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
264
            ;
265
        }
Tobias Hunger's avatar
Tobias Hunger committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
    } else if (((getUint8(data, 0) == 0xce || getUint8(data, 0) == 0xcf)
             && getUint8(data, 1) == 0xfa && getUint8(data, 2) == 0xed && getUint8(data, 3) == 0xfe
            )
            ||
            (getUint8(data, 0) == 0xfe && getUint8(data, 1) == 0xed && getUint8(data, 2) == 0xfa
             && (getUint8(data, 3) == 0xce || getUint8(data, 3) == 0xcf)
            )
           ) {
            // Mach-O format (Mac non-fat binary, 32 and 64bit magic):
            quint32 type = (getUint8(data, 1) ==  0xfa) ? getLEUint32(data, 4) : getBEUint32(data, 4);
            result.append(macAbiForCpu(type));
    } else if ((getUint8(data, 0) == 0xbe && getUint8(data, 1) == 0xba
                && getUint8(data, 2) == 0xfe && getUint8(data, 3) == 0xca)
               ||
               (getUint8(data, 0) == 0xca && getUint8(data, 1) == 0xfe
                && getUint8(data, 2) == 0xba && getUint8(data, 3) == 0xbe)
              ) {
        // Mach-0 format Fat binary header:
        bool isLE = (getUint8(data, 0) == 0xbe);
        quint32 count = isLE ? getLEUint32(data, 4) : getBEUint32(data, 4);
286 287 288 289 290
        int pos = 8;
        for (quint32 i = 0; i < count; ++i) {
            if (data.size() <= pos + 4)
                break;

Tobias Hunger's avatar
Tobias Hunger committed
291
            quint32 type = isLE ? getLEUint32(data, pos) : getBEUint32(data, pos);
292 293 294
            result.append(macAbiForCpu(type));
            pos += 20;
        }
295
    } else if (data.size() >= 64){
Tobias Hunger's avatar
Tobias Hunger committed
296
        // Windows PE: values are LE (except for a few exceptions which we will not use here).
297

Tobias Hunger's avatar
Tobias Hunger committed
298
        // MZ header first (ZM is also allowed, but rarely used)
Robert Loehning's avatar
Robert Loehning committed
299 300 301 302
        const quint8 firstChar = getUint8(data, 0);
        const quint8 secondChar = getUint8(data, 1);
        if ((firstChar != 'M' || secondChar != 'Z') && (firstChar != 'Z' || secondChar != 'M'))
            return result;
303 304

        // Get PE/COFF header position from MZ header:
Tobias Hunger's avatar
Tobias Hunger committed
305
        qint32 pePos = getLEUint32(data, 60);
306 307
        if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
            return result;
Tobias Hunger's avatar
Tobias Hunger committed
308 309
        if (getUint8(data, pePos) == 'P' && getUint8(data, pePos + 1) == 'E'
            && getUint8(data, pePos + 2) == 0 && getUint8(data, pePos + 3) == 0)
Tobias Hunger's avatar
Tobias Hunger committed
310
            result = parseCoffHeader(data.mid(pePos + 4));
311 312 313 314 315 316 317 318
    }
    return result;
}

// --------------------------------------------------------------------------
// Abi
// --------------------------------------------------------------------------

319
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
320
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
321 322 323
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
324
    case Abi::UnknownOS:
hjk's avatar
hjk committed
325
        m_osFlavor = UnknownFlavor;
326
        break;
327
    case Abi::LinuxOS:
328
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > AndroidLinuxFlavor)
hjk's avatar
hjk committed
329
            m_osFlavor = UnknownFlavor;
330
        break;
331
    case Abi::BsdOS:
332 333
        if (m_osFlavor < FreeBsdFlavor || m_osFlavor > OpenBsdFlavor)
            m_osFlavor = UnknownFlavor;
334
        break;
335 336
    case Abi::DarwinOS:
        if (m_osFlavor < GenericDarwinFlavor || m_osFlavor > GenericDarwinFlavor)
hjk's avatar
hjk committed
337
            m_osFlavor = UnknownFlavor;
338
        break;
339
    case Abi::UnixOS:
340
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > SolarisUnixFlavor)
hjk's avatar
hjk committed
341
            m_osFlavor = UnknownFlavor;
342
        break;
343
    case Abi::WindowsOS:
344
        if (m_osFlavor < WindowsMsvc2005Flavor || m_osFlavor > WindowsCEFlavor)
hjk's avatar
hjk committed
345
            m_osFlavor = UnknownFlavor;
346
        break;
347 348 349
    case Abi::VxWorks:
        if (m_osFlavor != VxWorksFlavor)
            m_osFlavor = VxWorksFlavor;
350 351 352 353
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
354 355
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
356
{
357
    const QVector<QStringRef> abiParts = abiString.splitRef(QLatin1Char('-'));
358 359
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
360
            m_architecture = UnknownArchitecture;
361
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
362
            m_architecture = ArmArchitecture;
363 364
        else if (abiParts.at(0) == QLatin1String("aarch64"))
            m_architecture = ArmArchitecture;
365
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
366
            m_architecture = X86Architecture;
367
        else if (abiParts.at(0) == QLatin1String("mips"))
Tobias Hunger's avatar
Tobias Hunger committed
368
            m_architecture = MipsArchitecture;
369
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
370
            m_architecture = PowerPCArchitecture;
371
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
372
            m_architecture = ItaniumArchitecture;
Tobias Hunger's avatar
Tobias Hunger committed
373 374
        else if (abiParts.at(0) == QLatin1String("sh"))
            m_architecture = ShArchitecture;
375 376 377 378 379 380
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
381
            m_os = UnknownOS;
382
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
383
            m_os = LinuxOS;
384 385
        else if (abiParts.at(1) == QLatin1String("bsd"))
            m_os = BsdOS;
386 387 388
        else if (abiParts.at(1) == QLatin1String("darwin")
                || abiParts.at(1) == QLatin1String("macos"))
            m_os = DarwinOS;
389
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
390
            m_os = UnixOS;
391
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
392
            m_os = WindowsOS;
393 394
        else if (abiParts.at(1) == QLatin1String("vxworks"))
            m_os = VxWorks;
395 396 397 398 399 400
        else
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
401 402 403
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
BogDan Vatra's avatar
BogDan Vatra committed
404 405
        else if (abiParts.at(2) == QLatin1String("android") && m_os == LinuxOS)
            m_osFlavor = AndroidLinuxFlavor;
406 407 408 409 410 411
        else if (abiParts.at(2) == QLatin1String("freebsd") && m_os == BsdOS)
            m_osFlavor = FreeBsdFlavor;
        else if (abiParts.at(2) == QLatin1String("netbsd") && m_os == BsdOS)
            m_osFlavor = NetBsdFlavor;
        else if (abiParts.at(2) == QLatin1String("openbsd") && m_os == BsdOS)
            m_osFlavor = OpenBsdFlavor;
412 413
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == DarwinOS)
            m_osFlavor = GenericDarwinFlavor;
hjk's avatar
hjk committed
414 415
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == UnixOS)
            m_osFlavor = GenericUnixFlavor;
416 417
        else if (abiParts.at(2) == QLatin1String("solaris") && m_os == UnixOS)
            m_osFlavor = SolarisUnixFlavor;
418 419 420 421 422 423
        else if (abiParts.at(2) == QLatin1String("msvc2005") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2005Flavor;
        else if (abiParts.at(2) == QLatin1String("msvc2008") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2008Flavor;
        else if (abiParts.at(2) == QLatin1String("msvc2010") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2010Flavor;
Tobias Hunger's avatar
Tobias Hunger committed
424 425
        else if (abiParts.at(2) == QLatin1String("msvc2012") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2012Flavor;
Yuchen Deng's avatar
Yuchen Deng committed
426 427
        else if (abiParts.at(2) == QLatin1String("msvc2013") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2013Flavor;
Joerg Bornemann's avatar
Joerg Bornemann committed
428 429
        else if (abiParts.at(2) == QLatin1String("msvc2015") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2015Flavor;
430 431
        else if (abiParts.at(2) == QLatin1String("msvc2017") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2017Flavor;
hjk's avatar
hjk committed
432 433 434 435
        else if (abiParts.at(2) == QLatin1String("msys") && m_os == WindowsOS)
            m_osFlavor = WindowsMSysFlavor;
        else if (abiParts.at(2) == QLatin1String("ce") && m_os == WindowsOS)
            m_osFlavor = WindowsCEFlavor;
436 437
        else if (abiParts.at(2) == QLatin1String("vxworks") && m_os == VxWorks)
            m_osFlavor = VxWorksFlavor;
438 439 440 441 442 443
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
444
            m_binaryFormat = UnknownFormat;
445
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
446
            m_binaryFormat = ElfFormat;
447
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
448
            m_binaryFormat = PEFormat;
449
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
450
            m_binaryFormat = MachOFormat;
451
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
452
            m_binaryFormat = RuntimeQmlFormat;
453 454 455 456 457
        else
            return;
    }

    if (abiParts.count() >= 5) {
458
        const QStringRef &bits = abiParts.at(4);
459 460 461 462
        if (!bits.endsWith(QLatin1String("bit")))
            return;

        bool ok = false;
463 464 465
        const QStringRef number =
            bits.string()->midRef(bits.position(), bits.count() - 3);
        const int bitCount = number.toInt(&ok);
466 467 468 469 470 471 472 473
        if (!ok)
            return;
        if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
            return;
        m_wordWidth = bitCount;
    }
}

474 475 476 477 478 479
Abi Abi::abiFromTargetTriplet(const QString &triple)
{
    QString machine = triple.toLower();
    if (machine.isEmpty())
        return Abi();

480
    const QVector<QStringRef> parts = machine.splitRef(QRegExp(QLatin1String("[ /-]")));
481 482 483 484 485

    Abi::Architecture arch = Abi::UnknownArchitecture;
    Abi::OS os = Abi::UnknownOS;
    Abi::OSFlavor flavor = Abi::UnknownFlavor;
    Abi::BinaryFormat format = Abi::UnknownFormat;
486
    unsigned char width = 0;
487 488
    int unknownCount = 0;

489
    for (const QStringRef &p : parts) {
490 491 492 493 494 495 496 497
        if (p == QLatin1String("unknown") || p == QLatin1String("pc") || p == QLatin1String("none")
                || p == QLatin1String("gnu") || p == QLatin1String("uclibc")
                || p == QLatin1String("86_64") || p == QLatin1String("redhat")
                || p == QLatin1String("gnueabi") || p == QLatin1String("w64")) {
            continue;
        } else if (p == QLatin1String("i386") || p == QLatin1String("i486") || p == QLatin1String("i586")
                   || p == QLatin1String("i686") || p == QLatin1String("x86")) {
            arch = Abi::X86Architecture;
498
            width = 32;
499 500
        } else if (p.startsWith(QLatin1String("arm"))) {
            arch = Abi::ArmArchitecture;
501
            width = p.contains(QLatin1String("64")) ? 64 : 32;
502 503 504
        } else if (p.startsWith(QLatin1String("aarch64"))) {
            arch = Abi::ArmArchitecture;
            width = 64;
505
        } else if (p.startsWith(QLatin1String("mips"))) {
506
            arch = Abi::MipsArchitecture;
Orgad Shaneh's avatar
Orgad Shaneh committed
507
            width = p.contains(QLatin1String("64")) ? 64 : 32;
508 509 510 511 512 513 514 515 516 517 518 519 520 521
        } else if (p == QLatin1String("x86_64") || p == QLatin1String("amd64")) {
            arch = Abi::X86Architecture;
            width = 64;
        } else if (p == QLatin1String("powerpc64")) {
            arch = Abi::PowerPCArchitecture;
            width = 64;
        } else if (p == QLatin1String("powerpc")) {
            arch = Abi::PowerPCArchitecture;
            width = 32;
        } else if (p == QLatin1String("linux") || p == QLatin1String("linux6e")) {
            os = Abi::LinuxOS;
            if (flavor == Abi::UnknownFlavor)
                flavor = Abi::GenericLinuxFlavor;
            format = Abi::ElfFormat;
522 523 524 525
        } else if (p == QLatin1String("android")) {
            flavor = Abi::AndroidLinuxFlavor;
        } else if (p == QLatin1String("androideabi")) {
            flavor = Abi::AndroidLinuxFlavor;
526 527 528 529 530
        } else if (p.startsWith(QLatin1String("freebsd"))) {
            os = Abi::BsdOS;
            if (flavor == Abi::UnknownFlavor)
                flavor = Abi::FreeBsdFlavor;
            format = Abi::ElfFormat;
Caspar Schutijser's avatar
Caspar Schutijser committed
531 532 533 534 535
        } else if (p.startsWith(QLatin1String("openbsd"))) {
            os = Abi::BsdOS;
            if (flavor == Abi::UnknownFlavor)
                flavor = Abi::OpenBsdFlavor;
            format = Abi::ElfFormat;
536
        } else if (p == QLatin1String("mingw32") || p == QLatin1String("win32")
537 538
                   || p == QLatin1String("mingw32msvc") || p == QLatin1String("msys")
                   || p == QLatin1String("cygwin")) {
539 540 541 542 543
            arch = Abi::X86Architecture;
            os = Abi::WindowsOS;
            flavor = Abi::WindowsMSysFlavor;
            format = Abi::PEFormat;
        } else if (p == QLatin1String("apple")) {
544 545
            os = Abi::DarwinOS;
            flavor = Abi::GenericDarwinFlavor;
546 547 548 549 550 551 552
            format = Abi::MachOFormat;
        } else if (p == QLatin1String("darwin10")) {
            width = 64;
        } else if (p == QLatin1String("darwin9")) {
            width = 32;
        } else if (p == QLatin1String("gnueabi")) {
            format = Abi::ElfFormat;
553 554 555 556 557 558
        } else if (p == QLatin1String("wrs")) {
            continue;
        } else if (p == QLatin1String("vxworks")) {
            os = Abi::VxWorks;
            flavor = Abi::VxWorksFlavor;
            format = Abi::ElfFormat;
559 560 561 562 563 564 565 566
        } else {
            ++unknownCount;
        }
    }

    return Abi(arch, os, flavor, format, width);
}

567 568 569 570 571 572 573 574 575
QString Abi::toString() const
{
    QStringList dn;
    dn << toString(m_architecture);
    dn << toString(m_os);
    dn << toString(m_osFlavor);
    dn << toString(m_binaryFormat);
    dn << toString(m_wordWidth);

hjk's avatar
hjk committed
576
    return dn.join(QLatin1Char('-'));
577 578
}

Tobias Hunger's avatar
Tobias Hunger committed
579 580 581 582 583
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

584 585 586 587 588 589 590 591 592 593 594
bool Abi::operator == (const Abi &other) const
{
    return m_architecture == other.m_architecture
            && m_os == other.m_os
            && m_osFlavor == other.m_osFlavor
            && m_binaryFormat == other.m_binaryFormat
            && m_wordWidth == other.m_wordWidth;
}

bool Abi::isCompatibleWith(const Abi &other) const
{
595 596 597 598 599
    bool isCompat = (architecture() == other.architecture() || other.architecture() == Abi::UnknownArchitecture)
                     && (os() == other.os() || other.os() == Abi::UnknownOS)
                     && (osFlavor() == other.osFlavor() || other.osFlavor() == Abi::UnknownFlavor)
                     && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == Abi::UnknownFormat)
                     && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
600 601 602 603 604 605
    // *-linux-generic-* is compatible with *-linux-* (both ways): This is for the benefit of
    // people building Qt themselves using e.g. a meego toolchain.
    //
    // We leave it to the specific targets to catch filter out the tool chains that do not
    // work for them.
    if (!isCompat && (architecture() == other.architecture() || other.architecture() == Abi::UnknownArchitecture)
Tobias Hunger's avatar
Tobias Hunger committed
606
                  && ((os() == other.os()) && (os() == LinuxOS))
607 608 609
                  && (osFlavor() == GenericLinuxFlavor || other.osFlavor() == GenericLinuxFlavor)
                  && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == Abi::UnknownFormat)
                  && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0))
610
        isCompat = true;
BogDan Vatra's avatar
BogDan Vatra committed
611 612
    if (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor)
        isCompat = (osFlavor() == other.osFlavor() && architecture() == other.architecture());
613
    return isCompat;
614 615 616 617
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
618 619 620 621
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
622 623 624
            && m_wordWidth != 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
625 626 627 628 629 630 631 632 633
bool Abi::isNull() const
{
    return m_architecture == UnknownArchitecture
            && m_os == UnknownOS
            && m_osFlavor == UnknownFlavor
            && m_binaryFormat == UnknownFormat
            && m_wordWidth == 0;
}

634 635 636
QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
637
    case ArmArchitecture:
638
        return QLatin1String("arm");
hjk's avatar
hjk committed
639
    case X86Architecture:
640
        return QLatin1String("x86");
Tobias Hunger's avatar
Tobias Hunger committed
641
    case MipsArchitecture:
642
        return QLatin1String("mips");
hjk's avatar
hjk committed
643
    case PowerPCArchitecture:
644
        return QLatin1String("ppc");
hjk's avatar
hjk committed
645
    case ItaniumArchitecture:
646
        return QLatin1String("itanium");
Tobias Hunger's avatar
Tobias Hunger committed
647
    case ShArchitecture:
Tobias Hunger's avatar
Tobias Hunger committed
648
        return QLatin1String("sh");
hjk's avatar
hjk committed
649
    case UnknownArchitecture: // fall through!
650 651 652 653 654 655 656 657
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
658
    case LinuxOS:
659
        return QLatin1String("linux");
660 661
    case BsdOS:
        return QLatin1String("bsd");
662 663
    case DarwinOS:
        return QLatin1String("darwin");
hjk's avatar
hjk committed
664
    case UnixOS:
665
        return QLatin1String("unix");
hjk's avatar
hjk committed
666
    case WindowsOS:
667
        return QLatin1String("windows");
668 669
    case VxWorks:
        return QLatin1String("vxworks");
hjk's avatar
hjk committed
670
    case UnknownOS: // fall through!
671 672 673 674 675
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
676
QString Abi::toString(const OSFlavor &of)
677 678
{
    switch (of) {
679
    case Abi::GenericLinuxFlavor:
680
        return QLatin1String("generic");
681
    case Abi::AndroidLinuxFlavor:
BogDan Vatra's avatar
BogDan Vatra committed
682
        return QLatin1String("android");
683
    case Abi::FreeBsdFlavor:
684
        return QLatin1String("freebsd");
685
    case Abi::NetBsdFlavor:
686
        return QLatin1String("netbsd");
687
    case Abi::OpenBsdFlavor:
688
        return QLatin1String("openbsd");
689
    case Abi::GenericDarwinFlavor:
690
        return QLatin1String("generic");
691
    case Abi::GenericUnixFlavor:
692
        return QLatin1String("generic");
693
    case Abi::SolarisUnixFlavor:
694
        return QLatin1String("solaris");
695
    case Abi::WindowsMsvc2005Flavor:
696
        return QLatin1String("msvc2005");
697
    case Abi::WindowsMsvc2008Flavor:
698
        return QLatin1String("msvc2008");
699
    case Abi::WindowsMsvc2010Flavor:
700
        return QLatin1String("msvc2010");
701
    case Abi::WindowsMsvc2012Flavor:
Tobias Hunger's avatar
Tobias Hunger committed
702
        return QLatin1String("msvc2012");
703
    case Abi::WindowsMsvc2013Flavor:
Yuchen Deng's avatar
Yuchen Deng committed
704
        return QLatin1String("msvc2013");
Joerg Bornemann's avatar
Joerg Bornemann committed
705 706
    case Abi::WindowsMsvc2015Flavor:
        return QLatin1String("msvc2015");
707 708
    case Abi::WindowsMsvc2017Flavor:
        return QLatin1String("msvc2017");
709
    case Abi::WindowsMSysFlavor:
710
        return QLatin1String("msys");
711
    case Abi::WindowsCEFlavor:
712
        return QLatin1String("ce");
713 714
    case Abi::VxWorksFlavor:
        return QLatin1String("vxworks");
715
    case Abi::UnknownFlavor: // fall through!
716 717 718 719 720 721 722 723
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
724
    case ElfFormat:
725
        return QLatin1String("elf");
hjk's avatar
hjk committed
726
    case PEFormat:
727
        return QLatin1String("pe");
hjk's avatar
hjk committed
728
    case MachOFormat:
729
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
730
    case RuntimeQmlFormat:
731
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
732
    case UnknownFormat: // fall through!
733 734 735 736 737 738 739 740 741 742 743 744
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(int w)
{
    if (w == 0)
        return QLatin1String("unknown");
    return QString::fromLatin1("%1bit").arg(w);
}

745 746 747 748 749
QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
{
    QList<OSFlavor> result;
    switch (o) {
    case BsdOS:
750
        return result << FreeBsdFlavor << OpenBsdFlavor << NetBsdFlavor << UnknownFlavor;
751
    case LinuxOS:
752
        return result << GenericLinuxFlavor << AndroidLinuxFlavor << UnknownFlavor;
753 754
    case DarwinOS:
        return result << GenericDarwinFlavor << UnknownFlavor;
755
    case UnixOS:
756
        return result << GenericUnixFlavor << SolarisUnixFlavor << UnknownFlavor;
757 758
    case WindowsOS:
        return result << WindowsMsvc2005Flavor << WindowsMsvc2008Flavor << WindowsMsvc2010Flavor
Joerg Bornemann's avatar
Joerg Bornemann committed
759
                      << WindowsMsvc2012Flavor << WindowsMsvc2013Flavor << WindowsMsvc2015Flavor
760
                      << WindowsMsvc2017Flavor
Joerg Bornemann's avatar
Joerg Bornemann committed
761
                      << WindowsMSysFlavor << WindowsCEFlavor << UnknownFlavor;
762
    case VxWorks:
763
        return result << VxWorksFlavor << UnknownFlavor;
764 765 766 767 768 769 770
    case UnknownOS:
        return result << UnknownFlavor;
    default:
        break;
    }
    return result;
}
771 772 773 774

Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
775 776 777
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
778 779

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
780
    os = WindowsOS;
781 782 783
#if _MSC_VER >= 1910
    subos = WindowsMsvc2017Flavor;
#elif _MSC_VER == 1900
784 785
    subos = WindowsMsvc2015Flavor;
#elif _MSC_VER == 1800
Yuchen Deng's avatar
Yuchen Deng committed
786 787
    subos = WindowsMsvc2013Flavor;
#elif _MSC_VER == 1700
Tobias Hunger's avatar
Tobias Hunger committed
788 789
    subos = WindowsMsvc2012Flavor;
#elif _MSC_VER == 1600
790 791 792 793 794
    subos = WindowsMsvc2010Flavor;
#elif _MSC_VER == 1500
    subos = WindowsMsvc2008Flavor;
#elif _MSC_VER == 1400
    subos = WindowsMsvc2005Flavor;
795
#elif defined (Q_CC_MINGW)
796 797
    subos = WindowsMSysFlavor;
#endif
hjk's avatar
hjk committed
798
    format = PEFormat;
799
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
800 801 802
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
803 804 805
#elif defined (Q_OS_DARWIN)
    os = DarwinOS;
    subos = GenericDarwinFlavor;
hjk's avatar
hjk committed
806
    format = MachOFormat;
807 808 809 810 811 812 813 814 815 816
#elif defined (Q_OS_BSD4)
    os = BsdOS;
# if defined (Q_OS_FREEBSD)
    subos = FreeBsdFlavor;
# elif defined (Q_OS_NETBSD)
    subos = NetBsdFlavor;
# elif defined (Q_OS_OPENBSD)
    subos = OpenBsdFlavor;
# endif
    format = ElfFormat;
817 818
#endif

Tobias Hunger's avatar
Tobias Hunger committed
819 820 821 822 823
    const Abi result(arch, os, subos, format, QSysInfo::WordSize);
    if (!result.isValid())
        qWarning("Unable to completely determine the host ABI (%s).",
                 qPrintable(result.toString()));
    return result;
824 825
}

Tobias Hunger's avatar
Tobias Hunger committed
826
QList<Abi> Abi::abisOfBinary(const Utils::FileName &path)
827
{
828
    QList<Abi> tmp;
829
    if (path.isEmpty())
830
        return tmp;
831

Tobias Hunger's avatar
Tobias Hunger committed
832
    QFile f(path.toString());
833
    if (!f.exists())
834
        return tmp;
835

Montel Laurent's avatar
Montel Laurent committed
836 837 838
    if (!f.open(QFile::ReadOnly))
        return tmp;

839
    QByteArray data = f.read(1024);
840
    if (data.size() >= 67
Tobias Hunger's avatar
Tobias Hunger committed
841 842 843
            && getUint8(data, 0) == '!' && getUint8(data, 1) == '<' && getUint8(data, 2) == 'a'
            && getUint8(data, 3) == 'r' && getUint8(data, 4) == 'c' && getUint8(data, 5) == 'h'
            && getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) {
844
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
845 846 847 848 849

        data = data.mid(8); // Cut of ar file magic
        quint64 offset = 8;

        while (!data.isEmpty()) {
Tobias Hunger's avatar
Tobias Hunger committed
850
            if ((getUint8(data, 58) != 0x60 || getUint8(data, 59) != 0x0a)) {
Tobias Hunger's avatar
Tobias Hunger committed
851
                qWarning() << path.toString() << ": Thought it was an ar-file, but it is not!";
852
                break;
853
            }
854

855 856 857
            const QString fileName = QString::fromLocal8Bit(data.mid(0, 16));
            quint64 fileNameOffset = 0;
            if (fileName.startsWith(QLatin1String("#1/")))
858
                fileNameOffset = fileName.midRef(3).toInt();
859
            const QString fileLength = QString::fromLatin1(data.mid(48, 10));
860

Tobias Hunger's avatar
Tobias Hunger committed
861
            int toSkip = 60 + fileNameOffset;
862
            offset += fileLength.toInt() + 60 /* header */;
863 864 865 866 867

            tmp.append(abiOf(data.mid(toSkip)));
            if (tmp.isEmpty() && fileName == QLatin1String("/0              "))
                tmp = parseCoffHeader(data.mid(toSkip, 20)); // This might be windws...

868 869
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
870
                break;
871

872
            offset += (offset % 2); // ar is 2 byte aligned
873
            f.seek(offset);
874
            data = f.read(1024);
875
        }
876
    } else {
877
        tmp = abiOf(data);
878
    }
879 880
    f.close();

881 882 883 884 885 886 887
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

888 889 890 891
    return result;
}

} // namespace ProjectExplorer
892 893 894 895

// Unit tests:
#ifdef WITH_TESTS
#   include <QTest>
896
#   include <QFileInfo>
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912

#   include "projectexplorer.h"

void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary_data()
{
    QTest::addColumn<QString>("file");
    QTest::addColumn<QStringList>("abis");

    QTest::newRow("no file")
            << QString()
            << (QStringList());
    QTest::newRow("non existing file")
            << QString::fromLatin1("/does/not/exist")
            << (QStringList());

    // Set up prefix for test data now that we can be sure to have some tests to run:
913
    QString prefix = QString::fromLocal8Bit(qgetenv("QTC_TEST_EXTRADATALOCATION"));