abi.cpp 48 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
#include <QDebug>
#include <QtEndian>
#include <QFile>
Samuel Gaist's avatar
Samuel Gaist committed
33
#include <QRegExp>
34 35 36
#include <QString>
#include <QStringList>
#include <QSysInfo>
37

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

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

    \sa ProjectExplorer::ToolChain
*/

47 48
namespace ProjectExplorer {

49 50 51 52
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------

Tobias Hunger's avatar
Tobias Hunger committed
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 87
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));
}

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

Tobias Hunger's avatar
Tobias Hunger committed
107 108 109 110 111 112 113 114 115 116 117
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
118
    quint16 machine = getLEUint16(data, 0);
Tobias Hunger's avatar
Tobias Hunger committed
119
    switch (machine) {
120 121 122 123 124 125
    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
126 127 128 129 130 131 132 133
    case 0x8664: // x86_64
        arch = Abi::X86Architecture;
        width = 64;
        break;
    case 0x014c: // i386
        arch = Abi::X86Architecture;
        width = 32;
        break;
134
    case 0x0166: // MIPS, little endian
Tobias Hunger's avatar
Tobias Hunger committed
135
        arch = Abi::MipsArchitecture;
136 137
        width = 32;
        break;
Tobias Hunger's avatar
Tobias Hunger committed
138 139 140 141 142 143
    case 0x0200: // ia64
        arch = Abi::ItaniumArchitecture;
        width = 64;
        break;
    }

144
    if (data.size() >= 24) {
Tobias Hunger's avatar
Tobias Hunger committed
145
        // Get Major and Minor Image Version from optional header fields
146 147 148 149
        quint8 minorLinker = data.at(23);
        switch (data.at(22)) {
        case 2:
        case 3: // not yet reached:-)
Tobias Hunger's avatar
Tobias Hunger committed
150
            flavor = Abi::WindowsMSysFlavor;
151 152 153 154 155 156 157 158 159 160 161 162 163
            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
164 165 166
        case 12:
            flavor = Abi::WindowsMsvc2013Flavor;
            break;
Joerg Bornemann's avatar
Joerg Bornemann committed
167
        case 14:
168 169 170
            flavor = minorLinker >= quint8(10)
                ? Abi::WindowsMsvc2017Flavor // MSVC2017 RC
                : Abi::WindowsMsvc2015Flavor;
171
            break;
172 173
        case 15:
            flavor = Abi::WindowsMsvc2017Flavor;
Joerg Bornemann's avatar
Joerg Bornemann committed
174
            break;
175 176 177 178
        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
179
                qWarning("%s: Unknown MSVC flavour encountered.", Q_FUNC_INFO);
180
            break;
181
        }
Tobias Hunger's avatar
Tobias Hunger committed
182 183 184 185 186 187 188 189
    }

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

    return result;
}

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

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

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

236 237
        switch (machine) {
        case 3: // EM_386
238
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
239 240
            break;
        case 8: // EM_MIPS
Tobias Hunger's avatar
Tobias Hunger committed
241
            result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
242 243
            break;
        case 20: // EM_PPC
244
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
245 246
            break;
        case 21: // EM_PPC64
247
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
248
            break;
249
        case 40: // EM_ARM
250
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
251
            break;
Daniel Teske's avatar
Daniel Teske committed
252 253 254
        case 183: // EM_AARCH64
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 64));
            break;
255
        case 62: // EM_X86_64
256
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
257
            break;
Tobias Hunger's avatar
Tobias Hunger committed
258 259 260
        case 42: // EM_SH
            result.append(Abi(Abi::ShArchitecture, os, flavor, Abi::ElfFormat, 32));
            break;
261
        case 50: // EM_IA_64
262
            result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
263 264
            break;
        default:
Nikolai Kosjar's avatar
Nikolai Kosjar committed
265
            ;
266
        }
