abi.cpp 18 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "abi.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QSysInfo>
Friedemann Kleint's avatar
Friedemann Kleint committed
41
#include <QtCore/QDebug>
42 43 44

namespace ProjectExplorer {

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
// --------------------------------------------------------------------------
// 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();
    }
}

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;
        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;
        }
    } else {
        // Windows PE
        // Windows can have its magic bytes everywhere...
        int pePos = data.indexOf("PE\0\0");
        if (pePos >= 0 && pePos + 72 < data.size()) {
            Abi::Architecture arch = Abi::UnknownArchitecture;
            Abi::OSFlavor flavor = Abi::UnknownFlavor;
            int width = 0;

            // Get machine field from COFF file header
            quint16 machine = (data.at(pePos + 5) << 8) + data.at(pePos + 4);
            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;
            }

            // Get Major and Minor Image Version from optional header fields
            quint32 image = (data.at(pePos + 71) << 24) + (data.at(pePos + 70) << 16)
                    + (data.at(pePos + 69) << 8) + data.at(pePos + 68);
            if (image == 1) // Image is 1 for mingw and 4.something for MSVC
                flavor = Abi::WindowsMSysFlavor;
            else
                flavor = Abi::WindowsMsvcFlavor;

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

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

163
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
164
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
165 166 167
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
168 169
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
170
        break;
hjk's avatar
hjk committed
171 172 173
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
174
        break;
hjk's avatar
hjk committed
175 176 177
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
178
        break;
hjk's avatar
hjk committed
179 180 181
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
182
        break;
hjk's avatar
hjk committed
183 184 185
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
186
        break;
hjk's avatar
hjk committed
187 188 189
    case ProjectExplorer::Abi::WindowsOS:
        if (m_osFlavor < WindowsMsvcFlavor || m_osFlavor > WindowsCEFlavor)
            m_osFlavor = UnknownFlavor;
190 191 192 193 194
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
195 196
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
197 198 199 200
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
201
            m_architecture = UnknownArchitecture;
202
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
203
            m_architecture = ArmArchitecture;
204
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
205
            m_architecture = X86Architecture;
206
        else if (abiParts.at(0) == QLatin1String("mips"))
hjk's avatar
hjk committed
207
            m_architecture = MipsArcitecture;
208
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
209
            m_architecture = PowerPCArchitecture;
210
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
211
            m_architecture = ItaniumArchitecture;
212 213 214 215 216 217
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
218
            m_os = UnknownOS;
219
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
220
            m_os = LinuxOS;
221
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
222
            m_os = MacOS;
223
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
224
            m_os = SymbianOS;
225
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
226
            m_os = UnixOS;
227
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
228
            m_os = WindowsOS;
229 230 231 232 233 234
        else
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
            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;
        else if (abiParts.at(2) == QLatin1String("msvc") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvcFlavor;
        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;
256 257 258 259 260 261
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
262
            m_binaryFormat = UnknownFormat;
263
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
264
            m_binaryFormat = ElfFormat;
265
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
266
            m_binaryFormat = PEFormat;
267
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
268
            m_binaryFormat = MachOFormat;
269
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
270
            m_binaryFormat = RuntimeQmlFormat;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        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("-"));
}

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
{
hjk's avatar
hjk committed
313 314 315 316
    return (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)
317 318 319 320 321
            && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
322 323 324 325
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
326 327 328 329 330 331
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
332
    case ArmArchitecture:
333
        return QLatin1String("arm");
hjk's avatar
hjk committed
334
    case X86Architecture:
335
        return QLatin1String("x86");
hjk's avatar
hjk committed
336
    case MipsArcitecture:
337
        return QLatin1String("mips");
hjk's avatar
hjk committed
338
    case PowerPCArchitecture:
339
        return QLatin1String("ppc");
hjk's avatar
hjk committed
340
    case ItaniumArchitecture:
341
        return QLatin1String("itanium");
hjk's avatar
hjk committed
342
    case UnknownArchitecture: // fall through!
343 344 345 346 347 348 349 350
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
351
    case LinuxOS:
352
        return QLatin1String("linux");
hjk's avatar
hjk committed
353
    case MacOS:
354
        return QLatin1String("macos");
hjk's avatar
hjk committed
355
    case SymbianOS:
356
        return QLatin1String("symbian");
hjk's avatar
hjk committed
357
    case UnixOS:
358
        return QLatin1String("unix");
hjk's avatar
hjk committed
359
    case WindowsOS:
360
        return QLatin1String("windows");
hjk's avatar
hjk committed
361
    case UnknownOS: // fall through!
362 363 364 365 366
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
367
QString Abi::toString(const OSFlavor &of)
368 369
{
    switch (of) {
hjk's avatar
hjk committed
370
    case ProjectExplorer::Abi::GenericLinuxFlavor:
371
        return QLatin1String("generic");
hjk's avatar
hjk committed
372
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
373
        return QLatin1String("maemo");
hjk's avatar
hjk committed
374
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
375
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
376
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
377
        return QLatin1String("meego");
hjk's avatar
hjk committed
378
    case ProjectExplorer::Abi::GenericMacFlavor:
379
        return QLatin1String("generic");
hjk's avatar
hjk committed
380
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
381
        return QLatin1String("device");
hjk's avatar
hjk committed
382
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
383
        return QLatin1String("emulator");
hjk's avatar
hjk committed
384
    case ProjectExplorer::Abi::GenericUnixFlavor:
385
        return QLatin1String("generic");
hjk's avatar
hjk committed
386
    case ProjectExplorer::Abi::WindowsMsvcFlavor:
387
        return QLatin1String("msvc");
hjk's avatar
hjk committed
388
    case ProjectExplorer::Abi::WindowsMSysFlavor:
389
        return QLatin1String("msys");
hjk's avatar
hjk committed
390
    case ProjectExplorer::Abi::WindowsCEFlavor:
391
        return QLatin1String("ce");
392
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
393 394 395 396 397 398 399 400
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
401
    case ElfFormat:
402
        return QLatin1String("elf");
hjk's avatar
hjk committed
403
    case PEFormat:
404
        return QLatin1String("pe");
hjk's avatar
hjk committed
405
    case MachOFormat:
406
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
407
    case RuntimeQmlFormat:
408
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
409
    case UnknownFormat: // fall through!
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
    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
426 427 428
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
429 430

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
431 432 433
    os = WindowsOS;
    subos = WindowsMsvcFlavor;
    format = PEFormat;
434
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
435 436 437
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
438
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
439 440 441
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
    QList<Abi> result;
    if (path.isEmpty())
        return result;

    QFile f(path);
    if (!f.exists())
        return result;

    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
459 460 461 462 463 464 465 466 467 468 469 470 471
    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) {
        // We got an ar file: possibly a static lib for ELF or Mach-O

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

        while (!data.isEmpty()) {
            if (data.at(58) != 0x60 || data.at(59) != 0x0a) {
                qWarning() << path << ": Thought it was an ar-file, but it is not!";
472
                break;
473
            }
474

475 476 477 478 479
            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));
480

481 482 483 484
            data = data.mid(60 + fileNameOffset);
            offset += fileLength.toInt() + 60 /* header */;
            result = abiOf(data);
            if (!result.isEmpty())
485
                break;
486

487 488
            f.seek(offset);
            data = f.read(1024);
489
        }
490 491
    } else {
        result = abiOf(data);
492
    }
493 494
    f.close();

495 496 497 498
    return result;
}

} // namespace ProjectExplorer