abi.cpp 15.5 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 44
/**************************************************************************
**
** 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 {

Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
45
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
46 47 48
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
49 50
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
51
        break;
hjk's avatar
hjk committed
52 53 54
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
55
        break;
hjk's avatar
hjk committed
56 57 58
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
59
        break;
hjk's avatar
hjk committed
60 61 62
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
63
        break;
hjk's avatar
hjk committed
64 65 66
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
67
        break;
hjk's avatar
hjk committed
68 69 70
    case ProjectExplorer::Abi::WindowsOS:
        if (m_osFlavor < WindowsMsvcFlavor || m_osFlavor > WindowsCEFlavor)
            m_osFlavor = UnknownFlavor;
71 72 73 74 75
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
76 77
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
78 79 80 81
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
82
            m_architecture = UnknownArchitecture;
83
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
84
            m_architecture = ArmArchitecture;
85
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
86
            m_architecture = X86Architecture;
87
        else if (abiParts.at(0) == QLatin1String("mips"))
hjk's avatar
hjk committed
88
            m_architecture = MipsArcitecture;
89
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
90
            m_architecture = PowerPCArchitecture;
91
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
92
            m_architecture = ItaniumArchitecture;
93 94 95 96 97 98
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
99
            m_os = UnknownOS;
100
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
101
            m_os = LinuxOS;
102
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
103
            m_os = MacOS;
104
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
105
            m_os = SymbianOS;
106
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
107
            m_os = UnixOS;
108
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
109
            m_os = WindowsOS;
110 111 112 113 114 115
        else
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
            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;
137 138 139 140 141 142
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
143
            m_binaryFormat = UnknownFormat;
144
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
145
            m_binaryFormat = ElfFormat;
146
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
147
            m_binaryFormat = PEFormat;
148
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
149
            m_binaryFormat = MachOFormat;
150
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
151
            m_binaryFormat = RuntimeQmlFormat;
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
        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
194 195 196 197
    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)
198 199 200 201 202
            && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
203 204 205 206
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
207 208 209 210 211 212
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
213
    case ArmArchitecture:
214
        return QLatin1String("arm");
hjk's avatar
hjk committed
215
    case X86Architecture:
216
        return QLatin1String("x86");
hjk's avatar
hjk committed
217
    case MipsArcitecture:
218
        return QLatin1String("mips");
hjk's avatar
hjk committed
219
    case PowerPCArchitecture:
220
        return QLatin1String("ppc");
hjk's avatar
hjk committed
221
    case ItaniumArchitecture:
222
        return QLatin1String("itanium");
hjk's avatar
hjk committed
223
    case UnknownArchitecture: // fall through!
224 225 226 227 228 229 230 231
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
232
    case LinuxOS:
233
        return QLatin1String("linux");
hjk's avatar
hjk committed
234
    case MacOS:
235
        return QLatin1String("macos");
hjk's avatar
hjk committed
236
    case SymbianOS:
237
        return QLatin1String("symbian");
hjk's avatar
hjk committed
238
    case UnixOS:
239
        return QLatin1String("unix");
hjk's avatar
hjk committed
240
    case WindowsOS:
241
        return QLatin1String("windows");
hjk's avatar
hjk committed
242
    case UnknownOS: // fall through!
243 244 245 246 247
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
248
QString Abi::toString(const OSFlavor &of)
249 250
{
    switch (of) {
hjk's avatar
hjk committed
251
    case ProjectExplorer::Abi::GenericLinuxFlavor:
252
        return QLatin1String("generic");
hjk's avatar
hjk committed
253
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
254
        return QLatin1String("maemo");
hjk's avatar
hjk committed
255
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
256
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
257
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
258
        return QLatin1String("meego");
hjk's avatar
hjk committed
259
    case ProjectExplorer::Abi::GenericMacFlavor:
260
        return QLatin1String("generic");
hjk's avatar
hjk committed
261
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
262
        return QLatin1String("device");
hjk's avatar
hjk committed
263
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
264
        return QLatin1String("emulator");
hjk's avatar
hjk committed
265
    case ProjectExplorer::Abi::GenericUnixFlavor:
266
        return QLatin1String("generic");
hjk's avatar
hjk committed
267
    case ProjectExplorer::Abi::WindowsMsvcFlavor:
268
        return QLatin1String("msvc");
hjk's avatar
hjk committed
269
    case ProjectExplorer::Abi::WindowsMSysFlavor:
270
        return QLatin1String("msys");
hjk's avatar
hjk committed
271
    case ProjectExplorer::Abi::WindowsCEFlavor:
272
        return QLatin1String("ce");
hjk's avatar
hjk committed
273
    case ProjectExplorer::Abi::UnknownFlavor: // fall throught!
274 275 276 277 278 279 280 281
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
282
    case ElfFormat:
283
        return QLatin1String("elf");
hjk's avatar
hjk committed
284
    case PEFormat:
285
        return QLatin1String("pe");
hjk's avatar
hjk committed
286
    case MachOFormat:
287
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
288
    case RuntimeQmlFormat:
289
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
290
    case UnknownFormat: // fall through!
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    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
307 308 309
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
310 311

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
312 313 314
    os = WindowsOS;
    subos = WindowsMsvcFlavor;
    format = PEFormat;
315
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
316 317 318
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
319
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
320 321 322
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
323 324 325 326 327 328 329 330
#endif

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

static Abi macAbiForCpu(quint32 type) {
    switch (type) {
    case 7: // CPU_TYPE_X86, CPU_TYPE_I386
hjk's avatar
hjk committed
331
        return Abi(Abi::X86Architecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
332
    case 0x01000000 +  7: // CPU_TYPE_X86_64
hjk's avatar
hjk committed
333
        return Abi(Abi::X86Architecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 64);
334
    case 18: // CPU_TYPE_POWERPC
hjk's avatar
hjk committed
335
        return Abi(Abi::PowerPCArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
336
    case 0x01000000 + 18: // CPU_TYPE_POWERPC64
hjk's avatar
hjk committed
337
        return Abi(Abi::PowerPCArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
338
    case 12: // CPU_TYPE_ARM
hjk's avatar
hjk committed
339
        return Abi(Abi::ArmArchitecture, Abi::MacOS, Abi::GenericMacFlavor, Abi::MachOFormat, 32);
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    default:
        return Abi();
    }
}

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);
    f.close();

    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
hjk's avatar
hjk committed
366
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
367 368
            break;
        case 8: // EM_MIPS
hjk's avatar
hjk committed
369
            result.append(Abi(Abi::MipsArcitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
370 371
            break;
        case 20: // EM_PPC
hjk's avatar
hjk committed
372
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
373 374
            break;
        case 21: // EM_PPC64
hjk's avatar
hjk committed
375
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
376 377
            break;
        case 62: // EM_X86_64
hjk's avatar
hjk committed
378
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
379 380
            break;
        case 50: // EM_IA_64
hjk's avatar
hjk committed
381
            result.append(Abi(Abi::ItaniumArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
            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 + 5 < data.size()) {
            quint16 machine = (data.at(pePos + 5) << 8) + data.at(pePos + 4);
            switch (machine) {
            case 0x8664: // x86_64
hjk's avatar
hjk committed
415
                result.append(Abi(Abi::X86Architecture, Abi::WindowsOS, Abi::WindowsMsvcFlavor, Abi::PEFormat, 64));
416 417
                break;
            case 0x014c: // i386
hjk's avatar
hjk committed
418
                result.append(Abi(Abi::X86Architecture, Abi::WindowsOS, Abi::WindowsMsvcFlavor, Abi::PEFormat, 32));
419 420
                break;
            case 0x0200: // ia64
hjk's avatar
hjk committed
421
                result.append(Abi(Abi::ItaniumArchitecture, Abi::WindowsOS, Abi::WindowsMsvcFlavor, Abi::PEFormat, 64));
422 423 424 425 426 427 428 429
                break;
            }
        }
    }
    return result;
}

} // namespace ProjectExplorer