Tobias Hunger's avatar
Tobias Hunger committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    } 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);
287 288 289 290 291
        int pos = 8;
        for (quint32 i = 0; i < count; ++i) {
            if (data.size() <= pos + 4)
                break;

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

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

        // Get PE/COFF header position from MZ header:
Tobias Hunger's avatar
Tobias Hunger committed
306
        qint32 pePos = getLEUint32(data, 60);
307 308
        if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
            return result;
Tobias Hunger's avatar
Tobias Hunger committed
309 310
        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
311
            result = parseCoffHeader(data.mid(pePos + 4));
312 313 314 315 316 317 318 319
    }
    return result;
}

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

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

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

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

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
407 408 409
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
BogDan Vatra's avatar
BogDan Vatra committed
410 411
        else if (abiParts.at(2) == QLatin1String("android") && m_os == LinuxOS)
            m_osFlavor = AndroidLinuxFlavor;
James McDonnell's avatar
James McDonnell committed
412 413
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == QnxOS)
            m_osFlavor = GenericQnxFlavor;
414 415 416 417 418 419
        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;
420 421
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == DarwinOS)
            m_osFlavor = GenericDarwinFlavor;
hjk's avatar
hjk committed
422 423
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == UnixOS)
            m_osFlavor = GenericUnixFlavor;
424 425
        else if (abiParts.at(2) == QLatin1String("solaris") && m_os == UnixOS)
            m_osFlavor = SolarisUnixFlavor;
426 427 428 429 430 431
        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
432 433
        else if (abiParts.at(2) == QLatin1String("msvc2012") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2012Flavor;
Yuchen Deng's avatar
Yuchen Deng committed
434 435
        else if (abiParts.at(2) == QLatin1String("msvc2013") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2013Flavor;
Joerg Bornemann's avatar
Joerg Bornemann committed
436 437
        else if (abiParts.at(2) == QLatin1String("msvc2015") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2015Flavor;
438 439
        else if (abiParts.at(2) == QLatin1String("msvc2017") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvc2017Flavor;
hjk's avatar
hjk committed
440 441 442 443
        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;
444 445
        else if (abiParts.at(2) == QLatin1String("vxworks") && m_os == VxWorks)
            m_osFlavor = VxWorksFlavor;
446 447 448 449 450 451
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
452
            m_binaryFormat = UnknownFormat;
453
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
454
            m_binaryFormat = ElfFormat;
455
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
456
            m_binaryFormat = PEFormat;
457
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
458
            m_binaryFormat = MachOFormat;
459
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
460
            m_binaryFormat = RuntimeQmlFormat;
461 462 463 464 465
        else
            return;
    }

    if (abiParts.count() >= 5) {
466
        const QStringRef &bits = abiParts.at(4);
467 468 469 470
        if (!bits.endsWith(QLatin1String("bit")))
            return;

        bool ok = false;
471 472 473
        const QStringRef number =
            bits.string()->midRef(bits.position(), bits.count() - 3);
        const int bitCount = number.toInt(&ok);
474 475 476 477 478 479 480 481
        if (!ok)
            return;
        if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
            return;
        m_wordWidth = bitCount;
    }
}

482 483 484 485 486 487
Abi Abi::abiFromTargetTriplet(const QString &triple)
{
    QString machine = triple.toLower();
    if (machine.isEmpty())
        return Abi();

488
    const QVector<QStringRef> parts = machine.splitRef(QRegExp(QLatin1String("[ /-]")));
489 490 491 492 493

    Abi::Architecture arch = Abi::UnknownArchitecture;
    Abi::OS os = Abi::UnknownOS;
    Abi::OSFlavor flavor = Abi::UnknownFlavor;
    Abi::BinaryFormat format = Abi::UnknownFormat;
494
    unsigned char width = 0;
495 496
    int unknownCount = 0;

497
    for (const QStringRef &p : parts) {
498 499 500 501 502 503 504 505
        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;
506
            width = 32;
507 508
        } else if (p.startsWith(QLatin1String("arm"))) {
            arch = Abi::ArmArchitecture;
509
            width = p.contains(QLatin1String("64")) ? 64 : 32;
510 511 512
        } else if (p.startsWith(QLatin1String("aarch64"))) {
            arch = Abi::ArmArchitecture;
            width = 64;
513
        } else if (p.startsWith(QLatin1String("mips"))) {
514
            arch = Abi::MipsArchitecture;
Orgad Shaneh's avatar
Orgad Shaneh committed
515
            width = p.contains(QLatin1String("64")) ? 64 : 32;
516 517 518 519 520 521 522 523 524 525 526 527 528 529
        } 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;
530 531 532 533
        } else if (p == QLatin1String("android")) {
            flavor = Abi::AndroidLinuxFlavor;
        } else if (p == QLatin1String("androideabi")) {
            flavor = Abi::AndroidLinuxFlavor;
534 535 536 537 538
        } 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
539 540 541 542 543
        } else if (p.startsWith(QLatin1String("openbsd"))) {
            os = Abi::BsdOS;
            if (flavor == Abi::UnknownFlavor)
                flavor = Abi::OpenBsdFlavor;
            format = Abi::ElfFormat;
544
        } else if (p == QLatin1String("mingw32") || p == QLatin1String("win32")
545 546
                   || p == QLatin1String("mingw32msvc") || p == QLatin1String("msys")
                   || p == QLatin1String("cygwin")) {
547 548 549 550 551
            arch = Abi::X86Architecture;
            os = Abi::WindowsOS;
            flavor = Abi::WindowsMSysFlavor;
            format = Abi::PEFormat;
        } else if (p == QLatin1String("apple")) {
552 553
            os = Abi::DarwinOS;
            flavor = Abi::GenericDarwinFlavor;
554 555 556 557 558 559 560
            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;
561 562 563 564 565 566
        } else if (p == QLatin1String("wrs")) {
            continue;
        } else if (p == QLatin1String("vxworks")) {
            os = Abi::VxWorks;
            flavor = Abi::VxWorksFlavor;
            format = Abi::ElfFormat;
James McDonnell's avatar
James McDonnell committed
567 568 569 570
        } else if (p.startsWith(QLatin1String("qnx"))) {
            os = Abi::QnxOS;
            flavor = Abi::GenericQnxFlavor;
            format = Abi::ElfFormat;
571 572 573 574 575 576 577 578
        } else {
            ++unknownCount;
        }
    }

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

