abi.cpp 30.7 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18 19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30 31 32 33 34 35
**
**************************************************************************/

#include "abi.h"

#include <QtCore/QCoreApplication>
Tobias Hunger's avatar
Tobias Hunger committed
36
#include <QtCore/QDebug>
37
#include <QtCore/QtEndian>
38 39 40 41 42
#include <QtCore/QFile>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QSysInfo>

43 44 45 46 47 48 49 50
/*!
    \class ProjectExplorer::Abi

    \brief Represents the Application Binary Interface (ABI) of a target platform.

    \sa ProjectExplorer::ToolChain
*/

51 52
namespace ProjectExplorer {

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------

static Abi macAbiForCpu(quint32 type) {
    switch (type) {
    case 7: // CPU_TYPE_X86, CPU_TYPE_I386
        return Abi(Abi::X86Architecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
    case 0x01000000 +  7: // CPU_TYPE_X86_64
        return Abi(Abi::X86Architecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 64);
    case 18: // CPU_TYPE_POWERPC
        return Abi(Abi::PowerPCArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
    case 0x01000000 + 18: // CPU_TYPE_POWERPC64
        return Abi(Abi::PowerPCArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
    case 12: // CPU_TYPE_ARM
        return Abi(Abi::ArmArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
    default:
        return Abi();
    }
}

Tobias Hunger's avatar
Tobias Hunger committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
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
    quint16 machine = (data.at(1) << 8) + data.at(0);
    switch (machine) {
    case 0x8664: // x86_64
        arch = Abi::X86Architecture;
        width = 64;
        break;
    case 0x014c: // i386
        arch = Abi::X86Architecture;
        width = 32;
        break;
95
    case 0x0166: // MIPS, little endian
Tobias Hunger's avatar
Tobias Hunger committed
96
        arch = Abi::MipsArchitecture;
97 98
        width = 32;
        break;
Tobias Hunger's avatar
Tobias Hunger committed
99 100 101 102 103 104 105 106 107
    case 0x0200: // ia64
        arch = Abi::ItaniumArchitecture;
        width = 64;
        break;
    }

    if (data.size() >= 68) {
        // Get Major and Minor Image Version from optional header fields
        quint32 image = (data.at(67) << 24) + (data.at(66) << 16) + (data.at(65) << 8) + data.at(64);
108
        if (image == 1) { // Image is 1 for mingw and higher for MSVC (4.something in some encoding)
Tobias Hunger's avatar
Tobias Hunger committed
109
            flavor = Abi::WindowsMSysFlavor;
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
        } else {
            switch (data.at(22)) {
            case 8:
                flavor = Abi::WindowsMsvc2005Flavor;
                break;
            case 9:
                flavor = Abi::WindowsMsvc2008Flavor;
                break;
            case 10:
                flavor = Abi::WindowsMsvc2010Flavor;
                break;
            default:
                // Keep unknown flavor
                break;
            }
        }
Tobias Hunger's avatar
Tobias Hunger committed
126 127 128 129 130 131 132 133
    }

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

    return result;
}

134 135 136 137 138 139 140 141
static QList<Abi> abiOf(const QByteArray &data)
{
    QList<Abi> result;

    if (data.size() >= 20
            && static_cast<unsigned char>(data.at(0)) == 0x7f && static_cast<unsigned char>(data.at(1)) == 'E'
            && static_cast<unsigned char>(data.at(2)) == 'L' && static_cast<unsigned char>(data.at(3)) == 'F') {
        // ELF format:
142
        bool isLsbEncoded = (static_cast<quint8>(data.at(5)) == 1);
143
        quint16 machine = (data.at(19) << 8) + data.at(18);
144 145
        if (!isLsbEncoded)
            machine = qFromBigEndian(machine);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        quint8 osAbi = static_cast<quint8>(data.at(7));

        Abi::OS os = Abi::UnixOS;
        Abi::OSFlavor flavor = Abi::GenericUnixFlavor;
        // http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
        switch (osAbi) {
        case 2: // NetBSD:
            os = Abi::BsdOS;
            flavor = Abi::NetBsdFlavor;
            break;
        case 3: // Linux:
        case 0: // no extra info available: Default to Linux:
            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;
        case 12: // OpenBSD:
            os = Abi::BsdOS;
            flavor = Abi::OpenBsdFlavor;
        }
173

174 175
        switch (machine) {
        case 3: // EM_386
176
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
177 178
            break;
        case 8: // EM_MIPS
Tobias Hunger's avatar
Tobias Hunger committed
179
            result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
180 181
            break;
        case 20: // EM_PPC
182
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
183 184
            break;
        case 21: // EM_PPC64
185
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
186
            break;
187
        case 40: // EM_ARM
188
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
189
            break;
190
        case 62: // EM_X86_64
191
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
192 193
            break;
        case 50: // EM_IA_64
194
            result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
            break;
        default:
            ;;
        }
    } else if (data.size() >= 8
               && (static_cast<unsigned char>(data.at(0)) == 0xce || static_cast<unsigned char>(data.at(0)) == 0xcf)
               && static_cast<unsigned char>(data.at(1)) == 0xfa
               && static_cast<unsigned char>(data.at(2)) == 0xed && static_cast<unsigned char>(data.at(3)) == 0xfe) {
        // Mach-O format (Mac non-fat binary, 32 and 64bit magic)
        quint32 type = (data.at(7) << 24) + (data.at(6) << 16) + (data.at(5) << 8) + data.at(4);
        result.append(macAbiForCpu(type));
    } else if (data.size() >= 8
               && static_cast<unsigned char>(data.at(0)) == 0xca && static_cast<unsigned char>(data.at(1)) == 0xfe
               && static_cast<unsigned char>(data.at(2)) == 0xba && static_cast<unsigned char>(data.at(3)) == 0xbe) {
        // Mac fat binary:
        quint32 count = (data.at(4) << 24) + (data.at(5) << 16) + (data.at(6) << 8) + data.at(7);
        int pos = 8;
        for (quint32 i = 0; i < count; ++i) {
            if (data.size() <= pos + 4)
                break;

            quint32 type = (data.at(pos) << 24) + (data.at(pos + 1) << 16) + (data.at(pos + 2) << 8) + data.at(pos + 3);
            result.append(macAbiForCpu(type));
            pos += 20;
        }
220 221 222 223
    } else if (data.size() >= 20
               && static_cast<unsigned char>(data.at(16)) == 'E' && static_cast<unsigned char>(data.at(17)) == 'P'
               && static_cast<unsigned char>(data.at(18)) == 'O' && static_cast<unsigned char>(data.at(19)) == 'C') {
        result.append(Abi(Abi::ArmArchitecture, Abi::SymbianOS, Abi::SymbianDeviceFlavor, Abi::ElfFormat, 32));
224
    } else if (data.size() >= 64){
225
        // Windows PE
226 227 228 229 230 231 232 233 234 235 236 237

        // MZ header first:
        if (!data.at(0) == 'M' || !data.at(1) == 'Z')
            return result;

        // Get PE/COFF header position from MZ header:
        qint32 pePos = static_cast<quint8>(data.at(60)) + static_cast<quint8>(data.at(61)) * 0x100
                       + static_cast<quint8>(data.at(62)) * 0x10000 + static_cast<quint8>(data.at(63)) * 0x1000000;
        if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
            return result;
        if (data.at(pePos) == 'P' && data.at(pePos + 1) == 'E'
            && data.at(pePos + 2) == 0 && data.at(pePos + 3) == 0)
Tobias Hunger's avatar
Tobias Hunger committed
238
            result = parseCoffHeader(data.mid(pePos + 4));
239 240 241 242 243 244 245 246
    }
    return result;
}

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

247
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
248
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
249 250 251
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
252 253
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
254
        break;
hjk's avatar
hjk committed
255 256 257
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
258
        break;
259 260
    case ProjectExplorer::Abi::BsdOS:
        m_osFlavor = FreeBsdFlavor;
261
        break;
hjk's avatar
hjk committed
262 263 264
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
265
        break;
hjk's avatar
hjk committed
266 267 268
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
269
        break;
hjk's avatar
hjk committed
270 271 272
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
273
        break;
hjk's avatar
hjk committed
274
    case ProjectExplorer::Abi::WindowsOS:
275
        if (m_osFlavor < WindowsMsvc2005Flavor || m_osFlavor > WindowsCEFlavor)
hjk's avatar
hjk committed
276
            m_osFlavor = UnknownFlavor;
277 278 279 280 281
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
282 283
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
284 285 286 287
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
288
            m_architecture = UnknownArchitecture;
289
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
290
            m_architecture = ArmArchitecture;
291
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
292
            m_architecture = X86Architecture;
293
        else if (abiParts.at(0) == QLatin1String("mips"))
Tobias Hunger's avatar
Tobias Hunger committed
294
            m_architecture = MipsArchitecture;
295
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
296
            m_architecture = PowerPCArchitecture;
297
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
298
            m_architecture = ItaniumArchitecture;
299 300 301 302 303 304
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
305
            m_os = UnknownOS;
306
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
307
            m_os = LinuxOS;
308 309
        else if (abiParts.at(1) == QLatin1String("bsd"))
            m_os = BsdOS;
310
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
311
            m_os = MacOS;
312
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
313
            m_os = SymbianOS;
314
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
315
            m_os = UnixOS;
316
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
317
            m_os = WindowsOS;
318
        else
319

320 321 322 323 324
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
325 326 327
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
328 329 330 331 332 333
        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;
hjk's avatar
hjk committed
334 335 336 337 338 339 340 341 342 343 344 345
        else if (abiParts.at(2) == QLatin1String("maemo") && m_os == LinuxOS)
            m_osFlavor = MaemoLinuxFlavor;
        else if (abiParts.at(2) == QLatin1String("meego") && m_os == LinuxOS)
            m_osFlavor = MeegoLinuxFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == MacOS)
            m_osFlavor = GenericMacFlavor;
        else if (abiParts.at(2) == QLatin1String("device") && m_os == SymbianOS)
            m_osFlavor = SymbianDeviceFlavor;
        else if (abiParts.at(2) == QLatin1String("emulator") && m_os == SymbianOS)
            m_osFlavor = SymbianEmulatorFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == UnixOS)
            m_osFlavor = GenericUnixFlavor;
346 347
        else if (abiParts.at(2) == QLatin1String("solaris") && m_os == UnixOS)
            m_osFlavor = SolarisUnixFlavor;
348 349 350 351 352 353
        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;
hjk's avatar
hjk committed
354 355 356 357
        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;
358 359 360 361 362 363
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
364
            m_binaryFormat = UnknownFormat;
365
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
366
            m_binaryFormat = ElfFormat;
367
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
368
            m_binaryFormat = PEFormat;
369
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
370
            m_binaryFormat = MachOFormat;
371
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
372
            m_binaryFormat = RuntimeQmlFormat;
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        else
            return;
    }

