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 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. 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. 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. 61 void StExecutableImage::setName(const std::string & inName) 62 { 63 m_name = inName; 64 } 65 66 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. 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. 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 //! 110 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 //! 129 void StExecutableImage::addAddressFilter(const AddressFilter & filter) 130 { 131 m_filters.push_back(filter); 132 m_filters.sort(); 133 } 134 135 //! 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. 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. 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. 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. 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. 402 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. 408 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. 422 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