xref: /aosp_15_r20/external/google-breakpad/src/common/windows/omap.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2013 Google LLC
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 LLC 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 #ifdef HAVE_CONFIG_H
104 #include <config.h>  // Must come first
105 #endif
106 
107 #include "common/windows/omap.h"
108 
109 #include <atlbase.h>
110 
111 #include <algorithm>
112 #include <cassert>
113 #include <set>
114 
115 #include "common/windows/dia_util.h"
116 
117 namespace google_breakpad {
118 
119 namespace {
120 
121 static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO";
122 static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM";
123 
124 // Dependending on where this is used in breakpad we sometimes get min/max from
125 // windef, and other times from algorithm. To get around this we simply
126 // define our own min/max functions.
127 template<typename T>
Min(const T & t1,const T & t2)128 const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
129 template<typename T>
Max(const T & t1,const T & t2)130 const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; }
131 
132 // It makes things more readable to have two different OMAP types. We cast
133 // normal OMAPs into these. They must be the same size as the OMAP structure
134 // for this to work, hence the static asserts.
135 struct OmapOrigToTran {
136   DWORD rva_original;
137   DWORD rva_transformed;
138 };
139 struct OmapTranToOrig {
140   DWORD rva_transformed;
141   DWORD rva_original;
142 };
143 static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP),
144               "OmapOrigToTran must have same size as OMAP.");
145 static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP),
146               "OmapTranToOrig must have same size as OMAP.");
147 typedef std::vector<OmapOrigToTran> OmapFromTable;
148 typedef std::vector<OmapTranToOrig> OmapToTable;
149 
150 // Used for sorting and searching through a Mapping.
MappedRangeOriginalLess(const MappedRange & lhs,const MappedRange & rhs)151 bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) {
152   if (lhs.rva_original < rhs.rva_original)
153     return true;
154   if (lhs.rva_original > rhs.rva_original)
155     return false;
156   return lhs.length < rhs.length;
157 }
MappedRangeMappedLess(const MappedRange & lhs,const MappedRange & rhs)158 bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) {
159   if (lhs.rva_transformed < rhs.rva_transformed)
160     return true;
161   if (lhs.rva_transformed > rhs.rva_transformed)
162     return false;
163   return lhs.length < rhs.length;
164 }
165 
166 // Used for searching through the EndpointIndexMap.
EndpointIndexLess(const EndpointIndex & ei1,const EndpointIndex & ei2)167 bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) {
168   return ei1.endpoint < ei2.endpoint;
169 }
170 
171 // Finds the debug stream with the given |name| in the given |session|, and
172 // populates |table| with its contents. Casts the data directly into OMAP
173 // structs.
FindAndLoadOmapTable(const wchar_t * name,IDiaSession * session,OmapTable * table)174 bool FindAndLoadOmapTable(const wchar_t* name,
175                           IDiaSession* session,
176                           OmapTable* table) {
177   assert(name != NULL);
178   assert(session != NULL);
179   assert(table != NULL);
180 
181   CComPtr<IDiaEnumDebugStreamData> stream;
182   if (!FindDebugStream(name, session, &stream))
183     return false;
184   assert(stream.p != NULL);
185 
186   LONG count = 0;
187   if (FAILED(stream->get_Count(&count))) {
188     fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream "
189                     "\"%ws\"\n", name);
190     return false;
191   }
192 
193   // Get the length of the stream in bytes.
194   DWORD bytes_read = 0;
195   ULONG count_read = 0;
196   if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) {
197     fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
198                     "length of stream \"%ws\"\n", name);
199     return false;
200   }
201 
202   // Ensure it's consistent with the OMAP data type.
203   DWORD bytes_expected = count * sizeof(OmapTable::value_type);
204   if (count * sizeof(OmapTable::value_type) != bytes_read) {
205     fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name);
206     return false;
207   }
208 
209   // Read the table.
210   table->resize(count);
211   bytes_read = 0;
212   count_read = 0;
213   if (FAILED(stream->Next(count, bytes_expected, &bytes_read,
214                           reinterpret_cast<BYTE*>(&table->at(0)),
215                           &count_read))) {
216     fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
217                     "data from stream \"%ws\"\n", name);
218     return false;
219   }
220 
221   return true;
222 }
223 
224 // This determines the original image length by looking through the segment
225 // table.
GetOriginalImageLength(IDiaSession * session,DWORD * image_length)226 bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) {
227   assert(session != NULL);
228   assert(image_length != NULL);
229 
230   CComPtr<IDiaEnumSegments> enum_segments;
231   if (!FindTable(session, &enum_segments))
232     return false;
233   assert(enum_segments.p != NULL);
234 
235   DWORD temp_image_length = 0;
236   CComPtr<IDiaSegment> segment;
237   ULONG fetched = 0;
238   while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) &&
239          fetched == 1) {
240     assert(segment.p != NULL);
241 
242     DWORD rva = 0;
243     DWORD length = 0;
244     DWORD frame = 0;
245     if (FAILED(segment->get_relativeVirtualAddress(&rva)) ||
246         FAILED(segment->get_length(&length)) ||
247         FAILED(segment->get_frame(&frame))) {
248       fprintf(stderr, "Failed to get basic properties for IDiaSegment\n");
249       return false;
250     }
251 
252     if (frame > 0) {
253       DWORD segment_end = rva + length;
254       if (segment_end > temp_image_length)
255         temp_image_length = segment_end;
256     }
257     segment.Release();
258   }
259 
260   *image_length = temp_image_length;
261   return true;
262 }
263 
264 // Detects regions of the original image that have been removed in the
265 // transformed image, and sets the 'removed' property on all mapped ranges
266 // immediately preceding a gap. The mapped ranges must be sorted by
267 // 'rva_original'.
FillInRemovedLengths(Mapping * mapping)268 void FillInRemovedLengths(Mapping* mapping) {
269   assert(mapping != NULL);
270 
271   // Find and fill gaps. We do this with two sweeps. We first sweep forward
272   // looking for gaps. When we identify a gap we then sweep forward with a
273   // second scan and set the 'removed' property for any intervals that
274   // immediately precede the gap.
275   //
276   // Gaps are typically between two successive intervals, but not always:
277   //
278   //   Range 1: ---------------
279   //   Range 2:     -------
280   //   Range 3:                      -------------
281   //   Gap    :                ******
282   //
283   // In the above example the gap is between range 1 and range 3. A forward
284   // sweep finds the gap, and a second forward sweep identifies that range 1
285   // immediately precedes the gap and sets its 'removed' property.
286 
287   size_t fill = 0;
288   DWORD rva_front = 0;
289   for (size_t find = 0; find < mapping->size(); ++find) {
290 #ifndef NDEBUG
291     // We expect the mapped ranges to be sorted by 'rva_original'.
292     if (find > 0) {
293       assert(mapping->at(find - 1).rva_original <=
294                  mapping->at(find).rva_original);
295     }
296 #endif
297 
298     if (rva_front < mapping->at(find).rva_original) {
299       // We've found a gap. Fill it in by setting the 'removed' property for
300       // any affected intervals.
301       DWORD removed = mapping->at(find).rva_original - rva_front;
302       for (; fill < find; ++fill) {
303         if (mapping->at(fill).rva_original + mapping->at(fill).length !=
304                 rva_front) {
305           continue;
306         }
307 
308         // This interval ends right where the gap starts. It needs to have its
309         // 'removed' information filled in.
310         mapping->at(fill).removed = removed;
311       }
312     }
313 
314     // Advance the front that indicates the covered portion of the image.
315     rva_front = mapping->at(find).rva_original + mapping->at(find).length;
316   }
317 }
318 
319 // Builds a unified view of the mapping between the original and transformed
320 // image space by merging OMAPTO and OMAPFROM data.
BuildMapping(const OmapData & omap_data,Mapping * mapping)321 void BuildMapping(const OmapData& omap_data, Mapping* mapping) {
322   assert(mapping != NULL);
323 
324   mapping->clear();
325 
326   if (omap_data.omap_from.empty() || omap_data.omap_to.empty())
327     return;
328 
329   // The names 'omap_to' and 'omap_from' are awfully confusing, so we make
330   // ourselves more explicit here. This cast is only safe because the underlying
331   // types have the exact same size.
332   const OmapToTable& tran2orig =
333       reinterpret_cast<const OmapToTable&>(omap_data.omap_to);
334   const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>(
335       omap_data.omap_from);
336 
337   // Handle the range of data at the beginning of the image. This is not usually
338   // specified by the OMAP data.
339   if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) {
340     DWORD header_transformed = tran2orig[0].rva_transformed;
341     DWORD header_original = orig2tran[0].rva_original;
342     DWORD header = Min(header_transformed, header_original);
343 
344     MappedRange mr = {};
345     mr.length = header;
346     mr.injected = header_transformed - header;
347     mr.removed = header_original - header;
348     mapping->push_back(mr);
349   }
350 
351   // Convert the implied lengths to explicit lengths, and infer which content
352   // has been injected into the transformed image. Injected content is inferred
353   // as regions of the transformed address space that does not map back to
354   // known valid content in the original image.
355   for (size_t i = 0; i < tran2orig.size(); ++i) {
356     const OmapTranToOrig& o1 = tran2orig[i];
357 
358     // This maps to content that is outside the original image, thus it
359     // describes injected content. We can skip this entry.
360     if (o1.rva_original >= omap_data.length_original)
361       continue;
362 
363     // Calculate the length of the current OMAP entry. This is implicit as the
364     // distance between successive |rva| values, capped at the end of the
365     // original image.
366     DWORD length = 0;
367     if (i + 1 < tran2orig.size()) {
368       const OmapTranToOrig& o2 = tran2orig[i + 1];
369 
370       // We expect the table to be sorted by rva_transformed.
371       assert(o1.rva_transformed <= o2.rva_transformed);
372 
373       length = o2.rva_transformed - o1.rva_transformed;
374       if (o1.rva_original + length > omap_data.length_original) {
375         length = omap_data.length_original - o1.rva_original;
376       }
377     } else {
378       length = omap_data.length_original - o1.rva_original;
379     }
380 
381     // Zero-length entries don't describe anything and can be ignored.
382     if (length == 0)
383       continue;
384 
385     // Any gaps in the transformed address-space are due to injected content.
386     if (!mapping->empty()) {
387       MappedRange& prev_mr = mapping->back();
388       prev_mr.injected += o1.rva_transformed -
389           (prev_mr.rva_transformed + prev_mr.length);
390     }
391 
392     MappedRange mr = {};
393     mr.rva_original = o1.rva_original;
394     mr.rva_transformed = o1.rva_transformed;
395     mr.length = length;
396     mapping->push_back(mr);
397   }
398 
399   // Sort based on the original image addresses.
400   std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess);
401 
402   // Fill in the 'removed' lengths by looking for gaps in the coverage of the
403   // original address space.
404   FillInRemovedLengths(mapping);
405 
406   return;
407 }
408 
BuildEndpointIndexMap(ImageMap * image_map)409 void BuildEndpointIndexMap(ImageMap* image_map) {
410   assert(image_map != NULL);
411 
412   if (image_map->mapping.size() == 0)
413     return;
414 
415   const Mapping& mapping = image_map->mapping;
416   EndpointIndexMap& eim = image_map->endpoint_index_map;
417 
418   // Get the unique set of interval endpoints.
419   std::set<DWORD> endpoints;
420   for (size_t i = 0; i < mapping.size(); ++i) {
421     endpoints.insert(mapping[i].rva_original);
422     endpoints.insert(mapping[i].rva_original +
423                          mapping[i].length +
424                          mapping[i].removed);
425   }
426 
427   // Use the endpoints to initialize the secondary search structure for the
428   // mapping.
429   eim.resize(endpoints.size());
430   std::set<DWORD>::const_iterator it = endpoints.begin();
431   for (size_t i = 0; it != endpoints.end(); ++it, ++i) {
432     eim[i].endpoint = *it;
433     eim[i].index = mapping.size();
434   }
435 
436   // For each endpoint we want the smallest index of any interval containing
437   // it. We iterate over the intervals and update the indices associated with
438   // each interval endpoint contained in the current interval. In the general
439   // case of an arbitrary set of intervals this is O(n^2), but the structure of
440   // OMAP data makes this O(n).
441   for (size_t i = 0; i < mapping.size(); ++i) {
442     EndpointIndex ei1 = { mapping[i].rva_original, 0 };
443     EndpointIndexMap::iterator it1 = std::lower_bound(
444         eim.begin(), eim.end(), ei1, EndpointIndexLess);
445 
446     EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length +
447                               mapping[i].removed, 0 };
448     EndpointIndexMap::iterator it2 = std::lower_bound(
449         eim.begin(), eim.end(), ei2, EndpointIndexLess);
450 
451     for (; it1 != it2; ++it1)
452       it1->index = Min(i, it1->index);
453   }
454 }
455 
BuildSubsequentRVAMap(const OmapData & omap_data,std::map<DWORD,DWORD> * subsequent)456 void BuildSubsequentRVAMap(const OmapData& omap_data,
457                            std::map<DWORD, DWORD>* subsequent) {
458   assert(subsequent->empty());
459   const OmapFromTable& orig2tran =
460       reinterpret_cast<const OmapFromTable&>(omap_data.omap_from);
461 
462   if (orig2tran.empty())
463     return;
464 
465   for (size_t i = 0; i < orig2tran.size() - 1; ++i) {
466     // Expect that orig2tran is sorted.
467     if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) {
468       fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n");
469       subsequent->clear();
470       return;
471     }
472     subsequent->insert(std::make_pair(orig2tran[i].rva_original,
473                                       orig2tran[i + 1].rva_original));
474   }
475 }
476 
477 // Clips the given mapped range.
ClipMappedRangeOriginal(const AddressRange & clip_range,MappedRange * mapped_range)478 void ClipMappedRangeOriginal(const AddressRange& clip_range,
479                              MappedRange* mapped_range) {
480   assert(mapped_range != NULL);
481 
482   // The clipping range is entirely outside of the mapped range.
483   if (clip_range.end() <= mapped_range->rva_original ||
484       mapped_range->rva_original + mapped_range->length +
485           mapped_range->removed <= clip_range.rva) {
486     mapped_range->length = 0;
487     mapped_range->injected = 0;
488     mapped_range->removed = 0;
489     return;
490   }
491 
492   // Clip the left side.
493   if (mapped_range->rva_original < clip_range.rva) {
494     DWORD clip_left = clip_range.rva - mapped_range->rva_original;
495     mapped_range->rva_original += clip_left;
496     mapped_range->rva_transformed += clip_left;
497 
498     if (clip_left > mapped_range->length) {
499       // The left clipping boundary entirely erases the content section of the
500       // range.
501       DWORD trim = clip_left - mapped_range->length;
502       mapped_range->length = 0;
503       mapped_range->injected -= Min(trim, mapped_range->injected);
504       // We know that trim <= mapped_range->remove.
505       mapped_range->removed -= trim;
506     } else {
507       // The left clipping boundary removes some, but not all, of the content.
508       // As such it leaves the removed/injected component intact.
509       mapped_range->length -= clip_left;
510     }
511   }
512 
513   // Clip the right side.
514   DWORD end_original = mapped_range->rva_original + mapped_range->length;
515   if (clip_range.end() < end_original) {
516     // The right clipping boundary lands in the 'content' section of the range,
517     // entirely clearing the injected/removed portion.
518     DWORD clip_right = end_original - clip_range.end();
519     mapped_range->length -= clip_right;
520     mapped_range->injected = 0;
521     mapped_range->removed = 0;
522     return;
523   } else {
524     // The right clipping boundary is outside of the content, but may affect
525     // the removed/injected portion of the range.
526     DWORD end_removed = end_original + mapped_range->removed;
527     if (clip_range.end() < end_removed)
528       mapped_range->removed = clip_range.end() - end_original;
529 
530     DWORD end_injected = end_original + mapped_range->injected;
531     if (clip_range.end() < end_injected)
532       mapped_range->injected = clip_range.end() - end_original;
533   }
534 
535   return;
536 }
537 
538 }  // namespace
539 
Compare(const AddressRange & rhs) const540 int AddressRange::Compare(const AddressRange& rhs) const {
541   if (end() <= rhs.rva)
542     return -1;
543   if (rhs.end() <= rva)
544     return 1;
545   return 0;
546 }
547 
GetOmapDataAndDisableTranslation(IDiaSession * session,OmapData * omap_data)548 bool GetOmapDataAndDisableTranslation(IDiaSession* session,
549                                       OmapData* omap_data) {
550   assert(session != NULL);
551   assert(omap_data != NULL);
552 
553   CComPtr<IDiaAddressMap> address_map;
554   if (FAILED(session->QueryInterface(&address_map))) {
555     fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
556     return false;
557   }
558   assert(address_map.p != NULL);
559 
560   BOOL omap_enabled = FALSE;
561   if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
562     fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
563     return false;
564   }
565 
566   if (!omap_enabled) {
567     // We indicate the non-presence of OMAP data by returning empty tables.
568     omap_data->omap_from.clear();
569     omap_data->omap_to.clear();
570     omap_data->length_original = 0;
571     return true;
572   }
573 
574   // OMAP data is present. Disable translation.
575   if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
576     fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
577     return false;
578   }
579 
580   // Read the OMAP streams.
581   if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
582                             session,
583                             &omap_data->omap_from)) {
584     return false;
585   }
586   if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
587                             session,
588                             &omap_data->omap_to)) {
589     return false;
590   }
591 
592   // Get the lengths of the address spaces.
593   if (!GetOriginalImageLength(session, &omap_data->length_original))
594     return false;
595 
596   return true;
597 }
598 
BuildImageMap(const OmapData & omap_data,ImageMap * image_map)599 void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
600   assert(image_map != NULL);
601 
602   BuildMapping(omap_data, &image_map->mapping);
603   BuildEndpointIndexMap(image_map);
604   BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block);
605 }
606 
MapAddressRange(const ImageMap & image_map,const AddressRange & original_range,AddressRangeVector * mapped_ranges)607 void MapAddressRange(const ImageMap& image_map,
608                      const AddressRange& original_range,
609                      AddressRangeVector* mapped_ranges) {
610   assert(mapped_ranges != NULL);
611 
612   const Mapping& map = image_map.mapping;
613 
614   // Handle the trivial case of an empty image_map. This means that there is
615   // no transformation to be applied, and we can simply return the original
616   // range.
617   if (map.empty()) {
618     mapped_ranges->push_back(original_range);
619     return;
620   }
621 
622   // If we get a query of length 0 we need to handle it by using a non-zero
623   // query length.
624   AddressRange query_range(original_range);
625   if (query_range.length == 0)
626     query_range.length = 1;
627 
628   // Find the range of intervals that can potentially intersect our query range.
629   size_t imin = 0;
630   size_t imax = 0;
631   {
632     // The index of the earliest possible range that can affect is us done by
633     // searching through the secondary indexing structure.
634     const EndpointIndexMap& eim = image_map.endpoint_index_map;
635     EndpointIndex q1 = { query_range.rva, 0 };
636     EndpointIndexMap::const_iterator it1 = std::lower_bound(
637         eim.begin(), eim.end(), q1, EndpointIndexLess);
638     if (it1 == eim.end()) {
639       imin  = map.size();
640     } else {
641       // Backup to find the interval that contains our query point.
642       if (it1 != eim.begin() && query_range.rva < it1->endpoint)
643         --it1;
644       imin = it1->index;
645     }
646 
647     // The first range that can't possibly intersect us is found by searching
648     // through the image map directly as it is already sorted by interval start
649     // point.
650     MappedRange q2 = { query_range.end(), 0 };
651     Mapping::const_iterator it2 = std::lower_bound(
652         map.begin(), map.end(), q2, MappedRangeOriginalLess);
653     imax = it2 - map.begin();
654   }
655 
656   // Find all intervals that intersect the query range.
657   Mapping temp_map;
658   for (size_t i = imin; i < imax; ++i) {
659     MappedRange mr = map[i];
660     ClipMappedRangeOriginal(query_range, &mr);
661     if (mr.length + mr.injected > 0)
662       temp_map.push_back(mr);
663   }
664 
665   // If there are no intersecting ranges then the query range has been removed
666   // from the image in question.
667   if (temp_map.empty())
668     return;
669 
670   // Sort based on transformed addresses.
671   std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
672 
673   // Zero-length queries can't actually be merged. We simply output the set of
674   // unique RVAs that correspond to the query RVA.
675   if (original_range.length == 0) {
676     mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
677     for (size_t i = 1; i < temp_map.size(); ++i) {
678       if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
679         mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
680     }
681     return;
682   }
683 
684   // Merge any ranges that are consecutive in the mapped image. We merge over
685   // injected content if it makes ranges contiguous, but we ignore any injected
686   // content at the tail end of a range. This allows us to detect symbols that
687   // have been lengthened by injecting content in the middle. However, it
688   // misses the case where content has been injected at the head or the tail.
689   // The problem is that it doesn't know whether to attribute it to the
690   // preceding or following symbol. It is up to the author of the transform to
691   // output explicit OMAP info in these cases to ensure full coverage of the
692   // transformed address space.
693   DWORD rva_begin = temp_map[0].rva_transformed;
694   DWORD rva_cur_content = rva_begin + temp_map[0].length;
695   DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
696   for (size_t i = 1; i < temp_map.size(); ++i) {
697     if (rva_cur_injected < temp_map[i].rva_transformed) {
698       // This marks the end of a continuous range in the image. Output the
699       // current range and start a new one.
700       if (rva_begin < rva_cur_content) {
701         mapped_ranges->push_back(
702             AddressRange(rva_begin, rva_cur_content - rva_begin));
703       }
704       rva_begin = temp_map[i].rva_transformed;
705     }
706 
707     rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
708     rva_cur_injected = rva_cur_content + temp_map[i].injected;
709   }
710 
711   // Output the range in progress.
712   if (rva_begin < rva_cur_content) {
713     mapped_ranges->push_back(
714         AddressRange(rva_begin, rva_cur_content - rva_begin));
715   }
716 
717   return;
718 }
719 
720 }  // namespace google_breakpad
721