    if (abiParts.count() >= 5) {
        const QString &bits = abiParts.at(4);
        if (!bits.endsWith(QLatin1String("bit")))
            return;

        bool ok = false;
        int bitCount = bits.left(bits.count() - 3).toInt(&ok);
        if (!ok)
            return;
        if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
            return;
        m_wordWidth = bitCount;
    }
}

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);

    return dn.join(QLatin1String("-"));
}

Tobias Hunger's avatar
Tobias Hunger committed
404 405 406 407 408
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

409 410 411 412 413 414 415 416 417 418 419
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
{
420 421 422 423 424 425 426 427 428 429 430 431 432 433
    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);
    // *-linux-generic-* is compatible with *-linux-*:
    if (!isCompat && architecture() == other.architecture()
                 && os() == other.os()
                 && osFlavor() == GenericLinuxFlavor
                 && other.os() == LinuxOS
                 && binaryFormat() == other.binaryFormat()
                 && wordWidth() == other.wordWidth())
        isCompat = true;
    return isCompat;
434 435 436 437
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
438 439 440 441
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
442 443 444 445 446 447
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
448
    case ArmArchitecture:
449
        return QLatin1String("arm");
hjk's avatar
hjk committed
450
    case X86Architecture:
451
        return QLatin1String("x86");
