abi.cpp 25.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 38 39 40 41
#include <QtCore/QFile>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QSysInfo>

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

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

    \sa ProjectExplorer::ToolChain
*/

50 51
namespace ProjectExplorer {

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
// --------------------------------------------------------------------------
// 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
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
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;
    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);
103
        if (image == 1) { // Image is 1 for mingw and higher for MSVC (4.something in some encoding)
Tobias Hunger's avatar
Tobias Hunger committed
104
            flavor = Abi::WindowsMSysFlavor;
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        } 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
121 122 123 124 125 126 127 128
    }

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

    return result;
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
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:
        quint16 machine = (data.at(19) << 8) + data.at(18);
        switch (machine) {
        case 3: // EM_386
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 8: // EM_MIPS
            result.append(Abi(Abi::MipsArcitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 20: // EM_PPC
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 21: // EM_PPC64
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            break;
151 152 153
        case 40: // EM_ARM
            result.append(Abi(Abi::ArmArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
        case 62: // EM_X86_64
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            break;
        case 50: // EM_IA_64
            result.append(Abi(Abi::ItaniumArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            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;
        }
184 185 186 187
    } 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));
188 189 190
    } else {
        // Windows PE
        // Windows can have its magic bytes everywhere...
191
        int pePos = data.indexOf(QByteArray("PE\0\0", 4));
Tobias Hunger's avatar
Tobias Hunger committed
192 193
        if (pePos >= 0)
            result = parseCoffHeader(data.mid(pePos + 4));
194 195 196 197 198 199 200 201
    }
    return result;
}

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

202
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
203
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
204 205 206
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
207 208
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
209
        break;
hjk's avatar
hjk committed
210 211 212
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
213
        break;
hjk's avatar
hjk committed
214 215 216
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
217
        break;
hjk's avatar
hjk committed
218 219 220
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
221
        break;
hjk's avatar
hjk committed
222 223 224
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
225
        break;
hjk's avatar
hjk committed
226
    case ProjectExplorer::Abi::WindowsOS:
227
        if (m_osFlavor < WindowsMsvc2005Flavor || m_osFlavor > WindowsCEFlavor)
hjk's avatar
hjk committed
228
            m_osFlavor = UnknownFlavor;
229 230 231 232 233
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
234 235
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
236 237 238 239
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
240
            m_architecture = UnknownArchitecture;
241
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
242
            m_architecture = ArmArchitecture;
243
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
244
            m_architecture = X86Architecture;
245
        else if (abiParts.at(0) == QLatin1String("mips"))
hjk's avatar
hjk committed
246
            m_architecture = MipsArcitecture;
247
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
248
            m_architecture = PowerPCArchitecture;
249
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
250
            m_architecture = ItaniumArchitecture;
251 252 253 254 255 256
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
257
            m_os = UnknownOS;
258
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
259
            m_os = LinuxOS;
260
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
261
            m_os = MacOS;
262
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
263
            m_os = SymbianOS;
264
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
265
            m_os = UnixOS;
266
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
267
            m_os = WindowsOS;
268
        else
269

270 271 272 273 274
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
        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;
290 291 292 293 294 295
        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
296 297 298 299
        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;
300 301 302 303 304 305
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
306
            m_binaryFormat = UnknownFormat;
307
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
308
            m_binaryFormat = ElfFormat;
309
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
310
            m_binaryFormat = PEFormat;
311
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
312
            m_binaryFormat = MachOFormat;
313
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
314
            m_binaryFormat = RuntimeQmlFormat;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
        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
346 347 348 349 350
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

351 352 353 354 355 356 357 358 359 360 361
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
{
362 363 364 365 366 367 368 369 370 371 372 373 374 375
    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;
376 377 378 379
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
380 381 382 383
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
384 385 386 387 388 389
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
390
    case ArmArchitecture:
391
        return QLatin1String("arm");
hjk's avatar
hjk committed
392
    case X86Architecture:
393
        return QLatin1String("x86");
hjk's avatar
hjk committed
394
    case MipsArcitecture:
395
        return QLatin1String("mips");
hjk's avatar
hjk committed
396
    case PowerPCArchitecture:
397
        return QLatin1String("ppc");
hjk's avatar
hjk committed
398
    case ItaniumArchitecture:
399
        return QLatin1String("itanium");
hjk's avatar
hjk committed
400
    case UnknownArchitecture: // fall through!
401 402 403 404 405 406 407 408
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
409
    case LinuxOS:
410
        return QLatin1String("linux");
hjk's avatar
hjk committed
411
    case MacOS:
412
        return QLatin1String("macos");
hjk's avatar
hjk committed
413
    case SymbianOS:
414
        return QLatin1String("symbian");
hjk's avatar
hjk committed
415
    case UnixOS:
416
        return QLatin1String("unix");
hjk's avatar
hjk committed
417
    case WindowsOS:
418
        return QLatin1String("windows");
hjk's avatar
hjk committed
419
    case UnknownOS: // fall through!
420 421 422 423 424
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
425
QString Abi::toString(const OSFlavor &of)
426 427
{
    switch (of) {
hjk's avatar
hjk committed
428
    case ProjectExplorer::Abi::GenericLinuxFlavor:
429
        return QLatin1String("generic");
hjk's avatar
hjk committed
430
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
431
        return QLatin1String("maemo");
hjk's avatar
hjk committed
432
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
433
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
434
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
435
        return QLatin1String("meego");
hjk's avatar
hjk committed
436
    case ProjectExplorer::Abi::GenericMacFlavor:
437
        return QLatin1String("generic");
hjk's avatar
hjk committed
438
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
439
        return QLatin1String("device");
hjk's avatar
hjk committed
440
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
441
        return QLatin1String("emulator");
hjk's avatar
hjk committed
442
    case ProjectExplorer::Abi::GenericUnixFlavor:
443
        return QLatin1String("generic");
444 445 446 447 448 449
    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
450
    case ProjectExplorer::Abi::WindowsMSysFlavor:
451
        return QLatin1String("msys");
hjk's avatar
hjk committed
452
    case ProjectExplorer::Abi::WindowsCEFlavor:
453
        return QLatin1String("ce");
454
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
455 456 457 458 459 460 461 462
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
463
    case ElfFormat:
464
        return QLatin1String("elf");
hjk's avatar
hjk committed
465
    case PEFormat:
466
        return QLatin1String("pe");
hjk's avatar
hjk committed
467
    case MachOFormat:
468
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
469
    case RuntimeQmlFormat:
470
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
471
    case UnknownFormat: // fall through!
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
    default:
        return QLatin1String("unknown");
    }
}

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


Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
488 489 490
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
491 492

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
493
    os = WindowsOS;
494 495 496 497 498 499 500 501 502
#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
503
    format = PEFormat;
504
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
505 506 507
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
508
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
509 510 511
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
512 513 514 515 516 517 518
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
519
    QList<Abi> tmp;
520
    if (path.isEmpty())
521
        return tmp;
522 523 524

    QFile f(path);
    if (!f.exists())
525
        return tmp;
526

Tobias Hunger's avatar
Tobias Hunger committed
527 528
    bool windowsStatic = path.endsWith(QLatin1String(".lib"));

529 530
    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
531 532 533 534 535
    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) {
536
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
537 538 539 540 541

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

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

547 548 549 550 551
            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));
