1 // Copyright 2013 Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google Inc. nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // This contains a suite of tools for transforming symbol information when
30 // when that information has been extracted from a PDB containing OMAP
31 // information.
32
33 // OMAP information is a lightweight description of a mapping between two
34 // address spaces. It consists of two streams, each of them a vector 2-tuples.
35 // The OMAPTO stream contains tuples of the form
36 //
37 // (RVA in transformed image, RVA in original image)
38 //
39 // while the OMAPFROM stream contains tuples of the form
40 //
41 // (RVA in original image, RVA in transformed image)
42 //
43 // The entries in each vector are sorted by the first value of the tuple, and
44 // the lengths associated with a mapping are implicit as the distance between
45 // two successive addresses in the vector.
46
47 // Consider a trivial 10-byte function described by the following symbol:
48 //
49 // Function: RVA 0x00001000, length 10, "foo"
50 //
51 // Now consider the same function, but with 5-bytes of instrumentation injected
52 // at offset 5. The OMAP streams describing this would look like:
53 //
54 // OMAPTO : [ [0x00001000, 0x00001000],
55 // [0x00001005, 0xFFFFFFFF],
56 // [0x0000100a, 0x00001005] ]
57 // OMAPFROM: [ [0x00001000, 0x00001000],
58 // [0x00001005, 0x0000100a] ]
59 //
60 // In this case the injected code has been marked as not originating in the
61 // source image, and thus it will have no symbol information at all. However,
62 // the injected code may also be associated with an original address range;
63 // for example, when prepending instrumentation to a basic block the
64 // instrumentation can be labelled as originating from the same source BB such
65 // that symbol resolution will still find the appropriate source code line
66 // number. In this case the OMAP stream would look like:
67 //
68 // OMAPTO : [ [0x00001000, 0x00001000],
69 // [0x00001005, 0x00001005],
70 // [0x0000100a, 0x00001005] ]
71 // OMAPFROM: [ [0x00001000, 0x00001000],
72 // [0x00001005, 0x0000100a] ]
73 //
74 // Suppose we asked DIA to lookup the symbol at location 0x0000100a of the
75 // instrumented image. It would first run this through the OMAPTO table and
76 // translate that address to 0x00001005. It would then lookup the symbol
77 // at that address and return the symbol for the function "foo". This is the
78 // correct result.
79 //
80 // However, if we query DIA for the length and address of the symbol it will
81 // tell us that it has length 10 and is at RVA 0x00001000. The location is
82 // correct, but the length doesn't take into account the 5-bytes of injected
83 // code. Symbol resolution works (starting from an instrumented address,
84 // mapping to an original address, and looking up a symbol), but the symbol
85 // metadata is incorrect.
86 //
87 // If we dump the symbols using DIA they will have their addresses
88 // appropriately transformed and reflect positions in the instrumented image.
89 // However, if we try to do a lookup using those symbols resolution can fail.
90 // For example, the address 0x0000100a will not map to the symbol for "foo",
91 // because DIA tells us it is at location 0x00001000 (correct) with length
92 // 10 (incorrect). The problem is one of order of operations: in this case
93 // we're attempting symbol resolution by looking up an instrumented address
94 // in the table of translated symbols.
95 //
96 // One way to handle this is to dump the OMAP information as part of the
97 // breakpad symbols. This requires the rest of the toolchain to be aware of
98 // OMAP information and to use it when present prior to performing lookup. The
99 // other option is to properly transform the symbols (updating length as well as
100 // position) so that resolution will work as expected for translated addresses.
101 // This is transparent to the rest of the toolchain.
102
103 #include "common/windows/omap.h"
104
105 #include <atlbase.h>
106
107 #include <algorithm>
108 #include <cassert>
109 #include <set>
110
111 #include "common/windows/dia_util.h"
112
113 namespace google_breakpad {
114
115 namespace {
116
117 static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO";
118 static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM";
119
120 // Dependending on where this is used in breakpad we sometimes get min/max from
121 // windef, and other times from algorithm. To get around this we simply
122 // define our own min/max functions.
123 template<typename T>
Min(const T & t1,const T & t2)124 const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
125 template<typename T>
Max(const T & t1,const T & t2)126 const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; }
127
128 // It makes things more readable to have two different OMAP types. We cast
129 // normal OMAPs into these. They must be the same size as the OMAP structure
130 // for this to work, hence the static asserts.
131 struct OmapOrigToTran {
132 DWORD rva_original;
133 DWORD rva_transformed;
134 };
135 struct OmapTranToOrig {
136 DWORD rva_transformed;
137 DWORD rva_original;
138 };
139 static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP),
140 "OmapOrigToTran must have same size as OMAP.");
141 static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP),
142 "OmapTranToOrig must have same size as OMAP.");
143 typedef std::vector<OmapOrigToTran> OmapFromTable;
144 typedef std::vector<OmapTranToOrig> OmapToTable;
145
146 // Used for sorting and searching through a Mapping.
MappedRangeOriginalLess(const MappedRange & lhs,const MappedRange & rhs)147 bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) {
148 if (lhs.rva_original < rhs.rva_original)
149 return true;
150 if (lhs.rva_original > rhs.rva_original)
151 return false;
152 return lhs.length < rhs.length;
153 }
MappedRangeMappedLess(const MappedRange & lhs,const MappedRange & rhs)154 bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) {
155 if (lhs.rva_transformed < rhs.rva_transformed)
156 return true;
157 if (lhs.rva_transformed > rhs.rva_transformed)
158 return false;
159 return lhs.length < rhs.length;
160 }
161
162 // Used for searching through the EndpointIndexMap.
EndpointIndexLess(const EndpointIndex & ei1,const EndpointIndex & ei2)163 bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) {
164 return ei1.endpoint < ei2.endpoint;
165 }
166
167 // Finds the debug stream with the given |name| in the given |session|, and
168 // populates |table| with its contents. Casts the data directly into OMAP
169 // structs.
FindAndLoadOmapTable(const wchar_t * name,IDiaSession * session,OmapTable * table)170 bool FindAndLoadOmapTable(const wchar_t* name,
171 IDiaSession* session,
172 OmapTable* table) {
173 assert(name != NULL);
174 assert(session != NULL);
175 assert(table != NULL);
176
177 CComPtr<IDiaEnumDebugStreamData> stream;
178 if (!FindDebugStream(name, session, &stream))
179 return false;
180 assert(stream.p != NULL);
181
182 LONG count = 0;
183 if (FAILED(stream->get_Count(&count))) {
184 fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream "
185 "\"%ws\"\n", name);
186 return false;
187 }
188
189 // Get the length of the stream in bytes.
190 DWORD bytes_read = 0;
191 ULONG count_read = 0;
192 if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) {
193 fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
194 "length of stream \"%ws\"\n", name);
195 return false;
196 }
197
198 // Ensure it's consistent with the OMAP data type.
199 DWORD bytes_expected = count * sizeof(OmapTable::value_type);
200 if (count * sizeof(OmapTable::value_type) != bytes_read) {
201 fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name);
202 return false;
203 }
204
205 // Read the table.
206 table->resize(count);
207 bytes_read = 0;
208 count_read = 0;
209 if (FAILED(stream->Next(count, bytes_expected, &bytes_read,
210 reinterpret_cast<BYTE*>(&table->at(0)),
211 &count_read))) {
212 fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
213 "data from stream \"%ws\"\n", name);
214 return false;
215 }
216
217 return true;
218 }
219
220 // This determines the original image length by looking through the segment
221 // table.
GetOriginalImageLength(IDiaSession * session,DWORD * image_length)222 bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) {
223 assert(session != NULL);
224 assert(image_length != NULL);
225
226 CComPtr<IDiaEnumSegments> enum_segments;
227 if (!FindTable(session, &enum_segments))
228 return false;
229 assert(enum_segments.p != NULL);
230
231 DWORD temp_image_length = 0;
232 CComPtr<IDiaSegment> segment;
233 ULONG fetched = 0;
234 while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) &&
235 fetched == 1) {
236 assert(segment.p != NULL);
237
238 DWORD rva = 0;
239 DWORD length = 0;
240 DWORD frame = 0;
241 if (FAILED(segment->get_relativeVirtualAddress(&rva)) ||
242 FAILED(segment->get_length(&length)) ||
243 FAILED(segment->get_frame(&frame))) {
244 fprintf(stderr, "Failed to get basic properties for IDiaSegment\n");
245 return false;
246 }
247
248 if (frame > 0) {
249 DWORD segment_end = rva + length;
250 if (segment_end > temp_image_length)
251 temp_image_length = segment_end;
252 }
253 segment.Release();
254 }
255
256 *image_length = temp_image_length;
257 return true;
258 }
259
260 // Detects regions of the original image that have been removed in the
261 // transformed image, and sets the 'removed' property on all mapped ranges
262 // immediately preceding a gap. The mapped ranges must be sorted by
263 // 'rva_original'.
FillInRemovedLengths(Mapping * mapping)264 void FillInRemovedLengths(Mapping* mapping) {
265 assert(mapping != NULL);
266
267 // Find and fill gaps. We do this with two sweeps. We first sweep forward
268 // looking for gaps. When we identify a gap we then sweep forward with a
269 // second scan and set the 'removed' property for any intervals that
270 // immediately precede the gap.
271 //
272 // Gaps are typically between two successive intervals, but not always:
273 //
274 // Range 1: ---------------
275 // Range 2: -------
276 // Range 3: -------------
277 // Gap : ******
278 //
279 // In the above example the gap is between range 1 and range 3. A forward
280 // sweep finds the gap, and a second forward sweep identifies that range 1
281 // immediately precedes the gap and sets its 'removed' property.
282
283 size_t fill = 0;
284 DWORD rva_front = 0;
285 for (size_t find = 0; find < mapping->size(); ++find) {
286 #ifndef NDEBUG
287 // We expect the mapped ranges to be sorted by 'rva_original'.
288 if (find > 0) {
289 assert(mapping->at(find - 1).rva_original <=
290 mapping->at(find).rva_original);
291 }
292 #endif
293
294 if (rva_front < mapping->at(find).rva_original) {
295 // We've found a gap. Fill it in by setting the 'removed' property for
296 // any affected intervals.
297 DWORD removed = mapping->at(find).rva_original - rva_front;
298 for (; fill < find; ++fill) {
299 if (mapping->at(fill).rva_original + mapping->at(fill).length !=
300 rva_front) {
301 continue;
302 }
303
304 // This interval ends right where the gap starts. It needs to have its
305 // 'removed' information filled in.
306 mapping->at(fill).removed = removed;
307 }
308 }
309
310 // Advance the front that indicates the covered portion of the image.
311 rva_front = mapping->at(find).rva_original + mapping->at(find).length;
312 }
313 }
314
315 // Builds a unified view of the mapping between the original and transformed
316 // image space by merging OMAPTO and OMAPFROM data.
BuildMapping(const OmapData & omap_data,Mapping * mapping)317 void BuildMapping(const OmapData& omap_data, Mapping* mapping) {
318 assert(mapping != NULL);
319
320 mapping->clear();
321
322 if (omap_data.omap_from.empty() || omap_data.omap_to.empty())
323 return;
324
325 // The names 'omap_to' and 'omap_from' are awfully confusing, so we make
326 // ourselves more explicit here. This cast is only safe because the underlying
327 // types have the exact same size.
328 const OmapToTable& tran2orig =
329 reinterpret_cast<const OmapToTable&>(omap_data.omap_to);
330 const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>(
331 omap_data.omap_from);
332
333 // Handle the range of data at the beginning of the image. This is not usually
334 // specified by the OMAP data.
335 if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) {
336 DWORD header_transformed = tran2orig[0].rva_transformed;
337 DWORD header_original = orig2tran[0].rva_original;
338 DWORD header = Min(header_transformed, header_original);
339
340 MappedRange mr = {};
341 mr.length = header;
342 mr.injected = header_transformed - header;
343 mr.removed = header_original - header;
344 mapping->push_back(mr);
345 }
346
347 // Convert the implied lengths to explicit lengths, and infer which content
348 // has been injected into the transformed image. Injected content is inferred
349 // as regions of the transformed address space that does not map back to
350 // known valid content in the original image.
351 for (size_t i = 0; i < tran2orig.size(); ++i) {
352 const OmapTranToOrig& o1 = tran2orig[i];
353
354 // This maps to content that is outside the original image, thus it
355 // describes injected content. We can skip this entry.
356 if (o1.rva_original >= omap_data.length_original)
357 continue;
358
359 // Calculate the length of the current OMAP entry. This is implicit as the
360 // distance between successive |rva| values, capped at the end of the
361 // original image.
362 DWORD length = 0;
363 if (i + 1 < tran2orig.size()) {
364 const OmapTranToOrig& o2 = tran2orig[i + 1];
365
366 // We expect the table to be sorted by rva_transformed.
367 assert(o1.rva_transformed <= o2.rva_transformed);
368
369 length = o2.rva_transformed - o1.rva_transformed;
370 if (o1.rva_original + length > omap_data.length_original) {
371 length = omap_data.length_original - o1.rva_original;
372 }
373 } else {
374 length = omap_data.length_original - o1.rva_original;
375 }
376
377 // Zero-length entries don't describe anything and can be ignored.
378 if (length == 0)
379 continue;
380
381 // Any gaps in the transformed address-space are due to injected content.
382 if (!mapping->empty()) {
383 MappedRange& prev_mr = mapping->back();
384 prev_mr.injected += o1.rva_transformed -
385 (prev_mr.rva_transformed + prev_mr.length);
386 }
387
388 MappedRange mr = {};
389 mr.rva_original = o1.rva_original;
390 mr.rva_transformed = o1.rva_transformed;
391 mr.length = length;
392 mapping->push_back(mr);
393 }
394
395 // Sort based on the original image addresses.
396 std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess);
397
398 // Fill in the 'removed' lengths by looking for gaps in the coverage of the
399 // original address space.
400 FillInRemovedLengths(mapping);
401
402 return;
403 }
404
BuildEndpointIndexMap(ImageMap * image_map)405 void BuildEndpointIndexMap(ImageMap* image_map) {
406 assert(image_map != NULL);
407
408 if (image_map->mapping.size() == 0)
409 return;
410
411 const Mapping& mapping = image_map->mapping;
412 EndpointIndexMap& eim = image_map->endpoint_index_map;
413
414 // Get the unique set of interval endpoints.
415 std::set<DWORD> endpoints;
416 for (size_t i = 0; i < mapping.size(); ++i) {
417 endpoints.insert(mapping[i].rva_original);
418 endpoints.insert(mapping[i].rva_original +
419 mapping[i].length +
420 mapping[i].removed);
421 }
422
423 // Use the endpoints to initialize the secondary search structure for the
424 // mapping.
425 eim.resize(endpoints.size());
426 std::set<DWORD>::const_iterator it = endpoints.begin();
427 for (size_t i = 0; it != endpoints.end(); ++it, ++i) {
428 eim[i].endpoint = *it;
429 eim[i].index = mapping.size();
430 }
431
432 // For each endpoint we want the smallest index of any interval containing
433 // it. We iterate over the intervals and update the indices associated with
434 // each interval endpoint contained in the current interval. In the general
435 // case of an arbitrary set of intervals this is O(n^2), but the structure of
436 // OMAP data makes this O(n).
437 for (size_t i = 0; i < mapping.size(); ++i) {
438 EndpointIndex ei1 = { mapping[i].rva_original, 0 };
439 EndpointIndexMap::iterator it1 = std::lower_bound(
440 eim.begin(), eim.end(), ei1, EndpointIndexLess);
441
442 EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length +
443 mapping[i].removed, 0 };
444 EndpointIndexMap::iterator it2 = std::lower_bound(
445 eim.begin(), eim.end(), ei2, EndpointIndexLess);
446
447 for (; it1 != it2; ++it1)
448 it1->index = Min(i, it1->index);
449 }
450 }
451
BuildSubsequentRVAMap(const OmapData & omap_data,std::map<DWORD,DWORD> * subsequent)452 void BuildSubsequentRVAMap(const OmapData &omap_data,
453 std::map<DWORD, DWORD> *subsequent) {
454 assert(subsequent->empty());
455 const OmapFromTable &orig2tran =
456 reinterpret_cast<const OmapFromTable &>(omap_data.omap_from);
457
458 if (orig2tran.empty())
459 return;
460
461 for (size_t i = 0; i < orig2tran.size() - 1; ++i) {
462 // Expect that orig2tran is sorted.
463 if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) {
464 fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n");
465 subsequent->clear();
466 return;
467 }
468 subsequent->insert(std::make_pair(orig2tran[i].rva_original,
469 orig2tran[i + 1].rva_original));
470 }
471 }
472
473 // Clips the given mapped range.
ClipMappedRangeOriginal(const AddressRange & clip_range,MappedRange * mapped_range)474 void ClipMappedRangeOriginal(const AddressRange& clip_range,
475 MappedRange* mapped_range) {
476 assert(mapped_range != NULL);
477
478 // The clipping range is entirely outside of the mapped range.
479 if (clip_range.end() <= mapped_range->rva_original ||
480 mapped_range->rva_original + mapped_range->length +
481 mapped_range->removed <= clip_range.rva) {
482 mapped_range->length = 0;
483 mapped_range->injected = 0;
484 mapped_range->removed = 0;
485 return;
486 }
487
488 // Clip the left side.
489 if (mapped_range->rva_original < clip_range.rva) {
490 DWORD clip_left = clip_range.rva - mapped_range->rva_original;
491 mapped_range->rva_original += clip_left;
492 mapped_range->rva_transformed += clip_left;
493
494 if (clip_left > mapped_range->length) {
495 // The left clipping boundary entirely erases the content section of the
496 // range.
497 DWORD trim = clip_left - mapped_range->length;
498 mapped_range->length = 0;
499 mapped_range->injected -= Min(trim, mapped_range->injected);
500 // We know that trim <= mapped_range->remove.
501 mapped_range->removed -= trim;
502 } else {
503 // The left clipping boundary removes some, but not all, of the content.
504 // As such it leaves the removed/injected component intact.
505 mapped_range->length -= clip_left;
506 }
507 }
508
509 // Clip the right side.
510 DWORD end_original = mapped_range->rva_original + mapped_range->length;
511 if (clip_range.end() < end_original) {
512 // The right clipping boundary lands in the 'content' section of the range,
513 // entirely clearing the injected/removed portion.
514 DWORD clip_right = end_original - clip_range.end();
515 mapped_range->length -= clip_right;
516 mapped_range->injected = 0;
517 mapped_range->removed = 0;
518 return;
519 } else {
520 // The right clipping boundary is outside of the content, but may affect
521 // the removed/injected portion of the range.
522 DWORD end_removed = end_original + mapped_range->removed;
523 if (clip_range.end() < end_removed)
524 mapped_range->removed = clip_range.end() - end_original;
525
526 DWORD end_injected = end_original + mapped_range->injected;
527 if (clip_range.end() < end_injected)
528 mapped_range->injected = clip_range.end() - end_original;
529 }
530
531 return;
532 }
533
534 } // namespace
535
Compare(const AddressRange & rhs) const536 int AddressRange::Compare(const AddressRange& rhs) const {
537 if (end() <= rhs.rva)
538 return -1;
539 if (rhs.end() <= rva)
540 return 1;
541 return 0;
542 }
543
GetOmapDataAndDisableTranslation(IDiaSession * session,OmapData * omap_data)544 bool GetOmapDataAndDisableTranslation(IDiaSession* session,
545 OmapData* omap_data) {
546 assert(session != NULL);
547 assert(omap_data != NULL);
548
549 CComPtr<IDiaAddressMap> address_map;
550 if (FAILED(session->QueryInterface(&address_map))) {
551 fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
552 return false;
553 }
554 assert(address_map.p != NULL);
555
556 BOOL omap_enabled = FALSE;
557 if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
558 fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
559 return false;
560 }
561
562 if (!omap_enabled) {
563 // We indicate the non-presence of OMAP data by returning empty tables.
564 omap_data->omap_from.clear();
565 omap_data->omap_to.clear();
566 omap_data->length_original = 0;
567 return true;
568 }
569
570 // OMAP data is present. Disable translation.
571 if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
572 fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
573 return false;
574 }
575
576 // Read the OMAP streams.
577 if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
578 session,
579 &omap_data->omap_from)) {
580 return false;
581 }
582 if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
583 session,
584 &omap_data->omap_to)) {
585 return false;
586 }
587
588 // Get the lengths of the address spaces.
589 if (!GetOriginalImageLength(session, &omap_data->length_original))
590 return false;
591
592 return true;
593 }
594
BuildImageMap(const OmapData & omap_data,ImageMap * image_map)595 void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
596 assert(image_map != NULL);
597
598 BuildMapping(omap_data, &image_map->mapping);
599 BuildEndpointIndexMap(image_map);
600 BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block);
601 }
602
MapAddressRange(const ImageMap & image_map,const AddressRange & original_range,AddressRangeVector * mapped_ranges)603 void MapAddressRange(const ImageMap& image_map,
604 const AddressRange& original_range,
605 AddressRangeVector* mapped_ranges) {
606 assert(mapped_ranges != NULL);
607
608 const Mapping& map = image_map.mapping;
609
610 // Handle the trivial case of an empty image_map. This means that there is
611 // no transformation to be applied, and we can simply return the original
612 // range.
613 if (map.empty()) {
614 mapped_ranges->push_back(original_range);
615 return;
616 }
617
618 // If we get a query of length 0 we need to handle it by using a non-zero
619 // query length.
620 AddressRange query_range(original_range);
621 if (query_range.length == 0)
622 query_range.length = 1;
623
624 // Find the range of intervals that can potentially intersect our query range.
625 size_t imin = 0;
626 size_t imax = 0;
627 {
628 // The index of the earliest possible range that can affect is us done by
629 // searching through the secondary indexing structure.
630 const EndpointIndexMap& eim = image_map.endpoint_index_map;
631 EndpointIndex q1 = { query_range.rva, 0 };
632 EndpointIndexMap::const_iterator it1 = std::lower_bound(
633 eim.begin(), eim.end(), q1, EndpointIndexLess);
634 if (it1 == eim.end()) {
635 imin = map.size();
636 } else {
637 // Backup to find the interval that contains our query point.
638 if (it1 != eim.begin() && query_range.rva < it1->endpoint)
639 --it1;
640 imin = it1->index;
641 }
642
643 // The first range that can't possibly intersect us is found by searching
644 // through the image map directly as it is already sorted by interval start
645 // point.
646 MappedRange q2 = { query_range.end(), 0 };
647 Mapping::const_iterator it2 = std::lower_bound(
648 map.begin(), map.end(), q2, MappedRangeOriginalLess);
649 imax = it2 - map.begin();
650 }
651
652 // Find all intervals that intersect the query range.
653 Mapping temp_map;
654 for (size_t i = imin; i < imax; ++i) {
655 MappedRange mr = map[i];
656 ClipMappedRangeOriginal(query_range, &mr);
657 if (mr.length + mr.injected > 0)
658 temp_map.push_back(mr);
659 }
660
661 // If there are no intersecting ranges then the query range has been removed
662 // from the image in question.
663 if (temp_map.empty())
664 return;
665
666 // Sort based on transformed addresses.
667 std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
668
669 // Zero-length queries can't actually be merged. We simply output the set of
670 // unique RVAs that correspond to the query RVA.
671 if (original_range.length == 0) {
672 mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
673 for (size_t i = 1; i < temp_map.size(); ++i) {
674 if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
675 mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
676 }
677 return;
678 }
679
680 // Merge any ranges that are consecutive in the mapped image. We merge over
681 // injected content if it makes ranges contiguous, but we ignore any injected
682 // content at the tail end of a range. This allows us to detect symbols that
683 // have been lengthened by injecting content in the middle. However, it
684 // misses the case where content has been injected at the head or the tail.
685 // The problem is that it doesn't know whether to attribute it to the
686 // preceding or following symbol. It is up to the author of the transform to
687 // output explicit OMAP info in these cases to ensure full coverage of the
688 // transformed address space.
689 DWORD rva_begin = temp_map[0].rva_transformed;
690 DWORD rva_cur_content = rva_begin + temp_map[0].length;
691 DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
692 for (size_t i = 1; i < temp_map.size(); ++i) {
693 if (rva_cur_injected < temp_map[i].rva_transformed) {
694 // This marks the end of a continuous range in the image. Output the
695 // current range and start a new one.
696 if (rva_begin < rva_cur_content) {
697 mapped_ranges->push_back(
698 AddressRange(rva_begin, rva_cur_content - rva_begin));
699 }
700 rva_begin = temp_map[i].rva_transformed;
701 }
702
703 rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
704 rva_cur_injected = rva_cur_content + temp_map[i].injected;
705 }
706
707 // Output the range in progress.
708 if (rva_begin < rva_cur_content) {
709 mapped_ranges->push_back(
710 AddressRange(rva_begin, rva_cur_content - rva_begin));
711 }
712
713 return;
714 }
715
716 } // namespace google_breakpad
717