/*************************************************************************** * Copyright (C) 2006 Nicolas Hadacek * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "coff_object.h" #include "common/common/misc.h" #include "devices/list/device_list.h" #include "devices/base/device_group.h" #include "devices/pic/base/pic_register.h" #include "coff_data.h" #include "common/global/pfile.h" //---------------------------------------------------------------------------- bool Coff::getName(const TQByteArray &data, uint &offset, uint nbChars, uint stringTableOffset, Log::Base &log, TQString &name) { TQ_UINT32 v; if ( !getULong(data, offset, 4, log, v) ) return false; if ( v!=0 ) { // name is not in string table offset -= 4; return getString(data, offset, nbChars, log, name); } if ( !getULong(data, offset, 4, log, v) ) return false; // ### do a sanity check here name = TQString(data.data()+stringTableOffset+v); return true; } const Coff::OptHeaderFormat::Data Coff::OptHeaderFormat::DATA[Nb_Types] = { { 0, I18N_NOOP("Old Microchip") }, { 0, I18N_NOOP("New Microchip") }, { 0, I18N_NOOP("PICC Compiler") }, { 0, I18N_NOOP("CCS Compiler") } }; const Coff::OptHeaderData Coff::OPT_HEADER_DATA[] = { { 0x5678, OptHeaderFormat::OldMicrochip, true }, { 0x0000, OptHeaderFormat::NewMicrochip, true }, { 0x0001, OptHeaderFormat::NewMicrochip, true }, // PIC30 with debug { 0x1388, OptHeaderFormat::Picc, false }, // PICC { 0x1B78, OptHeaderFormat::Ccsc, false }, // CCSC { 0x0000, OptHeaderFormat::Nb_Types, false } }; //---------------------------------------------------------------------------- const Coff::AuxSymbolType::Data Coff::AuxSymbolType::DATA[Nb_Types] = { { 0, I18N_NOOP("Direct") }, { 0, I18N_NOOP("File") }, { 0, I18N_NOOP("Indentifier") }, { 0, I18N_NOOP("Section") } }; Coff::AuxSymbol *Coff::AuxSymbol::factory(const Object &object, AuxSymbolType type, const TQByteArray &data, uint offset, uint stringTableOffset, Log::Base &log) { switch (type.type()) { case AuxSymbolType::Direct: return new AuxSymbolDirect(object, data, offset, stringTableOffset, log); case AuxSymbolType::File: return new AuxSymbolFile(object, data, offset, stringTableOffset, log); case AuxSymbolType::Identifier: return new AuxSymbolIdentifier(object, data, offset, stringTableOffset, log); case AuxSymbolType::Section: return new AuxSymbolSection(object, data, offset, stringTableOffset, log); case AuxSymbolType::Nb_Types: return new AuxSymbolUnknown(object); } Q_ASSERT(false); return 0; } Coff::AuxSymbolDirect::AuxSymbolDirect(const Object &object, const TQByteArray &data, uint start, uint stringTableOffset, Log::Base &log) : AuxSymbol(object) { uint offset = start; TQ_UINT32 v; if ( !getULong(data, offset, 1, log, v) ) return; _command = v; if ( !getULong(data, offset, 4, log, v) ) return; _string = TQString(data.data()+stringTableOffset+v); } Coff::AuxSymbolFile::AuxSymbolFile(const Object &object, const TQByteArray &data, uint start, uint stringTableOffset, Log::Base &log) : AuxSymbol(object) { uint offset = start; TQ_UINT32 v; if ( object.format()==Format::PIC30 ) { if ( !getName(data, offset, 14, stringTableOffset, log, _filename) ) return; _line = 0; } else { if ( !getULong(data, offset, 4, log, v) ) return; _filename = TQString(data.data()+stringTableOffset+v); if ( !getULong(data, offset, 4, log, v) ) return; _line = v; } } Coff::AuxSymbolIdentifier::AuxSymbolIdentifier(const Object &object, const TQByteArray &data, uint start, uint stringTableOffset, Log::Base &log) : AuxSymbol(object) { uint offset = start; TQ_UINT32 v; if ( !getULong(data, offset, 4, log, v) ) return; _string = TQString(data.data()+stringTableOffset+v); } Coff::AuxSymbolSection::AuxSymbolSection(const Object &object, const TQByteArray &data, uint start, uint, Log::Base &log) : AuxSymbol(object) { uint offset = start; TQ_UINT32 v; if ( !getULong(data, offset, 4, log, v) ) return; _length = v; if ( !getULong(data, offset, 2, log, v) ) return; _nbRelocations = v; if ( !getULong(data, offset, 2, log, v) ) return; _nbLineNumbers = v; } //---------------------------------------------------------------------------- const Coff::SymbolSectionType::Data Coff::SymbolSectionType::DATA[Nb_Types] = { { 0, I18N_NOOP("Inside Section") }, { 0, I18N_NOOP("Undefined Section") }, { 0, I18N_NOOP("Absolute Value") }, { 0, I18N_NOOP("Debug Symbol") } }; const Coff::SymbolClass::Data Coff::SymbolClass::DATA[Nb_Types] = { { 0, I18N_NOOP("Automatic Variable"), 1 }, { 0, I18N_NOOP("External Symbol"), 2 }, { 0, I18N_NOOP("Static Symbol"), 3 }, { 0, I18N_NOOP("Register Variable"), 4 }, { 0, I18N_NOOP("External Definition"), 5 }, { 0, I18N_NOOP("Label"), 6 }, { 0, I18N_NOOP("Undefined Label"), 7 }, { 0, I18N_NOOP("Member of Structure"), 8 }, { 0, I18N_NOOP("Function Argument"), 9 }, { 0, I18N_NOOP("Structure Tag"), 10 }, { 0, I18N_NOOP("Member of Union"), 11 }, { 0, I18N_NOOP("Union Tag"), 12 }, { 0, I18N_NOOP("Type Definition"), 13 }, { 0, I18N_NOOP("Undefined Static"), 14 }, { 0, I18N_NOOP("Enumeration Tag"), 15 }, { 0, I18N_NOOP("Member of Enumeration"), 16 }, { 0, I18N_NOOP("Register Parameter"), 17 }, { 0, I18N_NOOP("Bit Field"), 18 }, { 0, I18N_NOOP("Auto Argument"), 19 }, { 0, I18N_NOOP("Dummy Entry (end of block)"), 20 }, { 0, I18N_NOOP("Beginning or End of Block"), 100 }, { 0, I18N_NOOP("Beginning or End of Function"), 101 }, { 0, I18N_NOOP("End of Structure"), 102 }, { 0, I18N_NOOP("Filename"), 103 }, { 0, I18N_NOOP("Line Number"), 104 }, { 0, I18N_NOOP("Duplicate Tag"), 105 }, { 0, I18N_NOOP("Section"), 109 } }; const Coff::SymbolType::Data Coff::SymbolType::DATA[Nb_Types] = { { 0, I18N_NOOP("Void"), 0x0001 }, { 0, I18N_NOOP("Char"), 0x0010 }, { 0, I18N_NOOP("Short"), 0x0011 }, { 0, I18N_NOOP("Int"), 0x0100 }, { 0, I18N_NOOP("Long"), 0x0101 }, { 0, I18N_NOOP("Float"), 0x0110 }, { 0, I18N_NOOP("Double"), 0x0111 }, { 0, I18N_NOOP("Structure"), 0x1000 }, { 0, I18N_NOOP("Union"), 0x1001 }, { 0, I18N_NOOP("Enumeration"), 0x1010 }, { 0, I18N_NOOP("Member Of Enumeration"), 0x1011 }, { 0, I18N_NOOP("Unsigned Char"), 0x1100 }, { 0, I18N_NOOP("Unsigned Short"), 0x1101 }, { 0, I18N_NOOP("Unsigned Int"), 0x1110 }, { 0, I18N_NOOP("Unsigned Long"), 0x1111 }, { 0, I18N_NOOP("Long Double"), 0x10000 } }; const Coff::SymbolDerivedType::Data Coff::SymbolDerivedType::DATA[Nb_Types] = { { 0, I18N_NOOP("Pointer"), 0x010000 }, { 0, I18N_NOOP("Function"), 0x100000 }, { 0, I18N_NOOP("Array"), 0x110000 } }; Coff::Symbol::Symbol(const Object &object, const TQByteArray &data, uint start, uint stringTableOffset, const TQString &lastFilename, Log::Base &log) : BaseSymbol(object) { uint offset = start; TQ_UINT32 v; if ( !getName(data, offset, 8, stringTableOffset, log, _name) ) return; if ( !getULong(data, offset, 4, log, v) ) return; _value = v; if ( !getULong(data, offset, 2, log, v) ) return; _section = v; uint nb = (object.format()==Format::NewMicrochip ? 4 : 2); if ( !getULong(data, offset, nb, log, v) ) return; _type = SymbolType::Nb_Types; FOR_EACH(SymbolType, type) if ( (v & 0x001111)==type.data().id ) { _type = type; break; } _dtype = SymbolDerivedType::Nb_Types; FOR_EACH(SymbolDerivedType, dtype) if ( (v & 0x110000)==dtype.data().id ) { _dtype = dtype; break; } if ( !getULong(data, offset, 1, log, v) ) return; _sclass = SymbolClass::Nb_Types; FOR_EACH(SymbolClass, sclass) if ( v==sclass.data().id ) { _sclass = sclass; break; } if ( !getULong(data, offset, 1, log, v) ) return; uint nbAux = v; //qDebug("symbol: %s value=%s type=%i dtype=%i class=%i nbAux=%i section=%i", _name.latin1(), toHexLabel(_value, 4).latin1(), _type, _dtype, _class, nbAux, _section); AuxSymbolType auxType = AuxSymbolType::Nb_Types; if ( _name==".direct" ) auxType = AuxSymbolType::Direct; else if ( _name==".ident" ) auxType = AuxSymbolType::Identifier; else if ( _sclass==SymbolClass::Filename ) auxType = AuxSymbolType::File; else if ( _sclass==SymbolClass::Section ) auxType = AuxSymbolType::Section; if ( auxType!=AuxSymbolType::Nb_Types && nbAux==0 ) log.log(Log::LineType::Warning, i18n("Symbol without needed auxilliary symbol (type=%1)").tqarg(auxType.type())); Q_ASSERT( (offset-start)==object.size(SymbolSize) ); _aux.resize(nbAux); for (uint i=0; itype()==AuxSymbolType::File ) _filename = static_cast(_aux[i])->filename(); } Coff::SymbolSectionType Coff::Symbol::sectionType() const { switch (_section) { case 0x0000: return SymbolSectionType::UndefinedSection; case 0xFFFF: return SymbolSectionType::AbsoluteValue; case 0xFFFE: return SymbolSectionType::DebugSymbol; } return SymbolSectionType::InsideSection; } //---------------------------------------------------------------------------- Coff::Relocation::Relocation(const Object &object, const Section §ion, const TQByteArray &data, uint start, Log::Base &log) : Element(object), _symbol(0) { uint offset = start; TQ_UINT32 v; if ( !getULong(data, offset, 4, log, v) ) return; _address = v; if ( _address>section.size() ) log.log(Log::LineType::Warning, i18n("Relocation address beyong section size: %1/%2").tqarg(v).tqarg(section.size())); if ( !getULong(data, offset, 4, log, v) ) return; if ( v>=object.nbSymbols() ) { log.log(Log::LineType::Error, i18n("Relocation has unknown symbol: %1").tqarg(v)); return; } if ( object.symbol(v)->isAuxSymbol() ) { log.log(Log::LineType::Error, i18n("Relocation is an auxiliary symbol: %1").tqarg(v)); return; } _symbol = static_cast(object.symbol(v)); if ( object.format()!=Format::PIC30 ) { if ( !getULong(data, offset, 2, log, v) ) return; _offset = short(v); } if ( !getULong(data, offset, 2, log, v) ) return; _type = v; //qDebug("reloc %s: address=%s offset=%i type=%i", _symbol->_name.latin1(), toHexLabel(_address, 4).latin1(), _offset, _type); } //---------------------------------------------------------------------------- Coff::CodeLine::CodeLine(const Object &object, const Section §ion, const TQByteArray &data, uint start, const TQString &lastFilename, Log::Base &log) : Element(object), _section(section), _symbol(0) { uint offset = start; TQ_UINT32 v; if ( !getULong(data, offset, 4, log, v) ) return; uint tmp = v; if ( object.format()==Format::PIC30 ) { if ( !getULong(data, offset, 4, log, v) ) return; _line = v; if ( _line!=0 ) { _address = tmp; _filename = lastFilename; //qDebug("code line %i: %s", _line, toHexLabel(_address, nbChars(_address)).latin1()); } else { if ( tmp>=object.nbSymbols() ) { log.log(Log::LineType::Error, i18n("Codeline has unknown symbol: %1").tqarg(tmp)); return; } if ( object.symbol(tmp)->isAuxSymbol() ) { log.log(Log::LineType::Error, i18n("Codeline is an auxiliary symbol: %1").tqarg(tmp)); return; } _symbol = static_cast(object.symbol(tmp)); _filename = _symbol->filename(); //qDebug("code line %i: %s", _line, _symbol->_name.latin1()); } } else { if ( tmp>=object.nbSymbols() ) { log.log(Log::LineType::Error, i18n("Codeline has unknown symbol: %1").tqarg(tmp)); return; } if ( object.symbol(tmp)->isAuxSymbol() ) { log.log(Log::LineType::Error, i18n("Codeline is an auxiliary symbol: %1").tqarg(tmp)); return; } _symbol = static_cast(object.symbol(tmp)); _filename = _symbol->filename(); if ( !getULong(data, offset, 2, log, v) ) return; _line = v; if ( object.optHeaderFormat()==OptHeaderFormat::Picc && _line>=2 ) _line -= 2; // #### ?? if ( !getULong(data, offset, 4, log, v) ) return; _address = v; if ( !getULong(data, offset, 2, log, v) ) return; // flags if ( !getULong(data, offset, 4, log, v) ) return; // function index //qDebug("code line %i: %s", _line, toHexLabel(_address, nbChars(_address)).latin1()); } // if ( _symbol && _symbol->_class!=Symbol::CFile ) // log.log(Log::LineType::Warning, i18n("Line without file symbol associated (%1:%2 %3).") // .tqarg(_section._name).tqarg(toHexLabel(_address, nbChars(_address))).tqarg(_symbol->_class)); } //---------------------------------------------------------------------------- const Coff::SectionType::Data Coff::SectionType::DATA[Nb_Types] = { { 0, I18N_NOOP("Config") }, { 0, I18N_NOOP("Device ID") }, { 0, I18N_NOOP("User IDs") }, { 0, I18N_NOOP("Uninitialized Data") }, { 0, I18N_NOOP("Initialized Data") }, { 0, I18N_NOOP("Rom Data") }, { 0, I18N_NOOP("Code") } }; Coff::Section::Section(const Device::Data &device, const Object &object, const TQByteArray &data, uint start, uint stringTableOffset, Log::Base &log) : Element(object) { uint offset = start; TQ_UINT32 v; if ( !getName(data, offset, 8, stringTableOffset, log, _name) ) return; if ( !getULong(data, offset, 4, log, v) ) return; _address = v; if ( !getULong(data, offset, 4, log, v) ) return; //if ( _address!=v ) log.log(Log::LineType::Warning, i18n("Virtual address (%1) does not match physical address (%2) in %3.") // .tqarg(toHexLabel(v, 4)).tqarg(toHexLabel(_address, 4)).tqarg(_name)); if ( !getULong(data, offset, 4, log, v) ) return; _size = v; if ( !getULong(data, offset, 4, log, v) ) return; uint dataOffset = v; if ( !getULong(data, offset, 4, log, v) ) return; uint relocationOffset = v; if ( !getULong(data, offset, 4, log, v) ) return; uint lineNumberOffset = v; if ( !getULong(data, offset, 2, log, v) ) return; uint nbRelocations = v; if ( !getULong(data, offset, 2, log, v) ) return; uint nbLineNumbers = v; if ( !getULong(data, offset, 4, log, v) ) return; _flags = v; // read data Q_ASSERT ( device.group().name()=="pic" ); const Pic::Data &pdata = static_cast(device); //qDebug("section %s: address=%s size=%i flags=%i", _name.data(), toHexLabel(_address, 4).latin1(), _size, int(_flags)); if ( _size!=0 && dataOffset!=0 ) { uint inc = 1; uint nbWords = _size; uint nbBytesWord = 1; bool b = ( (_flags & FText) || (_flags & FDataRom) ); if (b) { nbBytesWord = pdata.nbBytesWord(Pic::MemoryRangeType::Code); nbWords /= nbBytesWord; inc = pdata.addressIncrement(Pic::MemoryRangeType::Code); } for (uint i=0; ifilename(); lineNumberOffset += object.size(LineNumberSize); if ( log.hasError() ) return; } } } Coff::Section::~Section() { for (uint i=0; ifilename().isEmpty() ) lastFilename = s->filename(); _symbols[i] = s; _msymbols[s->name()] = s; _symbolOffset += size(SymbolSize); for (uint k=0; kauxSymbols().count()); k++) { i++; _symbols[i] = s->auxSymbols()[k]; _symbolOffset += size(SymbolSize); } } // parse sections Q_ASSERT( offset==(size(HeaderSize) + size(OptHeaderSize)) ); _sections.resize(_nbSections); for (uint i=0; i<_nbSections; i++) { _sections[i] = new Section(*_device, *this, data, offset, stringTableOffset, log); offset += size(SectionHeaderSize); if ( log.hasError() ) return false; } // extract filenames for (uint i=0; i<_nbSymbols; i++) { if ( _symbols[i]==0 || _symbols[i]->isAuxSymbol() ) continue; TQString s = static_cast(_symbols[i])->filename(); if ( s.isEmpty() || s=="fake" || _filenames.contains(s) ) continue; _filenames.append(s); } // extract variables for (uint i=0; iisAuxSymbol() ) continue; const Symbol *sym = static_cast(_symbols[i]); if ( sym->symbolClass()!=SymbolClass::Static ) continue; if ( sym->sectionType()!=SymbolSectionType::InsideSection ) continue; TQString name = sym->name(); if ( name.startsWith("_$_") || name.startsWith("__") || name.startsWith(".") ) continue; // special variables (?) _variables[name] = sym->value() & 0xFFF; // #### ?? } return true; } bool Coff::Object::parseHeader(const TQByteArray &data, uint &offset, Log::Base &log) { TQ_UINT32 v; if ( !getULong(data, offset, 2, log, v) ) return false; _nbSections = v; if ( !getULong(data, offset, 4, log, v) ) return false; // time_t time = v; if ( !getULong(data, offset, 4, log, v) ) return false; _symbolOffset = v; if ( !getULong(data, offset, 4, log, v) ) return false; _nbSymbols = v; if ( !getULong(data, offset, 2, log, v) ) return false; if ( v!=size(OptHeaderSize) ) { log.log(Log::LineType::Error, i18n("Optionnal header size is not %1: %2").tqarg(size(OptHeaderSize)).tqarg(v)); return false; } if ( !getULong(data, offset, 2, log, v) ) return false; _flags = Flags(v); return true; } bool Coff::Object::parseOptionnalHeader(const TQByteArray &data, uint &offset, Log::Base &log) { TQ_UINT32 v; int nb = (_format==Format::NewMicrochip ? 4 : 2); if ( !getULong(data, offset, nb, log, v) ) return false; // version stamp if ( _format==Format::PIC30 ) { if ( !getULong(data, offset, 4, log, v) ) return false; // text size in bytes, padded to firmware boundary if ( !getULong(data, offset, 4, log, v) ) return false; // initialized data " if ( !getULong(data, offset, 4, log, v) ) return false; // uninitialized data " if ( !getULong(data, offset, 4, log, v) ) return false; // entry point if ( !getULong(data, offset, 4, log, v) ) return false; // offset of text if ( !getULong(data, offset, 4, log, v) ) return false; // offset of data if ( _device==0 ) _device = Device::lister().data("30F2010"); // for e.g. } else { if ( !getULong(data, offset, 4, log, v) ) return false; // #### at least for C18 compiler, it can be compiled for generic processor: in such case // the pic type will be 18C452 in non-extended mode and 18F4620 for extended mode... TQString name = Coff::findId(v); log.log(Log::DebugLevel::Normal, TQString("Device name: \"%1\"").tqarg(name)); if ( name.isEmpty() ) { log.log(Log::DebugLevel::Normal, TQString("Unknown processor type: %1").tqarg(toHexLabel(v, 4))); log.log(Log::LineType::Error, i18n("Could not determine processor (%1).").tqarg(toHexLabel(v, 4))); return false; } else if ( _device==0 ) _device = Device::lister().data(name); else if ( name!=_device->name() ) log.log(Log::DebugLevel::Normal, TQString("Different processor name: %1").tqarg(name)); if ( !getULong(data, offset, 4, log, v) ) return false; const Pic::Data *pdata = static_cast(_device); if (pdata) { uint nbBits = pdata->nbBitsWord(Pic::MemoryRangeType::Code) / pdata->addressIncrement(Pic::MemoryRangeType::Code); if ( v!=nbBits ) log.log(Log::DebugLevel::Normal, TQString("Rom width is not %1: %2").tqarg(nbBits).tqarg(v)); } if ( !getULong(data, offset, 4, log, v) ) return false; if (pdata) { uint nbBits = pdata->registersData().nbBits(); if ( v!=nbBits ) log.log(Log::DebugLevel::Normal, TQString("Ram width is not %1: %2").tqarg(nbBits).tqarg(v)); } } return true; } Coff::Object::~Object() { for (uint i=0; i::const_iterator it; for (it=_variables.begin(); it!=_variables.end(); ++it) if ( it.data()==address ) return it.key(); return TQString(); } //---------------------------------------------------------------------------- TQValueVector Pic::sfrList(const Pic::Data &data) { TQValueVector list; const Pic::RegistersData &rdata = data.registersData(); for (uint i=0; i::const_iterator it; for (it=rdata.combined.begin(); it!=rdata.combined.end(); ++it) { Register::TypeData td(it.key(), it.data().address, it.data().nbChars); list.append(Pic::RegisterNameData(it.key(), td)); } if ( data.architecture()==Pic::Architecture::P16X ) list.append(Pic::RegisterNameData("WREG", Register::TypeData("WREG", rdata.nbChars()))); qHeapSort(list); return list; } TQValueVector Pic::gprList(const Pic::Data &data, const Coff::Object *coff) { TQValueVector list; const Pic::RegistersData &rdata = data.registersData(); for (uint i=0; ivariableName(address); if ( !name.isEmpty() ) s += " (" + name + ")"; } Register::TypeData rtd(address, rdata.nbChars()); list.append(Pic::RegisterNameData(s, rtd)); } } return list; } TQValueVector Pic::variableList(const Pic::Data &data, const Coff::Object &coff) { TQValueVector list; const Pic::RegistersData &rdata = data.registersData(); TQMap variables = coff.variables(); TQMap::const_iterator vit; for (vit=variables.begin(); vit!=variables.end(); ++vit) { Register::TypeData rtd(vit.data(), rdata.nbChars()); list.append(Pic::RegisterNameData(vit.key() + " (" + toHexLabel(vit.data(), rdata.nbCharsAddress()) + ")", rtd)); } qHeapSort(list); return list; }