552

Tobias Hunger's avatar
Tobias Hunger committed
553
            int toSkip = 60 + fileNameOffset;
554
            offset += fileLength.toInt() + 60 /* header */;
Tobias Hunger's avatar
Tobias Hunger committed
555 556
            if (windowsStatic) {
                if (fileName == QLatin1String("/0              "))
557
                    tmp = parseCoffHeader(data.mid(toSkip, 20));
Tobias Hunger's avatar
Tobias Hunger committed
558
            } else {
559
                tmp.append(abiOf(data.mid(toSkip)));
Tobias Hunger's avatar
Tobias Hunger committed
560
            }
561 562
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
563
                break;
564

565 566
            offset += (offset % 2); // ar is 2 byte alligned
            f.seek(offset);
567
            data = f.read(1024);
568
        }
569
    } else {
570
        tmp = abiOf(data);
571
    }
572 573
    f.close();

574 575 576 577 578 579 580
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

581 582 583 584
    return result;
}

} // namespace ProjectExplorer
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618

// 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;
    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")
619
            << QString::fromLatin1("%1/abi/static/win-msvc2008-release.lib").arg(prefix)
620
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
621 622 623
    QTest::newRow("static QtCore: win msvc2008 II")
            << QString::fromLatin1("%1/abi/static/win-msvc2008-release2.lib").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
624
    QTest::newRow("static QtCore: win msvc2008 (debug)")
625
            << QString::fromLatin1("%1/abi/static/win-msvc2008-debug.lib").arg(prefix)
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
    QTest::newRow("static QtCore: mac (debug)")
            << QString::fromLatin1("%1/abi/static/mac-32bit-debug.a").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit"));
    QTest::newRow("static QtCore: linux 32bit")
            << QString::fromLatin1("%1/abi/static/linux-32bit-release.a").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
    QTest::newRow("static QtCore: linux 64bit")
            << QString::fromLatin1("%1/abi/static/linux-64bit-release.a").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));

    QTest::newRow("static stdc++: mac fat")
            << QString::fromLatin1("%1/abi/static/mac-fat.a").arg(prefix)
            << (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")
            << QString::fromLatin1("%1/abi/dynamic/symbian.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("arm-symbian-device-elf-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2010 64bit")
            << QString::fromLatin1("%1/abi/dynamic/win-msvc2010-64bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
    QTest::newRow("dynamic QtCore: win msvc2008 32bit")
            << QString::fromLatin1("%1/abi/dynamic/win-msvc2008-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2005 32bit")
            << QString::fromLatin1("%1/abi/dynamic/win-msvc2005-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msys 32bit")
            << QString::fromLatin1("%1/abi/dynamic/win-mingw-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msys 32bit")
            << QString::fromLatin1("%1/abi/dynamic/win-mingw-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
661
    QTest::newRow("dynamic stdc++: mac fat")
662 663 664 665
            << QString::fromLatin1("%1/abi/dynamic/mac-fat.dylib").arg(prefix)
            << (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"));
666 667 668
    QTest::newRow("dynamic QtCore: arm linux 32bit")
            << QString::fromLatin1("%1/abi/dynamic/arm-linux.so").arg(prefix)
            << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
Tobias Hunger's avatar
Tobias Hunger committed
669 670 671 672
    QTest::newRow("dynamic QtCore: mips linux 32bit")
            << QString::fromLatin1("%1/abi/dynamic/mips-linux.so").arg(prefix)
            << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));

673 674 675 676 677 678 679 680 681 682 683 684 685 686
}

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

#endif