summaryrefslogtreecommitdiffstats
path: root/common/SRecordSourceFile.cpp
blob: 3521c693789a294989244b769bd42948d094b6e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
 * 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 <assert.h>
#include <string.h>
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<uint8_t> 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);
	}
}