Tobias Hunger's avatar
Tobias Hunger committed
452
    case MipsArchitecture:
453
        return QLatin1String("mips");
hjk's avatar
hjk committed
454
    case PowerPCArchitecture:
455
        return QLatin1String("ppc");
hjk's avatar
hjk committed
456
    case ItaniumArchitecture:
457
        return QLatin1String("itanium");
hjk's avatar
hjk committed
458
    case UnknownArchitecture: // fall through!
459 460 461 462 463 464 465 466
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
467
    case LinuxOS:
468
        return QLatin1String("linux");
469 470
    case BsdOS:
        return QLatin1String("bsd");
hjk's avatar
hjk committed
471
    case MacOS:
472
        return QLatin1String("macos");
hjk's avatar
hjk committed
473
    case SymbianOS:
474
        return QLatin1String("symbian");
hjk's avatar
hjk committed
475
    case UnixOS:
476
        return QLatin1String("unix");
hjk's avatar
hjk committed
477
    case WindowsOS:
478
        return QLatin1String("windows");
hjk's avatar
hjk committed
479
    case UnknownOS: // fall through!
480 481 482 483 484
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
485
QString Abi::toString(const OSFlavor &of)
486 487
{
    switch (of) {
hjk's avatar
hjk committed
488
    case ProjectExplorer::Abi::GenericLinuxFlavor:
489
        return QLatin1String("generic");
490 491 492 493 494 495
    case ProjectExplorer::Abi::FreeBsdFlavor:
        return QLatin1String("freebsd");
    case ProjectExplorer::Abi::NetBsdFlavor:
        return QLatin1String("netbsd");
    case ProjectExplorer::Abi::OpenBsdFlavor:
        return QLatin1String("openbsd");
hjk's avatar
hjk committed
496
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
497
        return QLatin1String("maemo");
hjk's avatar
hjk committed
498
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
499
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
500
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
501
        return QLatin1String("meego");
hjk's avatar
hjk committed
502
    case ProjectExplorer::Abi::GenericMacFlavor:
503
        return QLatin1String("generic");
hjk's avatar
hjk committed
504
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
505
        return QLatin1String("device");
hjk's avatar
hjk committed
506
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
507
        return QLatin1String("emulator");
hjk's avatar
hjk committed
508
    case ProjectExplorer::Abi::GenericUnixFlavor:
509
        return QLatin1String("generic");
510 511
    case ProjectExplorer::Abi::SolarisUnixFlavor:
        return QLatin1String("solaris");
512 513 514 515 516 517
    case ProjectExplorer::Abi::WindowsMsvc2005Flavor:
        return QLatin1String("msvc2005");
    case ProjectExplorer::Abi::WindowsMsvc2008Flavor:
        return QLatin1String("msvc2008");
    case ProjectExplorer::Abi::WindowsMsvc2010Flavor:
        return QLatin1String("msvc2010");
hjk's avatar
hjk committed
518
    case ProjectExplorer::Abi::WindowsMSysFlavor:
519
        return QLatin1String("msys");
hjk's avatar
hjk committed
520
    case ProjectExplorer::Abi::WindowsCEFlavor:
521
        return QLatin1String("ce");
522
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
523 524 525 526 527 528 529 530
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
531
    case ElfFormat:
532
        return QLatin1String("elf");
hjk's avatar
hjk committed
533
    case PEFormat:
534
        return QLatin1String("pe");
hjk's avatar
hjk committed
535
    case MachOFormat:
536
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
537
    case RuntimeQmlFormat:
538
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
539
    case UnknownFormat: // fall through!
540 541 542 543 544 545 546 547 548 549 550 551
    default:
        return QLatin1String("unknown");
    }
}

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

