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 41 42 43
/**************************************************************************
**
** 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>

namespace ProjectExplorer {

44 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
// --------------------------------------------------------------------------
// 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
// --------------------------------------------------------------------------

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

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

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

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

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
261
            m_binaryFormat = UnknownFormat;
262
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
263
            m_binaryFormat = ElfFormat;
264
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
265
            m_binaryFormat = PEFormat;
266
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
267
            m_binaryFormat = MachOFormat;
268
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
269
            m_binaryFormat = RuntimeQmlFormat;
270 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
        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
312 313 314 315
    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)
316 317 318 319 320
            && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
}

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

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

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

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

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

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
430 431 432
    os = WindowsOS;
    subos = WindowsMsvcFlavor;
    format = PEFormat;
433
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
434 435 436
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
437
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
438 439 440
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
#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);
458 459 460 461 462 463 464 465 466 467 468 469 470
    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!";
471
                break;
472
            }
473

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

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

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

494 495 496 497
    return result;
}

} // namespace ProjectExplorer