xref: /netbsd-src/external/bsd/elftosb/dist/common/StExecutableImage.cpp (revision 93f5e2c30272859d294e5c9c68ed3d0473b9c75d)
1 /*
2  * File:	StExecutableImage.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7 
8 #include "StExecutableImage.h"
9 #include <stdexcept>
10 #include <algorithm>
11 #include <string.h>
12 #include <stdio.h>
13 
StExecutableImage(int inAlignment)14 StExecutableImage::StExecutableImage(int inAlignment)
15 :	m_alignment(inAlignment),
16 	m_hasEntry(false),
17 	m_entry(0)
18 {
19 }
20 
21 //! Makes a duplicate of each memory region.
StExecutableImage(const StExecutableImage & inOther)22 StExecutableImage::StExecutableImage(const StExecutableImage & inOther)
23 :	m_name(inOther.m_name),
24     m_alignment(inOther.m_alignment),
25 	m_hasEntry(inOther.m_hasEntry),
26 	m_entry(inOther.m_entry),
27     m_filters(inOther.m_filters)
28 {
29 	const_iterator it = inOther.getRegionBegin();
30 	for (; it != inOther.getRegionEnd(); ++it)
31 	{
32 		const MemoryRegion & region = *it;
33 
34 		MemoryRegion regionCopy(region);
35 		if (region.m_type == FILL_REGION && region.m_data != NULL)
36 		{
37 			regionCopy.m_data = new uint8_t[region.m_length];
38 			memcpy(regionCopy.m_data, region.m_data, region.m_length);
39 		}
40 
41 		m_image.push_back(regionCopy);
42 	}
43 }
44 
45 //! Disposes of memory allocated for each region.
~StExecutableImage()46 StExecutableImage::~StExecutableImage()
47 {
48 	MemoryRegionList::iterator it;
49 	for (it = m_image.begin(); it != m_image.end(); ++it)
50 	{
51 		if (it->m_data)
52 		{
53 			delete [] it->m_data;
54 			it->m_data = NULL;
55 		}
56 	}
57 }
58 
59 //! A copy of \a inName is made, so the original may be disposed of by the caller
60 //! after this method returns.
setName(const std::string & inName)61 void StExecutableImage::setName(const std::string & inName)
62 {
63 	m_name = inName;
64 }
65 
getName() const66 std::string StExecutableImage::getName() const
67 {
68 	return m_name;
69 }
70 
71 // The region is added with read and write flags set.
72 //! \exception std::runtime_error will be thrown if the new overlaps an
73 //!		existing region.
addFillRegion(uint32_t inAddress,unsigned inLength)74 void StExecutableImage::addFillRegion(uint32_t inAddress, unsigned inLength)
75 {
76 	MemoryRegion region;
77 	region.m_type = FILL_REGION;
78 	region.m_address = inAddress;
79 	region.m_data = NULL;
80 	region.m_length = inLength;
81 	region.m_flags = REGION_RW_FLAG;
82 
83 	insertOrMergeRegion(region);
84 }
85 
86 //! A copy of \a inData is made before returning. The copy will be deleted when
87 //! the executable image is destructed. Currently, the text region is created with
88 //! read, write, and executable flags set.
89 //! \exception std::runtime_error will be thrown if the new overlaps an
90 //!		existing region.
91 //! \exception std::bad_alloc is thrown if memory for the copy of \a inData
92 //!		cannot be allocated.
addTextRegion(uint32_t inAddress,const uint8_t * inData,unsigned inLength)93 void StExecutableImage::addTextRegion(uint32_t inAddress, const uint8_t * inData, unsigned inLength)
94 {
95 	MemoryRegion region;
96 	region.m_type = TEXT_REGION;
97 	region.m_address = inAddress;
98 	region.m_flags = REGION_RW_FLAG | REGION_EXEC_FLAG;
99 
100 	// copy the data
101 	region.m_data = new uint8_t[inLength];
102 	region.m_length = inLength;
103 	memcpy(region.m_data, inData, inLength);
104 
105 	insertOrMergeRegion(region);
106 }
107 
108 //! \exception std::out_of_range is thrown if \a inIndex is out of range.
109 //!
getRegionAtIndex(unsigned inIndex) const110 const StExecutableImage::MemoryRegion & StExecutableImage::getRegionAtIndex(unsigned inIndex) const
111 {
112 	// check bounds
113 	if (inIndex >= m_image.size())
114 		throw std::out_of_range("inIndex");
115 
116 	// find region by index
117 	MemoryRegionList::const_iterator it = m_image.begin();
118 	unsigned i = 0;
119 	for (; it != m_image.end(); ++it, ++i)
120 	{
121 		if (i == inIndex)
122 			break;
123 	}
124 	return *it;
125 }
126 
127 //! The list of address filters is kept sorted as filters are added.
128 //!
addAddressFilter(const AddressFilter & filter)129 void StExecutableImage::addAddressFilter(const AddressFilter & filter)
130 {
131     m_filters.push_back(filter);
132     m_filters.sort();
133 }
134 
135 //!
clearAddressFilters()136 void StExecutableImage::clearAddressFilters()
137 {
138     m_filters.clear();
139 }
140 
141 //! \exception StExecutableImage::address_filter_exception Raised when a filter
142 //!     with the type #ADDR_FILTER_ERROR or #ADDR_FILTER_WARNING is matched.
143 //!
144 //! \todo Build a list of all matching filters and then execute them at once.
145 //!     For the warning and error filters, a single exception should be raised
146 //!     that identifies all the overlapping errors. Currently the user will only
147 //!     see the first (lowest address) overlap.
applyAddressFilters()148 void StExecutableImage::applyAddressFilters()
149 {
150 restart_loops:
151     // Iterate over filters.
152     AddressFilterList::const_iterator fit = m_filters.begin();
153     for (; fit != m_filters.end(); ++fit)
154     {
155         const AddressFilter & filter = *fit;
156 
157         // Iterator over regions.
158         MemoryRegionList::iterator rit = m_image.begin();
159         for (; rit != m_image.end(); ++rit)
160         {
161             MemoryRegion & region = *rit;
162 
163             if (filter.matchesMemoryRegion(region))
164             {
165                 switch (filter.m_action)
166                 {
167                     case ADDR_FILTER_NONE:
168                         // Do nothing.
169                         break;
170 
171                     case ADDR_FILTER_ERROR:
172                         // throw error exception
173                         throw address_filter_exception(true, m_name, filter);
174                         break;
175 
176                     case ADDR_FILTER_WARNING:
177                         // throw warning exception
178                         throw address_filter_exception(false, m_name, filter);
179                         break;
180 
181                     case ADDR_FILTER_CROP:
182                         // Delete the offending portion of the region and restart
183                         // the iteration loops.
184                         cropRegionToFilter(region, filter);
185                         goto restart_loops;
186                         break;
187                 }
188             }
189         }
190     }
191 }
192 
193 //! There are several possible cases here:
194 //!     - No overlap at all. Nothing is done.
195 //!
196 //!     - All of the memory region is matched by the \a filter. The region is
197 //!         removed from #StExecutableImage::m_image and its data memory freed.
198 //!
199 //!     - The remaining portion of the region is one contiguous chunk. In this
200 //!         case, \a region is simply modified.
201 //!
202 //!     - The region is split in the middle by the filter. The original \a region
203 //!         is modified to match the first remaining chunk. And a new #StExecutableImage::MemoryRegion
204 //!         instance is created to hold the other leftover piece.
cropRegionToFilter(MemoryRegion & region,const AddressFilter & filter)205 void StExecutableImage::cropRegionToFilter(MemoryRegion & region, const AddressFilter & filter)
206 {
207     uint32_t firstByte = region.m_address;      // first byte occupied by this region
208     uint32_t lastByte = region.endAddress();    // last used byte in this region
209 
210     // compute new address range
211     uint32_t cropFrom = filter.m_fromAddress;
212     if (cropFrom < firstByte)
213     {
214         cropFrom = firstByte;
215     }
216 
217     uint32_t cropTo = filter.m_toAddress;
218     if (cropTo > lastByte)
219     {
220         cropTo = lastByte;
221     }
222 
223     // is there actually a match?
224     if (cropFrom > filter.m_toAddress || cropTo < filter.m_fromAddress)
225     {
226         // nothing to do, so bail
227         return;
228     }
229 
230     printf("Deleting region 0x%08x-0x%08x\n", cropFrom, cropTo);
231 
232     // handle if the entire region is to be deleted
233     if (cropFrom == firstByte && cropTo == lastByte)
234     {
235         delete [] region.m_data;
236         region.m_data = NULL;
237         m_image.remove(region);
238     }
239 
240     // there is at least a little of the original region remaining
241     uint32_t newLength = cropTo - cropFrom + 1;
242     uint32_t leftoverLength = lastByte - cropTo;
243     uint8_t * oldData = region.m_data;
244 
245     // update the region
246     region.m_address = cropFrom;
247     region.m_length = newLength;
248 
249     // crop data buffer for text regions
250     if (region.m_type == TEXT_REGION && oldData)
251     {
252         region.m_data = new uint8_t[newLength];
253         memcpy(region.m_data, &oldData[cropFrom - firstByte], newLength);
254 
255         // dispose of old data
256         delete [] oldData;
257     }
258 
259     // create a new region for any part of the original region that was past
260     // the crop to address. this will happen if the filter range falls in the
261     // middle of the region.
262     if (leftoverLength)
263     {
264         MemoryRegion newRegion;
265         newRegion.m_type = region.m_type;
266         newRegion.m_flags = region.m_flags;
267         newRegion.m_address = cropTo + 1;
268         newRegion.m_length = leftoverLength;
269 
270         if (region.m_type == TEXT_REGION && oldData)
271         {
272             newRegion.m_data = new uint8_t[leftoverLength];
273             memcpy(newRegion.m_data, &oldData[cropTo - firstByte + 1], leftoverLength);
274         }
275 
276         insertOrMergeRegion(newRegion);
277     }
278 }
279 
280 //! \exception std::runtime_error will be thrown if \a inRegion overlaps an
281 //!		existing region.
282 //!
283 //! \todo Need to investigate if we can use the STL sort algorithm at all. Even
284 //!     though we're doing merges too, we could sort first then examine the list
285 //!     for merges.
insertOrMergeRegion(MemoryRegion & inRegion)286 void StExecutableImage::insertOrMergeRegion(MemoryRegion & inRegion)
287 {
288 	uint32_t newStart = inRegion.m_address;
289 	uint32_t newEnd = newStart + inRegion.m_length;
290 
291 	MemoryRegionList::iterator it = m_image.begin();
292 	MemoryRegionList::iterator sortedPosition = m_image.begin();
293 	for (; it != m_image.end(); ++it)
294 	{
295 		MemoryRegion & region = *it;
296 		uint32_t thisStart = region.m_address;
297 		uint32_t thisEnd = thisStart + region.m_length;
298 
299 		// keep track of where to insert it to retain sort order
300 		if (thisStart >= newEnd)
301 		{
302 			break;
303 		}
304 
305 		// region types and flags must match in order to merge
306 		if (region.m_type == inRegion.m_type && region.m_flags == inRegion.m_flags)
307 		{
308 			if (newStart == thisEnd || newEnd == thisStart)
309 			{
310 				mergeRegions(region, inRegion);
311 				return;
312 			}
313 			else if ((newStart >= thisStart && newStart < thisEnd) || (newEnd >= thisStart && newEnd < thisEnd))
314 			{
315 				throw std::runtime_error("new region overlaps existing region");
316 			}
317 		}
318 	}
319 
320 	// not merged, so just insert it in the sorted position
321 	m_image.insert(it, inRegion);
322 }
323 
324 //! Extends \a inNewRegion to include the data in \a inOldRegion. It is
325 //! assumed that the two regions are compatible. The new region may come either
326 //! before or after the old region in memory. Note that while the two regions
327 //! don't necessarily have to be touching, it's probably a good idea. That's
328 //! because any data between the regions will be set to 0.
329 //!
330 //! For TEXT_REGION types, the two original regions will have their data deleted
331 //! during the merge. Thus, this method is not safe if any outside callers may
332 //! be accessing the region's data.
mergeRegions(MemoryRegion & inOldRegion,MemoryRegion & inNewRegion)333 void StExecutableImage::mergeRegions(MemoryRegion & inOldRegion, MemoryRegion & inNewRegion)
334 {
335 	bool isOldBefore = inOldRegion.m_address < inNewRegion.m_address;
336 	uint32_t oldEnd = inOldRegion.m_address + inOldRegion.m_length;
337 	uint32_t newEnd = inNewRegion.m_address + inNewRegion.m_length;
338 
339 	switch (inOldRegion.m_type)
340 	{
341 		case TEXT_REGION:
342 		{
343 			// calculate new length
344 			unsigned newLength;
345 			if (isOldBefore)
346 			{
347 				newLength = newEnd - inOldRegion.m_address;
348 			}
349 			else
350 			{
351 				newLength = oldEnd - inNewRegion.m_address;
352 			}
353 
354 			// alloc memory
355 			uint8_t * newData = new uint8_t[newLength];
356 			memset(newData, 0, newLength);
357 
358 			// copy data from the two regions into new block
359 			if (isOldBefore)
360 			{
361 				memcpy(newData, inOldRegion.m_data, inOldRegion.m_length);
362 				memcpy(&newData[newLength - inNewRegion.m_length], inNewRegion.m_data, inNewRegion.m_length);
363 			}
364 			else
365 			{
366 				memcpy(newData, inNewRegion.m_data, inNewRegion.m_length);
367 				memcpy(&newData[newLength - inOldRegion.m_length], inOldRegion.m_data, inOldRegion.m_length);
368 
369 				inOldRegion.m_address = inNewRegion.m_address;
370 			}
371 
372 			// replace old region's data
373 			delete [] inOldRegion.m_data;
374 			inOldRegion.m_data = newData;
375 			inOldRegion.m_length = newLength;
376 
377 			// delete new region's data
378 			delete [] inNewRegion.m_data;
379 			inNewRegion.m_data = NULL;
380 			break;
381 		}
382 
383 		case FILL_REGION:
384 		{
385 			if (isOldBefore)
386 			{
387 				inOldRegion.m_length = newEnd - inOldRegion.m_address;
388 			}
389 			else
390 			{
391 				inOldRegion.m_length = oldEnd - inNewRegion.m_address;
392 				inOldRegion.m_address = inNewRegion.m_address;
393 			}
394 			break;
395 		}
396 	}
397 }
398 
399 //! Used when we remove a region from the region list by value. Because this
400 //! operator compares the #m_data member, it will only return true for either an
401 //! exact copy or a reference to the original.
operator ==(const MemoryRegion & other) const402 bool StExecutableImage::MemoryRegion::operator == (const MemoryRegion & other) const
403 {
404    return (m_type == other.m_type) && (m_address == other.m_address) && (m_length == other.m_length) && (m_flags == other.m_flags) && (m_data == other.m_data);
405 }
406 
407 //! Returns true if the address filter overlaps \a region.
matchesMemoryRegion(const MemoryRegion & region) const408 bool StExecutableImage::AddressFilter::matchesMemoryRegion(const MemoryRegion & region) const
409 {
410     uint32_t firstByte = region.m_address;      // first byte occupied by this region
411     uint32_t lastByte = region.endAddress();    // last used byte in this region
412     return (firstByte >= m_fromAddress && firstByte <= m_toAddress) || (lastByte >= m_fromAddress && lastByte <= m_toAddress);
413 }
414 
415 //! The comparison does \em not take the action into account. It only looks at the
416 //! priority and address ranges of each filter. Priority is considered only if the two
417 //! filters overlap. Lower priority filters will come after higher priority ones.
418 //!
419 //! \retval -1 This filter is less than filter \a b.
420 //! \retval 0 This filter is equal to filter \a b.
421 //! \retval 1 This filter is greater than filter \a b.
compare(const AddressFilter & other) const422 int StExecutableImage::AddressFilter::compare(const AddressFilter & other) const
423 {
424     if (m_priority != other.m_priority && ((m_fromAddress >= other.m_fromAddress && m_fromAddress <= other.m_toAddress) || (m_toAddress >= other.m_fromAddress && m_toAddress <= other.m_toAddress)))
425     {
426         // we know the priorities are not equal
427         if (m_priority > other.m_priority)
428         {
429             return -1;
430         }
431         else
432         {
433             return 1;
434         }
435     }
436 
437     if (m_fromAddress == other.m_fromAddress)
438     {
439         if (m_toAddress == other.m_toAddress)
440         {
441             return 0;
442         }
443         else if (m_toAddress < other.m_toAddress)
444         {
445             return -1;
446         }
447         else
448         {
449             return 1;
450         }
451     }
452     else if (m_fromAddress < other.m_fromAddress)
453     {
454         return -1;
455     }
456     else
457     {
458         return 1;
459     }
460 }
461 
462 
463 
464