/* * File: SRecordSourceFile.cpp * * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. * See included license file for license details. */ #include "SRecordSourceFile.h" #include "Logging.h" #include "smart_ptr.h" #include #include enum { //! Size in bytes of the buffer used to collect S-record data records //! before adding them to the executable image. Currently 64KB. COLLECTION_BUFFER_SIZE = 64 * 1024 }; using namespace elftosb; SRecordSourceFile::SRecordSourceFile(const std::string & path) : SourceFile(path), m_image(0), m_hasEntryRecord(false) { } bool SRecordSourceFile::isSRecordFile(std::istream & stream) { StSRecordFile srec(stream); return srec.isSRecordFile(); } void SRecordSourceFile::open() { SourceFile::open(); // create file parser and examine file m_file = new StSRecordFile(*m_stream); m_file->parse(); // build an image of the file m_image = new StExecutableImage(); buildMemoryImage(); // dispose of file parser object delete m_file; m_file = 0; } void SRecordSourceFile::close() { assert(m_image); SourceFile::close(); // dispose of memory image delete m_image; m_image = 0; } //! \pre The file must be open before this method can be called. //! DataSource * SRecordSourceFile::createDataSource() { assert(m_image); return new MemoryImageDataSource(m_image); } //! \retval true The file has an S7, S8, or S9 record. //! \retval false No entry point is available. bool SRecordSourceFile::hasEntryPoint() { return m_hasEntryRecord; } //! If no entry point is available then 0 is returned instead. The method scans //! the records in the file looking for S7, S8, or S9 records. Thus, 16-bit, //! 24-bit, and 32-bit entry point records are supported. //! //! \return Entry point address. //! \retval 0 No entry point is available. uint32_t SRecordSourceFile::getEntryPointAddress() { if (m_hasEntryRecord) { // the address in the record is the entry point Log::log(Logger::DEBUG2, "entry point address is 0x%08x\n", m_entryRecord.m_address); return m_entryRecord.m_address; } return 0; } //! Scans the S-records of the file looking for data records. These are S3, S2, or //! S1 records. The contents of these records are added to an StExecutableImage //! object, which coalesces the individual records into contiguous regions of //! memory. //! //! Also looks for S7, S8, or S9 records that contain the entry point. The first //! match of one of these records is saved off into the #m_entryRecord member. //! //! \pre The #m_file member must be valid. //! \pre The #m_image member variable must have been instantiated. void SRecordSourceFile::buildMemoryImage() { assert(m_file); assert(m_image); // Clear the entry point related members. m_hasEntryRecord = false; memset(&m_entryRecord, 0, sizeof(m_entryRecord)); // Allocate buffer to hold data before adding it to the executable image. // Contiguous records are added to this buffer. When overflowed or when a // non-contiguous record is encountered the buffer is added to the executable // image where it will be coalesced further. We don't add records individually // to the image because coalescing record by record is very slow. smart_array_ptr buffer = new uint8_t[COLLECTION_BUFFER_SIZE]; unsigned startAddress; unsigned nextAddress; unsigned dataLength = 0; // process SRecords StSRecordFile::const_iterator it = m_file->getBegin(); for (; it != m_file->getEnd(); it++) { const StSRecordFile::SRecord & theRecord = *it; // only handle S3,2,1 records bool isDataRecord = theRecord.m_type == 3 || theRecord.m_type == 2 || theRecord.m_type == 1; bool hasData = theRecord.m_data && theRecord.m_dataCount; if (isDataRecord && hasData) { // If this record's data would overflow the collection buffer, or if the // record is not contiguous with the rest of the data in the collection // buffer, then flush the buffer to the executable image and restart. if (dataLength && ((dataLength + theRecord.m_dataCount > COLLECTION_BUFFER_SIZE) || (theRecord.m_address != nextAddress))) { m_image->addTextRegion(startAddress, buffer, dataLength); dataLength = 0; } // Capture addresses when starting an empty buffer. if (dataLength == 0) { startAddress = theRecord.m_address; nextAddress = startAddress; } // Copy record data into place in the collection buffer and update // size and address. memcpy(&buffer[dataLength], theRecord.m_data, theRecord.m_dataCount); dataLength += theRecord.m_dataCount; nextAddress += theRecord.m_dataCount; } else if (!m_hasEntryRecord) { // look for S7,8,9 records bool isEntryPointRecord = theRecord.m_type == 7 || theRecord.m_type == 8 || theRecord.m_type == 9; if (isEntryPointRecord) { // save off the entry point record so we don't have to scan again memcpy(&m_entryRecord, &theRecord, sizeof(m_entryRecord)); m_hasEntryRecord = true; } } } // Add any leftover data in the collection buffer to the executable image. if (dataLength) { m_image->addTextRegion(startAddress, buffer, dataLength); } }