579 580 581 582 583 584 585 586 587
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
588
    return dn.join(QLatin1Char('-'));
589 590
}

Tobias Hunger's avatar
Tobias Hunger committed
591 592 593 594 595
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

596 597 598 599 600 601 602 603 604 605 606
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
{
607 608 609 610 611
    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);
612 613 614 615 616 617
    // *-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
618
                  && ((os() == other.os()) && (os() == LinuxOS))
619 620 621
                  && (osFlavor() == GenericLinuxFlavor || other.osFlavor() == GenericLinuxFlavor)
                  && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == Abi::UnknownFormat)
                  && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0))
622
        isCompat = true;
BogDan Vatra's avatar
BogDan Vatra committed
623 624
    if (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor)
        isCompat = (osFlavor() == other.osFlavor() && architecture() == other.architecture());
625
    return isCompat;
626 627 628 629
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
630 631 632 633
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
634 635 636
            && m_wordWidth != 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
637 638 639 640 641 642 643 644 645
bool Abi::isNull() const
{
    return m_architecture == UnknownArchitecture
            && m_os == UnknownOS
            && m_osFlavor == UnknownFlavor
            && m_binaryFormat == UnknownFormat
            && m_wordWidth == 0;
}

646 647 648
QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
649
    case ArmArchitecture:
650
        return QLatin1String("arm");
hjk's avatar
hjk committed
651
    case X86Architecture:
652
        return QLatin1String("x86");
Tobias Hunger's avatar
Tobias Hunger committed
653
    case MipsArchitecture:
654
        return QLatin1String("mips");
hjk's avatar
hjk committed
655
    case PowerPCArchitecture:
656
        return QLatin1String("ppc");
hjk's avatar
hjk committed
657
    case ItaniumArchitecture:
658
        return QLatin1String("itanium");
Tobias Hunger's avatar
Tobias Hunger committed
659
    case ShArchitecture:
Tobias Hunger's avatar
Tobias Hunger committed
660
        return QLatin1String("sh");
