abi.cpp 30.7 KB
Newer Older
1
2
3
4
5
6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
33
34
35
**
**************************************************************************/

#include "abi.h"

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

43
44
45
46
47
48
49
50
/*!
    \class ProjectExplorer::Abi

    \brief Represents the Application Binary Interface (ABI) of a target platform.

    \sa ProjectExplorer::ToolChain
*/

51
52
namespace ProjectExplorer {

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// --------------------------------------------------------------------------
// 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
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;
95
    case 0x0166: // MIPS, little endian
Tobias Hunger's avatar
Tobias Hunger committed
96
        arch = Abi::MipsArchitecture;
97
98
        width = 32;
        break;
Tobias Hunger's avatar
Tobias Hunger committed
99
100
101
102
103
104
105
106
107
    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);
108
        if (image == 1) { // Image is 1 for mingw and higher for MSVC (4.something in some encoding)
Tobias Hunger's avatar
Tobias Hunger committed
109
            flavor = Abi::WindowsMSysFlavor;
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
        } 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
126
127
128
129
130
131
132
133
    }

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

    return result;
}

134
135
136
137
138
139
140
141
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:
142
        bool isLsbEncoded = (static_cast<quint8>(data.at(5)) == 1);
143
        quint16 machine = (data.at(19) << 8) + data.at(18);
144
145
        if (!isLsbEncoded)
            machine = qFromBigEndian(machine);
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
        quint8 osAbi = static_cast<quint8>(data.at(7));

        Abi::OS os = Abi::UnixOS;
        Abi::OSFlavor flavor = Abi::GenericUnixFlavor;
        // http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
        switch (osAbi) {
        case 2: // NetBSD:
            os = Abi::BsdOS;
            flavor = Abi::NetBsdFlavor;
            break;
        case 3: // Linux:
        case 0: // no extra info available: Default to Linux:
            os = Abi::LinuxOS;
            flavor = Abi::GenericLinuxFlavor;
            break;
        case 6: // Solaris:
            os = Abi::UnixOS;
            flavor = Abi::SolarisUnixFlavor;
            break;
        case 9: // FreeBSD:
            os = Abi::BsdOS;
            flavor = Abi::FreeBsdFlavor;
            break;
        case 12: // OpenBSD:
            os = Abi::BsdOS;
            flavor = Abi::OpenBsdFlavor;
        }
173

174
175
        switch (machine) {
        case 3: // EM_386
176
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
177
178
            break;
        case 8: // EM_MIPS
Tobias Hunger's avatar
Tobias Hunger committed
179
            result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
180
181
            break;
        case 20: // EM_PPC
182
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
183
184
            break;
        case 21: // EM_PPC64
185
            result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
186
            break;
187
        case 40: // EM_ARM
188
            result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
189
            break;
190
        case 62: // EM_X86_64
191
            result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
192
193
            break;
        case 50: // EM_IA_64
194
            result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
            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;
        }
220
221
222
223
    } 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));
224
    } else if (data.size() >= 64){
225
        // Windows PE
226
227
228
229
230
231
232
233
234
235
236
237

        // MZ header first:
        if (!data.at(0) == 'M' || !data.at(1) == 'Z')
            return result;

        // Get PE/COFF header position from MZ header:
        qint32 pePos = static_cast<quint8>(data.at(60)) + static_cast<quint8>(data.at(61)) * 0x100
                       + static_cast<quint8>(data.at(62)) * 0x10000 + static_cast<quint8>(data.at(63)) * 0x1000000;
        if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
            return result;
        if (data.at(pePos) == 'P' && data.at(pePos + 1) == 'E'
            && data.at(pePos + 2) == 0 && data.at(pePos + 3) == 0)
Tobias Hunger's avatar
Tobias Hunger committed
238
            result = parseCoffHeader(data.mid(pePos + 4));
239
240
241
242
243
244
245
246
    }
    return result;
}

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

247
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
248
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
249
250
251
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
252
253
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
254
        break;
hjk's avatar
hjk committed
255
256
257
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
258
        break;
259
260
    case ProjectExplorer::Abi::BsdOS:
        m_osFlavor = FreeBsdFlavor;
261
        break;
hjk's avatar
hjk committed
262
263
264
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
265
        break;
hjk's avatar
hjk committed
266
267
268
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
269
        break;
hjk's avatar
hjk committed
270
271
272
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
273
        break;
hjk's avatar
hjk committed
274
    case ProjectExplorer::Abi::WindowsOS:
275
        if (m_osFlavor < WindowsMsvc2005Flavor || m_osFlavor > WindowsCEFlavor)
