abi.cpp 26.4 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
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);
138 139 140 141 142

        //http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
        const Abi::OS os = static_cast<quint8>(data.at(7)) == 9 ? Abi::FreeBSDOS : Abi::LinuxOS;
        const Abi::OSFlavor flavor = os == Abi::FreeBSDOS ? Abi::GenericFreeBSDFlavor : Abi::GenericLinuxFlavor;

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

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

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

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

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
265
            m_os = UnknownOS;
266
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
267
            m_os = LinuxOS;
268 269
        else if (abiParts.at(1) == QLatin1String("freebsd"))
            m_os = FreeBSDOS;
270
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
271
            m_os = MacOS;
272
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
273
            m_os = SymbianOS;
274
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
275
            m_os = UnixOS;
276
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
277
            m_os = WindowsOS;
278
        else
279

280 281 282 283 284
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
285 286 287
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
288 289
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == FreeBSDOS)
            m_osFlavor = GenericFreeBSDFlavor;
hjk's avatar
hjk committed
290 291 292 293 294 295 296 297 298 299 300 301
        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;
302 303 304 305 306 307
        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
308 309 310 311
        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;
312 313 314 315 316 317
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
318
            m_binaryFormat = UnknownFormat;
319
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
320
            m_binaryFormat = ElfFormat;
321
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
322
            m_binaryFormat = PEFormat;
323
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
324
            m_binaryFormat = MachOFormat;
325
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
326
            m_binaryFormat = RuntimeQmlFormat;
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        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
358 359 360 361 362
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

363 364 365 366 367 368 369 370 371 372 373
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
{
374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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;
388 389 390 391
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
392 393 394 395
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
396 397 398 399 400 401
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
402
    case ArmArchitecture:
403
        return QLatin1String("arm");
hjk's avatar
hjk committed
404
    case X86Architecture:
405
        return QLatin1String("x86");
hjk's avatar
hjk committed
406
    case MipsArcitecture:
407
        return QLatin1String("mips");
hjk's avatar
hjk committed
408
    case PowerPCArchitecture:
409
        return QLatin1String("ppc");
hjk's avatar
hjk committed
410
    case ItaniumArchitecture:
411
        return QLatin1String("itanium");
hjk's avatar
hjk committed
412
    case UnknownArchitecture: // fall through!
413 414 415 416 417 418 419 420
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
421
    case LinuxOS:
422
        return QLatin1String("linux");
423 424
    case FreeBSDOS:
        return QLatin1String("freebsd");
hjk's avatar
hjk committed
425
    case MacOS:
426
        return QLatin1String("macos");
hjk's avatar
hjk committed
427
    case SymbianOS:
428
        return QLatin1String("symbian");
hjk's avatar
hjk committed
429
    case UnixOS:
430
        return QLatin1String("unix");
hjk's avatar
hjk committed
431
    case WindowsOS:
432
        return QLatin1String("windows");
hjk's avatar
hjk committed
433
    case UnknownOS: // fall through!
434 435 436 437 438
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
439
QString Abi::toString(const OSFlavor &of)
440 441
{
    switch (of) {
hjk's avatar
hjk committed
442
    case ProjectExplorer::Abi::GenericLinuxFlavor:
443
        return QLatin1String("generic");
444 445
    case ProjectExplorer::Abi::GenericFreeBSDFlavor:
        return QLatin1String("generic");
hjk's avatar
hjk committed
446
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
447
        return QLatin1String("maemo");
hjk's avatar
hjk committed
448
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
449
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
450
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
451
        return QLatin1String("meego");
hjk's avatar
hjk committed
452
    case ProjectExplorer::Abi::GenericMacFlavor:
453
        return QLatin1String("generic");
hjk's avatar
hjk committed
454
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
455
        return QLatin1String("device");
hjk's avatar
hjk committed
456
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
457
        return QLatin1String("emulator");
hjk's avatar
hjk committed
458
    case ProjectExplorer::Abi::GenericUnixFlavor:
459
        return QLatin1String("generic");
460 461 462 463 464 465
    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
466
    case ProjectExplorer::Abi::WindowsMSysFlavor:
467
        return QLatin1String("msys");
hjk's avatar
hjk committed
468
    case ProjectExplorer::Abi::WindowsCEFlavor:
469
        return QLatin1String("ce");
470
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
471 472 473 474 475 476 477 478
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
479
    case ElfFormat:
480
        return QLatin1String("elf");
hjk's avatar
hjk committed
481
    case PEFormat:
482
        return QLatin1String("pe");
hjk's avatar
hjk committed
483
    case MachOFormat:
484
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
485
    case RuntimeQmlFormat:
486
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
487
    case UnknownFormat: // fall through!
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    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
504 505 506
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
507 508

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
509
    os = WindowsOS;
510 511 512 513 514 515 516 517 518
#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
519
    format = PEFormat;
520
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
521 522 523
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
524
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
525 526 527
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
528 529 530 531 532 533 534
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
535
    QList<Abi> tmp;
536
    if (path.isEmpty())
537
        return tmp;
538 539 540

    QFile f(path);
    if (!f.exists())
541
        return tmp;
542 543 544

    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
545 546 547 548 549
    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) {
550
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
551 552 553 554 555

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

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

561 562 563 564 565
            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));
566

Tobias Hunger's avatar
Tobias Hunger committed
567
            int toSkip = 60 + fileNameOffset;
568
            offset += fileLength.toInt() + 60 /* header */;
569 570 571 572 573

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

574 575
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
576
                break;
577

578
            offset += (offset % 2); // ar is 2 byte aligned
579
            f.seek(offset);
580
            data = f.read(1024);
581
        }
582
    } else {
583
        tmp = abiOf(data);
584
    }
585 586
    f.close();

587 588 589 590 591 592 593
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

594 595 596 597
    return result;
}

} // namespace ProjectExplorer
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631

// 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")
632
            << QString::fromLatin1("%1/abi/static/win-msvc2008-release.lib").arg(prefix)
633
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
634 635 636
    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"));
637
    QTest::newRow("static QtCore: win msvc2008 (debug)")
638
            << QString::fromLatin1("%1/abi/static/win-msvc2008-debug.lib").arg(prefix)
639
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
640 641 642
    QTest::newRow("static QtCore: win mingw")
            << QString::fromLatin1("%1/abi/static/win-mingw.a").arg(prefix)
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
    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"));
677
    QTest::newRow("dynamic stdc++: mac fat")
678 679 680 681
            << 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"));
682 683 684
    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
685 686 687 688
    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"));

689 690 691 692 693 694 695 696 697 698 699 700 701 702
}

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