hjk's avatar
hjk committed
661
    case UnknownArchitecture: // fall through!
662 663 664 665 666 667 668 669
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
670
    case LinuxOS:
671
        return QLatin1String("linux");
672 673
    case BsdOS:
        return QLatin1String("bsd");
674 675
    case DarwinOS:
        return QLatin1String("darwin");
hjk's avatar
hjk committed
676
    case UnixOS:
677
        return QLatin1String("unix");
hjk's avatar
hjk committed
678
    case WindowsOS:
679
        return QLatin1String("windows");
680 681
    case VxWorks:
        return QLatin1String("vxworks");
James McDonnell's avatar
James McDonnell committed
682 683
    case QnxOS:
        return QLatin1String("qnx");
hjk's avatar
hjk committed
684
    case UnknownOS: // fall through!
685 686 687 688 689
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
690
QString Abi::toString(const OSFlavor &of)
691 692
{
    switch (of) {
693
    case Abi::GenericLinuxFlavor:
694
        return QLatin1String("generic");
695
    case Abi::AndroidLinuxFlavor:
BogDan Vatra's avatar
BogDan Vatra committed
696
        return QLatin1String("android");
697
    case Abi::FreeBsdFlavor:
698
        return QLatin1String("freebsd");
699
    case Abi::NetBsdFlavor:
700
        return QLatin1String("netbsd");
701
    case Abi::OpenBsdFlavor:
702
        return QLatin1String("openbsd");
703
    case Abi::GenericDarwinFlavor:
704
        return QLatin1String("generic");
705
    case Abi::GenericUnixFlavor:
706
        return QLatin1String("generic");
707
    case Abi::SolarisUnixFlavor:
708
        return QLatin1String("solaris");
709
    case Abi::WindowsMsvc2005Flavor:
710
        return QLatin1String("msvc2005");
711
    case Abi::WindowsMsvc2008Flavor:
712
        return QLatin1String("msvc2008");
713
    case Abi::WindowsMsvc2010Flavor:
714
        return QLatin1String("msvc2010");
715
    case Abi::WindowsMsvc2012Flavor:
Tobias Hunger's avatar
Tobias Hunger committed
716
        return QLatin1String("msvc2012");
717
    case Abi::WindowsMsvc2013Flavor:
Yuchen Deng's avatar
Yuchen Deng committed
718
        return QLatin1String("msvc2013");
Joerg Bornemann's avatar
Joerg Bornemann committed
719 720
    case Abi::WindowsMsvc2015Flavor:
        return QLatin1String("msvc2015");
721 722
    case Abi::WindowsMsvc2017Flavor:
        return QLatin1String("msvc2017");
723
    case Abi::WindowsMSysFlavor:
724
        return QLatin1String("msys");
725
    case Abi::WindowsCEFlavor:
726
        return QLatin1String("ce");
727 728
    case Abi::VxWorksFlavor:
        return QLatin1String("vxworks");
James McDonnell's avatar
James McDonnell committed
729 730
    case Abi::GenericQnxFlavor:
        return QLatin1String("generic");
731
    case Abi::UnknownFlavor: // fall through!
732 733 734 735 736 737 738 739
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
740
    case ElfFormat:
741
        return QLatin1String("elf");
hjk's avatar
hjk committed
742
    case PEFormat:
743
        return QLatin1String("pe");
hjk's avatar
hjk committed
744
    case MachOFormat:
745
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
746
    case RuntimeQmlFormat:
747
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
748
    case UnknownFormat: // fall through!
749 750 751 752 753 754 755 756 757 758 759 760
    default:
        return QLatin1String("unknown");
    }
}

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

