/*
* 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> &data : this->dataList)
{
// Copy code to flash list
for(const QSharedPointer<CodeSection> &codeSection : data->getCodeSectionList())
{
codeSection->setAddress(flashUsage + flashOffset);
for(const QSharedPointer<CodeItem> &codeItem : codeSection->getCodeItemList())
{
codeItem->setAddress(flashUsage + flashOffset);
flashUsage += static_cast<unsigned int>(codeItem->getDataVector().length());
this->flashVector.append(codeItem->getDataVector());
}
}
}
// Move data to ram and flash
for(const QSharedPointer<Data> &data : this->dataList)
{
// Count ram
for(const QSharedPointer<RamItem> &ramItem : data->getRamItemList())
{
ramItem->setAddress(ramUsage + ramOffset);
ramUsage += ramItem->getDataSize();
}
// Copy flash to flash list
for(const QSharedPointer<FlashItem> &flashItem : data->getFlashItemList())
{
flashItem->setAddress(flashUsage + flashOffset);
flashUsage += static_cast<unsigned int>(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<double>(ramUsage) / static_cast<double>(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<double>(flashUsage) / static_cast<double>(flashSize)) * 100.0)));
return(false);
}
// Update addresses
for(const QSharedPointer<Data> &data : this->dataList)
{
for(const QSharedPointer<CodeSection> &codeSection : data->getCodeSectionList())
{
for(const QSharedPointer<CodeItem> &codeItem : codeSection->getCodeItemList())
{
QHashIterator<QString, unsigned int> 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<int>(offset), static_cast<unsigned char>(address % 256));
this->flashVector.replace(static_cast<int>(offset + 1), static_cast<unsigned char>(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<int>(offset), static_cast<unsigned char>(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<int>(offset), static_cast<unsigned char>(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<double>(ramUsage) / static_cast<double>(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<double>(flashUsage) / static_cast<double>(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<unsigned char>((ROM_FLASH_SIZE - this->flashVector.size()), 0));
}
QFile file(path);
if(file.open(QIODevice::WriteOnly))
{
file.write(reinterpret_cast<const char *>(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> &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> &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> &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> &codeSection : data.getCodeSectionList())
{
if(!codeSection->getName().compare(name, Qt::CaseInsensitive))
{
address = codeSection->getAddress();
return(true);
}
}
for(const QSharedPointer<CodeSection> &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> &ramItem : data.getRamItemList())
{
if(!ramItem->getName().compare(name, Qt::CaseInsensitive))
{
address = ramItem->getAddress();
return(true);
}
}
for(const QSharedPointer<FlashItem> &flashItem : data.getFlashItemList())
{
if(!flashItem->getName().compare(name, Qt::CaseInsensitive))
{
address = flashItem->getAddress();
return(true);
}
}
for(const QSharedPointer<RamItem> &ramItem : this->dataList.getGlobalRamItemList())
{
if(!ramItem->getName().compare(name, Qt::CaseInsensitive))
{
address = ramItem->getAddress();
return(true);
}
}
for(const QSharedPointer<FlashItem> &flashItem : this->dataList.getGlobalFlashItemList())
{
if(!flashItem->getName().compare(name, Qt::CaseInsensitive))
{
address = flashItem->getAddress();
return(true);
}
}
return(false);
}