abi.cpp 30.2 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 225 226
    } else {
        // Windows PE
        // Windows can have its magic bytes everywhere...
227
        int pePos = data.indexOf(QByteArray("PE\0\0", 4));
Tobias Hunger's avatar
Tobias Hunger committed
228 229
        if (pePos >= 0)
            result = parseCoffHeader(data.mid(pePos + 4));
230 231 232 233 234 235 236 237
    }
    return result;
}

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

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

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
273 274
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
275 276 277 278
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
279
            m_architecture = UnknownArchitecture;
280
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
281
            m_architecture = ArmArchitecture;
282
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
283
            m_architecture = X86Architecture;
284
        else if (abiParts.at(0) == QLatin1String("mips"))
Tobias Hunger's avatar
Tobias Hunger committed
285
            m_architecture = MipsArchitecture;
286
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
287
            m_architecture = PowerPCArchitecture;
288
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
289
            m_architecture = ItaniumArchitecture;
290 291 292 293 294 295
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
296
            m_os = UnknownOS;
297
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
298
            m_os = LinuxOS;
299 300
        else if (abiParts.at(1) == QLatin1String("bsd"))
            m_os = BsdOS;
301
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
302
            m_os = MacOS;
303
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
304
            m_os = SymbianOS;
305
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
306
            m_os = UnixOS;
307
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
308
            m_os = WindowsOS;
309
        else
310

311 312 313 314 315
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
316 317 318
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
319 320 321 322 323 324
        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
325 326 327 328 329 330 331 332 333 334 335 336
        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;
337 338
        else if (abiParts.at(2) == QLatin1String("solaris") && m_os == UnixOS)
            m_osFlavor = SolarisUnixFlavor;
339 340 341 342 343 344
        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
345 346 347 348
        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;
349 350 351 352 353 354
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
355
            m_binaryFormat = UnknownFormat;
356
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
357
            m_binaryFormat = ElfFormat;
358
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
359
            m_binaryFormat = PEFormat;
360
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
361
            m_binaryFormat = MachOFormat;
362
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
363
            m_binaryFormat = RuntimeQmlFormat;
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
        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
395 396 397 398 399
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

400 401 402 403 404 405 406 407 408 409 410
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
{
411 412 413 414 415 416 417 418 419 420 421 422 423 424
    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;
425 426 427 428
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
429 430 431 432
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
433 434 435 436 437 438
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
439
    case ArmArchitecture:
440
        return QLatin1String("arm");
hjk's avatar
hjk committed
441
    case X86Architecture:
442
        return QLatin1String("x86");
Tobias Hunger's avatar
Tobias Hunger committed
443
    case MipsArchitecture:
444
        return QLatin1String("mips");
hjk's avatar
hjk committed
445
    case PowerPCArchitecture:
446
        return QLatin1String("ppc");
hjk's avatar
hjk committed
447
    case ItaniumArchitecture:
448
        return QLatin1String("itanium");
hjk's avatar
hjk committed
449
    case UnknownArchitecture: // fall through!
450 451 452 453 454 455 456 457
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
458
    case LinuxOS:
459
        return QLatin1String("linux");
460 461
    case BsdOS:
        return QLatin1String("bsd");
hjk's avatar
hjk committed
462
    case MacOS:
463
        return QLatin1String("macos");
hjk's avatar
hjk committed
464
    case SymbianOS:
465
        return QLatin1String("symbian");
hjk's avatar
hjk committed
466
    case UnixOS:
467
        return QLatin1String("unix");
hjk's avatar
hjk committed
468
    case WindowsOS:
469
        return QLatin1String("windows");
hjk's avatar
hjk committed
470
    case UnknownOS: // fall through!
471 472 473 474 475
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
476
QString Abi::toString(const OSFlavor &of)
477 478
{
    switch (of) {
hjk's avatar
hjk committed
479
    case ProjectExplorer::Abi::GenericLinuxFlavor:
480
        return QLatin1String("generic");
481 482 483 484 485 486
    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
487
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
488
        return QLatin1String("maemo");
hjk's avatar
hjk committed
489
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
490
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
491
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
492
        return QLatin1String("meego");
hjk's avatar
hjk committed
493
    case ProjectExplorer::Abi::GenericMacFlavor:
494
        return QLatin1String("generic");
hjk's avatar
hjk committed
495
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
496
        return QLatin1String("device");
hjk's avatar
hjk committed
497
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
498
        return QLatin1String("emulator");
hjk's avatar
hjk committed
499
    case ProjectExplorer::Abi::GenericUnixFlavor:
500
        return QLatin1String("generic");
501 502
    case ProjectExplorer::Abi::SolarisUnixFlavor:
        return QLatin1String("solaris");
503 504 505 506 507 508
    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
509
    case ProjectExplorer::Abi::WindowsMSysFlavor:
510
        return QLatin1String("msys");
hjk's avatar
hjk committed
511
    case ProjectExplorer::Abi::WindowsCEFlavor:
512
        return QLatin1String("ce");
513
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
514 515 516 517 518 519 520 521
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
522
    case ElfFormat:
523
        return QLatin1String("elf");
hjk's avatar
hjk committed
524
    case PEFormat:
525
        return QLatin1String("pe");
hjk's avatar
hjk committed
526
    case MachOFormat:
527
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
528
    case RuntimeQmlFormat:
529
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
530
    case UnknownFormat: // fall through!
531 532 533 534 535 536 537 538 539 540 541 542
    default:
        return QLatin1String("unknown");
    }
}

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

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
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;
}
567 568 569 570

Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
571 572 573
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
574 575

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
576
    os = WindowsOS;