552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
{
    QList<OSFlavor> result;
    switch (o) {
    case BsdOS:
        return result << FreeBsdFlavor << OpenBsdFlavor << NetBsdFlavor;
    case LinuxOS:
        return result << GenericLinuxFlavor << HarmattanLinuxFlavor << MaemoLinuxFlavor << MeegoLinuxFlavor;
    case MacOS:
        return result << GenericMacFlavor;
    case SymbianOS:
        return  result << SymbianDeviceFlavor << SymbianEmulatorFlavor;
    case UnixOS:
        return result << GenericUnixFlavor << SolarisUnixFlavor;
    case WindowsOS:
        return result << WindowsMsvc2005Flavor << WindowsMsvc2008Flavor << WindowsMsvc2010Flavor
                      << WindowsMSysFlavor << WindowsCEFlavor;
    case UnknownOS:
        return result << UnknownFlavor;
    default:
        break;
    }
    return result;
}
576 577 578 579

Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
580 581 582
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
583 584

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
585
    os = WindowsOS;
586 587 588 589 590 591 592 593 594
#if _MSC_VER == 1600
    subos = WindowsMsvc2010Flavor;
#elif _MSC_VER == 1500
    subos = WindowsMsvc2008Flavor;
#elif _MSC_VER == 1400
    subos = WindowsMsvc2005Flavor;
