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");
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 
452 // Clips the given mapped range.
ClipMappedRangeOriginal(const AddressRange & clip_range,MappedRange * mapped_range)453 void ClipMappedRangeOriginal(const AddressRange& clip_range,
454                              MappedRange* mapped_range) {
455   assert(mapped_range != NULL);
456 
457   // The clipping range is entirely outside of the mapped range.
458   if (clip_range.end() <= mapped_range->rva_original ||
459       mapped_range->rva_original + mapped_range->length +
460           mapped_range->removed <= clip_range.rva) {
461     mapped_range->length = 0;
462     mapped_range->injected = 0;
463     mapped_range->removed = 0;
464     return;
465   }
466 
467   // Clip the left side.
468   if (mapped_range->rva_original < clip_range.rva) {
469     DWORD clip_left = clip_range.rva - mapped_range->rva_original;
470     mapped_range->rva_original += clip_left;
471     mapped_range->rva_transformed += clip_left;
472 
473     if (clip_left > mapped_range->length) {
474       // The left clipping boundary entirely erases the content section of the
475       // range.
476       DWORD trim = clip_left - mapped_range->length;
477       mapped_range->length = 0;
478       mapped_range->injected -= Min(trim, mapped_range->injected);
479       // We know that trim <= mapped_range->remove.
480       mapped_range->removed -= trim;
481     } else {
482       // The left clipping boundary removes some, but not all, of the content.
483       // As such it leaves the removed/injected component intact.
484       mapped_range->length -= clip_left;
485     }
486   }
487 
488   // Clip the right side.
489   DWORD end_original = mapped_range->rva_original + mapped_range->length;
490   if (clip_range.end() < end_original) {
491     // The right clipping boundary lands in the 'content' section of the range,
492     // entirely clearing the injected/removed portion.
493     DWORD clip_right = end_original - clip_range.end();
494     mapped_range->length -= clip_right;
495     mapped_range->injected = 0;
496     mapped_range->removed = 0;
497     return;
498   } else {
499     // The right clipping boundary is outside of the content, but may affect
500     // the removed/injected portion of the range.
501     DWORD end_removed = end_original + mapped_range->removed;
502     if (clip_range.end() < end_removed)
503       mapped_range->removed = clip_range.end() - end_original;
504 
505     DWORD end_injected = end_original + mapped_range->injected;
506     if (clip_range.end() < end_injected)
507       mapped_range->injected = clip_range.end() - end_original;
508   }
509 
510   return;
511 }
512 
513 }  // namespace
514 
Compare(const AddressRange & rhs) const515 int AddressRange::Compare(const AddressRange& rhs) const {
516   if (end() <= rhs.rva)
517     return -1;
518   if (rhs.end() <= rva)
519     return 1;
520   return 0;
521 }
522 
GetOmapDataAndDisableTranslation(IDiaSession * session,OmapData * omap_data)523 bool GetOmapDataAndDisableTranslation(IDiaSession* session,
524                                       OmapData* omap_data) {
525   assert(session != NULL);
526   assert(omap_data != NULL);
527 
528   CComPtr<IDiaAddressMap> address_map;
529   if (FAILED(session->QueryInterface(&address_map))) {
530     fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
531     return false;
532   }
533   assert(address_map.p != NULL);
534 
535   BOOL omap_enabled = FALSE;
536   if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
537     fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
538     return false;
539   }
540 
541   if (!omap_enabled) {
542     // We indicate the non-presence of OMAP data by returning empty tables.
543     omap_data->omap_from.clear();
544     omap_data->omap_to.clear();
545     omap_data->length_original = 0;
546     return true;
547   }
548 
549   // OMAP data is present. Disable translation.
550   if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
551     fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
552     return false;
553   }
554 
555   // Read the OMAP streams.
556   if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
557                             session,
558                             &omap_data->omap_from)) {
559     return false;
560   }
561   if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
562                             session,
563                             &omap_data->omap_to)) {
564     return false;
565   }
566 
567   // Get the lengths of the address spaces.
568   if (!GetOriginalImageLength(session, &omap_data->length_original))
569     return false;
570 
571   return true;
572 }
573 
BuildImageMap(const OmapData & omap_data,ImageMap * image_map)574 void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
575   assert(image_map != NULL);
576 
577   BuildMapping(omap_data, &image_map->mapping);
578   BuildEndpointIndexMap(image_map);
579 }
580 
MapAddressRange(const ImageMap & image_map,const AddressRange & original_range,AddressRangeVector * mapped_ranges)581 void MapAddressRange(const ImageMap& image_map,
582                      const AddressRange& original_range,
583                      AddressRangeVector* mapped_ranges) {
584   assert(mapped_ranges != NULL);
585 
586   const Mapping& map = image_map.mapping;
587 
588   // Handle the trivial case of an empty image_map. This means that there is
589   // no transformation to be applied, and we can simply return the original
590   // range.
591   if (map.empty()) {
592     mapped_ranges->push_back(original_range);
593     return;
594   }
595 
596   // If we get a query of length 0 we need to handle it by using a non-zero
597   // query length.
598   AddressRange query_range(original_range);
599   if (query_range.length == 0)
600     query_range.length = 1;
601 
602   // Find the range of intervals that can potentially intersect our query range.
603   size_t imin = 0;
604   size_t imax = 0;
605   {
606     // The index of the earliest possible range that can affect is us done by
607     // searching through the secondary indexing structure.
608     const EndpointIndexMap& eim = image_map.endpoint_index_map;
609     EndpointIndex q1 = { query_range.rva, 0 };
610     EndpointIndexMap::const_iterator it1 = std::lower_bound(
611         eim.begin(), eim.end(), q1, EndpointIndexLess);
612     if (it1 == eim.end()) {
613       imin  = map.size();
614     } else {
615       // Backup to find the interval that contains our query point.
616       if (it1 != eim.begin() && query_range.rva < it1->endpoint)
617         --it1;
618       imin = it1->index;
619     }
620 
621     // The first range that can't possibly intersect us is found by searching
622     // through the image map directly as it is already sorted by interval start
623     // point.
624     MappedRange q2 = { query_range.end(), 0 };
625     Mapping::const_iterator it2 = std::lower_bound(
626         map.begin(), map.end(), q2, MappedRangeOriginalLess);
627     imax = it2 - map.begin();
628   }
629 
630   // Find all intervals that intersect the query range.
631   Mapping temp_map;
632   for (size_t i = imin; i < imax; ++i) {
633     MappedRange mr = map[i];
634     ClipMappedRangeOriginal(query_range, &mr);
635     if (mr.length + mr.injected > 0)
636       temp_map.push_back(mr);
637   }
638 
639   // If there are no intersecting ranges then the query range has been removed
640   // from the image in question.
641   if (temp_map.empty())
642     return;
643 
644   // Sort based on transformed addresses.
645   std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
646 
647   // Zero-length queries can't actually be merged. We simply output the set of
648   // unique RVAs that correspond to the query RVA.
649   if (original_range.length == 0) {
650     mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
651     for (size_t i = 1; i < temp_map.size(); ++i) {
652       if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
653         mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
654     }
655     return;
656   }
657 
658   // Merge any ranges that are consecutive in the mapped image. We merge over
659   // injected content if it makes ranges contiguous, but we ignore any injected
660   // content at the tail end of a range. This allows us to detect symbols that
661   // have been lengthened by injecting content in the middle. However, it
662   // misses the case where content has been injected at the head or the tail.
663   // The problem is that it doesn't know whether to attribute it to the
664   // preceding or following symbol. It is up to the author of the transform to
665   // output explicit OMAP info in these cases to ensure full coverage of the
666   // transformed address space.
667   DWORD rva_begin = temp_map[0].rva_transformed;
668   DWORD rva_cur_content = rva_begin + temp_map[0].length;
669   DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
670   for (size_t i = 1; i < temp_map.size(); ++i) {
671     if (rva_cur_injected < temp_map[i].rva_transformed) {
672       // This marks the end of a continuous range in the image. Output the
673       // current range and start a new one.
674       if (rva_begin < rva_cur_content) {
675         mapped_ranges->push_back(
676             AddressRange(rva_begin, rva_cur_content - rva_begin));
677       }
678       rva_begin = temp_map[i].rva_transformed;
679     }
680 
681     rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
682     rva_cur_injected = rva_cur_content + temp_map[i].injected;
683   }
684 
685   // Output the range in progress.
686   if (rva_begin < rva_cur_content) {
687     mapped_ranges->push_back(
688         AddressRange(rva_begin, rva_cur_content - rva_begin));
689   }
690 
691   return;
692 }
693 
694 }  // namespace google_breakpad