1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "abi.h"
27 
28 #include <utils/algorithm.h>
29 #include <utils/fileutils.h>
30 #include <utils/qtcassert.h>
31 
32 #include <QDebug>
33 #include <QtEndian>
34 #include <QRegularExpression>
35 #include <QString>
36 #include <QStringList>
37 #include <QSysInfo>
38 
39 /*!
40     \class ProjectExplorer::Abi
41 
42     \brief The Abi class represents the Application Binary Interface (ABI) of
43     a target platform.
44 
45     \sa ProjectExplorer::ToolChain
46 */
47 
48 namespace ProjectExplorer {
49 
50 // --------------------------------------------------------------------------
51 // Helpers
52 // --------------------------------------------------------------------------
53 
54 static std::vector<QByteArray> m_registeredOsFlavors;
55 static std::map<int, QList<Abi::OSFlavor>> m_osToOsFlavorMap;
56 
moveGenericAndUnknownLast(const QList<Abi::OSFlavor> & in)57 static QList<Abi::OSFlavor> moveGenericAndUnknownLast(const QList<Abi::OSFlavor> &in)
58 {
59     QList<Abi::OSFlavor> result = in;
60     // Move unknown and generic to the back
61     if (result.removeOne(Abi::GenericFlavor))
62         result.append(Abi::GenericFlavor);
63     if (result.removeOne(Abi::UnknownFlavor))
64         result.append(Abi::UnknownFlavor);
65 
66     return result;
67 }
68 
insertIntoOsFlavorMap(const std::vector<Abi::OS> & oses,const Abi::OSFlavor flavor)69 static void insertIntoOsFlavorMap(const std::vector<Abi::OS> &oses, const Abi::OSFlavor flavor)
70 {
71     QTC_ASSERT(oses.size() > 0, return);
72     for (const Abi::OS &os : oses) {
73         auto it = m_osToOsFlavorMap.find(os);
74         if (it == m_osToOsFlavorMap.end()) {
75             m_osToOsFlavorMap[os] = {flavor};
76             continue;
77         }
78         QList<Abi::OSFlavor> flavors = it->second;
79         if (!flavors.contains(flavor)) {
80             flavors.append(flavor);
81             m_osToOsFlavorMap[os] = moveGenericAndUnknownLast(flavors);
82         }
83     }
84 }
85 
registerOsFlavor(const Abi::OSFlavor & flavor,const QByteArray & flavorName,const std::vector<Abi::OS> & oses)86 static void registerOsFlavor(const Abi::OSFlavor &flavor, const QByteArray &flavorName,
87                              const std::vector<Abi::OS> &oses)
88 {
89     const auto pos = static_cast<size_t>(flavor);
90     if (m_registeredOsFlavors.size() <= pos)
91         m_registeredOsFlavors.resize(pos + 1);
92 
93     m_registeredOsFlavors[pos] = flavorName;
94 
95     insertIntoOsFlavorMap(oses, flavor);
96 }
97 
setupPreregisteredOsFlavors()98 static void setupPreregisteredOsFlavors() {
99     m_registeredOsFlavors.resize(static_cast<size_t>(Abi::UnknownFlavor));
100 
101     registerOsFlavor(Abi::FreeBsdFlavor, "freebsd", {Abi::OS::BsdOS});
102     registerOsFlavor(Abi::NetBsdFlavor, "netbsd", {Abi::OS::BsdOS});
103     registerOsFlavor(Abi::OpenBsdFlavor, "openbsd", {Abi::OS::BsdOS});
104     registerOsFlavor(Abi::AndroidLinuxFlavor, "android", {Abi::OS::LinuxOS});
105     registerOsFlavor(Abi::SolarisUnixFlavor, "solaris", {Abi::OS::UnixOS});
106     registerOsFlavor(Abi::WindowsMsvc2005Flavor, "msvc2005", {Abi::OS::WindowsOS});
107     registerOsFlavor(Abi::WindowsMsvc2008Flavor, "msvc2008", {Abi::OS::WindowsOS});
108     registerOsFlavor(Abi::WindowsMsvc2010Flavor, "msvc2010", {Abi::OS::WindowsOS});
109     registerOsFlavor(Abi::WindowsMsvc2012Flavor, "msvc2012", {Abi::OS::WindowsOS});
110     registerOsFlavor(Abi::WindowsMsvc2013Flavor, "msvc2013", {Abi::OS::WindowsOS});
111     registerOsFlavor(Abi::WindowsMsvc2015Flavor, "msvc2015", {Abi::OS::WindowsOS});
112     registerOsFlavor(Abi::WindowsMsvc2017Flavor, "msvc2017", {Abi::OS::WindowsOS});
113     registerOsFlavor(Abi::WindowsMsvc2019Flavor, "msvc2019", {Abi::OS::WindowsOS});
114     registerOsFlavor(Abi::WindowsMSysFlavor, "msys", {Abi::OS::WindowsOS});
115     registerOsFlavor(Abi::WindowsCEFlavor, "ce", {Abi::OS::WindowsOS});
116     registerOsFlavor(Abi::VxWorksFlavor, "vxworks", {Abi::OS::VxWorks});
117     registerOsFlavor(Abi::RtosFlavor, "rtos", {Abi::OS::WindowsOS});
118     registerOsFlavor(Abi::GenericFlavor, "generic", {Abi::OS::LinuxOS,
119                                                      Abi::OS::DarwinOS,
120                                                      Abi::OS::UnixOS,
121                                                      Abi::OS::QnxOS,
122                                                      Abi::OS::BareMetalOS});
123     registerOsFlavor(Abi::UnknownFlavor, "unknown", {Abi::OS::BsdOS,
124                                                      Abi::OS::LinuxOS,
125                                                      Abi::OS::DarwinOS,
126                                                      Abi::OS::UnixOS,
127                                                      Abi::OS::WindowsOS,
128                                                      Abi::OS::VxWorks,
129                                                      Abi::OS::QnxOS,
130                                                      Abi::OS::BareMetalOS,
131                                                      Abi::OS::UnknownOS});
132 }
133 
registeredOsFlavors()134 static std::vector<QByteArray> &registeredOsFlavors() {
135     if (m_registeredOsFlavors.size() == 0)
136         setupPreregisteredOsFlavors();
137     return m_registeredOsFlavors;
138 }
139 
indexOfFlavor(const QByteArray & flavor)140 static int indexOfFlavor(const QByteArray &flavor)
141 {
142     return Utils::indexOf(registeredOsFlavors(), [flavor](const QByteArray &f) { return f == flavor; });
143 }
144 
architectureFromQt()145 static Abi::Architecture architectureFromQt()
146 {
147     const QString arch = QSysInfo::buildCpuArchitecture();
148     if (arch.startsWith("arm"))
149         return Abi::ArmArchitecture;
150     if (arch.startsWith("x86") || arch == "i386")
151         return Abi::X86Architecture;
152     if (arch == "ia64")
153         return Abi::ItaniumArchitecture;
154     if (arch.startsWith("mips"))
155         return Abi::MipsArchitecture;
156     if (arch.startsWith("power"))
157         return Abi::PowerPCArchitecture;
158     if (arch.startsWith("sh")) // Not in Qt documentation!
159         return Abi::ShArchitecture;
160     if (arch.startsWith("avr32")) // Not in Qt documentation!
161         return Abi::Avr32Architecture;
162     if (arch.startsWith("avr")) // Not in Qt documentation!
163         return Abi::AvrArchitecture;
164     if (arch.startsWith("asmjs"))
165         return Abi::AsmJsArchitecture;
166 
167     return Abi::UnknownArchitecture;
168 }
169 
getUint8(const QByteArray & data,int pos)170 static quint8 getUint8(const QByteArray &data, int pos)
171 {
172     return static_cast<quint8>(data.at(pos));
173 }
174 
getLEUint32(const QByteArray & ba,int pos)175 static quint32 getLEUint32(const QByteArray &ba, int pos)
176 {
177     Q_ASSERT(ba.size() >= pos + 3);
178     return (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 3))) << 24)
179             + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2)) << 16))
180             + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 8)
181             + static_cast<quint8>(ba.at(pos));
182 }
183 
getBEUint32(const QByteArray & ba,int pos)184 static quint32 getBEUint32(const QByteArray &ba, int pos)
185 {
186     Q_ASSERT(ba.size() >= pos + 3);
187     return (static_cast<quint32>(static_cast<quint8>(ba.at(pos))) << 24)
188             + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 1))) << 16)
189             + (static_cast<quint32>(static_cast<quint8>(ba.at(pos + 2))) << 8)
190             + static_cast<quint8>(ba.at(pos + 3));
191 }
192 
getLEUint16(const QByteArray & ba,int pos)193 static quint32 getLEUint16(const QByteArray &ba, int pos)
194 {
195     Q_ASSERT(ba.size() >= pos + 1);
196     return (static_cast<quint16>(static_cast<quint8>(ba.at(pos + 1))) << 8) + static_cast<quint8>(ba.at(pos));
197 }
198 
getBEUint16(const QByteArray & ba,int pos)199 static quint32 getBEUint16(const QByteArray &ba, int pos)
200 {
201     Q_ASSERT(ba.size() >= pos + 1);
202     return (static_cast<quint16>(static_cast<quint8>(ba.at(pos))) << 8) + static_cast<quint8>(ba.at(pos + 1));
203 }
204 
macAbiForCpu(quint32 type)205 static Abi macAbiForCpu(quint32 type) {
206     switch (type) {
207     case 7: // CPU_TYPE_X86, CPU_TYPE_I386
208         return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
209     case 0x01000000 +  7: // CPU_TYPE_X86_64
210         return Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 64);
211     case 18: // CPU_TYPE_POWERPC
212     case 0x01000000 + 18: // CPU_TYPE_POWERPC64
213         return Abi(Abi::PowerPCArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
214     case 12: // CPU_TYPE_ARM
215         return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 32);
216     case 0x01000000 + 12: // CPU_TYPE_ARM64
217         return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 64);
218     default:
219         return Abi();
220     }
221 }
222 
parseCoffHeader(const QByteArray & data)223 static Abis parseCoffHeader(const QByteArray &data)
224 {
225     Abis result;
226     if (data.size() < 20)
227         return result;
228 
229     Abi::Architecture arch = Abi::UnknownArchitecture;
230     Abi::OSFlavor flavor = Abi::UnknownFlavor;
231     int width = 0;
232 
233     // Get machine field from COFF file header
234     quint16 machine = getLEUint16(data, 0);
235     switch (machine) {
236     case 0x01c0: // ARM LE
237     case 0x01c2: // ARM or thumb
238     case 0x01c4: // ARMv7 thumb
239         arch = Abi::ArmArchitecture;
240         width = 32;
241         break;
242     case 0xaa64: // ARM64
243         arch = Abi::ArmArchitecture;
244         width = 64;
245         break;
246     case 0x8664: // x86_64
247         arch = Abi::X86Architecture;
248         width = 64;
249         break;
250     case 0x014c: // i386
251         arch = Abi::X86Architecture;
252         width = 32;
253         break;
254     case 0x0166: // MIPS, little endian
255         arch = Abi::MipsArchitecture;
256         width = 32;
257         break;
258     case 0x0200: // ia64
259         arch = Abi::ItaniumArchitecture;
260         width = 64;
261         break;
262     }
263 
264     if (data.size() >= 24) {
265         // Get Major and Minor Image Version from optional header fields
266         quint8 minorLinker = data.at(23);
267         switch (data.at(22)) {
268         case 2:
269         case 3: // not yet reached:-)
270             flavor = Abi::WindowsMSysFlavor;
271             break;
272         case 8:
273             flavor = Abi::WindowsMsvc2005Flavor;
274             break;
275         case 9:
276             flavor = Abi::WindowsMsvc2008Flavor;
277             break;
278         case 10:
279             flavor = Abi::WindowsMsvc2010Flavor;
280             break;
281         case 11:
282             flavor = Abi::WindowsMsvc2012Flavor;
283             break;
284         case 12:
285             flavor = Abi::WindowsMsvc2013Flavor;
286             break;
287         case 14:
288             if (minorLinker >= quint8(20))
289                 flavor = Abi::WindowsMsvc2019Flavor;
290             else if (minorLinker >= quint8(10))
291                 flavor = Abi::WindowsMsvc2017Flavor;
292             else
293                 flavor = Abi::WindowsMsvc2015Flavor;
294             break;
295         case 15:
296             flavor = Abi::WindowsMsvc2019Flavor;
297             break;
298         default: // Keep unknown flavor
299             if (minorLinker != 0)
300                 flavor = Abi::WindowsMSysFlavor; // MSVC seems to avoid using minor numbers
301             else
302                 qWarning("%s: Unknown MSVC flavour encountered.", Q_FUNC_INFO);
303             break;
304         }
305     }
306 
307     if (arch != Abi::UnknownArchitecture && width != 0)
308         result.append(Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, width));
309 
310     return result;
311 }
312 
abiOf(const QByteArray & data)313 static Abis abiOf(const QByteArray &data)
314 {
315     Abis result;
316     if (data.size() <= 8)
317         return result;
318 
319     if (data.size() >= 20
320             && getUint8(data, 0) == 0x7f && getUint8(data, 1) == 'E' && getUint8(data, 2) == 'L'
321             && getUint8(data, 3) == 'F') {
322         // ELF format:
323         bool isLE = (getUint8(data, 5) == 1);
324         quint16 machine = isLE ? getLEUint16(data, 18) : getBEUint16(data, 18);
325         quint8 osAbi = getUint8(data, 7);
326 
327         Abi::OS os = Abi::UnixOS;
328         Abi::OSFlavor flavor = Abi::GenericFlavor;
329         // http://www.sco.com/developers/gabi/latest/ch4.eheader.html#elfid
330         switch (osAbi) {
331 #if defined(Q_OS_NETBSD)
332         case 0: // NetBSD: ELFOSABI_NETBSD  2, however, NetBSD uses 0
333             os = Abi::BsdOS;
334             flavor = Abi::NetBsdFlavor;
335             break;
336 #elif defined(Q_OS_OPENBSD)
337         case 0: // OpenBSD: ELFOSABI_OPENBSD 12, however, OpenBSD uses 0
338             os = Abi::BsdOS;
339             flavor = Abi::OpenBsdFlavor;
340             break;
341 #else
342         case 0: // no extra info available: Default to Linux:
343 #endif
344         case 3: // Linux:
345         case 97: // ARM, also linux most of the time.
346             os = Abi::LinuxOS;
347             flavor = Abi::GenericFlavor;
348             break;
349         case 6: // Solaris:
350             os = Abi::UnixOS;
351             flavor = Abi::SolarisUnixFlavor;
352             break;
353         case 9: // FreeBSD:
354             os = Abi::BsdOS;
355             flavor = Abi::FreeBsdFlavor;
356             break;
357         }
358 
359         switch (machine) {
360         case 3: // EM_386
361             result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 32));
362             break;
363         case 8: // EM_MIPS
364             result.append(Abi(Abi::MipsArchitecture, os, flavor, Abi::ElfFormat, 32));
365             break;
366         case 20: // EM_PPC
367             result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 32));
368             break;
369         case 21: // EM_PPC64
370             result.append(Abi(Abi::PowerPCArchitecture, os, flavor, Abi::ElfFormat, 64));
371             break;
372         case 40: // EM_ARM
373             result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 32));
374             break;
375         case 183: // EM_AARCH64
376             result.append(Abi(Abi::ArmArchitecture, os, flavor, Abi::ElfFormat, 64));
377             break;
378         case 62: // EM_X86_64
379             result.append(Abi(Abi::X86Architecture, os, flavor, Abi::ElfFormat, 64));
380             break;
381         case 42: // EM_SH
382             result.append(Abi(Abi::ShArchitecture, os, flavor, Abi::ElfFormat, 32));
383             break;
384         case 50: // EM_IA_64
385             result.append(Abi(Abi::ItaniumArchitecture, os, flavor, Abi::ElfFormat, 64));
386             break;
387         default:
388             ;
389         }
390     } else if (((getUint8(data, 0) == 0xce || getUint8(data, 0) == 0xcf)
391              && getUint8(data, 1) == 0xfa && getUint8(data, 2) == 0xed && getUint8(data, 3) == 0xfe
392             )
393             ||
394             (getUint8(data, 0) == 0xfe && getUint8(data, 1) == 0xed && getUint8(data, 2) == 0xfa
395              && (getUint8(data, 3) == 0xce || getUint8(data, 3) == 0xcf)
396             )
397            ) {
398             // Mach-O format (Mac non-fat binary, 32 and 64bit magic):
399             quint32 type = (getUint8(data, 1) ==  0xfa) ? getLEUint32(data, 4) : getBEUint32(data, 4);
400             result.append(macAbiForCpu(type));
401     } else if ((getUint8(data, 0) == 0xbe && getUint8(data, 1) == 0xba
402                 && getUint8(data, 2) == 0xfe && getUint8(data, 3) == 0xca)
403                ||
404                (getUint8(data, 0) == 0xca && getUint8(data, 1) == 0xfe
405                 && getUint8(data, 2) == 0xba && getUint8(data, 3) == 0xbe)
406               ) {
407         // Mach-0 format Fat binary header:
408         bool isLE = (getUint8(data, 0) == 0xbe);
409         quint32 count = isLE ? getLEUint32(data, 4) : getBEUint32(data, 4);
410         int pos = 8;
411         for (quint32 i = 0; i < count; ++i) {
412             if (data.size() <= pos + 4)
413                 break;
414 
415             quint32 type = isLE ? getLEUint32(data, pos) : getBEUint32(data, pos);
416             result.append(macAbiForCpu(type));
417             pos += 20;
418         }
419     } else if (// Qt 5.15+ https://webassembly.github.io/spec/core/binary/modules.html#binary-module
420                (getUint8(data, 0) == 0 && getUint8(data, 1) == 'a'
421                 && getUint8(data, 2) == 's' && getUint8(data, 3) == 'm')
422 
423                // Qt < 5.15 https://llvm.org/docs/BitCodeFormat.html#llvm-ir-magic-number
424                || (getUint8(data, 0) == 'B' && getUint8(data, 1) == 'C'
425                    && getUint8(data, 2) == 0xc0 && getUint8(data, 3) == 0xde)) {
426         result.append(Abi(Abi::AsmJsArchitecture, Abi::UnknownOS, Abi::UnknownFlavor,
427                           Abi::EmscriptenFormat, 32));
428     } else if (data.size() >= 64){
429         // Windows PE: values are LE (except for a few exceptions which we will not use here).
430 
431         // MZ header first (ZM is also allowed, but rarely used)
432         const quint8 firstChar = getUint8(data, 0);
433         const quint8 secondChar = getUint8(data, 1);
434         if ((firstChar != 'M' || secondChar != 'Z') && (firstChar != 'Z' || secondChar != 'M'))
435             return result;
436 
437         // Get PE/COFF header position from MZ header:
438         qint32 pePos = getLEUint32(data, 60);
439         if (pePos <= 0 || data.size() < pePos + 4 + 20) // PE magic bytes plus COFF header
440             return result;
441         if (getUint8(data, pePos) == 'P' && getUint8(data, pePos + 1) == 'E'
442             && getUint8(data, pePos + 2) == 0 && getUint8(data, pePos + 3) == 0)
443             result = parseCoffHeader(data.mid(pePos + 4));
444     }
445     return result;
446 }
447 
448 // --------------------------------------------------------------------------
449 // Abi
450 // --------------------------------------------------------------------------
451 
Abi(const Architecture & a,const OS & o,const OSFlavor & of,const BinaryFormat & f,unsigned char w,const QString & p)452 Abi::Abi(const Architecture &a, const OS &o,
453          const OSFlavor &of, const BinaryFormat &f, unsigned char w, const QString &p) :
454     m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w), m_param(p)
455 {
456     QTC_ASSERT(osSupportsFlavor(o, of), m_osFlavor = UnknownFlavor);
457 }
458 
abiFromTargetTriplet(const QString & triple)459 Abi Abi::abiFromTargetTriplet(const QString &triple)
460 {
461     const QString machine = triple.toLower();
462     if (machine.isEmpty())
463         return Abi();
464 
465     const QStringList parts = machine.split(QRegularExpression("[ /-]"));
466 
467     Architecture arch = UnknownArchitecture;
468     OS os = UnknownOS;
469     OSFlavor flavor = UnknownFlavor;
470     BinaryFormat format = UnknownFormat;
471     unsigned char width = 0;
472     int unknownCount = 0;
473 
474     for (const QString &p : parts) {
475         if (p == "unknown" || p == "pc"
476                 || p == "gnu" || p == "uclibc"
477                 || p == "86_64" || p == "redhat"
478                 || p == "w64") {
479             continue;
480         } else if (p == "i386" || p == "i486" || p == "i586"
481                    || p == "i686" || p == "x86") {
482             arch = X86Architecture;
483             width = 32;
484         } else if (p == "xtensa") {
485             arch = XtensaArchitecture;
486             os = BareMetalOS;
487             flavor = GenericFlavor;
488             format = ElfFormat;
489             width = 32;
490         } else if (p.startsWith("arm")) {
491             arch = ArmArchitecture;
492             width = p.contains("64") ? 64 : 32;
493         } else if (p.startsWith("aarch64")) {
494             arch = ArmArchitecture;
495             width = 64;
496         } else if (p == "avr") {
497             arch = AvrArchitecture;
498             os = BareMetalOS;
499             flavor = GenericFlavor;
500             format = ElfFormat;
501             width = 16;
502         } else if (p == "avr32") {
503             arch = Avr32Architecture;
504             os = BareMetalOS;
505             flavor = GenericFlavor;
506             format = ElfFormat;
507             width = 32;
508         } else if (p == "cr16") {
509             arch = CR16Architecture;
510             os = BareMetalOS;
511             flavor = GenericFlavor;
512             format = ElfFormat;
513             // Note that GCC macro returns 32-bit value for this architecture.
514             width = 32;
515         } else if (p == "msp430") {
516             arch = Msp430Architecture;
517             os = BareMetalOS;
518             flavor = GenericFlavor;
519             format = ElfFormat;
520             width = 16;
521         } else if (p == "rl78") {
522             arch = Rl78Architecture;
523             os = BareMetalOS;
524             flavor = GenericFlavor;
525             format = ElfFormat;
526             width = 16;
527         } else if (p == "rx") {
528             arch = RxArchitecture;
529             os = BareMetalOS;
530             flavor = GenericFlavor;
531             format = ElfFormat;
532             width = 32;
533         } else if (p == "sh") {
534             arch = ShArchitecture;
535             os = BareMetalOS;
536             flavor = GenericFlavor;
537             format = ElfFormat;
538             width = 32;
539         } else if (p == "v850") {
540             arch = V850Architecture;
541             os = BareMetalOS;
542             flavor = GenericFlavor;
543             format = ElfFormat;
544             width = 32;
545         } else if (p == "m68k") {
546             arch = M68KArchitecture;
547             os = BareMetalOS;
548             flavor = GenericFlavor;
549             format = ElfFormat;
550             width = 16;
551         } else if (p == "m32c") {
552             arch = M32CArchitecture;
553             os = BareMetalOS;
554             flavor = GenericFlavor;
555             format = ElfFormat;
556             width = 16;
557         } else if (p == "m32r") {
558             arch = M32RArchitecture;
559             os = BareMetalOS;
560             flavor = GenericFlavor;
561             format = ElfFormat;
562             width = 32;
563         } else if (p.startsWith("riscv")) {
564             arch = RiscVArchitecture;
565             os = BareMetalOS;
566             flavor = GenericFlavor;
567             format = ElfFormat;
568             width = p.contains("64") ? 64 : 32;
569         } else if (p.startsWith("mips")) {
570             arch = MipsArchitecture;
571             width = p.contains("64") ? 64 : 32;
572         } else if (p == "x86_64" || p == "amd64") {
573             arch = X86Architecture;
574             width = 64;
575         } else if (p == "powerpc64") {
576             arch = PowerPCArchitecture;
577             width = 64;
578         } else if (p == "powerpc") {
579             arch = PowerPCArchitecture;
580             width = 32;
581         } else if (p == "linux" || p == "linux6e") {
582             os = LinuxOS;
583             if (flavor == UnknownFlavor)
584                 flavor = GenericFlavor;
585             format = ElfFormat;
586         } else if (p == "android" || p == "androideabi") {
587             flavor = AndroidLinuxFlavor;
588         } else if (p.startsWith("freebsd")) {
589             os = BsdOS;
590             if (flavor == UnknownFlavor)
591                 flavor = FreeBsdFlavor;
592             format = ElfFormat;
593         } else if (p.startsWith("openbsd")) {
594             os = BsdOS;
595             if (flavor == UnknownFlavor)
596                 flavor = OpenBsdFlavor;
597             format = ElfFormat;
598         } else if (p == "mingw32" || p == "win32"
599                    || p == "mingw32msvc" || p == "msys"
600                    || p == "cygwin" || p == "windows") {
601             arch = X86Architecture;
602             os = WindowsOS;
603             flavor = WindowsMSysFlavor;
604             format = PEFormat;
605         } else if (p == "apple") {
606             os = DarwinOS;
607             flavor = GenericFlavor;
608             format = MachOFormat;
609         } else if (p == "darwin10") {
610             width = 64;
611         } else if (p == "darwin9") {
612             width = 32;
613         } else if (p == "gnueabi" || p == "elf") {
614             format = ElfFormat;
615         } else if (p == "wrs") {
616             continue;
617         } else if (p == "vxworks") {
618             os = VxWorks;
619             flavor = VxWorksFlavor;
620             format = ElfFormat;
621         } else if (p.startsWith("qnx")) {
622             os = QnxOS;
623             flavor = GenericFlavor;
624             format = ElfFormat;
625         } else if (p.startsWith("emscripten")) {
626             format = EmscriptenFormat;
627             width = 32;
628         } else if (p.startsWith("asmjs")) {
629             arch = AsmJsArchitecture;
630         } else if (p == "none") {
631             os = BareMetalOS;
632             flavor = GenericFlavor;
633             format = ElfFormat;
634         } else {
635             ++unknownCount;
636         }
637     }
638 
639     return Abi(arch, os, flavor, format, width);
640 }
641 
abiOsToOsType(const Abi::OS os)642 Utils::OsType Abi::abiOsToOsType(const Abi::OS os)
643 {
644     switch (os) {
645     case ProjectExplorer::Abi::LinuxOS:
646         return Utils::OsType::OsTypeLinux;
647     case ProjectExplorer::Abi::DarwinOS:
648         return Utils::OsType::OsTypeMac;
649     case ProjectExplorer::Abi::BsdOS:
650     case ProjectExplorer::Abi::UnixOS:
651         return Utils::OsType::OsTypeOtherUnix;
652     case ProjectExplorer::Abi::WindowsOS:
653         return Utils::OsType::OsTypeWindows;
654     case ProjectExplorer::Abi::VxWorks:
655     case ProjectExplorer::Abi::QnxOS:
656     case ProjectExplorer::Abi::BareMetalOS:
657     case ProjectExplorer::Abi::UnknownOS:
658         return Utils::OsType::OsTypeOther;
659     }
660     return Utils::OsType::OsTypeOther;
661 }
662 
toString() const663 QString Abi::toString() const
664 {
665     const QStringList dn = {toString(m_architecture), toString(m_os), toString(m_osFlavor),
666                             toString(m_binaryFormat), toString(m_wordWidth)};
667     return dn.join('-');
668 }
669 
param() const670 QString Abi::param() const
671 {
672     if (m_param.isEmpty())
673         return toString();
674     return m_param;
675 }
676 
operator !=(const Abi & other) const677 bool Abi::operator != (const Abi &other) const
678 {
679     return !operator ==(other);
680 }
681 
operator ==(const Abi & other) const682 bool Abi::operator == (const Abi &other) const
683 {
684     return m_architecture == other.m_architecture
685             && m_os == other.m_os
686             && m_osFlavor == other.m_osFlavor
687             && m_binaryFormat == other.m_binaryFormat
688             && m_wordWidth == other.m_wordWidth;
689 }
690 
compatibleMSVCFlavors(const Abi::OSFlavor & left,const Abi::OSFlavor & right)691 static bool compatibleMSVCFlavors(const Abi::OSFlavor &left, const Abi ::OSFlavor &right)
692 {
693     // MSVC 2019, 2017 and 2015 are compatible
694     return left >= Abi::WindowsMsvc2015Flavor && left <= Abi::WindowsMsvc2019Flavor
695            && right >= Abi::WindowsMsvc2015Flavor && right <= Abi::WindowsMsvc2019Flavor;
696 }
697 
isCompatibleWith(const Abi & other) const698 bool Abi::isCompatibleWith(const Abi &other) const
699 {
700     // Generic match: If stuff is identical or the other side is unknown, then this is a match.
701     bool isCompat = (architecture() == other.architecture() || other.architecture() == UnknownArchitecture)
702                      && (os() == other.os() || other.os() == UnknownOS)
703                      && (osFlavor() == other.osFlavor() || other.osFlavor() == UnknownFlavor)
704                      && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == UnknownFormat)
705                      && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
706 
707     // *-linux-generic-* is compatible with *-linux-* (both ways): This is for the benefit of
708     // people building Qt themselves using e.g. a meego toolchain.
709     //
710     // We leave it to the specific targets to filter out the tool chains that do not
711     // work for them.
712     if (!isCompat && (architecture() == other.architecture() || other.architecture() == UnknownArchitecture)
713                   && ((os() == other.os()) && (os() == LinuxOS))
714                   && (osFlavor() == GenericFlavor || other.osFlavor() == GenericFlavor)
715                   && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == UnknownFormat)
716                   && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0)) {
717         isCompat = true;
718     }
719 
720     // Make Android matching more strict than the generic Linux matches so far:
721     if (isCompat && (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor))
722         isCompat = (architecture() == other.architecture()) &&  (osFlavor() == other.osFlavor());
723 
724     if (!isCompat && wordWidth() == other.wordWidth()
725             && compatibleMSVCFlavors(osFlavor(), other.osFlavor())) {
726         isCompat = true;
727     }
728 
729     return isCompat;
730 }
731 
isValid() const732 bool Abi::isValid() const
733 {
734     return m_architecture != UnknownArchitecture
735             && m_os != UnknownOS
736             && m_osFlavor != UnknownFlavor
737             && m_binaryFormat != UnknownFormat
738             && m_wordWidth != 0;
739 }
740 
isNull() const741 bool Abi::isNull() const
742 {
743     return m_architecture == UnknownArchitecture
744             && m_os == UnknownOS
745             && m_osFlavor == UnknownFlavor
746             && m_binaryFormat == UnknownFormat
747             && m_wordWidth == 0;
748 }
749 
toString(const Architecture & a)750 QString Abi::toString(const Architecture &a)
751 {
752     switch (a) {
753     case ArmArchitecture:
754         return QLatin1String("arm");
755     case AvrArchitecture:
756         return QLatin1String("avr");
757     case Avr32Architecture:
758         return QLatin1String("avr32");
759     case XtensaArchitecture:
760         return QLatin1String("xtensa");
761     case X86Architecture:
762         return QLatin1String("x86");
763     case Mcs51Architecture:
764         return QLatin1String("mcs51");
765     case Mcs251Architecture:
766         return QLatin1String("mcs251");
767     case MipsArchitecture:
768         return QLatin1String("mips");
769     case PowerPCArchitecture:
770         return QLatin1String("ppc");
771     case ItaniumArchitecture:
772         return QLatin1String("itanium");
773     case ShArchitecture:
774         return QLatin1String("sh");
775     case AsmJsArchitecture:
776         return QLatin1String("asmjs");
777     case Stm8Architecture:
778         return QLatin1String("stm8");
779     case Msp430Architecture:
780         return QLatin1String("msp430");
781     case Rl78Architecture:
782         return QLatin1String("rl78");
783     case C166Architecture:
784         return QLatin1String("c166");
785     case V850Architecture:
786         return QLatin1String("v850");
787     case Rh850Architecture:
788         return QLatin1String("rh850");
789     case RxArchitecture:
790         return QLatin1String("rx");
791     case K78Architecture:
792         return QLatin1String("78k");
793     case M68KArchitecture:
794         return QLatin1String("m68k");
795     case M32CArchitecture:
796         return QLatin1String("m32c");
797     case M16CArchitecture:
798         return QLatin1String("m16c");
799     case M32RArchitecture:
800         return QLatin1String("m32r");
801     case R32CArchitecture:
802         return QLatin1String("r32c");
803     case CR16Architecture:
804         return QLatin1String("cr16");
805     case RiscVArchitecture:
806         return QLatin1String("riscv");
807     case UnknownArchitecture:
808         Q_FALLTHROUGH();
809     default:
810         return QLatin1String("unknown");
811     }
812 }
813 
toString(const OS & o)814 QString Abi::toString(const OS &o)
815 {
816     switch (o) {
817     case LinuxOS:
818         return QLatin1String("linux");
819     case BsdOS:
820         return QLatin1String("bsd");
821     case DarwinOS:
822         return QLatin1String("darwin");
823     case UnixOS:
824         return QLatin1String("unix");
825     case WindowsOS:
826         return QLatin1String("windows");
827     case VxWorks:
828         return QLatin1String("vxworks");
829     case QnxOS:
830         return QLatin1String("qnx");
831     case BareMetalOS:
832         return QLatin1String("baremetal");
833     case UnknownOS:
834         Q_FALLTHROUGH();
835     default:
836         return QLatin1String("unknown");
837     };
838 }
839 
toString(const OSFlavor & of)840 QString Abi::toString(const OSFlavor &of)
841 {
842     const auto index = static_cast<size_t>(of);
843     const std::vector<QByteArray> &flavors = registeredOsFlavors();
844     QTC_ASSERT(index < flavors.size(),
845                return QString::fromUtf8(flavors.at(int(UnknownFlavor))));
846     return QString::fromUtf8(flavors.at(index));
847 }
848 
toString(const BinaryFormat & bf)849 QString Abi::toString(const BinaryFormat &bf)
850 {
851     switch (bf) {
852     case ElfFormat:
853         return QLatin1String("elf");
854     case PEFormat:
855         return QLatin1String("pe");
856     case MachOFormat:
857         return QLatin1String("mach_o");
858     case RuntimeQmlFormat:
859         return QLatin1String("qml_rt");
860     case UbrofFormat:
861         return QLatin1String("ubrof");
862     case OmfFormat:
863         return QLatin1String("omf");
864     case EmscriptenFormat:
865         return QLatin1String("emscripten");
866     case UnknownFormat:
867         Q_FALLTHROUGH();
868     default:
869         return QLatin1String("unknown");
870     }
871 }
872 
toString(int w)873 QString Abi::toString(int w)
874 {
875     if (w == 0)
876         return QLatin1String("unknown");
877     return QString::fromLatin1("%1bit").arg(w);
878 }
879 
fromString(const QString & abiString)880 Abi Abi::fromString(const QString &abiString)
881 {
882     Abi::Architecture architecture = UnknownArchitecture;
883     const QStringList abiParts = abiString.split('-');
884     if (!abiParts.isEmpty()) {
885         architecture = architectureFromString(abiParts.at(0));
886         if (abiParts.at(0) != toString(architecture))
887             return Abi();
888     }
889 
890     Abi::OS os = UnknownOS;
891     if (abiParts.count() >= 2) {
892         os = osFromString(abiParts.at(1));
893         if (abiParts.at(1) != toString(os))
894             return Abi(architecture, UnknownOS, UnknownFlavor, UnknownFormat, 0);
895     }
896 
897     Abi::OSFlavor flavor = UnknownFlavor;
898     if (abiParts.count() >= 3) {
899         flavor = osFlavorFromString(abiParts.at(2), os);
900         if (abiParts.at(2) != toString(flavor))
901             return Abi(architecture, os, UnknownFlavor, UnknownFormat, 0);
902     }
903 
904     Abi::BinaryFormat format = UnknownFormat;
905     if (abiParts.count() >= 4) {
906         format = binaryFormatFromString(abiParts.at(3));
907         if (abiParts.at(3) != toString(format))
908             return Abi(architecture, os, flavor, UnknownFormat, 0);
909     }
910 
911     unsigned char wordWidth = 0;
912     if (abiParts.count() >= 5) {
913         wordWidth = wordWidthFromString(abiParts.at(4));
914         if (abiParts.at(4) != toString(wordWidth))
915             return Abi(architecture, os, flavor, format, 0);
916     }
917 
918     return Abi(architecture, os, flavor, format, wordWidth);
919 }
920 
architectureFromString(const QString & a)921 Abi::Architecture Abi::architectureFromString(const QString &a)
922 {
923     if (a == "unknown")
924         return UnknownArchitecture;
925     if (a == "arm")
926         return ArmArchitecture;
927     if (a == "aarch64")
928         return ArmArchitecture;
929     if (a == "avr")
930         return AvrArchitecture;
931     if (a == "avr32")
932         return Avr32Architecture;
933     if (a == "x86")
934         return X86Architecture;
935     if (a == "mcs51")
936         return Mcs51Architecture;
937     if (a == "mcs251")
938         return Mcs251Architecture;
939     if (a == "mips")
940         return MipsArchitecture;
941     if (a == "ppc")
942         return PowerPCArchitecture;
943     if (a == "itanium")
944         return ItaniumArchitecture;
945     if (a == "sh")
946         return ShArchitecture;
947     if (a == "stm8")
948         return Stm8Architecture;
949     if (a == "msp430")
950         return Msp430Architecture;
951     if (a == "rl78")
952         return Rl78Architecture;
953     if (a == "c166")
954         return C166Architecture;
955     if (a == "v850")
956         return V850Architecture;
957     if (a == "rh850")
958         return Rh850Architecture;
959     if (a == "rx")
960         return RxArchitecture;
961     if (a == "78k")
962         return K78Architecture;
963     if (a == "m68k")
964         return M68KArchitecture;
965     if (a == "m32c")
966         return M32CArchitecture;
967     if (a == "m16c")
968         return M16CArchitecture;
969     if (a == "m32r")
970         return M32RArchitecture;
971     if (a == "r32c")
972         return R32CArchitecture;
973     if (a == "cr16")
974         return CR16Architecture;
975     if (a == "riscv")
976         return RiscVArchitecture;
977     else if (a == "xtensa")
978         return XtensaArchitecture;
979     if (a == "asmjs")
980         return AsmJsArchitecture;
981 
982     return UnknownArchitecture;
983 }
984 
osFromString(const QString & o)985 Abi::OS Abi::osFromString(const QString &o)
986 {
987     if (o == "unknown")
988         return UnknownOS;
989     if (o == "linux")
990         return LinuxOS;
991     if (o == "bsd")
992         return BsdOS;
993     if (o == "darwin" || o == "macos")
994         return DarwinOS;
995     if (o == "unix")
996         return UnixOS;
997     if (o == "windows")
998         return WindowsOS;
999     if (o == "vxworks")
1000         return VxWorks;
1001     if (o == "qnx")
1002         return QnxOS;
1003     if (o == "baremetal")
1004         return BareMetalOS;
1005     return UnknownOS;
1006 }
1007 
osFlavorFromString(const QString & of,const OS os)1008 Abi::OSFlavor Abi::osFlavorFromString(const QString &of, const OS os)
1009 {
1010     const int index = indexOfFlavor(of.toUtf8());
1011     const auto flavor = OSFlavor(index);
1012     if (index >= 0 && osSupportsFlavor(os, flavor))
1013         return flavor;
1014     return UnknownFlavor;
1015 }
1016 
binaryFormatFromString(const QString & bf)1017 Abi::BinaryFormat Abi::binaryFormatFromString(const QString &bf)
1018 {
1019     if (bf == "unknown")
1020         return UnknownFormat;
1021     if (bf == "elf")
1022         return ElfFormat;
1023     if (bf == "pe")
1024         return PEFormat;
1025     if (bf == "mach_o")
1026         return MachOFormat;
1027     if (bf == "ubrof")
1028         return UbrofFormat;
1029     if (bf == "omf")
1030         return OmfFormat;
1031     if (bf == "qml_rt")
1032         return RuntimeQmlFormat;
1033     if (bf == "emscripten")
1034         return EmscriptenFormat;
1035     return UnknownFormat;
1036 }
1037 
wordWidthFromString(const QString & w)1038 unsigned char Abi::wordWidthFromString(const QString &w)
1039 {
1040     if (!w.endsWith("bit"))
1041         return 0;
1042 
1043     bool ok = false;
1044     const QString number = w.left(w.size() - 3);
1045     const int bitCount = number.toInt(&ok);
1046     if (!ok)
1047         return 0;
1048     if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
1049         return 0;
1050     return static_cast<unsigned char>(bitCount);
1051 }
1052 
registerOsFlavor(const std::vector<OS> & oses,const QString & flavorName)1053 Abi::OSFlavor Abi::registerOsFlavor(const std::vector<OS> &oses, const QString &flavorName)
1054 {
1055     QTC_ASSERT(oses.size() > 0, return UnknownFlavor);
1056     const QByteArray flavorBytes = flavorName.toUtf8();
1057 
1058     int index = indexOfFlavor(flavorBytes);
1059     if (index < 0)
1060         index = int(registeredOsFlavors().size());
1061 
1062     auto toRegister = OSFlavor(index);
1063     ProjectExplorer::registerOsFlavor(toRegister, flavorBytes, oses);
1064     return toRegister;
1065 }
1066 
flavorsForOs(const Abi::OS & o)1067 QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o)
1068 {
1069     registeredOsFlavors(); // Make sure m_osToOsFlavorMap is populated!
1070     auto it = m_osToOsFlavorMap.find(o);
1071     if (it == m_osToOsFlavorMap.end())
1072         return {};
1073 
1074     return it->second;
1075 }
1076 
allOsFlavors()1077 QList<Abi::OSFlavor> Abi::allOsFlavors()
1078 {
1079     QList<OSFlavor> result;
1080     for (size_t i = 0; i < registeredOsFlavors().size(); ++i)
1081         result << OSFlavor(i);
1082     return moveGenericAndUnknownLast(result);
1083 }
1084 
osSupportsFlavor(const Abi::OS & os,const Abi::OSFlavor & flavor)1085 bool Abi::osSupportsFlavor(const Abi::OS &os, const Abi::OSFlavor &flavor)
1086 {
1087     return flavorsForOs(os).contains(flavor);
1088 }
1089 
flavorForMsvcVersion(int version)1090 Abi::OSFlavor Abi::flavorForMsvcVersion(int version)
1091 {
1092     if (version >= 1920)
1093         return WindowsMsvc2019Flavor;
1094     if (version >= 1910)
1095         return WindowsMsvc2017Flavor;
1096     switch (version) {
1097     case 1900:
1098         return WindowsMsvc2015Flavor;
1099     case 1800:
1100         return WindowsMsvc2013Flavor;
1101     case 1700:
1102         return WindowsMsvc2012Flavor;
1103     case 1600:
1104         return WindowsMsvc2010Flavor;
1105     case 1500:
1106         return WindowsMsvc2008Flavor;
1107     case 1400:
1108         return WindowsMsvc2005Flavor;
1109     default:
1110         return WindowsMSysFlavor;
1111     }
1112 }
1113 
hostAbi()1114 Abi Abi::hostAbi()
1115 {
1116     Architecture arch = architectureFromQt();
1117     OS os = UnknownOS;
1118     OSFlavor subos = UnknownFlavor;
1119     BinaryFormat format = UnknownFormat;
1120 
1121 #if defined (Q_OS_WIN)
1122     os = WindowsOS;
1123 #ifdef _MSC_VER
1124     subos = flavorForMsvcVersion(_MSC_VER);
1125 #elif defined (Q_CC_MINGW)
1126     subos = WindowsMSysFlavor;
1127 #endif
1128     format = PEFormat;
1129 #elif defined (Q_OS_LINUX)
1130     os = LinuxOS;
1131     subos = GenericFlavor;
1132     format = ElfFormat;
1133 #elif defined (Q_OS_DARWIN)
1134     os = DarwinOS;
1135     subos = GenericFlavor;
1136     format = MachOFormat;
1137 #elif defined (Q_OS_BSD4)
1138     os = BsdOS;
1139 # if defined (Q_OS_FREEBSD)
1140     subos = FreeBsdFlavor;
1141 # elif defined (Q_OS_NETBSD)
1142     subos = NetBsdFlavor;
1143 # elif defined (Q_OS_OPENBSD)
1144     subos = OpenBsdFlavor;
1145 # endif
1146     format = ElfFormat;
1147 #endif
1148 
1149     const Abi result(arch, os, subos, format, QSysInfo::WordSize);
1150     if (!result.isValid())
1151         qWarning("Unable to completely determine the host ABI (%s).",
1152                  qPrintable(result.toString()));
1153     return result;
1154 }
1155 
1156 //! Extract available ABIs from a binary using heuristics.
abisOfBinary(const Utils::FilePath & path)1157 Abis Abi::abisOfBinary(const Utils::FilePath &path)
1158 {
1159     Abis tmp;
1160     if (path.isEmpty())
1161         return tmp;
1162 
1163     QByteArray data = path.fileContents(1024);
1164     if (data.size() >= 67
1165             && getUint8(data, 0) == '!' && getUint8(data, 1) == '<' && getUint8(data, 2) == 'a'
1166             && getUint8(data, 3) == 'r' && getUint8(data, 4) == 'c' && getUint8(data, 5) == 'h'
1167             && getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) {
1168         // We got an ar file: possibly a static lib for ELF, PE or Mach-O
1169 
1170         data = data.mid(8); // Cut of ar file magic
1171         quint64 offset = 8;
1172 
1173         while (!data.isEmpty()) {
1174             if ((getUint8(data, 58) != 0x60 || getUint8(data, 59) != 0x0a)) {
1175                 qWarning() << path.toString() << ": Thought it was an ar-file, but it is not!";
1176                 break;
1177             }
1178 
1179             const QString fileName = QString::fromLocal8Bit(data.mid(0, 16));
1180             quint64 fileNameOffset = 0;
1181             if (fileName.startsWith("#1/"))
1182                 fileNameOffset = fileName.mid(3).toInt();
1183             const QString fileLength = QString::fromLatin1(data.mid(48, 10));
1184 
1185             int toSkip = 60 + fileNameOffset;
1186             offset += fileLength.toInt() + 60 /* header */;
1187 
1188             tmp.append(abiOf(data.mid(toSkip)));
1189             if (tmp.isEmpty() && fileName == "/0              ")
1190                 tmp = parseCoffHeader(data.mid(toSkip, 20)); // This might be windws...
1191 
1192             if (!tmp.isEmpty() && tmp.at(0).binaryFormat() != MachOFormat)
1193                 break;
1194 
1195             offset += (offset % 2); // ar is 2 byte aligned
1196             data = path.fileContents(1024, offset);
1197         }
1198     } else {
1199         tmp = abiOf(data);
1200     }
1201 
1202     // Remove duplicates:
1203     Abis result;
1204     for (const Abi &a : qAsConst(tmp)) {
1205         if (!result.contains(a))
1206             result.append(a);
1207     }
1208 
1209     return result;
1210 }
1211 
1212 } // namespace ProjectExplorer
1213 
1214 // Unit tests:
1215 #ifdef WITH_TESTS
1216 #   include <QTest>
1217 #   include <QFileInfo>
1218 
1219 #   include "projectexplorer.h"
1220 
isGenericFlavor(ProjectExplorer::Abi::OSFlavor f)1221 static bool isGenericFlavor(ProjectExplorer::Abi::OSFlavor f)
1222 {
1223     return f == ProjectExplorer::Abi::GenericFlavor;
1224 }
1225 
testAbiRoundTrips()1226 void ProjectExplorer::ProjectExplorerPlugin::testAbiRoundTrips()
1227 {
1228     for (int i = 0; i <= Abi::UnknownArchitecture; ++i) {
1229         const QString string = Abi::toString(static_cast<Abi::Architecture>(i));
1230         const Abi::Architecture arch = Abi::architectureFromString(string);
1231         QCOMPARE(static_cast<Abi::Architecture>(i), arch);
1232     }
1233     for (int i = 0; i <= Abi::UnknownOS; ++i) {
1234         const QString string = Abi::toString(static_cast<Abi::OS>(i));
1235         const Abi::OS os = Abi::osFromString(string);
1236         QCOMPARE(static_cast<Abi::OS>(i), os);
1237     }
1238     for (const Abi::OSFlavor flavorIt : Abi::allOsFlavors()) {
1239         const QString string = Abi::toString(flavorIt);
1240         for (int os = 0; os <= Abi::UnknownOS; ++os) {
1241             const auto osEnum = static_cast<Abi::OS>(os);
1242             const Abi::OSFlavor flavor = Abi::osFlavorFromString(string, osEnum);
1243             if (isGenericFlavor(flavorIt) && flavor != Abi::UnknownFlavor)
1244                 QVERIFY(isGenericFlavor(flavor));
1245             else if (flavor == Abi::UnknownFlavor && flavorIt != Abi::UnknownFlavor)
1246                 QVERIFY(!Abi::flavorsForOs(osEnum).contains(flavorIt));
1247             else
1248                 QCOMPARE(flavorIt, flavor);
1249         }
1250     }
1251     for (int i = 0; i <= Abi::UnknownFormat; ++i) {
1252         QString string = Abi::toString(static_cast<Abi::BinaryFormat>(i));
1253         Abi::BinaryFormat format = Abi::binaryFormatFromString(string);
1254         QCOMPARE(static_cast<Abi::BinaryFormat>(i), format);
1255     }
1256     for (unsigned char i : {0, 8, 16, 32, 64}) {
1257         QString string = Abi::toString(i);
1258         unsigned char wordwidth = Abi::wordWidthFromString(string);
1259         QCOMPARE(i, wordwidth);
1260     }
1261 }
1262 
testAbiOfBinary_data()1263 void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary_data()
1264 {
1265     QTest::addColumn<QString>("file");
1266     QTest::addColumn<QStringList>("abis");
1267 
1268     QTest::newRow("no file")
1269             << QString()
1270             << (QStringList());
1271     QTest::newRow("non existing file")
1272             << QString::fromLatin1("/does/not/exist")
1273             << (QStringList());
1274 
1275     // Clone test data from: https://git.qt.io/chstenge/creator-test-data
1276     // Set up prefix for test data now that we can be sure to have some tests to run:
1277     QString prefix = QString::fromLocal8Bit(qgetenv("QTC_TEST_EXTRADATALOCATION"));
1278     if (prefix.isEmpty())
1279         return;
1280     prefix += "/projectexplorer/abi";
1281 
1282     QFileInfo fi(prefix);
1283     if (!fi.exists() || !fi.isDir())
1284         return;
1285     prefix = fi.absoluteFilePath();
1286 
1287     QTest::newRow("text file")
1288             << QString::fromLatin1("%1/broken/text.txt").arg(prefix)
1289             << (QStringList());
1290 
1291     QTest::newRow("static QtCore: win msvc2008")
1292             << QString::fromLatin1("%1/static/win-msvc2008-release.lib").arg(prefix)
1293             << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1294     QTest::newRow("static QtCore: win msvc2008 II")
1295             << QString::fromLatin1("%1/static/win-msvc2008-release2.lib").arg(prefix)
1296             << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-64bit"));
1297     QTest::newRow("static QtCore: win msvc2008 (debug)")
1298             << QString::fromLatin1("%1/static/win-msvc2008-debug.lib").arg(prefix)
1299             << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1300     QTest::newRow("static QtCore: win mingw")
1301             << QString::fromLatin1("%1/static/win-mingw.a").arg(prefix)
1302             << (QStringList() << QString::fromLatin1("x86-windows-unknown-pe-32bit"));
1303     QTest::newRow("static QtCore: mac (debug)")
1304             << QString::fromLatin1("%1/static/mac-32bit-debug.a").arg(prefix)
1305             << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit"));
1306     QTest::newRow("static QtCore: linux 32bit")
1307             << QString::fromLatin1("%1/static/linux-32bit-release.a").arg(prefix)
1308             << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-32bit"));
1309     QTest::newRow("static QtCore: linux 64bit")
1310             << QString::fromLatin1("%1/static/linux-64bit-release.a").arg(prefix)
1311             << (QStringList() << QString::fromLatin1("x86-linux-generic-elf-64bit"));
1312     QTest::newRow("static QtCore: asmjs emscripten 32bit (Qt < 5.15)")
1313             << QString::fromLatin1("%1/static/asmjs-emscripten.a").arg(prefix)
1314             << (QStringList() << QString::fromLatin1("asmjs-unknown-unknown-emscripten-32bit"));
1315     QTest::newRow("static QtCore: asmjs emscripten 32bit (Qt >= 5.15)")
1316             << QString::fromLatin1("%1/static/asmjs-emscripten-5.15.a").arg(prefix)
1317             << (QStringList() << QString::fromLatin1("asmjs-unknown-unknown-emscripten-32bit"));
1318 
1319     QTest::newRow("static stdc++: mac fat")
1320             << QString::fromLatin1("%1/static/mac-fat.a").arg(prefix)
1321             << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit")
1322                               << QString::fromLatin1("ppc-darwin-generic-mach_o-32bit")
1323                               << QString::fromLatin1("x86-darwin-generic-mach_o-64bit"));
1324 
1325     QTest::newRow("executable: win msvc2013 64bit")
1326             << QString::fromLatin1("%1/executables/x86-windows-mvsc2013-pe-64bit.exe").arg(prefix)
1327             << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-64bit"));
1328     QTest::newRow("executable: win msvc2013 32bit")
1329             << QString::fromLatin1("%1/executables/x86-windows-mvsc2013-pe-32bit.exe").arg(prefix)
1330             << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-32bit"));
1331     QTest::newRow("dynamic: win msvc2013 64bit")
1332             << QString::fromLatin1("%1/dynamic/x86-windows-mvsc2013-pe-64bit.dll").arg(prefix)
1333             << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-64bit"));
1334     QTest::newRow("dynamic: win msvc2013 32bit")
1335             << QString::fromLatin1("%1/dynamic/x86-windows-mvsc2013-pe-32bit.dll").arg(prefix)
1336             << (QStringList() << QString::fromLatin1("x86-windows-msvc2013-pe-32bit"));
1337     QTest::newRow("dynamic QtCore: win msvc2010 64bit")
1338             << QString::fromLatin1("%1/dynamic/win-msvc2010-64bit.dll").arg(prefix)
1339             << (QStringList() << QString::fromLatin1("x86-windows-msvc2010-pe-64bit"));
1340     QTest::newRow("dynamic QtCore: win msvc2008 32bit")
1341             << QString::fromLatin1("%1/dynamic/win-msvc2008-32bit.dll").arg(prefix)
1342             << (QStringList() << QString::fromLatin1("x86-windows-msvc2008-pe-32bit"));
1343     QTest::newRow("dynamic QtCore: win msvc2005 32bit")
1344             << QString::fromLatin1("%1/dynamic/win-msvc2005-32bit.dll").arg(prefix)
1345             << (QStringList() << QString::fromLatin1("x86-windows-msvc2005-pe-32bit"));
1346     QTest::newRow("dynamic QtCore: win msys 32bit")
1347             << QString::fromLatin1("%1/dynamic/win-mingw-32bit.dll").arg(prefix)
1348             << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1349     QTest::newRow("dynamic QtCore: win mingw 64bit")
1350             << QString::fromLatin1("%1/dynamic/win-mingw-64bit.dll").arg(prefix)
1351             << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-64bit"));
1352     QTest::newRow("dynamic QtCore: wince mips msvc2005 32bit")
1353             << QString::fromLatin1("%1/dynamic/wince-32bit.dll").arg(prefix)
1354             << (QStringList() << QString::fromLatin1("mips-windows-msvc2005-pe-32bit"));
1355     QTest::newRow("dynamic QtCore: wince arm msvc2008 32bit")
1356             << QString::fromLatin1("%1/dynamic/arm-win-ce-pe-32bit").arg(prefix)
1357             << (QStringList() << QString::fromLatin1("arm-windows-msvc2008-pe-32bit"));
1358 
1359     QTest::newRow("dynamic stdc++: mac fat")
1360             << QString::fromLatin1("%1/dynamic/mac-fat.dylib").arg(prefix)
1361             << (QStringList() << QString::fromLatin1("x86-darwin-generic-mach_o-32bit")
1362                               << QString::fromLatin1("ppc-darwin-generic-mach_o-32bit")
1363                               << QString::fromLatin1("x86-darwin-generic-mach_o-64bit"));
1364     QTest::newRow("dynamic QtCore: arm linux 32bit")
1365             << QString::fromLatin1("%1/dynamic/arm-linux.so").arg(prefix)
1366             << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1367     QTest::newRow("dynamic QtCore: arm linux 32bit, using ARM as OSABI")
1368             << QString::fromLatin1("%1/dynamic/arm-linux2.so").arg(prefix)
1369             << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1370     QTest::newRow("dynamic QtCore: arm linux 32bit (angstrom)")
1371             << QString::fromLatin1("%1/dynamic/arm-angstrom-linux.so").arg(prefix)
1372             << (QStringList() << QString::fromLatin1("arm-linux-generic-elf-32bit"));
1373     QTest::newRow("dynamic QtCore: sh4 linux 32bit")
1374             << QString::fromLatin1("%1/dynamic/sh4-linux.so").arg(prefix)
1375             << (QStringList() << QString::fromLatin1("sh-linux-generic-elf-32bit"));
1376     QTest::newRow("dynamic QtCore: mips linux 32bit")
1377             << QString::fromLatin1("%1/dynamic/mips-linux.so").arg(prefix)
1378             << (QStringList() << QString::fromLatin1("mips-linux-generic-elf-32bit"));
1379     QTest::newRow("dynamic QtCore: projectexplorer/abi/static/win-msvc2010-32bit.libppc be linux 32bit")
1380             << QString::fromLatin1("%1/dynamic/ppcbe-linux-32bit.so").arg(prefix)
1381             << (QStringList() << QString::fromLatin1("ppc-linux-generic-elf-32bit"));
1382     QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
1383             << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
1384             << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
1385     QTest::newRow("dynamic QtCore: x86 freebsd 64bit")
1386             << QString::fromLatin1("%1/dynamic/freebsd-elf-64bit.so").arg(prefix)
1387             << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-64bit"));
1388     QTest::newRow("dynamic QtCore: x86 freebsd 32bit")
1389             << QString::fromLatin1("%1/dynamic/freebsd-elf-32bit.so").arg(prefix)
1390             << (QStringList() << QString::fromLatin1("x86-bsd-freebsd-elf-32bit"));
1391 
1392     QTest::newRow("executable: x86 win 32bit cygwin executable")
1393             << QString::fromLatin1("%1/executable/cygwin-32bit.exe").arg(prefix)
1394             << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1395     QTest::newRow("executable: x86 win 32bit mingw executable")
1396             << QString::fromLatin1("%1/executable/mingw-32bit.exe").arg(prefix)
1397             << (QStringList() << QString::fromLatin1("x86-windows-msys-pe-32bit"));
1398 }
1399 
testAbiOfBinary()1400 void ProjectExplorer::ProjectExplorerPlugin::testAbiOfBinary()
1401 {
1402     QFETCH(QString, file);
1403     QFETCH(QStringList, abis);
1404 
1405     const Utils::FilePath fp = Utils::FilePath::fromString(file);
1406     const QString dataTag = QString::fromLocal8Bit(QTest::currentDataTag());
1407     if (!fp.exists() && dataTag != "no file" && dataTag != "non existing file")
1408         QSKIP("binary file not present");
1409 
1410     const Abis result = Abi::abisOfBinary(fp);
1411     QCOMPARE(result.count(), abis.count());
1412     for (int i = 0; i < abis.count(); ++i)
1413         QCOMPARE(result.at(i).toString(), abis.at(i));
1414 }
1415 
testAbiFromTargetTriplet_data()1416 void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet_data()
1417 {
1418     QTest::addColumn<int>("architecture");
1419     QTest::addColumn<int>("os");
1420     QTest::addColumn<int>("osFlavor");
1421     QTest::addColumn<int>("binaryFormat");
1422     QTest::addColumn<int>("wordWidth");
1423 
1424     QTest::newRow("x86_64-apple-darwin") << int(Abi::X86Architecture)
1425                                          << int(Abi::DarwinOS) << int(Abi::GenericFlavor)
1426                                          << int(Abi::MachOFormat) << 64;
1427 
1428     QTest::newRow("x86_64-apple-darwin12.5.0") << int(Abi::X86Architecture)
1429                                                << int(Abi::DarwinOS) << int(Abi::GenericFlavor)
1430                                                << int(Abi::MachOFormat) << 64;
1431 
1432     QTest::newRow("x86_64-linux-gnu") << int(Abi::X86Architecture)
1433                                       << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1434                                       << int(Abi::ElfFormat) << 64;
1435 
1436     QTest::newRow("x86_64-pc-mingw32msvc") << int(Abi::X86Architecture)
1437                                            << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1438                                            << int(Abi::PEFormat) << 64;
1439 
1440     QTest::newRow("i586-pc-mingw32msvc") << int(Abi::X86Architecture)
1441                                          << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1442                                          << int(Abi::PEFormat) << 32;
1443 
1444     QTest::newRow("i686-linux-gnu") << int(Abi::X86Architecture)
1445                                     << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1446                                     << int(Abi::ElfFormat) << 32;
1447 
1448     QTest::newRow("i686-linux-android") << int(Abi::X86Architecture)
1449                                         << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1450                                         << int(Abi::ElfFormat) << 32;
1451 
1452     QTest::newRow("i686-pc-linux-android") << int(Abi::X86Architecture)
1453                                            << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1454                                            << int(Abi::ElfFormat) << 32;
1455 
1456     QTest::newRow("i686-pc-mingw32") << int(Abi::X86Architecture)
1457                                      << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1458                                      << int(Abi::PEFormat) << 32;
1459 
1460     QTest::newRow("i686-w64-mingw32") << int(Abi::X86Architecture)
1461                                       << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1462                                       << int(Abi::PEFormat) << 32;
1463 
1464     QTest::newRow("x86_64-pc-msys") << int(Abi::X86Architecture)
1465                                     << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1466                                     << int(Abi::PEFormat) << 64;
1467 
1468     QTest::newRow("x86_64-pc-cygwin") << int(Abi::X86Architecture)
1469                                       << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1470                                       << int(Abi::PEFormat) << 64;
1471 
1472     QTest::newRow("x86-pc-windows-msvc") << int(Abi::X86Architecture)
1473                                          << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1474                                          << int(Abi::PEFormat) << 32;
1475 
1476     QTest::newRow("mingw32") << int(Abi::X86Architecture)
1477                              << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor)
1478                              << int(Abi::PEFormat) << 0;
1479 
1480     QTest::newRow("arm-linux-android") << int(Abi::ArmArchitecture)
1481                                        << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1482                                        << int(Abi::ElfFormat) << 32;
1483 
1484     QTest::newRow("arm-linux-androideabi") << int(Abi::ArmArchitecture)
1485                                            << int(Abi::LinuxOS) << int(Abi::AndroidLinuxFlavor)
1486                                            << int(Abi::ElfFormat) << 32;
1487 
1488     QTest::newRow("arm-none-linux-gnueabi") << int(Abi::ArmArchitecture)
1489                                             << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1490                                             << int(Abi::ElfFormat) << 32;
1491 
1492     QTest::newRow("mips-linux-gnu") << int(Abi::MipsArchitecture)
1493                                     << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1494                                     << int(Abi::ElfFormat) << 32;
1495 
1496     QTest::newRow("mips64-linux-octeon-gnu") << int(Abi::MipsArchitecture)
1497                                              << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1498                                              << int(Abi::ElfFormat) << 64;
1499 
1500     QTest::newRow("mips64el-linux-gnuabi") << int(Abi::MipsArchitecture)
1501                                     << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1502                                     << int(Abi::ElfFormat) << 64;
1503 
1504     QTest::newRow("arm-wrs-vxworks") << int(Abi::ArmArchitecture)
1505                                      << int(Abi::VxWorks) << int(Abi::VxWorksFlavor)
1506                                      << int(Abi::ElfFormat) << 32;
1507 
1508     QTest::newRow("x86_64-unknown-openbsd") << int(Abi::X86Architecture)
1509                                             << int(Abi::BsdOS) << int(Abi::OpenBsdFlavor)
1510                                             << int(Abi::ElfFormat) << 64;
1511 
1512     QTest::newRow("aarch64-unknown-linux-gnu") << int(Abi::ArmArchitecture)
1513                                                << int(Abi::LinuxOS) << int(Abi::GenericFlavor)
1514                                                << int(Abi::ElfFormat) << 64;
1515     QTest::newRow("xtensa-lx106-elf")  << int(Abi::XtensaArchitecture)
1516                                        << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1517                                        << int(Abi::ElfFormat) << 32;
1518 
1519     // Yes, that's the entire triplet
1520     QTest::newRow("avr") << int(Abi::AvrArchitecture)
1521                          << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1522                          << int(Abi::ElfFormat) << 16;
1523 
1524     QTest::newRow("avr32") << int(Abi::Avr32Architecture)
1525                          << int(Abi::BareMetalOS) << int(Abi::GenericFlavor)
1526                          << int(Abi::ElfFormat) << 32;
1527 
1528     QTest::newRow("asmjs-unknown-emscripten") << int(Abi::AsmJsArchitecture)
1529                                               << int(Abi::UnknownOS) << int(Abi::UnknownFlavor)
1530                                               << int(Abi::EmscriptenFormat) << 32;
1531 }
1532 
testAbiFromTargetTriplet()1533 void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet()
1534 {
1535     QFETCH(int, architecture);
1536     QFETCH(int, os);
1537     QFETCH(int, osFlavor);
1538     QFETCH(int, binaryFormat);
1539     QFETCH(int, wordWidth);
1540 
1541     const Abi expectedAbi = Abi(Abi::Architecture(architecture),
1542                                 Abi::OS(os), Abi::OSFlavor(osFlavor),
1543                                 Abi::BinaryFormat(binaryFormat),
1544                                 static_cast<unsigned char>(wordWidth));
1545 
1546     QCOMPARE(Abi::abiFromTargetTriplet(QLatin1String(QTest::currentDataTag())), expectedAbi);
1547 }
1548 
testAbiUserOsFlavor_data()1549 void ProjectExplorer::ProjectExplorerPlugin::testAbiUserOsFlavor_data()
1550 {
1551     QTest::addColumn<int>("os");
1552     QTest::addColumn<QString>("osFlavorName");
1553     QTest::addColumn<int>("expectedFlavor");
1554 
1555     QTest::newRow("linux-generic flavor")
1556             << int(Abi::LinuxOS) << "generic" << int(Abi::GenericFlavor);
1557     QTest::newRow("linux-unknown flavor")
1558             << int(Abi::LinuxOS) << "unknown" << int(Abi::UnknownFlavor);
1559     QTest::newRow("windows-msvc2010 flavor")
1560             << int(Abi::WindowsOS) << "msvc2010" << int(Abi::WindowsMsvc2010Flavor);
1561     QTest::newRow("windows-unknown flavor")
1562             << int(Abi::WindowsOS) << "unknown" << int(Abi::UnknownFlavor);
1563 
1564     QTest::newRow("windows-msvc2100 flavor")
1565             << int(Abi::WindowsOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1566     QTest::newRow("linux-msvc2100 flavor")
1567             << int(Abi::LinuxOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1568 
1569     QTest::newRow("windows-msvc2100 flavor reregister")
1570             << int(Abi::WindowsOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1571     QTest::newRow("linux-msvc2100 flavor reregister")
1572             << int(Abi::LinuxOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1573     QTest::newRow("unix-msvc2100 flavor register")
1574             << int(Abi::UnixOS) << "msvc2100" << int(Abi::UnknownFlavor) + 1;
1575 }
1576 
testAbiUserOsFlavor()1577 void ProjectExplorer::ProjectExplorerPlugin::testAbiUserOsFlavor()
1578 {
1579     QFETCH(int, os);
1580     QFETCH(QString, osFlavorName);
1581     QFETCH(int, expectedFlavor);
1582 
1583     QMap<int, QList<Abi::OSFlavor>> osFlavorMap;
1584     for (int i = 0; i <= Abi::UnknownOS; ++i)
1585         osFlavorMap.insert(i, Abi::flavorsForOs(static_cast<ProjectExplorer::Abi::OS>(i)));
1586 
1587      Abi::OSFlavor osFlavor = Abi::registerOsFlavor({static_cast<Abi::OS>(os)}, osFlavorName);
1588      QCOMPARE(osFlavor, static_cast<Abi::OSFlavor>(expectedFlavor));
1589 
1590      for (int i = 0; i <= Abi::UnknownOS; ++i) {
1591          const QList<Abi::OSFlavor> flavors = Abi::flavorsForOs(static_cast<Abi::OS>(i));
1592          const QList<Abi::OSFlavor> previousFlavors = osFlavorMap.value(static_cast<Abi::OS>(i));
1593          const int previousCount = previousFlavors.count();
1594 
1595          if (i == os && previousCount != flavors.count()) {
1596              QVERIFY(flavors.count() == previousCount + 1);
1597              QVERIFY(flavors.contains(osFlavor));
1598              for (const Abi::OSFlavor &f : previousFlavors) {
1599                  if (f == osFlavor)
1600                      continue;
1601                  QVERIFY(previousFlavors.contains(f));
1602              }
1603          } else {
1604              QCOMPARE(flavors, previousFlavors);
1605          }
1606      }
1607 }
1608 
1609 
1610 #endif
1611