577 578 579 580 581 582 583 584 585
#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
586
    format = PEFormat;
587
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
588 589 590
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
591
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
592 593 594
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
595 596 597 598 599 600 601
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
602
    QList<Abi> tmp;
603
    if (path.isEmpty())
604
        return tmp;
605 606 607

    QFile f(path);
    if (!f.exists())
608
        return tmp;
609 610 611

    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
612 613 614 615 616
    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) {
617
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
618 619 620 621 622

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

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

628 629 630 631 632
            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));
633

Tobias Hunger's avatar
Tobias Hunger committed
634
            int toSkip = 60 + fileNameOffset;
635
            offset += fileLength.toInt() + 60 /* header */;
636 637 638 639 640

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

641 642
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
643
                break;
644

645
            offset += (offset % 2); // ar is 2 byte aligned
646
            f.seek(offset);
647
            data = f.read(1024);
648
        }
649
    } else {
650
        tmp = abiOf(data);
651
    }
652 653
    f.close();

654 655 656 657 658 659 660
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

661 662 663 664
    return result;
}

} // namespace ProjectExplorer
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

// 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;
689 690
    prefix += "/projectexplorer/abi";

691 692 693 694 695 696 697 698 699 700
    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")
701
            << QString::fromLatin1("%1/static/win-msvc2008-release.lib").arg(prefix)
702
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
703
    QTest::newRow("static QtCore: win msvc2008 II")
704
            << QString::fromLatin1("%1/static/win-msvc2008-release2.lib").arg(prefix)
705
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
706
    QTest::newRow("static QtCore: win msvc2008 (debug)")
707
            << QString::fromLatin1("%1/static/win-msvc2008-debug.lib").arg(prefix)
708
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
709
    QTest::newRow("static QtCore: win mingw")
710
            << QString::fromLatin1("%1/static/win-mingw.a").arg(prefix)
711
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
712
    QTest::newRow("static QtCore: mac (debug)")
713
            << QString::fromLatin1("%1/static/mac-32bit-debug.a").arg(prefix)
714 715
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit"));
    QTest::newRow("static QtCore: linux 32bit")
716
            << QString::fromLatin1("%1/static/linux-32bit-release.a").arg(prefix)
717 718
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
    QTest::newRow("static QtCore: linux 64bit")
719
            << QString::fromLatin1("%1/static/linux-64bit-release.a").arg(prefix)
720 721 722
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));

    QTest::newRow("static stdc++: mac fat")
723
            << QString::fromLatin1("%1/static/mac-fat.a").arg(prefix)
724 725 726 727 728
            << (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")
729
            << QString::fromLatin1("%1/dynamic/symbian.dll").arg(prefix)
730 731
            << (QStringList() << QString::fromLatin1("arm-symbian-device-elf-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2010 64bit")
732
            << QString::fromLatin1("%1/dynamic/win-msvc2010-64bit.dll").arg(prefix)
733 734
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
    QTest::newRow("dynamic QtCore: win msvc2008 32bit")
735
            << QString::fromLatin1("%1/dynamic/win-msvc2008-32bit.dll").arg(prefix)
736 737
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2005 32bit")
738
            << QString::fromLatin1("%1/dynamic/win-msvc2005-32bit.dll").arg(prefix)
739 740
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msys 32bit")
741
            << QString::fromLatin1("%1/dynamic/win-mingw-32bit.dll").arg(prefix)
742
            << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
743 744 745
    QTest::newRow("dynamic QtCore: wince msvc2005 32bit")
            << QString::fromLatin1("%1/dynamic/wince-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("mips-windows-msvc2005-pe-32bit"));
746
    QTest::newRow("dynamic stdc++: mac fat")
747
            << QString::fromLatin1("%1/dynamic/mac-fat.dylib").arg(prefix)
748 749 750
            << (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"));
751
    QTest::newRow("dynamic QtCore: arm linux 32bit")
752
            << QString::fromLatin1("%1/dynamic/arm-linux.so").arg(prefix)
753
            << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
Tobias Hunger's avatar
Tobias Hunger committed
754
    QTest::newRow("dynamic QtCore: mips linux 32bit")
755
            << QString::fromLatin1("%1/dynamic/mips-linux.so").arg(prefix)
Tobias Hunger's avatar
Tobias Hunger committed
756
            << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));
757 758 759
    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"));
760
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
761
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
762 763
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
764
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
765 766
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 32bit")
767
            << QString::fromLatin1("%1/dynamic/freebsd-elf-32bit.so").arg(prefix)
768
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-32bit"));
769 770 771 772 773 774 775 776 777 778 779 780 781
}

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

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
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);
    }
}

801
#endif