761 762 763 764 765
QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
{
    QList<OSFlavor> result;
    switch (o) {
    case BsdOS:
766
        return result << FreeBsdFlavor << OpenBsdFlavor << NetBsdFlavor << UnknownFlavor;
767
    case LinuxOS:
768
        return result << GenericLinuxFlavor << AndroidLinuxFlavor << UnknownFlavor;
769 770
    case DarwinOS:
        return result << GenericDarwinFlavor << UnknownFlavor;
771
    case UnixOS:
772
        return result << GenericUnixFlavor << SolarisUnixFlavor << UnknownFlavor;
773 774
    case WindowsOS:
        return result << WindowsMsvc2005Flavor << WindowsMsvc2008Flavor << WindowsMsvc2010Flavor
Joerg Bornemann's avatar
Joerg Bornemann committed
775
                      << WindowsMsvc2012Flavor << WindowsMsvc2013Flavor << WindowsMsvc2015Flavor
776
                      << WindowsMsvc2017Flavor
Joerg Bornemann's avatar
Joerg Bornemann committed
777
                      << WindowsMSysFlavor << WindowsCEFlavor << UnknownFlavor;
778
    case VxWorks:
779
        return result << VxWorksFlavor << UnknownFlavor;
James McDonnell's avatar
James McDonnell committed
780 781
    case QnxOS:
        return result << GenericQnxFlavor << UnknownFlavor;
782 783 784 785 786 787 788
    case UnknownOS:
        return result << UnknownFlavor;
    default:
        break;
    }
    return result;
}
789 790 791 792

Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
793 794 795
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
796 797

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
798
    os = WindowsOS;
799 800 801
#if _MSC_VER >= 1910
    subos = WindowsMsvc2017Flavor;
#elif _MSC_VER == 1900
802 803
    subos = WindowsMsvc2015Flavor;
#elif _MSC_VER == 1800
Yuchen Deng's avatar
Yuchen Deng committed
804 805
    subos = WindowsMsvc2013Flavor;
#elif _MSC_VER == 1700
Tobias Hunger's avatar
Tobias Hunger committed
806 807
    subos = WindowsMsvc2012Flavor;
#elif _MSC_VER == 1600
808 809 810 811 812
    subos = WindowsMsvc2010Flavor;
#elif _MSC_VER == 1500
    subos = WindowsMsvc2008Flavor;
#elif _MSC_VER == 1400
    subos = WindowsMsvc2005Flavor;
813
#elif defined (Q_CC_MINGW)
814 815
    subos = WindowsMSysFlavor;
#endif
hjk's avatar
hjk committed
816
    format = PEFormat;
817
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
818 819 820
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
821 822 823
#elif defined (Q_OS_DARWIN)
    os = DarwinOS;
    subos = GenericDarwinFlavor;
hjk's avatar
hjk committed
824
    format = MachOFormat;
825 826 827 828 829 830 831 832 833 834
#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;
835 836
#endif

Tobias Hunger's avatar
Tobias Hunger committed
837 838 839 840 841
    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;
842 843
}

Tobias Hunger's avatar
Tobias Hunger committed
844
QList<Abi> Abi::abisOfBinary(const Utils::FileName &path)
845
{
846
    QList<Abi> tmp;
847
    if (path.isEmpty())
848
        return tmp;
849

Tobias Hunger's avatar
Tobias Hunger committed
850
    QFile f(path.toString());
851
    if (!f.exists())
852
        return tmp;
853

Montel Laurent's avatar
Montel Laurent committed
854 855 856
    if (!f.open(QFile::ReadOnly))
        return tmp;

857
    QByteArray data = f.read(1024);
858
    if (data.size() >= 67
Tobias Hunger's avatar
Tobias Hunger committed
859 860 861
            && 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) {
862
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
863 864 865 866 867

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

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

873 874 875
            const QString fileName = QString::fromLocal8Bit(data.mid(0, 16));
            quint64 fileNameOffset = 0;
            if (fileName.startsWith(QLatin1String("#1/")))
876
                fileNameOffset = fileName.midRef(3).toInt();
877
            const QString fileLength = QString::fromLatin1(data.mid(48, 10));
878

Tobias Hunger's avatar
Tobias Hunger committed
879
            int toSkip = 60 + fileNameOffset;
880
            offset += fileLength.toInt() + 60 /* header */;
881 882 883 884 885

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

886 887
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
888
                break;
889

890
            offset += (offset % 2); // ar is 2 byte aligned
891
            f.seek(offset);
892
            data = f.read(1024);
893
        }
894
    } else {
895
        tmp = abiOf(data);
896
    }
897 898
    f.close();