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

#include "abi.h"

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

namespace ProjectExplorer {

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
// --------------------------------------------------------------------------
// 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
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
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;
    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);
        if (image == 1) // Image is 1 for mingw and higher for MSVC (4.something in some encoding)
            flavor = Abi::WindowsMSysFlavor;
        else
            flavor = Abi::WindowsMsvcFlavor;
    }

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

    return result;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
static QList<Abi> abiOf(const QByteArray &data)
{
    QList<Abi> result;

    if (data.size() >= 20
            && static_cast<unsigned char>(data.at(0)) == 0x7f && static_cast<unsigned char>(data.at(1)) == 'E'
            && static_cast<unsigned char>(data.at(2)) == 'L' && static_cast<unsigned char>(data.at(3)) == 'F') {
        // ELF format:
        quint16 machine = (data.at(19) << 8) + data.at(18);
        switch (machine) {
        case 3: // EM_386
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 8: // EM_MIPS
            result.append(Abi(Abi::MipsArcitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 20: // EM_PPC
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 32));
            break;
        case 21: // EM_PPC64
            result.append(Abi(Abi::PowerPCArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            break;
        case 62: // EM_X86_64
            result.append(Abi(Abi::X86Architecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            break;
        case 50: // EM_IA_64
            result.append(Abi(Abi::ItaniumArchitecture, Abi::LinuxOS, Abi::GenericLinuxFlavor, Abi::ElfFormat, 64));
            break;
        default:
            ;;
        }
    } else if (data.size() >= 8
               && (static_cast<unsigned char>(data.at(0)) == 0xce || static_cast<unsigned char>(data.at(0)) == 0xcf)
               && static_cast<unsigned char>(data.at(1)) == 0xfa
               && static_cast<unsigned char>(data.at(2)) == 0xed && static_cast<unsigned char>(data.at(3)) == 0xfe) {
        // Mach-O format (Mac non-fat binary, 32 and 64bit magic)
        quint32 type = (data.at(7) << 24) + (data.at(6) << 16) + (data.at(5) << 8) + data.at(4);
        result.append(macAbiForCpu(type));
    } else if (data.size() >= 8
               && static_cast<unsigned char>(data.at(0)) == 0xca && static_cast<unsigned char>(data.at(1)) == 0xfe
               && static_cast<unsigned char>(data.at(2)) == 0xba && static_cast<unsigned char>(data.at(3)) == 0xbe) {
        // Mac fat binary:
        quint32 count = (data.at(4) << 24) + (data.at(5) << 16) + (data.at(6) << 8) + data.at(7);
        int pos = 8;
        for (quint32 i = 0; i < count; ++i) {
            if (data.size() <= pos + 4)
                break;

            quint32 type = (data.at(pos) << 24) + (data.at(pos + 1) << 16) + (data.at(pos + 2) << 8) + data.at(pos + 3);
            result.append(macAbiForCpu(type));
            pos += 20;
        }
    } else {
        // Windows PE
        // Windows can have its magic bytes everywhere...
163
        int pePos = data.indexOf(QByteArray("PE\0\0", 4));
Tobias Hunger's avatar
Tobias Hunger committed
164 165
        if (pePos >= 0)
            result = parseCoffHeader(data.mid(pePos + 4));
166 167 168 169 170 171 172 173
    }
    return result;
}

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

174
Abi::Abi(const Architecture &a, const OS &o,
hjk's avatar
hjk committed
175
         const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
176 177 178
    m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
{
    switch (m_os) {
hjk's avatar
hjk committed
179 180
    case ProjectExplorer::Abi::UnknownOS:
        m_osFlavor = UnknownFlavor;
181
        break;
hjk's avatar
hjk committed
182 183 184
    case ProjectExplorer::Abi::LinuxOS:
        if (m_osFlavor < GenericLinuxFlavor || m_osFlavor > MeegoLinuxFlavor)
            m_osFlavor = UnknownFlavor;
185
        break;
hjk's avatar
hjk committed
186 187 188
    case ProjectExplorer::Abi::MacOS:
        if (m_osFlavor < GenericMacFlavor || m_osFlavor > GenericMacFlavor)
            m_osFlavor = UnknownFlavor;
189
        break;
hjk's avatar
hjk committed
190 191 192
    case ProjectExplorer::Abi::SymbianOS:
        if (m_osFlavor < SymbianDeviceFlavor || m_osFlavor > SymbianEmulatorFlavor)
            m_osFlavor = UnknownFlavor;
193
        break;
hjk's avatar
hjk committed
194 195 196
    case ProjectExplorer::Abi::UnixOS:
        if (m_osFlavor < GenericUnixFlavor || m_osFlavor > GenericUnixFlavor)
            m_osFlavor = UnknownFlavor;
197
        break;
hjk's avatar
hjk committed
198 199 200
    case ProjectExplorer::Abi::WindowsOS:
        if (m_osFlavor < WindowsMsvcFlavor || m_osFlavor > WindowsCEFlavor)
            m_osFlavor = UnknownFlavor;
201 202 203 204 205
        break;
    }
}

Abi::Abi(const QString &abiString) :
hjk's avatar
hjk committed
206 207
    m_architecture(UnknownArchitecture), m_os(UnknownOS),
    m_osFlavor(UnknownFlavor), m_binaryFormat(UnknownFormat), m_wordWidth(0)
208 209 210 211
{
    QStringList abiParts = abiString.split(QLatin1Char('-'));
    if (abiParts.count() >= 1) {
        if (abiParts.at(0) == QLatin1String("unknown"))
hjk's avatar
hjk committed
212
            m_architecture = UnknownArchitecture;
213
        else if (abiParts.at(0) == QLatin1String("arm"))
hjk's avatar
hjk committed
214
            m_architecture = ArmArchitecture;
215
        else if (abiParts.at(0) == QLatin1String("x86"))
hjk's avatar
hjk committed
216
            m_architecture = X86Architecture;
217
        else if (abiParts.at(0) == QLatin1String("mips"))
hjk's avatar
hjk committed
218
            m_architecture = MipsArcitecture;
219
        else if (abiParts.at(0) == QLatin1String("ppc"))
hjk's avatar
hjk committed
220
            m_architecture = PowerPCArchitecture;
221
        else if (abiParts.at(0) == QLatin1String("itanium"))
hjk's avatar
hjk committed
222
            m_architecture = ItaniumArchitecture;
223 224 225 226 227 228
        else
            return;
    }

    if (abiParts.count() >= 2) {
        if (abiParts.at(1) == QLatin1String("unknown"))
hjk's avatar
hjk committed
229
            m_os = UnknownOS;
230
        else if (abiParts.at(1) == QLatin1String("linux"))
hjk's avatar
hjk committed
231
            m_os = LinuxOS;
232
        else if (abiParts.at(1) == QLatin1String("macos"))
hjk's avatar
hjk committed
233
            m_os = MacOS;
234
        else if (abiParts.at(1) == QLatin1String("symbian"))
hjk's avatar
hjk committed
235
            m_os = SymbianOS;
236
        else if (abiParts.at(1) == QLatin1String("unix"))
hjk's avatar
hjk committed
237
            m_os = UnixOS;
238
        else if (abiParts.at(1) == QLatin1String("windows"))
hjk's avatar
hjk committed
239
            m_os = WindowsOS;
240 241 242 243 244 245
        else
            return;
    }

    if (abiParts.count() >= 3) {
        if (abiParts.at(2) == QLatin1String("unknown"))
hjk's avatar
hjk committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
            m_osFlavor = UnknownFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == LinuxOS)
            m_osFlavor = GenericLinuxFlavor;
        else if (abiParts.at(2) == QLatin1String("maemo") && m_os == LinuxOS)
            m_osFlavor = MaemoLinuxFlavor;
        else if (abiParts.at(2) == QLatin1String("meego") && m_os == LinuxOS)
            m_osFlavor = MeegoLinuxFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == MacOS)
            m_osFlavor = GenericMacFlavor;
        else if (abiParts.at(2) == QLatin1String("device") && m_os == SymbianOS)
            m_osFlavor = SymbianDeviceFlavor;
        else if (abiParts.at(2) == QLatin1String("emulator") && m_os == SymbianOS)
            m_osFlavor = SymbianEmulatorFlavor;
        else if (abiParts.at(2) == QLatin1String("generic") && m_os == UnixOS)
            m_osFlavor = GenericUnixFlavor;
        else if (abiParts.at(2) == QLatin1String("msvc") && m_os == WindowsOS)
            m_osFlavor = WindowsMsvcFlavor;
        else if (abiParts.at(2) == QLatin1String("msys") && m_os == WindowsOS)
            m_osFlavor = WindowsMSysFlavor;
        else if (abiParts.at(2) == QLatin1String("ce") && m_os == WindowsOS)
            m_osFlavor = WindowsCEFlavor;
267 268 269 270 271 272
        else
            return;
    }

    if (abiParts.count() >= 4) {
        if (abiParts.at(3) == QLatin1String("unknown"))
hjk's avatar
hjk committed
273
            m_binaryFormat = UnknownFormat;
274
        else if (abiParts.at(3) == QLatin1String("elf"))
hjk's avatar
hjk committed
275
            m_binaryFormat = ElfFormat;
276
        else if (abiParts.at(3) == QLatin1String("pe"))
hjk's avatar
hjk committed
277
            m_binaryFormat = PEFormat;
278
        else if (abiParts.at(3) == QLatin1String("mach_o"))
hjk's avatar
hjk committed
279
            m_binaryFormat = MachOFormat;
280
        else if (abiParts.at(3) == QLatin1String("qml_rt"))
hjk's avatar
hjk committed
281
            m_binaryFormat = RuntimeQmlFormat;
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
        else
            return;
    }

    if (abiParts.count() >= 5) {
        const QString &bits = abiParts.at(4);
        if (!bits.endsWith(QLatin1String("bit")))
            return;

        bool ok = false;
        int bitCount = bits.left(bits.count() - 3).toInt(&ok);
        if (!ok)
            return;
        if (bitCount != 8 && bitCount != 16 && bitCount != 32 && bitCount != 64)
            return;
        m_wordWidth = bitCount;
    }
}

QString Abi::toString() const
{
    QStringList dn;
    dn << toString(m_architecture);
    dn << toString(m_os);
    dn << toString(m_osFlavor);
    dn << toString(m_binaryFormat);
    dn << toString(m_wordWidth);

    return dn.join(QLatin1String("-"));
}

bool Abi::operator == (const Abi &other) const
{
    return m_architecture == other.m_architecture
            && m_os == other.m_os
            && m_osFlavor == other.m_osFlavor
            && m_binaryFormat == other.m_binaryFormat
            && m_wordWidth == other.m_wordWidth;
}

bool Abi::isCompatibleWith(const Abi &other) const
{
hjk's avatar
hjk committed
324 325 326 327
    return (architecture() == other.architecture() || other.architecture() == Abi::UnknownArchitecture)
            && (os() == other.os() || other.os() == Abi::UnknownOS)
            && (osFlavor() == other.osFlavor() || other.osFlavor() == Abi::UnknownFlavor)
            && (binaryFormat() == other.binaryFormat() || other.binaryFormat() == Abi::UnknownFormat)
328 329 330 331 332
            && ((wordWidth() == other.wordWidth() && wordWidth() != 0) || other.wordWidth() == 0);
}

bool Abi::isValid() const
{
hjk's avatar
hjk committed
333 334 335 336
    return m_architecture != UnknownArchitecture
            && m_os != UnknownOS
            && m_osFlavor != UnknownFlavor
            && m_binaryFormat != UnknownFormat
337 338 339 340 341 342
            && m_wordWidth != 0;
}

QString Abi::toString(const Architecture &a)
{
    switch (a) {
hjk's avatar
hjk committed
343
    case ArmArchitecture:
344
        return QLatin1String("arm");
hjk's avatar
hjk committed
345
    case X86Architecture:
346
        return QLatin1String("x86");
hjk's avatar
hjk committed
347
    case MipsArcitecture:
348
        return QLatin1String("mips");
hjk's avatar
hjk committed
349
    case PowerPCArchitecture:
350
        return QLatin1String("ppc");
hjk's avatar
hjk committed
351
    case ItaniumArchitecture:
352
        return QLatin1String("itanium");
hjk's avatar
hjk committed
353
    case UnknownArchitecture: // fall through!
354 355 356 357 358 359 360 361
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const OS &o)
{
    switch (o) {
hjk's avatar
hjk committed
362
    case LinuxOS:
363
        return QLatin1String("linux");
hjk's avatar
hjk committed
364
    case MacOS:
365
        return QLatin1String("macos");
hjk's avatar
hjk committed
366
    case SymbianOS:
367
        return QLatin1String("symbian");
hjk's avatar
hjk committed
368
    case UnixOS:
369
        return QLatin1String("unix");
hjk's avatar
hjk committed
370
    case WindowsOS:
371
        return QLatin1String("windows");
hjk's avatar
hjk committed
372
    case UnknownOS: // fall through!
373 374 375 376 377
    default:
        return QLatin1String("unknown");
    };
}

hjk's avatar
hjk committed
378
QString Abi::toString(const OSFlavor &of)
379 380
{
    switch (of) {
hjk's avatar
hjk committed
381
    case ProjectExplorer::Abi::GenericLinuxFlavor:
382
        return QLatin1String("generic");
hjk's avatar
hjk committed
383
    case ProjectExplorer::Abi::MaemoLinuxFlavor:
384
        return QLatin1String("maemo");
hjk's avatar
hjk committed
385
    case ProjectExplorer::Abi::HarmattanLinuxFlavor:
386
        return QLatin1String("harmattan");
hjk's avatar
hjk committed
387
    case ProjectExplorer::Abi::MeegoLinuxFlavor:
388
        return QLatin1String("meego");
hjk's avatar
hjk committed
389
    case ProjectExplorer::Abi::GenericMacFlavor:
390
        return QLatin1String("generic");
hjk's avatar
hjk committed
391
    case ProjectExplorer::Abi::SymbianDeviceFlavor:
392
        return QLatin1String("device");
hjk's avatar
hjk committed
393
    case ProjectExplorer::Abi::SymbianEmulatorFlavor:
394
        return QLatin1String("emulator");
hjk's avatar
hjk committed
395
    case ProjectExplorer::Abi::GenericUnixFlavor:
396
        return QLatin1String("generic");
hjk's avatar
hjk committed
397
    case ProjectExplorer::Abi::WindowsMsvcFlavor:
398
        return QLatin1String("msvc");
hjk's avatar
hjk committed
399
    case ProjectExplorer::Abi::WindowsMSysFlavor:
400
        return QLatin1String("msys");
hjk's avatar
hjk committed
401
    case ProjectExplorer::Abi::WindowsCEFlavor:
402
        return QLatin1String("ce");
403
    case ProjectExplorer::Abi::UnknownFlavor: // fall through!
404 405 406 407 408 409 410 411
    default:
        return QLatin1String("unknown");
    }
}

QString Abi::toString(const BinaryFormat &bf)
{
    switch (bf) {
hjk's avatar
hjk committed
412
    case ElfFormat:
413
        return QLatin1String("elf");
hjk's avatar
hjk committed
414
    case PEFormat:
415
        return QLatin1String("pe");
hjk's avatar
hjk committed
416
    case MachOFormat:
417
        return QLatin1String("mach_o");
hjk's avatar
hjk committed
418
    case RuntimeQmlFormat:
419
        return QLatin1String("qml_rt");
hjk's avatar
hjk committed
420
    case UnknownFormat: // fall through!
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    default:
        return QLatin1String("unknown");
    }
}

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


Abi Abi::hostAbi()
{
    Architecture arch = QTC_CPU; // define set by qmake
hjk's avatar
hjk committed
437 438 439
    OS os = UnknownOS;
    OSFlavor subos = UnknownFlavor;
    BinaryFormat format = UnknownFormat;
440 441

#if defined (Q_OS_WIN)
hjk's avatar
hjk committed
442 443 444
    os = WindowsOS;
    subos = WindowsMsvcFlavor;
    format = PEFormat;
445
#elif defined (Q_OS_LINUX)
hjk's avatar
hjk committed
446 447 448
    os = LinuxOS;
    subos = GenericLinuxFlavor;
    format = ElfFormat;
449
#elif defined (Q_OS_MAC)
hjk's avatar
hjk committed
450 451 452
    os = MacOS;
    subos = GenericMacFlavor;
    format = MachOFormat;
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
#endif

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
468 469
    bool windowsStatic = path.endsWith(QLatin1String(".lib"));

470 471
    f.open(QFile::ReadOnly);
    QByteArray data = f.read(1024);
472 473 474 475 476 477 478 479 480 481 482
    if (data.size() >= 67
            && static_cast<unsigned char>(data.at(0)) == '!' && static_cast<unsigned char>(data.at(1)) == '<'
            && static_cast<unsigned char>(data.at(2)) == 'a' && static_cast<unsigned char>(data.at(3)) == 'r'
            && static_cast<unsigned char>(data.at(4)) == 'c' && static_cast<unsigned char>(data.at(5)) == 'h'
            && static_cast<unsigned char>(data.at(6)) == '>' && static_cast<unsigned char>(data.at(7)) == 0x0a) {
        // We got an ar file: possibly a static lib for ELF or Mach-O

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

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

488 489 490 491 492
            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));
493

Tobias Hunger's avatar
Tobias Hunger committed
494
            int toSkip = 60 + fileNameOffset;
495
            offset += fileLength.toInt() + 60 /* header */;
Tobias Hunger's avatar
Tobias Hunger committed
496 497 498 499 500 501
            if (windowsStatic) {
                if (fileName == QLatin1String("/0              "))
                    result = parseCoffHeader(data.mid(toSkip, 20));
            } else {
                result = abiOf(data.mid(toSkip));
            }
502
            if (!result.isEmpty())
503
                break;
504

Tobias Hunger's avatar
Tobias Hunger committed
505
            f.seek(offset + (offset % 2)); // ar is 2 byte alligned
506
            data = f.read(1024);
507
        }
508 509
    } else {
        result = abiOf(data);
510
    }
511 512
    f.close();

513 514 515 516
    return result;
}

} // namespace ProjectExplorer