1 /* 2 * File: SRecordSourceFile.cpp 3 * 4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. 5 * See included license file for license details. 6 */ 7 8 #include "SRecordSourceFile.h" 9 #include "Logging.h" 10 #include "smart_ptr.h" 11 #include <assert.h> 12 #include <string.h> 13 enum 14 { 15 //! Size in bytes of the buffer used to collect S-record data records 16 //! before adding them to the executable image. Currently 64KB. 17 COLLECTION_BUFFER_SIZE = 64 * 1024 18 }; 19 20 using namespace elftosb; 21 22 SRecordSourceFile::SRecordSourceFile(const std::string & path) 23 : SourceFile(path), m_image(0), m_hasEntryRecord(false) 24 { 25 } 26 27 bool SRecordSourceFile::isSRecordFile(std::istream & stream) 28 { 29 StSRecordFile srec(stream); 30 return srec.isSRecordFile(); 31 } 32 33 void SRecordSourceFile::open() 34 { 35 SourceFile::open(); 36 37 // create file parser and examine file 38 m_file = new StSRecordFile(*m_stream); 39 m_file->parse(); 40 41 // build an image of the file 42 m_image = new StExecutableImage(); 43 buildMemoryImage(); 44 45 // dispose of file parser object 46 delete m_file; 47 m_file = 0; 48 } 49 50 void SRecordSourceFile::close() 51 { 52 assert(m_image); 53 54 SourceFile::close(); 55 56 // dispose of memory image 57 delete m_image; 58 m_image = 0; 59 } 60 61 //! \pre The file must be open before this method can be called. 62 //! 63 DataSource * SRecordSourceFile::createDataSource() 64 { 65 assert(m_image); 66 return new MemoryImageDataSource(m_image); 67 } 68 69 //! \retval true The file has an S7, S8, or S9 record. 70 //! \retval false No entry point is available. 71 bool SRecordSourceFile::hasEntryPoint() 72 { 73 return m_hasEntryRecord; 74 } 75 76 //! If no entry point is available then 0 is returned instead. The method scans 77 //! the records in the file looking for S7, S8, or S9 records. Thus, 16-bit, 78 //! 24-bit, and 32-bit entry point records are supported. 79 //! 80 //! \return Entry point address. 81 //! \retval 0 No entry point is available. 82 uint32_t SRecordSourceFile::getEntryPointAddress() 83 { 84 if (m_hasEntryRecord) 85 { 86 // the address in the record is the entry point 87 Log::log(Logger::DEBUG2, "entry point address is 0x%08x\n", m_entryRecord.m_address); 88 return m_entryRecord.m_address; 89 } 90 91 return 0; 92 } 93 94 //! Scans the S-records of the file looking for data records. These are S3, S2, or 95 //! S1 records. The contents of these records are added to an StExecutableImage 96 //! object, which coalesces the individual records into contiguous regions of 97 //! memory. 98 //! 99 //! Also looks for S7, S8, or S9 records that contain the entry point. The first 100 //! match of one of these records is saved off into the #m_entryRecord member. 101 //! 102 //! \pre The #m_file member must be valid. 103 //! \pre The #m_image member variable must have been instantiated. 104 void SRecordSourceFile::buildMemoryImage() 105 { 106 assert(m_file); 107 assert(m_image); 108 109 // Clear the entry point related members. 110 m_hasEntryRecord = false; 111 memset(&m_entryRecord, 0, sizeof(m_entryRecord)); 112 113 // Allocate buffer to hold data before adding it to the executable image. 114 // Contiguous records are added to this buffer. When overflowed or when a 115 // non-contiguous record is encountered the buffer is added to the executable 116 // image where it will be coalesced further. We don't add records individually 117 // to the image because coalescing record by record is very slow. 118 smart_array_ptr<uint8_t> buffer = new uint8_t[COLLECTION_BUFFER_SIZE]; 119 unsigned startAddress; 120 unsigned nextAddress; 121 unsigned dataLength = 0; 122 123 // process SRecords 124 StSRecordFile::const_iterator it = m_file->getBegin(); 125 for (; it != m_file->getEnd(); it++) 126 { 127 const StSRecordFile::SRecord & theRecord = *it; 128 129 // only handle S3,2,1 records 130 bool isDataRecord = theRecord.m_type == 3 || theRecord.m_type == 2 || theRecord.m_type == 1; 131 bool hasData = theRecord.m_data && theRecord.m_dataCount; 132 if (isDataRecord && hasData) 133 { 134 // If this record's data would overflow the collection buffer, or if the 135 // record is not contiguous with the rest of the data in the collection 136 // buffer, then flush the buffer to the executable image and restart. 137 if (dataLength && ((dataLength + theRecord.m_dataCount > COLLECTION_BUFFER_SIZE) || (theRecord.m_address != nextAddress))) 138 { 139 m_image->addTextRegion(startAddress, buffer, dataLength); 140 141 dataLength = 0; 142 } 143 144 // Capture addresses when starting an empty buffer. 145 if (dataLength == 0) 146 { 147 startAddress = theRecord.m_address; 148 nextAddress = startAddress; 149 } 150 151 // Copy record data into place in the collection buffer and update 152 // size and address. 153 memcpy(&buffer[dataLength], theRecord.m_data, theRecord.m_dataCount); 154 dataLength += theRecord.m_dataCount; 155 nextAddress += theRecord.m_dataCount; 156 } 157 else if (!m_hasEntryRecord) 158 { 159 // look for S7,8,9 records 160 bool isEntryPointRecord = theRecord.m_type == 7 || theRecord.m_type == 8 || theRecord.m_type == 9; 161 if (isEntryPointRecord) 162 { 163 // save off the entry point record so we don't have to scan again 164 memcpy(&m_entryRecord, &theRecord, sizeof(m_entryRecord)); 165 m_hasEntryRecord = true; 166 } 167 } 168 } 169 170 // Add any leftover data in the collection buffer to the executable image. 171 if (dataLength) 172 { 173 m_image->addTextRegion(startAddress, buffer, dataLength); 174 } 175 } 176 177