hjk's avatar
hjk committed
276
            m_osFlavor = UnknownFlavor;
277
278
279
280
281
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
282
283
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
284
285
286
287
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
288
            m_architecture = UnknownArchitecture;
289
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
290
            m_architecture = ArmArchitecture;
291
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
292
            m_architecture = X86Architecture;
293
        else if (abiParts.at(0) == QLatin1String("mips"))
Tobias Hunger's avatar
Tobias Hunger committed
294
            m_architecture = MipsArchitecture;
295
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
296
            m_architecture = PowerPCArchitecture;
297
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
298
            m_architecture = ItaniumArchitecture;
299
300
301
302
303
304
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
305
            m_os = UnknownOS;
306
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
307
            m_os = LinuxOS;
308
309
        else if (abiParts.at(1) == QLatin1String("bsd"))
            m_os = BsdOS;
310
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
311
            m_os = MacOS;
312
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
313
            m_os = SymbianOS;
314
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
315
            m_os = UnixOS;
316
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
317
            m_os = WindowsOS;
318
        else
319

320
321
322
323
324
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
325
326
327
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
328
329
330
331
332
333
        else if (abiParts.at(2) == QLatin1String("freebsd") && m_os == BsdOS)
            m_osFlavor = FreeBsdFlavor;
        else if (abiParts.at(2) == QLatin1String("netbsd") && m_os == BsdOS)
            m_osFlavor = NetBsdFlavor;
        else if (abiParts.at(2) == QLatin1String("openbsd") && m_os == BsdOS)
            m_osFlavor = OpenBsdFlavor;
hjk's avatar
hjk committed
334
335
336
337
338
339
340
341
342
343
344
345
        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;
346
347
        else if (abiParts.at(2) == QLatin1String("solaris") && m_os == UnixOS)
            m_osFlavor = SolarisUnixFlavor;
348
349
350
351
352
353
        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
354
355
356
357
        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;
358
359
360
361
362
363
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
364
            m_binaryFormat = UnknownFormat;
365
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
366
            m_binaryFormat = ElfFormat;
367
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
368
            m_binaryFormat = PEFormat;
369
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
370
            m_binaryFormat = MachOFormat;
371
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
372
            m_binaryFormat = RuntimeQmlFormat;
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
        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
404
405
406
407
408
bool Abi::operator != (const Abi &other) const
{
    return !operator ==(other);
}