#elif defined (mingw32)
    subos = WindowsMSysFlavor;
#endif
hjk's avatar
hjk committed
595
    format = PEFormat;
596
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
597 598 599
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
600
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
601 602 603
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
604 605 606 607 608 609 610
#endif

    return Abi(arch, os, subos, format, QSysInfo::WordSize);
}

QList<Abi> Abi::abisOfBinary(const QString &path)
{
611
    QList<Abi> tmp;
612
    if (path.isEmpty())
613
        return tmp;
614 615 616

    QFile f(path);
    if (!f.exists())
617
        return tmp;
618 619 620

    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
621 622 623 624 625
    if (data.size() >= 67
            && static_cast<unsigned char>(data.at(0)) == '!' && static_cast<unsigned char>(data.at(1)) == '<'
            && static_cast<unsigned char>(data.at(2)) == 'a' && static_cast<unsigned char>(data.at(3)) == 'r'
            && static_cast<unsigned char>(data.at(4)) == 'c' && static_cast<unsigned char>(data.at(5)) == 'h'
            && static_cast<unsigned char>(data.at(6)) == '>' && static_cast<unsigned char>(data.at(7)) == 0x0a) {
626
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
627 628 629 630 631

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

        while (!data.isEmpty()) {
Tobias Hunger's avatar
Tobias Hunger committed
632
            if ((data.at(58) != 0x60 || data.at(59) != 0x0a)) {
633
                qWarning() << path << ": Thought it was an ar-file, but it is not!";
634
                break;
635
            }
636

637 638 639 640 641
            const QString fileName = QString::fromLocal8Bit(data.mid(0, 16));
            quint64 fileNameOffset = 0;
            if (fileName.startsWith(QLatin1String("#1/")))
                fileNameOffset = fileName.mid(3).toInt();
            const QString fileLength = QString::fromAscii(data.mid(48, 10));
642

Tobias Hunger's avatar
Tobias Hunger committed
643
            int toSkip = 60 + fileNameOffset;
644
            offset += fileLength.toInt() + 60 /* header */;
645 646 647 648 649

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

650 651
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
652
                break;
653

654
            offset += (offset % 2); // ar is 2 byte aligned
655
            f.seek(offset);
656
            data = f.read(1024);
657
        }
658
    } else {
659
        tmp = abiOf(data);
660
    }
661 662
    f.close();

663 664 665 666 667 668 669
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

670 671 672 673
    return result;
}

} // namespace ProjectExplorer
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

// Unit tests:
#ifdef WITH_TESTS
#   include <QTest>
#   include <QtCore/QFileInfo>

#   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:
    QString prefix = qgetenv("QTC_TEST_EXTRADATALOCATION");
    if (prefix.isEmpty())
        return;
698 699
    prefix += "/projectexplorer/abi";

700 701 702 703 704 705 706 707 708 709
    QFileInfo fi(prefix);
    if (!fi.exists() || !fi.isDir())
        return;
    prefix = fi.absoluteFilePath();

    QTest::newRow("text file")
            << QString::fromLatin1("%1/broken/text.txt").arg(prefix)
            << (QStringList());

    QTest::newRow("static QtCore: win msvc2008")
710
            << QString::fromLatin1("%1/static/win-msvc2008-release.lib").arg(prefix)
711
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
712
    QTest::newRow("static QtCore: win msvc2008 II")
713
            << QString::fromLatin1("%1/static/win-msvc2008-release2.lib").arg(prefix)
714
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
715
    QTest::newRow("static QtCore: win msvc2008 (debug)")
716
            << QString::fromLatin1("%1/static/win-msvc2008-debug.lib").arg(prefix)
717
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
718
    QTest::newRow("static QtCore: win mingw")
719
            << QString::fromLatin1("%1/static/win-mingw.a").arg(prefix)
720
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
721
    QTest::newRow("static QtCore: mac (debug)")
722
            << QString::fromLatin1("%1/static/mac-32bit-debug.a").arg(prefix)
723 724
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit"));
    QTest::newRow("static QtCore: linux 32bit")
725
            << QString::fromLatin1("%1/static/linux-32bit-release.a").arg(prefix)
726 727
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
    QTest::newRow("static QtCore: linux 64bit")
