/* * Author: Pawel Jablonski * E-mail: pj@xirx.net * WWW: xirx.net * GIT: git.xirx.net * * License: You can use this code however you like * but leave information about the original author. * Code is free for non-commercial and commercial use. */ #include "linker.h" /** * Constructor of the linker * * @param dataList Data list */ Linker::Linker(DataList &dataList) : dataList(dataList) { } /** * Link all parts of the application together and fill addresses * * @return Status of linking the application */ bool Linker::link() { QTextStream out(stdout); unsigned int ramSize = ((this->dataList.getMode() == DataList::Mode::Rom) ? ROM_RAM_SIZE : APP_RAM_SIZE); unsigned int flashSize = ((this->dataList.getMode() == DataList::Mode::Rom) ? ROM_FLASH_SIZE : APP_FLASH_SIZE); unsigned int ramOffset = ((this->dataList.getMode() == DataList::Mode::Rom) ? ROM_RAM_OFFSET : APP_RAM_OFFSET); unsigned int flashOffset = ((this->dataList.getMode() == DataList::Mode::Rom) ? ROM_FLASH_OFFSET : APP_FLASH_OFFSET); unsigned int ramUsage = 0; unsigned int flashUsage = 0; this->flashVector.clear(); // Move code to flash for(const QSharedPointer &data : this->dataList) { // Copy code to flash list for(const QSharedPointer &codeSection : data->getCodeSectionList()) { codeSection->setAddress(flashUsage + flashOffset); for(const QSharedPointer &codeItem : codeSection->getCodeItemList()) { codeItem->setAddress(flashUsage + flashOffset); flashUsage += static_cast(codeItem->getDataVector().length()); this->flashVector.append(codeItem->getDataVector()); } } } // Move data to ram and flash for(const QSharedPointer &data : this->dataList) { // Count ram for(const QSharedPointer &ramItem : data->getRamItemList()) { ramItem->setAddress(ramUsage + ramOffset); ramUsage += ramItem->getDataSize(); } // Copy flash to flash list for(const QSharedPointer &flashItem : data->getFlashItemList()) { flashItem->setAddress(flashUsage + flashOffset); flashUsage += static_cast(flashItem->getDataSize()); this->flashVector.append(flashItem->getDataVector()); } } if(ramUsage > ramSize) { out << QString("ERROR: %1 of %2 (%3%) bytes of RAM are used\n").arg(ramUsage).arg(ramSize).arg(QString::asprintf("%.1f", ((static_cast(ramUsage) / static_cast(ramSize)) * 100.0))); return(false); } if(flashUsage > flashSize) { out << QString("ERROR: %1 of %2 (%3%) bytes of FLASH are used\n").arg(flashUsage).arg(flashSize).arg(QString::asprintf("%.1f", ((static_cast(flashUsage) / static_cast(flashSize)) * 100.0))); return(false); } // Update addresses for(const QSharedPointer &data : this->dataList) { for(const QSharedPointer &codeSection : data->getCodeSectionList()) { for(const QSharedPointer &codeItem : codeSection->getCodeItemList()) { QHashIterator it(codeItem->getToFillUpHash()); while(it.hasNext()) { it.next(); const QString &label = it.key(); unsigned int offset = it.value(); unsigned int address; offset += (codeItem->getAddress() - flashOffset); bool isOk = false; if((label.left(1) == ".") || (label.left(1) == "*")) { if(this->findAddress(*data, label, address)) { this->flashVector.replace(static_cast(offset), static_cast(address % 256)); this->flashVector.replace(static_cast(offset + 1), static_cast(address / 256)); isOk = true; } } else if(!label.leftRef(3).compare(QString("low"), Qt::CaseInsensitive)) { QString tLabel = label.mid(4, (label.length() - 5)); if(this->findAddress(*data, tLabel, address)) { this->flashVector.replace(static_cast(offset), static_cast(address % 256)); isOk = true; } } else if(!label.leftRef(4).compare(QString("high"), Qt::CaseInsensitive)) { QString tLabel = label.mid(5, (label.length() - 6)); if(this->findAddress(*data, tLabel, address)) { this->flashVector.replace(static_cast(offset), static_cast(address / 256)); isOk = true; } } if(!isOk) { out << QString("ERROR: Unknown label name \"%1\" at line %2 in file: \"%3\"\n").arg(label).arg(codeItem->getLine()).arg(data->getFileName()); return(false); } } } } } out << QString("INFO: %1 of %2 (%3%) bytes of RAM are used\n").arg(ramUsage).arg(ramSize).arg(QString::asprintf("%.1f", ((static_cast(ramUsage) / static_cast(ramSize)) * 100.0))); out << QString("INFO: %1 of %2 (%3%) bytes of FLASH are used\n").arg(flashUsage).arg(flashSize).arg(QString::asprintf("%.1f", ((static_cast(flashUsage) / static_cast(flashSize)) * 100.0))); return(true); } /** * Save the binary to the file * * @param path Path to the binary file * @param pathMap Path to the map file * * @return Status of saving data to the file */ bool Linker::saveFile(const QString &path, const QString &pathMap) { if(!this->flashVector.empty()) { if(this->flashVector.size() > ROM_FLASH_SIZE) { return(false); } if(this->dataList.getMode() == DataList::Mode::Rom) { this->flashVector.append(QVector((ROM_FLASH_SIZE - this->flashVector.size()), 0)); } QFile file(path); if(file.open(QIODevice::WriteOnly)) { file.write(reinterpret_cast(this->flashVector.constData()), this->flashVector.size()); file.close(); } else { return(false); } } else { return(false); } if(this->dataList.getMode() == DataList::Mode::Rom) { QFile file(pathMap); if(file.open(QIODevice::WriteOnly)) { for(const QSharedPointer &ramItem : this->dataList.getExternalRamItemList()) { QString line = QString("external var %1 = 0x%2\n").arg(ramItem->getName()).arg(ramItem->getAddress(), 4, 16, QChar('0')); file.write(line.toUtf8()); } for(const QSharedPointer &flashItem : this->dataList.getExternalFlashItemList()) { QString line = QString("external const %1 = 0x%2\n").arg(flashItem->getName()).arg(flashItem->getAddress(), 4, 16, QChar('0')); file.write(line.toUtf8()); } for(const QSharedPointer &codeSection : this->dataList.getExternalCodeSectionList()) { QString line = QString("%1 = 0x%2\n").arg(codeSection->getName()).arg(codeSection->getAddress(), 4, 16, QChar('0')); file.write(line.toUtf8()); } file.close(); } else { return(false); } } return(true); } /** * Find the correct address in memory * * @param data Data of the compiled application * @param label Label name to find * @param address Found address * * @return Status of searching the address */ bool Linker::findAddress(Data &data, const QString &label, unsigned int &address) { QString name; if(label.contains(QRegularExpression(R"(^(\.[0-9a-zA-Z_-]+)$)"))) { name = label; return(this->findCodeAddress(data, name, address)); } else if(label.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+)$)"))) { name = label.mid(1); return(this->findMemoryAddress(data, name, address)); } else if(label.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+\[[0-9]+\])$)"))) { int cOffset = label.indexOf("["); name = label.mid(1, (cOffset - 1)); unsigned int offset = label.midRef((cOffset + 1), (label.length() - cOffset - 2)).toUInt(); if(this->findMemoryAddress(data, name, address)) { address += offset; return(true); } } return(false); } /** * Find the correct address of code in memory * * @param data Data of the compiled application * @param name Name to find * @param address Found address * * @return Status of searching the address */ bool Linker::findCodeAddress(Data &data, const QString &name, unsigned int &address) { for(const QSharedPointer &codeSection : data.getCodeSectionList()) { if(!codeSection->getName().compare(name, Qt::CaseInsensitive)) { address = codeSection->getAddress(); return(true); } } for(const QSharedPointer &codeSection : this->dataList.getGlobalCodeSectionList()) { if(!codeSection->getName().compare(name, Qt::CaseInsensitive)) { address = codeSection->getAddress(); return(true); } } return(false); } /** * Find the correct address of variable or const in memory * * @param data Data of the compiled application * @param name Name to find * @param address Found address * * @return Status of searching the address */ bool Linker::findMemoryAddress(Data &data, const QString &name, unsigned int &address) { for(const QSharedPointer &ramItem : data.getRamItemList()) { if(!ramItem->getName().compare(name, Qt::CaseInsensitive)) { address = ramItem->getAddress(); return(true); } } for(const QSharedPointer &flashItem : data.getFlashItemList()) { if(!flashItem->getName().compare(name, Qt::CaseInsensitive)) { address = flashItem->getAddress(); return(true); } } for(const QSharedPointer &ramItem : this->dataList.getGlobalRamItemList()) { if(!ramItem->getName().compare(name, Qt::CaseInsensitive)) { address = ramItem->getAddress(); return(true); } } for(const QSharedPointer &flashItem : this->dataList.getGlobalFlashItemList()) { if(!flashItem->getName().compare(name, Qt::CaseInsensitive)) { address = flashItem->getAddress(); return(true); } } return(false); }