409
410
411
412
413
414
415
416
417
418
419
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
{
420
421
422
423
424
425
426
427
428
429
430
431
432
433
    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;
434
435
436
437
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
438
439
440
441
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
442
443
444
445
446
447
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
448
    case ArmArchitecture:
449
        return QLatin1String("arm");
hjk's avatar
hjk committed
450
    case X86Architecture:
451
        return QLatin1String("x86");
Tobias Hunger's avatar
Tobias Hunger committed
452
    case MipsArchitecture:
453
        return QLatin1String("mips");
hjk's avatar
hjk committed
454
    case PowerPCArchitecture:
455
        return QLatin1String("ppc");
hjk's avatar
hjk committed
456
    case ItaniumArchitecture:
457
        return QLatin1String("itanium");
hjk's avatar
hjk committed
458
    case UnknownArchitecture: // fall through!
459
460
461
462
463
464
465
466
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
467
    case LinuxOS:
468
        return QLatin1String("linux");
469
470
    case BsdOS:
        return QLatin1String("bsd");
hjk's avatar
hjk committed
471
    case MacOS:
472
        return QLatin1String("macos");
hjk's avatar
hjk committed
473
    case SymbianOS:
474
        return QLatin1String("symbian");
hjk's avatar
hjk committed
475
    case UnixOS:
476
        return QLatin1String("unix");
hjk's avatar
hjk committed
477
    case WindowsOS:
478
        return QLatin1String("windows");
hjk's avatar
hjk committed
479
    case UnknownOS: // fall through!
480
481
482
483
484
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
485
QString Abi::toString(const OSFlavor &of)
486
487
{
    switch (of) {
hjk's avatar
hjk committed
488
    case ProjectExplorer::Abi::GenericLinuxFlavor:
489
        return QLatin1String("generic");
490
491
492
493
494
495
    case ProjectExplorer::Abi::FreeBsdFlavor:
        return QLatin1String("freebsd");
    case ProjectExplorer::Abi::NetBsdFlavor:
        return QLatin1String("netbsd");
    case ProjectExplorer::Abi::OpenBsdFlavor:
        return QLatin1String("openbsd");
hjk's avatar
hjk committed
496
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
497
        return QLatin1String("maemo");
hjk's avatar
hjk committed
498
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
499
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
500
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
501
        return QLatin1String("meego");
hjk's avatar
hjk committed
502
    case ProjectExplorer::Abi::GenericMacFlavor:
503
        return QLatin1String("generic");
hjk's avatar
hjk committed
504
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
505
        return QLatin1String("device");
hjk's avatar
hjk committed
506
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
507
        return QLatin1String("emulator");
hjk's avatar
hjk committed
508
    case ProjectExplorer::Abi::GenericUnixFlavor:
509
        return QLatin1String("generic");
510
511
    case ProjectExplorer::Abi::SolarisUnixFlavor:
        return QLatin1String("solaris");
512
513
514
515
516
517
    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
518
    case ProjectExplorer::Abi::WindowsMSysFlavor:
519
        return QLatin1String("msys");
hjk's avatar
hjk committed
520
    case ProjectExplorer::Abi::WindowsCEFlavor:
521
        return QLatin1String("ce");
522
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
523
524
525
526
527
528
529
530
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
531
    case ElfFormat:
532
        return QLatin1String("elf");
hjk's avatar
hjk committed
533
    case PEFormat:
534
        return QLatin1String("pe");
hjk's avatar
hjk committed
535
    case MachOFormat:
536
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
537
    case RuntimeQmlFormat:
538
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
539
    case UnknownFormat: // fall through!
540
541
542
543
544
545
546
547
548
549
550
551
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(int w)
{
    if (w == 0)
        return QLatin1String("unknown");
    return QString::fromLatin1("%1bit").arg(w);
}

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
{
    QList<OSFlavor> result;
    switch (o) {
    case BsdOS:
        return result << FreeBsdFlavor << OpenBsdFlavor << NetBsdFlavor;
    case LinuxOS:
        return result << GenericLinuxFlavor << HarmattanLinuxFlavor << MaemoLinuxFlavor << MeegoLinuxFlavor;
    case MacOS:
        return result << GenericMacFlavor;
    case SymbianOS:
        return  result << SymbianDeviceFlavor << SymbianEmulatorFlavor;
    case UnixOS:
        return result << GenericUnixFlavor << SolarisUnixFlavor;
    case WindowsOS:
        return result << WindowsMsvc2005Flavor << WindowsMsvc2008Flavor << WindowsMsvc2010Flavor
                      << WindowsMSysFlavor << WindowsCEFlavor;
    case UnknownOS:
        return result << UnknownFlavor;
    default:
        break;
    }
    return result;
}
576
577
578
579

Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
580
581
582
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
583
584

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
585
    os = WindowsOS;
586
587
588
589
590
591
592
593
594
#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
595
    format = PEFormat;
596
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
597
598
599
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
600
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
601
602
603
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
604
605
606
607
608
609
610
#endif

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

QList<Abi> Abi::abisOfBinary(const QString &path)
{
611
    QList<Abi> tmp;
612
    if (path.isEmpty())
613
        return tmp;
614
615
616

    QFile f(path);
    if (!f.exists())
617
        return tmp;
618
619
620

    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
621
622
623
624
625
    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) {
626
        // We got an ar file: possibly a static lib for ELF, PE or Mach-O
627
628
629
630
631

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

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

637
638
639
640
641
            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));
642

Tobias Hunger's avatar
Tobias Hunger committed
643
            int toSkip = 60 + fileNameOffset;
644
            offset += fileLength.toInt() + 60 /* header */;
645
646
647
648
649

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

650
651
            if (!tmp.isEmpty()
                    && tmp.at(0).binaryFormat() != Abi::MachOFormat)
652
                break;
653

654
            offset += (offset % 2); // ar is 2 byte aligned
655
            f.seek(offset);
656
            data = f.read(1024);
657
        }
658
    } else {
659
        tmp = abiOf(data);
660
    }
661
662
    f.close();

663
664
665
666
667
668
669
    // Remove duplicates:
    QList<Abi> result;
    foreach (const Abi &a, tmp) {
        if (!result.contains(a))
            result.append(a);
    }

670
671
672
673
    return result;
}

} // namespace ProjectExplorer
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697

// 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;
698
699
    prefix += "/projectexplorer/abi";

700
701
702
703
704
705
706
707
708
709
    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")
710
            << QString::fromLatin1("%1/static/win-msvc2008-release.lib").arg(prefix)
711
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
712
    QTest::newRow("static QtCore: win msvc2008 II")
713
            << QString::fromLatin1("%1/static/win-msvc2008-release2.lib").arg(prefix)
714
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
715
    QTest::newRow("static QtCore: win msvc2008 (debug)")