728
            << QString::fromLatin1("%1/static/linux-64bit-release.a").arg(prefix)
729 730 731
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));

    QTest::newRow("static stdc++: mac fat")
732
            << QString::fromLatin1("%1/static/mac-fat.a").arg(prefix)
733 734 735 736 737
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit")
                              << QString::fromLatin1("ppc-macos-generic-mach_o-32bit")
                              << QString::fromLatin1("x86-macos-generic-mach_o-64bit"));

    QTest::newRow("dynamic QtCore: symbian")
738
            << QString::fromLatin1("%1/dynamic/symbian.dll").arg(prefix)
739 740
            << (QStringList() << QString::fromLatin1("arm-symbian-device-elf-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2010 64bit")
741
            << QString::fromLatin1("%1/dynamic/win-msvc2010-64bit.dll").arg(prefix)
742 743
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
    QTest::newRow("dynamic QtCore: win msvc2008 32bit")
744
            << QString::fromLatin1("%1/dynamic/win-msvc2008-32bit.dll").arg(prefix)
745 746
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2005 32bit")
747
            << QString::fromLatin1("%1/dynamic/win-msvc2005-32bit.dll").arg(prefix)
748 749
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msys 32bit")
750
            << QString::fromLatin1("%1/dynamic/win-mingw-32bit.dll").arg(prefix)
751
            << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
752 753 754
    QTest::newRow("dynamic QtCore: wince msvc2005 32bit")
            << QString::fromLatin1("%1/dynamic/wince-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("mips-windows-msvc2005-pe-32bit"));
755
    QTest::newRow("dynamic stdc++: mac fat")
756
            << QString::fromLatin1("%1/dynamic/mac-fat.dylib").arg(prefix)
757 758 759
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit")
                              << QString::fromLatin1("ppc-macos-generic-mach_o-32bit")
                              << QString::fromLatin1("x86-macos-generic-mach_o-64bit"));
760
    QTest::newRow("dynamic QtCore: arm linux 32bit")
761
            << QString::fromLatin1("%1/dynamic/arm-linux.so").arg(prefix)
762
            << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
Tobias Hunger's avatar
Tobias Hunger committed
763
    QTest::newRow("dynamic QtCore: mips linux 32bit")
764
            << QString::fromLatin1("%1/dynamic/mips-linux.so").arg(prefix)
Tobias Hunger's avatar
Tobias Hunger committed
765
            << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));
766 767 768
    QTest::newRow("dynamic QtCore: projectexplorer/abi/static/win-msvc2010-32bit.libppc be linux 32bit")
            << QString::fromLatin1("%1/dynamic/ppcbe-linux-32bit.so").arg(prefix)
            << (QStringList() << QString::fromLatin1("ppc-linux-generic-elf-32bit"));
769
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
770
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
771 772
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
773
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
774 775
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 32bit")
776
            << QString::fromLatin1("%1/dynamic/freebsd-elf-32bit.so").arg(prefix)
777
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-32bit"));
778 779 780 781 782 783 784 785 786 787 788 789 790
}

void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary()
{
    QFETCH(QString, file);
    QFETCH(QStringList, abis);

    QList<ProjectExplorer::Abi> result = Abi::abisOfBinary(file);
    QCOMPARE(result.count(), abis.count());
    for (int i = 0; i < abis.count(); ++i)
        QCOMPARE(result.at(i).toString(), abis.at(i));
}

791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
void ProjectExplorer::ProjectExplorerPlugin::testFlavorForOs()
{
    QList<QList<ProjectExplorer::Abi::OSFlavor> > flavorLists;
    for (int i = 0; i != static_cast<int>(Abi::UnknownOS); ++i)
        flavorLists.append(Abi::flavorsForOs(static_cast<Abi::OS>(i)));

    int foundCounter = 0;
    for (int i = 0; i != Abi::UnknownFlavor; ++i) {
        foundCounter = 0;
        // make sure i is in exactly on of the flavor lists!
        foreach (const QList<Abi::OSFlavor> &l, flavorLists) {
            QVERIFY(!l.contains(Abi::UnknownFlavor));
            if (l.contains(static_cast<Abi::OSFlavor>(i)))
                ++foundCounter;
        }
        QCOMPARE(foundCounter, 1);
    }
}

810
#endif