abi.cpp 25.6 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 29 30 31 32 33 34 35
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "abi.h"

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

namespace ProjectExplorer {

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
// --------------------------------------------------------------------------
// 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
65 66 67 68 69 70 71 72 73 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;
    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);
95
        if (image == 1) { // Image is 1 for mingw and higher for MSVC (4.something in some encoding)
Tobias Hunger's avatar
Tobias Hunger committed
96
            flavor = Abi::WindowsMSysFlavor;
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
        } 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
113 114 115 116 117 118 119 120
    }

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

    return result;
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
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;
143 144 145
        case 40: // EM_ARM
            result.append(Abi(Abi::ArmArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
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 173 174 175
        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;
        }
176 177 178 179
    } 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));
180 181 182
    } else {
        // Windows PE
        // Windows can have its magic bytes everywhere...
183
        int pePos = data.indexOf(QByteArray("PE\0\0", 4));
Tobias Hunger's avatar
Tobias Hunger committed
184 185
        if (pePos >= 0)
            result = parseCoffHeader(data.mid(pePos + 4));
186 187 188 189 190 191 192 193
    }
    return result;
}

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

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

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

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
249
            m_os = UnknownOS;
250
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
251
            m_os = LinuxOS;
252
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
253
            m_os = MacOS;
254
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
255
            m_os = SymbianOS;
256
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
257
            m_os = UnixOS;
258
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
259
            m_os = WindowsOS;
260 261 262 263 264 265
        else
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
            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;
281 282 283 284 285 286
        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
287 288 289 290
        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;
291 292 293 294 295 296
        else
            return;
    }

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

342 343 344 345 346 347 348 349 350 351 352
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
{
353 354 355 356 357 358 359 360 361 362 363 364 365 366
    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;
367 368 369 370
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
371 372 373 374
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
375 376 377 378 379 380
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
381
    case ArmArchitecture:
382
        return QLatin1String("arm");
hjk's avatar
hjk committed
383
    case X86Architecture:
384
        return QLatin1String("x86");
hjk's avatar
hjk committed
385
    case MipsArcitecture:
386
        return QLatin1String("mips");
hjk's avatar
hjk committed
387
    case PowerPCArchitecture:
388
        return QLatin1String("ppc");
hjk's avatar
hjk committed
389
    case ItaniumArchitecture:
390
        return QLatin1String("itanium");
hjk's avatar
hjk committed
391
    case UnknownArchitecture: // fall through!
392 393 394 395 396 397 398 399
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
400
    case LinuxOS:
401
        return QLatin1String("linux");
hjk's avatar
hjk committed
402
    case MacOS:
403
        return QLatin1String("macos");
hjk's avatar
hjk committed
404
    case SymbianOS:
405
        return QLatin1String("symbian");
hjk's avatar
hjk committed
406
    case UnixOS:
407
        return QLatin1String("unix");
hjk's avatar
hjk committed
408
    case WindowsOS:
409
        return QLatin1String("windows");
hjk's avatar
hjk committed
410
    case UnknownOS: // fall through!
411 412 413 414 415
    default:
        return QLatin1String("unknown");
    };
}

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

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
454
    case ElfFormat:
455
        return QLatin1String("elf");
hjk's avatar
hjk committed
456
    case PEFormat:
457
        return QLatin1String("pe");
hjk's avatar
hjk committed
458
    case MachOFormat:
459
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
460
    case RuntimeQmlFormat:
461
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
462
    case UnknownFormat: // fall through!
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
    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
479 480 481
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
482 483

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
484
    os = WindowsOS;
485 486 487 488 489 490 491 492 493
#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
494
    format = PEFormat;
495
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
496 497 498
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
499
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
500 501 502
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
503 504 505 506 507 508 509
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
510
    QList<Abi> tmp;
511
    if (path.isEmpty())
512
        return tmp;
513 514 515

    QFile f(path);
    if (!f.exists())
516
        return tmp;
517

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

520 521
    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
522 523 524 525 526
    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) {
527
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
528 529 530 531 532

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

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

538 539 540 541 542
            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));
543

Tobias Hunger's avatar
Tobias Hunger committed
544
            int toSkip = 60 + fileNameOffset;
545
            offset += fileLength.toInt() + 60 /* header */;
Tobias Hunger's avatar
Tobias Hunger committed
546 547
            if (windowsStatic) {
                if (fileName == QLatin1String("/0              "))
548
                    tmp = parseCoffHeader(data.mid(toSkip, 20));
Tobias Hunger's avatar
Tobias Hunger committed
549
            } else {
550
                tmp.append(abiOf(data.mid(toSkip)));
Tobias Hunger's avatar
Tobias Hunger committed
551
            }
552 553
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
554
                break;
555

556 557
            offset += (offset % 2); // ar is 2 byte alligned
            f.seek(offset);
558
            data = f.read(1024);
559
        }
560
    } else {
561
        tmp = abiOf(data);
562
    }
563 564
    f.close();

565 566 567 568 569 570 571
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

572 573 574 575
    return result;
}

} // namespace ProjectExplorer
576 577 578 579 580 581 582 583 584 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

// 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")
610
            << QString::fromLatin1("%1/abi/static/win-msvc2008-release.lib").arg(prefix)
611
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
612 613 614
    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"));
615
    QTest::newRow("static QtCore: win msvc2008 (debug)")
616
            << QString::fromLatin1("%1/abi/static/win-msvc2008-debug.lib").arg(prefix)
617 618 619 620 621 622 623 624 625 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
            << (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"));
652
    QTest::newRow("dynamic stdc++: mac fat")
653 654 655 656
            << 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"));
657 658 659
    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
660 661 662 663
    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"));

664 665 666 667 668 669 670 671 672 673 674 675 676 677
}

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