716
            << QString::fromLatin1("%1/static/win-msvc2008-debug.lib").arg(prefix)
717
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
718
    QTest::newRow("static QtCore: win mingw")
719
            << QString::fromLatin1("%1/static/win-mingw.a").arg(prefix)
720
            << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
721
    QTest::newRow("static QtCore: mac (debug)")
722
            << QString::fromLatin1("%1/static/mac-32bit-debug.a").arg(prefix)
723
724
            << (QStringList() << QString::fromLatin1("x86-macos-generic-mach_o-32bit"));
    QTest::newRow("static QtCore: linux 32bit")
725
            << QString::fromLatin1("%1/static/linux-32bit-release.a").arg(prefix)
726
727
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
    QTest::newRow("static QtCore: linux 64bit")
728
            << QString::fromLatin1("%1/static/linux-64bit-release.a").arg(prefix)
729
730
731
            << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));

    QTest::newRow("static stdc++: mac fat")
732
            << QString::fromLatin1("%1/static/mac-fat.a").arg(prefix)
733
734
735
736
737
            << (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")
738
            << QString::fromLatin1("%1/dynamic/symbian.dll").arg(prefix)
739
740
            << (QStringList() << QString::fromLatin1("arm-symbian-device-elf-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2010 64bit")
741
            << QString::fromLatin1("%1/dynamic/win-msvc2010-64bit.dll").arg(prefix)
742
743
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
    QTest::newRow("dynamic QtCore: win msvc2008 32bit")
744
            << QString::fromLatin1("%1/dynamic/win-msvc2008-32bit.dll").arg(prefix)
745
746
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msvc2005 32bit")
747
            << QString::fromLatin1("%1/dynamic/win-msvc2005-32bit.dll").arg(prefix)
748
749
            << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
    QTest::newRow("dynamic QtCore: win msys 32bit")
750
            << QString::fromLatin1("%1/dynamic/win-mingw-32bit.dll").arg(prefix)
751
            << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
752
753
754
    QTest::newRow("dynamic QtCore: wince msvc2005 32bit")
            << QString::fromLatin1("%1/dynamic/wince-32bit.dll").arg(prefix)
            << (QStringList() << QString::fromLatin1("mips-windows-msvc2005-pe-32bit"));
755
    QTest::newRow("dynamic stdc++: mac fat")
756
            << QString::fromLatin1("%1/dynamic/mac-fat.dylib").arg(prefix)
757
758
759
            << (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"));
760
    QTest::newRow("dynamic QtCore: arm linux 32bit")
761
            << QString::fromLatin1("%1/dynamic/arm-linux.so").arg(prefix)
762
            << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
Tobias Hunger's avatar
Tobias Hunger committed
763
    QTest::newRow("dynamic QtCore: mips linux 32bit")
764
            << QString::fromLatin1("%1/dynamic/mips-linux.so").arg(prefix)
Tobias Hunger's avatar
Tobias Hunger committed
765
            << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));
766
767
768
    QTest::newRow("dynamic QtCore: projectexplorer/abi/static/win-msvc2010-32bit.libppc be linux 32bit")
            << QString::fromLatin1("%1/dynamic/ppcbe-linux-32bit.so").arg(prefix)
            << (QStringList() << QString::fromLatin1("ppc-linux-generic-elf-32bit"));
769
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
770
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
771
772
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
773
            << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
774
775
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
    QTest::newRow("dynamic QtCore: x86 freebsd 32bit")
776
            << QString::fromLatin1("%1/dynamic/freebsd-elf-32bit.so").arg(prefix)
777
            << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-32bit"));
778
779
780
781
782
783
784
785
786
787
788
789
790
}

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));
}

791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
void ProjectExplorer::ProjectExplorerPlugin::testFlavorForOs()
{
    QList<QList<ProjectExplorer::Abi::OSFlavor> > flavorLists;
    for (int i = 0; i != static_cast<int>(Abi::UnknownOS); ++i)
        flavorLists.append(Abi::flavorsForOs(static_cast<Abi::OS>(i)));

    int foundCounter = 0;
    for (int i = 0; i != Abi::UnknownFlavor; ++i) {
        foundCounter = 0;
        // make sure i is in exactly on of the flavor lists!
        foreach (const QList<Abi::OSFlavor> &l, flavorLists) {
            QVERIFY(!l.contains(Abi::UnknownFlavor));
            if (l.contains(static_cast<Abi::OSFlavor>(i)))
                ++foundCounter;
        }
        QCOMPARE(foundCounter, 1);
    }
}

810
#endif