diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/windows/dia_util.cc | 2 | ||||
-rw-r--r-- | src/common/windows/omap.cc | 1388 | ||||
-rw-r--r-- | src/common/windows/pdb_source_line_writer.cc | 2 | ||||
-rw-r--r-- | src/tools/windows/binaries/symupload.exe | bin | 206336 -> 195072 bytes | |||
-rw-r--r-- | src/tools/windows/symupload/symupload.cc | 53 | ||||
-rw-r--r-- | src/tools/windows/symupload/symupload.vcproj | 251 |
6 files changed, 737 insertions, 959 deletions
diff --git a/src/common/windows/dia_util.cc b/src/common/windows/dia_util.cc index 25a9a33f..ed8cb5b6 100644 --- a/src/common/windows/dia_util.cc +++ b/src/common/windows/dia_util.cc @@ -89,4 +89,4 @@ bool FindTable(REFIID iid, IDiaSession* session, void** table) { return false;
}
-} // namespace google_breakpad
+} // namespace google_breakpad
\ No newline at end of file diff --git a/src/common/windows/omap.cc b/src/common/windows/omap.cc index 129ee4ae..67b92065 100644 --- a/src/common/windows/omap.cc +++ b/src/common/windows/omap.cc @@ -1,694 +1,694 @@ -// Copyright 2013 Google Inc. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// This contains a suite of tools for transforming symbol information when
-// when that information has been extracted from a PDB containing OMAP
-// information.
-
-// OMAP information is a lightweight description of a mapping between two
-// address spaces. It consists of two streams, each of them a vector 2-tuples.
-// The OMAPTO stream contains tuples of the form
-//
-// (RVA in transformed image, RVA in original image)
-//
-// while the OMAPFROM stream contains tuples of the form
-//
-// (RVA in original image, RVA in transformed image)
-//
-// The entries in each vector are sorted by the first value of the tuple, and
-// the lengths associated with a mapping are implicit as the distance between
-// two successive addresses in the vector.
-
-// Consider a trivial 10-byte function described by the following symbol:
-//
-// Function: RVA 0x00001000, length 10, "foo"
-//
-// Now consider the same function, but with 5-bytes of instrumentation injected
-// at offset 5. The OMAP streams describing this would look like:
-//
-// OMAPTO : [ [0x00001000, 0x00001000],
-// [0x00001005, 0xFFFFFFFF],
-// [0x0000100a, 0x00001005] ]
-// OMAPFROM: [ [0x00001000, 0x00001000],
-// [0x00001005, 0x0000100a] ]
-//
-// In this case the injected code has been marked as not originating in the
-// source image, and thus it will have no symbol information at all. However,
-// the injected code may also be associated with an original address range;
-// for example, when prepending instrumentation to a basic block the
-// instrumentation can be labelled as originating from the same source BB such
-// that symbol resolution will still find the appropriate source code line
-// number. In this case the OMAP stream would look like:
-//
-// OMAPTO : [ [0x00001000, 0x00001000],
-// [0x00001005, 0x00001005],
-// [0x0000100a, 0x00001005] ]
-// OMAPFROM: [ [0x00001000, 0x00001000],
-// [0x00001005, 0x0000100a] ]
-//
-// Suppose we asked DIA to lookup the symbol at location 0x0000100a of the
-// instrumented image. It would first run this through the OMAPTO table and
-// translate that address to 0x00001005. It would then lookup the symbol
-// at that address and return the symbol for the function "foo". This is the
-// correct result.
-//
-// However, if we query DIA for the length and address of the symbol it will
-// tell us that it has length 10 and is at RVA 0x00001000. The location is
-// correct, but the length doesn't take into account the 5-bytes of injected
-// code. Symbol resolution works (starting from an instrumented address,
-// mapping to an original address, and looking up a symbol), but the symbol
-// metadata is incorrect.
-//
-// If we dump the symbols using DIA they will have their addresses
-// appropriately transformed and reflect positions in the instrumented image.
-// However, if we try to do a lookup using those symbols resolution can fail.
-// For example, the address 0x0000100a will not map to the symbol for "foo",
-// because DIA tells us it is at location 0x00001000 (correct) with length
-// 10 (incorrect). The problem is one of order of operations: in this case
-// we're attempting symbol resolution by looking up an instrumented address
-// in the table of translated symbols.
-//
-// One way to handle this is to dump the OMAP information as part of the
-// breakpad symbols. This requires the rest of the toolchain to be aware of
-// OMAP information and to use it when present prior to performing lookup. The
-// other option is to properly transform the symbols (updating length as well as
-// position) so that resolution will work as expected for translated addresses.
-// This is transparent to the rest of the toolchain.
-
-#include "common/windows/omap.h"
-
-#include <atlbase.h>
-
-#include <algorithm>
-#include <cassert>
-#include <set>
-
-#include "common/windows/dia_util.h"
-
-namespace google_breakpad {
-
-namespace {
-
-static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO";
-static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM";
-
-// Dependending on where this is used in breakpad we sometimes get min/max from
-// windef, and other times from algorithm. To get around this we simply
-// define our own min/max functions.
-template<typename T>
-const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
-template<typename T>
-const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; }
-
-// It makes things more readable to have two different OMAP types. We cast
-// normal OMAPs into these. They must be the same size as the OMAP structure
-// for this to work, hence the static asserts.
-struct OmapOrigToTran {
- DWORD rva_original;
- DWORD rva_transformed;
-};
-struct OmapTranToOrig {
- DWORD rva_transformed;
- DWORD rva_original;
-};
-static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP),
- "OmapOrigToTran must have same size as OMAP.");
-static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP),
- "OmapTranToOrig must have same size as OMAP.");
-typedef std::vector<OmapOrigToTran> OmapFromTable;
-typedef std::vector<OmapTranToOrig> OmapToTable;
-
-// Used for sorting and searching through a Mapping.
-bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) {
- if (lhs.rva_original < rhs.rva_original)
- return true;
- if (lhs.rva_original > rhs.rva_original)
- return false;
- return lhs.length < rhs.length;
-}
-bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) {
- if (lhs.rva_transformed < rhs.rva_transformed)
- return true;
- if (lhs.rva_transformed > rhs.rva_transformed)
- return false;
- return lhs.length < rhs.length;
-}
-
-// Used for searching through the EndpointIndexMap.
-bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) {
- return ei1.endpoint < ei2.endpoint;
-}
-
-// Finds the debug stream with the given |name| in the given |session|, and
-// populates |table| with its contents. Casts the data directly into OMAP
-// structs.
-bool FindAndLoadOmapTable(const wchar_t* name,
- IDiaSession* session,
- OmapTable* table) {
- assert(name != NULL);
- assert(session != NULL);
- assert(table != NULL);
-
- CComPtr<IDiaEnumDebugStreamData> stream;
- if (!FindDebugStream(name, session, &stream))
- return false;
- assert(stream.p != NULL);
-
- LONG count = 0;
- if (FAILED(stream->get_Count(&count))) {
- fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream "
- "\"%ws\"\n", name);
- return false;
- }
-
- // Get the length of the stream in bytes.
- DWORD bytes_read = 0;
- ULONG count_read = 0;
- if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) {
- fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
- "length of stream \"%ws\"\n", name);
- return false;
- }
-
- // Ensure it's consistent with the OMAP data type.
- DWORD bytes_expected = count * sizeof(OmapTable::value_type);
- if (count * sizeof(OmapTable::value_type) != bytes_read) {
- fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name);
- return false;
- }
-
- // Read the table.
- table->resize(count);
- bytes_read = 0;
- count_read = 0;
- if (FAILED(stream->Next(count, bytes_expected, &bytes_read,
- reinterpret_cast<BYTE*>(&table->at(0)),
- &count_read))) {
- fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
- "data from stream \"%ws\"\n");
- return false;
- }
-
- return true;
-}
-
-// This determines the original image length by looking through the segment
-// table.
-bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) {
- assert(session != NULL);
- assert(image_length != NULL);
-
- CComPtr<IDiaEnumSegments> enum_segments;
- if (!FindTable(session, &enum_segments))
- return false;
- assert(enum_segments.p != NULL);
-
- DWORD temp_image_length = 0;
- CComPtr<IDiaSegment> segment;
- ULONG fetched = 0;
- while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) &&
- fetched == 1) {
- assert(segment.p != NULL);
-
- DWORD rva = 0;
- DWORD length = 0;
- DWORD frame = 0;
- if (FAILED(segment->get_relativeVirtualAddress(&rva)) ||
- FAILED(segment->get_length(&length)) ||
- FAILED(segment->get_frame(&frame))) {
- fprintf(stderr, "Failed to get basic properties for IDiaSegment\n");
- return false;
- }
-
- if (frame > 0) {
- DWORD segment_end = rva + length;
- if (segment_end > temp_image_length)
- temp_image_length = segment_end;
- }
- segment.Release();
- }
-
- *image_length = temp_image_length;
- return true;
-}
-
-// Detects regions of the original image that have been removed in the
-// transformed image, and sets the 'removed' property on all mapped ranges
-// immediately preceding a gap. The mapped ranges must be sorted by
-// 'rva_original'.
-void FillInRemovedLengths(Mapping* mapping) {
- assert(mapping != NULL);
-
- // Find and fill gaps. We do this with two sweeps. We first sweep forward
- // looking for gaps. When we identify a gap we then sweep forward with a
- // second scan and set the 'removed' property for any intervals that
- // immediately precede the gap.
- //
- // Gaps are typically between two successive intervals, but not always:
- //
- // Range 1: ---------------
- // Range 2: -------
- // Range 3: -------------
- // Gap : ******
- //
- // In the above example the gap is between range 1 and range 3. A forward
- // sweep finds the gap, and a second forward sweep identifies that range 1
- // immediately precedes the gap and sets its 'removed' property.
-
- size_t fill = 0;
- DWORD rva_front = 0;
- for (size_t find = 0; find < mapping->size(); ++find) {
-#ifndef NDEBUG
- // We expect the mapped ranges to be sorted by 'rva_original'.
- if (find > 0) {
- assert(mapping->at(find - 1).rva_original <=
- mapping->at(find).rva_original);
- }
-#endif
-
- if (rva_front < mapping->at(find).rva_original) {
- // We've found a gap. Fill it in by setting the 'removed' property for
- // any affected intervals.
- DWORD removed = mapping->at(find).rva_original - rva_front;
- for (; fill < find; ++fill) {
- if (mapping->at(fill).rva_original + mapping->at(fill).length !=
- rva_front) {
- continue;
- }
-
- // This interval ends right where the gap starts. It needs to have its
- // 'removed' information filled in.
- mapping->at(fill).removed = removed;
- }
- }
-
- // Advance the front that indicates the covered portion of the image.
- rva_front = mapping->at(find).rva_original + mapping->at(find).length;
- }
-}
-
-// Builds a unified view of the mapping between the original and transformed
-// image space by merging OMAPTO and OMAPFROM data.
-void BuildMapping(const OmapData& omap_data, Mapping* mapping) {
- assert(mapping != NULL);
-
- mapping->clear();
-
- if (omap_data.omap_from.empty() || omap_data.omap_to.empty())
- return;
-
- // The names 'omap_to' and 'omap_from' are awfully confusing, so we make
- // ourselves more explicit here. This cast is only safe because the underlying
- // types have the exact same size.
- const OmapToTable& tran2orig =
- reinterpret_cast<const OmapToTable&>(omap_data.omap_to);
- const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>(
- omap_data.omap_from);
-
- // Handle the range of data at the beginning of the image. This is not usually
- // specified by the OMAP data.
- if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) {
- DWORD header_transformed = tran2orig[0].rva_transformed;
- DWORD header_original = orig2tran[0].rva_original;
- DWORD header = Min(header_transformed, header_original);
-
- MappedRange mr = {};
- mr.length = header;
- mr.injected = header_transformed - header;
- mr.removed = header_original - header;
- mapping->push_back(mr);
- }
-
- // Convert the implied lengths to explicit lengths, and infer which content
- // has been injected into the transformed image. Injected content is inferred
- // as regions of the transformed address space that does not map back to
- // known valid content in the original image.
- for (size_t i = 0; i < tran2orig.size(); ++i) {
- const OmapTranToOrig& o1 = tran2orig[i];
-
- // This maps to content that is outside the original image, thus it
- // describes injected content. We can skip this entry.
- if (o1.rva_original >= omap_data.length_original)
- continue;
-
- // Calculate the length of the current OMAP entry. This is implicit as the
- // distance between successive |rva| values, capped at the end of the
- // original image.
- DWORD length = 0;
- if (i + 1 < tran2orig.size()) {
- const OmapTranToOrig& o2 = tran2orig[i + 1];
-
- // We expect the table to be sorted by rva_transformed.
- assert(o1.rva_transformed <= o2.rva_transformed);
-
- length = o2.rva_transformed - o1.rva_transformed;
- if (o1.rva_original + length > omap_data.length_original) {
- length = omap_data.length_original - o1.rva_original;
- }
- } else {
- length = omap_data.length_original - o1.rva_original;
- }
-
- // Zero-length entries don't describe anything and can be ignored.
- if (length == 0)
- continue;
-
- // Any gaps in the transformed address-space are due to injected content.
- if (!mapping->empty()) {
- MappedRange& prev_mr = mapping->back();
- prev_mr.injected += o1.rva_transformed -
- (prev_mr.rva_transformed + prev_mr.length);
- }
-
- MappedRange mr = {};
- mr.rva_original = o1.rva_original;
- mr.rva_transformed = o1.rva_transformed;
- mr.length = length;
- mapping->push_back(mr);
- }
-
- // Sort based on the original image addresses.
- std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess);
-
- // Fill in the 'removed' lengths by looking for gaps in the coverage of the
- // original address space.
- FillInRemovedLengths(mapping);
-
- return;
-}
-
-void BuildEndpointIndexMap(ImageMap* image_map) {
- assert(image_map != NULL);
-
- if (image_map->mapping.size() == 0)
- return;
-
- const Mapping& mapping = image_map->mapping;
- EndpointIndexMap& eim = image_map->endpoint_index_map;
-
- // Get the unique set of interval endpoints.
- std::set<DWORD> endpoints;
- for (size_t i = 0; i < mapping.size(); ++i) {
- endpoints.insert(mapping[i].rva_original);
- endpoints.insert(mapping[i].rva_original +
- mapping[i].length +
- mapping[i].removed);
- }
-
- // Use the endpoints to initialize the secondary search structure for the
- // mapping.
- eim.resize(endpoints.size());
- std::set<DWORD>::const_iterator it = endpoints.begin();
- for (size_t i = 0; it != endpoints.end(); ++it, ++i) {
- eim[i].endpoint = *it;
- eim[i].index = mapping.size();
- }
-
- // For each endpoint we want the smallest index of any interval containing
- // it. We iterate over the intervals and update the indices associated with
- // each interval endpoint contained in the current interval. In the general
- // case of an arbitrary set of intervals this is O(n^2), but the structure of
- // OMAP data makes this O(n).
- for (size_t i = 0; i < mapping.size(); ++i) {
- EndpointIndex ei1 = { mapping[i].rva_original, 0 };
- EndpointIndexMap::iterator it1 = std::lower_bound(
- eim.begin(), eim.end(), ei1, EndpointIndexLess);
-
- EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length +
- mapping[i].removed, 0 };
- EndpointIndexMap::iterator it2 = std::lower_bound(
- eim.begin(), eim.end(), ei2, EndpointIndexLess);
-
- for (; it1 != it2; ++it1)
- it1->index = Min(i, it1->index);
- }
-}
-
-// Clips the given mapped range.
-void ClipMappedRangeOriginal(const AddressRange& clip_range,
- MappedRange* mapped_range) {
- assert(mapped_range != NULL);
-
- // The clipping range is entirely outside of the mapped range.
- if (clip_range.end() <= mapped_range->rva_original ||
- mapped_range->rva_original + mapped_range->length +
- mapped_range->removed <= clip_range.rva) {
- mapped_range->length = 0;
- mapped_range->injected = 0;
- mapped_range->removed = 0;
- return;
- }
-
- // Clip the left side.
- if (mapped_range->rva_original < clip_range.rva) {
- DWORD clip_left = clip_range.rva - mapped_range->rva_original;
- mapped_range->rva_original += clip_left;
- mapped_range->rva_transformed += clip_left;
-
- if (clip_left > mapped_range->length) {
- // The left clipping boundary entirely erases the content section of the
- // range.
- DWORD trim = clip_left - mapped_range->length;
- mapped_range->length = 0;
- mapped_range->injected -= Min(trim, mapped_range->injected);
- // We know that trim <= mapped_range->remove.
- mapped_range->removed -= trim;
- } else {
- // The left clipping boundary removes some, but not all, of the content.
- // As such it leaves the removed/injected component intact.
- mapped_range->length -= clip_left;
- }
- }
-
- // Clip the right side.
- DWORD end_original = mapped_range->rva_original + mapped_range->length;
- if (clip_range.end() < end_original) {
- // The right clipping boundary lands in the 'content' section of the range,
- // entirely clearing the injected/removed portion.
- DWORD clip_right = end_original - clip_range.end();
- mapped_range->length -= clip_right;
- mapped_range->injected = 0;
- mapped_range->removed = 0;
- return;
- } else {
- // The right clipping boundary is outside of the content, but may affect
- // the removed/injected portion of the range.
- DWORD end_removed = end_original + mapped_range->removed;
- if (clip_range.end() < end_removed)
- mapped_range->removed = clip_range.end() - end_original;
-
- DWORD end_injected = end_original + mapped_range->injected;
- if (clip_range.end() < end_injected)
- mapped_range->injected = clip_range.end() - end_original;
- }
-
- return;
-}
-
-} // namespace
-
-int AddressRange::Compare(const AddressRange& rhs) const {
- if (end() <= rhs.rva)
- return -1;
- if (rhs.end() <= rva)
- return 1;
- return 0;
-}
-
-bool GetOmapDataAndDisableTranslation(IDiaSession* session,
- OmapData* omap_data) {
- assert(session != NULL);
- assert(omap_data != NULL);
-
- CComPtr<IDiaAddressMap> address_map;
- if (FAILED(session->QueryInterface(&address_map))) {
- fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
- return false;
- }
- assert(address_map.p != NULL);
-
- BOOL omap_enabled = FALSE;
- if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
- fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
- return false;
- }
-
- if (!omap_enabled) {
- // We indicate the non-presence of OMAP data by returning empty tables.
- omap_data->omap_from.clear();
- omap_data->omap_to.clear();
- omap_data->length_original = 0;
- return true;
- }
-
- // OMAP data is present. Disable translation.
- if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
- fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
- return false;
- }
-
- // Read the OMAP streams.
- if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
- session,
- &omap_data->omap_from)) {
- return false;
- }
- if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
- session,
- &omap_data->omap_to)) {
- return false;
- }
-
- // Get the lengths of the address spaces.
- if (!GetOriginalImageLength(session, &omap_data->length_original))
- return false;
-
- return true;
-}
-
-void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
- assert(image_map != NULL);
-
- BuildMapping(omap_data, &image_map->mapping);
- BuildEndpointIndexMap(image_map);
-}
-
-void MapAddressRange(const ImageMap& image_map,
- const AddressRange& original_range,
- AddressRangeVector* mapped_ranges) {
- assert(mapped_ranges != NULL);
-
- const Mapping& map = image_map.mapping;
-
- // Handle the trivial case of an empty image_map. This means that there is
- // no transformation to be applied, and we can simply return the original
- // range.
- if (map.empty()) {
- mapped_ranges->push_back(original_range);
- return;
- }
-
- // If we get a query of length 0 we need to handle it by using a non-zero
- // query length.
- AddressRange query_range(original_range);
- if (query_range.length == 0)
- query_range.length = 1;
-
- // Find the range of intervals that can potentially intersect our query range.
- size_t imin = 0;
- size_t imax = 0;
- {
- // The index of the earliest possible range that can affect is us done by
- // searching through the secondary indexing structure.
- const EndpointIndexMap& eim = image_map.endpoint_index_map;
- EndpointIndex q1 = { query_range.rva, 0 };
- EndpointIndexMap::const_iterator it1 = std::lower_bound(
- eim.begin(), eim.end(), q1, EndpointIndexLess);
- if (it1 == eim.end()) {
- imin = map.size();
- } else {
- // Backup to find the interval that contains our query point.
- if (it1 != eim.begin() && query_range.rva < it1->endpoint)
- --it1;
- imin = it1->index;
- }
-
- // The first range that can't possibly intersect us is found by searching
- // through the image map directly as it is already sorted by interval start
- // point.
- MappedRange q2 = { query_range.end(), 0 };
- Mapping::const_iterator it2 = std::lower_bound(
- map.begin(), map.end(), q2, MappedRangeOriginalLess);
- imax = it2 - map.begin();
- }
-
- // Find all intervals that intersect the query range.
- Mapping temp_map;
- for (size_t i = imin; i < imax; ++i) {
- MappedRange mr = map[i];
- ClipMappedRangeOriginal(query_range, &mr);
- if (mr.length + mr.injected > 0)
- temp_map.push_back(mr);
- }
-
- // If there are no intersecting ranges then the query range has been removed
- // from the image in question.
- if (temp_map.empty())
- return;
-
- // Sort based on transformed addresses.
- std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
-
- // Zero-length queries can't actually be merged. We simply output the set of
- // unique RVAs that correspond to the query RVA.
- if (original_range.length == 0) {
- mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
- for (size_t i = 1; i < temp_map.size(); ++i) {
- if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
- mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
- }
- return;
- }
-
- // Merge any ranges that are consecutive in the mapped image. We merge over
- // injected content if it makes ranges contiguous, but we ignore any injected
- // content at the tail end of a range. This allows us to detect symbols that
- // have been lengthened by injecting content in the middle. However, it
- // misses the case where content has been injected at the head or the tail.
- // The problem is that it doesn't know whether to attribute it to the
- // preceding or following symbol. It is up to the author of the transform to
- // output explicit OMAP info in these cases to ensure full coverage of the
- // transformed address space.
- DWORD rva_begin = temp_map[0].rva_transformed;
- DWORD rva_cur_content = rva_begin + temp_map[0].length;
- DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
- for (size_t i = 1; i < temp_map.size(); ++i) {
- if (rva_cur_injected < temp_map[i].rva_transformed) {
- // This marks the end of a continuous range in the image. Output the
- // current range and start a new one.
- if (rva_begin < rva_cur_content) {
- mapped_ranges->push_back(
- AddressRange(rva_begin, rva_cur_content - rva_begin));
- }
- rva_begin = temp_map[i].rva_transformed;
- }
-
- rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
- rva_cur_injected = rva_cur_content + temp_map[i].injected;
- }
-
- // Output the range in progress.
- if (rva_begin < rva_cur_content) {
- mapped_ranges->push_back(
- AddressRange(rva_begin, rva_cur_content - rva_begin));
- }
-
- return;
-}
-
-} // namespace google_breakpad
+// Copyright 2013 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This contains a suite of tools for transforming symbol information when +// when that information has been extracted from a PDB containing OMAP +// information. + +// OMAP information is a lightweight description of a mapping between two +// address spaces. It consists of two streams, each of them a vector 2-tuples. +// The OMAPTO stream contains tuples of the form +// +// (RVA in transformed image, RVA in original image) +// +// while the OMAPFROM stream contains tuples of the form +// +// (RVA in original image, RVA in transformed image) +// +// The entries in each vector are sorted by the first value of the tuple, and +// the lengths associated with a mapping are implicit as the distance between +// two successive addresses in the vector. + +// Consider a trivial 10-byte function described by the following symbol: +// +// Function: RVA 0x00001000, length 10, "foo" +// +// Now consider the same function, but with 5-bytes of instrumentation injected +// at offset 5. The OMAP streams describing this would look like: +// +// OMAPTO : [ [0x00001000, 0x00001000], +// [0x00001005, 0xFFFFFFFF], +// [0x0000100a, 0x00001005] ] +// OMAPFROM: [ [0x00001000, 0x00001000], +// [0x00001005, 0x0000100a] ] +// +// In this case the injected code has been marked as not originating in the +// source image, and thus it will have no symbol information at all. However, +// the injected code may also be associated with an original address range; +// for example, when prepending instrumentation to a basic block the +// instrumentation can be labelled as originating from the same source BB such +// that symbol resolution will still find the appropriate source code line +// number. In this case the OMAP stream would look like: +// +// OMAPTO : [ [0x00001000, 0x00001000], +// [0x00001005, 0x00001005], +// [0x0000100a, 0x00001005] ] +// OMAPFROM: [ [0x00001000, 0x00001000], +// [0x00001005, 0x0000100a] ] +// +// Suppose we asked DIA to lookup the symbol at location 0x0000100a of the +// instrumented image. It would first run this through the OMAPTO table and +// translate that address to 0x00001005. It would then lookup the symbol +// at that address and return the symbol for the function "foo". This is the +// correct result. +// +// However, if we query DIA for the length and address of the symbol it will +// tell us that it has length 10 and is at RVA 0x00001000. The location is +// correct, but the length doesn't take into account the 5-bytes of injected +// code. Symbol resolution works (starting from an instrumented address, +// mapping to an original address, and looking up a symbol), but the symbol +// metadata is incorrect. +// +// If we dump the symbols using DIA they will have their addresses +// appropriately transformed and reflect positions in the instrumented image. +// However, if we try to do a lookup using those symbols resolution can fail. +// For example, the address 0x0000100a will not map to the symbol for "foo", +// because DIA tells us it is at location 0x00001000 (correct) with length +// 10 (incorrect). The problem is one of order of operations: in this case +// we're attempting symbol resolution by looking up an instrumented address +// in the table of translated symbols. +// +// One way to handle this is to dump the OMAP information as part of the +// breakpad symbols. This requires the rest of the toolchain to be aware of +// OMAP information and to use it when present prior to performing lookup. The +// other option is to properly transform the symbols (updating length as well as +// position) so that resolution will work as expected for translated addresses. +// This is transparent to the rest of the toolchain. + +#include "common/windows/omap.h" + +#include <atlbase.h> + +#include <algorithm> +#include <cassert> +#include <set> + +#include "common/windows/dia_util.h" + +namespace google_breakpad { + +namespace { + +static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO"; +static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM"; + +// Dependending on where this is used in breakpad we sometimes get min/max from +// windef, and other times from algorithm. To get around this we simply +// define our own min/max functions. +template<typename T> +const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; } +template<typename T> +const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; } + +// It makes things more readable to have two different OMAP types. We cast +// normal OMAPs into these. They must be the same size as the OMAP structure +// for this to work, hence the static asserts. +struct OmapOrigToTran { + DWORD rva_original; + DWORD rva_transformed; +}; +struct OmapTranToOrig { + DWORD rva_transformed; + DWORD rva_original; +}; +static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP), + "OmapOrigToTran must have same size as OMAP."); +static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP), + "OmapTranToOrig must have same size as OMAP."); +typedef std::vector<OmapOrigToTran> OmapFromTable; +typedef std::vector<OmapTranToOrig> OmapToTable; + +// Used for sorting and searching through a Mapping. +bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) { + if (lhs.rva_original < rhs.rva_original) + return true; + if (lhs.rva_original > rhs.rva_original) + return false; + return lhs.length < rhs.length; +} +bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) { + if (lhs.rva_transformed < rhs.rva_transformed) + return true; + if (lhs.rva_transformed > rhs.rva_transformed) + return false; + return lhs.length < rhs.length; +} + +// Used for searching through the EndpointIndexMap. +bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) { + return ei1.endpoint < ei2.endpoint; +} + +// Finds the debug stream with the given |name| in the given |session|, and +// populates |table| with its contents. Casts the data directly into OMAP +// structs. +bool FindAndLoadOmapTable(const wchar_t* name, + IDiaSession* session, + OmapTable* table) { + assert(name != NULL); + assert(session != NULL); + assert(table != NULL); + + CComPtr<IDiaEnumDebugStreamData> stream; + if (!FindDebugStream(name, session, &stream)) + return false; + assert(stream.p != NULL); + + LONG count = 0; + if (FAILED(stream->get_Count(&count))) { + fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream " + "\"%ws\"\n", name); + return false; + } + + // Get the length of the stream in bytes. + DWORD bytes_read = 0; + ULONG count_read = 0; + if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) { + fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading " + "length of stream \"%ws\"\n", name); + return false; + } + + // Ensure it's consistent with the OMAP data type. + DWORD bytes_expected = count * sizeof(OmapTable::value_type); + if (count * sizeof(OmapTable::value_type) != bytes_read) { + fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name); + return false; + } + + // Read the table. + table->resize(count); + bytes_read = 0; + count_read = 0; + if (FAILED(stream->Next(count, bytes_expected, &bytes_read, + reinterpret_cast<BYTE*>(&table->at(0)), + &count_read))) { + fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading " + "data from stream \"%ws\"\n"); + return false; + } + + return true; +} + +// This determines the original image length by looking through the segment +// table. +bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) { + assert(session != NULL); + assert(image_length != NULL); + + CComPtr<IDiaEnumSegments> enum_segments; + if (!FindTable(session, &enum_segments)) + return false; + assert(enum_segments.p != NULL); + + DWORD temp_image_length = 0; + CComPtr<IDiaSegment> segment; + ULONG fetched = 0; + while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) && + fetched == 1) { + assert(segment.p != NULL); + + DWORD rva = 0; + DWORD length = 0; + DWORD frame = 0; + if (FAILED(segment->get_relativeVirtualAddress(&rva)) || + FAILED(segment->get_length(&length)) || + FAILED(segment->get_frame(&frame))) { + fprintf(stderr, "Failed to get basic properties for IDiaSegment\n"); + return false; + } + + if (frame > 0) { + DWORD segment_end = rva + length; + if (segment_end > temp_image_length) + temp_image_length = segment_end; + } + segment.Release(); + } + + *image_length = temp_image_length; + return true; +} + +// Detects regions of the original image that have been removed in the +// transformed image, and sets the 'removed' property on all mapped ranges +// immediately preceding a gap. The mapped ranges must be sorted by +// 'rva_original'. +void FillInRemovedLengths(Mapping* mapping) { + assert(mapping != NULL); + + // Find and fill gaps. We do this with two sweeps. We first sweep forward + // looking for gaps. When we identify a gap we then sweep forward with a + // second scan and set the 'removed' property for any intervals that + // immediately precede the gap. + // + // Gaps are typically between two successive intervals, but not always: + // + // Range 1: --------------- + // Range 2: ------- + // Range 3: ------------- + // Gap : ****** + // + // In the above example the gap is between range 1 and range 3. A forward + // sweep finds the gap, and a second forward sweep identifies that range 1 + // immediately precedes the gap and sets its 'removed' property. + + size_t fill = 0; + DWORD rva_front = 0; + for (size_t find = 0; find < mapping->size(); ++find) { +#ifndef NDEBUG + // We expect the mapped ranges to be sorted by 'rva_original'. + if (find > 0) { + assert(mapping->at(find - 1).rva_original <= + mapping->at(find).rva_original); + } +#endif + + if (rva_front < mapping->at(find).rva_original) { + // We've found a gap. Fill it in by setting the 'removed' property for + // any affected intervals. + DWORD removed = mapping->at(find).rva_original - rva_front; + for (; fill < find; ++fill) { + if (mapping->at(fill).rva_original + mapping->at(fill).length != + rva_front) { + continue; + } + + // This interval ends right where the gap starts. It needs to have its + // 'removed' information filled in. + mapping->at(fill).removed = removed; + } + } + + // Advance the front that indicates the covered portion of the image. + rva_front = mapping->at(find).rva_original + mapping->at(find).length; + } +} + +// Builds a unified view of the mapping between the original and transformed +// image space by merging OMAPTO and OMAPFROM data. +void BuildMapping(const OmapData& omap_data, Mapping* mapping) { + assert(mapping != NULL); + + mapping->clear(); + + if (omap_data.omap_from.empty() || omap_data.omap_to.empty()) + return; + + // The names 'omap_to' and 'omap_from' are awfully confusing, so we make + // ourselves more explicit here. This cast is only safe because the underlying + // types have the exact same size. + const OmapToTable& tran2orig = + reinterpret_cast<const OmapToTable&>(omap_data.omap_to); + const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>( + omap_data.omap_from); + + // Handle the range of data at the beginning of the image. This is not usually + // specified by the OMAP data. + if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) { + DWORD header_transformed = tran2orig[0].rva_transformed; + DWORD header_original = orig2tran[0].rva_original; + DWORD header = Min(header_transformed, header_original); + + MappedRange mr = {}; + mr.length = header; + mr.injected = header_transformed - header; + mr.removed = header_original - header; + mapping->push_back(mr); + } + + // Convert the implied lengths to explicit lengths, and infer which content + // has been injected into the transformed image. Injected content is inferred + // as regions of the transformed address space that does not map back to + // known valid content in the original image. + for (size_t i = 0; i < tran2orig.size(); ++i) { + const OmapTranToOrig& o1 = tran2orig[i]; + + // This maps to content that is outside the original image, thus it + // describes injected content. We can skip this entry. + if (o1.rva_original >= omap_data.length_original) + continue; + + // Calculate the length of the current OMAP entry. This is implicit as the + // distance between successive |rva| values, capped at the end of the + // original image. + DWORD length = 0; + if (i + 1 < tran2orig.size()) { + const OmapTranToOrig& o2 = tran2orig[i + 1]; + + // We expect the table to be sorted by rva_transformed. + assert(o1.rva_transformed <= o2.rva_transformed); + + length = o2.rva_transformed - o1.rva_transformed; + if (o1.rva_original + length > omap_data.length_original) { + length = omap_data.length_original - o1.rva_original; + } + } else { + length = omap_data.length_original - o1.rva_original; + } + + // Zero-length entries don't describe anything and can be ignored. + if (length == 0) + continue; + + // Any gaps in the transformed address-space are due to injected content. + if (!mapping->empty()) { + MappedRange& prev_mr = mapping->back(); + prev_mr.injected += o1.rva_transformed - + (prev_mr.rva_transformed + prev_mr.length); + } + + MappedRange mr = {}; + mr.rva_original = o1.rva_original; + mr.rva_transformed = o1.rva_transformed; + mr.length = length; + mapping->push_back(mr); + } + + // Sort based on the original image addresses. + std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess); + + // Fill in the 'removed' lengths by looking for gaps in the coverage of the + // original address space. + FillInRemovedLengths(mapping); + + return; +} + +void BuildEndpointIndexMap(ImageMap* image_map) { + assert(image_map != NULL); + + if (image_map->mapping.size() == 0) + return; + + const Mapping& mapping = image_map->mapping; + EndpointIndexMap& eim = image_map->endpoint_index_map; + + // Get the unique set of interval endpoints. + std::set<DWORD> endpoints; + for (size_t i = 0; i < mapping.size(); ++i) { + endpoints.insert(mapping[i].rva_original); + endpoints.insert(mapping[i].rva_original + + mapping[i].length + + mapping[i].removed); + } + + // Use the endpoints to initialize the secondary search structure for the + // mapping. + eim.resize(endpoints.size()); + std::set<DWORD>::const_iterator it = endpoints.begin(); + for (size_t i = 0; it != endpoints.end(); ++it, ++i) { + eim[i].endpoint = *it; + eim[i].index = mapping.size(); + } + + // For each endpoint we want the smallest index of any interval containing + // it. We iterate over the intervals and update the indices associated with + // each interval endpoint contained in the current interval. In the general + // case of an arbitrary set of intervals this is O(n^2), but the structure of + // OMAP data makes this O(n). + for (size_t i = 0; i < mapping.size(); ++i) { + EndpointIndex ei1 = { mapping[i].rva_original, 0 }; + EndpointIndexMap::iterator it1 = std::lower_bound( + eim.begin(), eim.end(), ei1, EndpointIndexLess); + + EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length + + mapping[i].removed, 0 }; + EndpointIndexMap::iterator it2 = std::lower_bound( + eim.begin(), eim.end(), ei2, EndpointIndexLess); + + for (; it1 != it2; ++it1) + it1->index = Min(i, it1->index); + } +} + +// Clips the given mapped range. +void ClipMappedRangeOriginal(const AddressRange& clip_range, + MappedRange* mapped_range) { + assert(mapped_range != NULL); + + // The clipping range is entirely outside of the mapped range. + if (clip_range.end() <= mapped_range->rva_original || + mapped_range->rva_original + mapped_range->length + + mapped_range->removed <= clip_range.rva) { + mapped_range->length = 0; + mapped_range->injected = 0; + mapped_range->removed = 0; + return; + } + + // Clip the left side. + if (mapped_range->rva_original < clip_range.rva) { + DWORD clip_left = clip_range.rva - mapped_range->rva_original; + mapped_range->rva_original += clip_left; + mapped_range->rva_transformed += clip_left; + + if (clip_left > mapped_range->length) { + // The left clipping boundary entirely erases the content section of the + // range. + DWORD trim = clip_left - mapped_range->length; + mapped_range->length = 0; + mapped_range->injected -= Min(trim, mapped_range->injected); + // We know that trim <= mapped_range->remove. + mapped_range->removed -= trim; + } else { + // The left clipping boundary removes some, but not all, of the content. + // As such it leaves the removed/injected component intact. + mapped_range->length -= clip_left; + } + } + + // Clip the right side. + DWORD end_original = mapped_range->rva_original + mapped_range->length; + if (clip_range.end() < end_original) { + // The right clipping boundary lands in the 'content' section of the range, + // entirely clearing the injected/removed portion. + DWORD clip_right = end_original - clip_range.end(); + mapped_range->length -= clip_right; + mapped_range->injected = 0; + mapped_range->removed = 0; + return; + } else { + // The right clipping boundary is outside of the content, but may affect + // the removed/injected portion of the range. + DWORD end_removed = end_original + mapped_range->removed; + if (clip_range.end() < end_removed) + mapped_range->removed = clip_range.end() - end_original; + + DWORD end_injected = end_original + mapped_range->injected; + if (clip_range.end() < end_injected) + mapped_range->injected = clip_range.end() - end_original; + } + + return; +} + +} // namespace + +int AddressRange::Compare(const AddressRange& rhs) const { + if (end() <= rhs.rva) + return -1; + if (rhs.end() <= rva) + return 1; + return 0; +} + +bool GetOmapDataAndDisableTranslation(IDiaSession* session, + OmapData* omap_data) { + assert(session != NULL); + assert(omap_data != NULL); + + CComPtr<IDiaAddressMap> address_map; + if (FAILED(session->QueryInterface(&address_map))) { + fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n"); + return false; + } + assert(address_map.p != NULL); + + BOOL omap_enabled = FALSE; + if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) { + fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n"); + return false; + } + + if (!omap_enabled) { + // We indicate the non-presence of OMAP data by returning empty tables. + omap_data->omap_from.clear(); + omap_data->omap_to.clear(); + omap_data->length_original = 0; + return true; + } + + // OMAP data is present. Disable translation. + if (FAILED(address_map->put_addressMapEnabled(FALSE))) { + fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n"); + return false; + } + + // Read the OMAP streams. + if (!FindAndLoadOmapTable(kOmapFromDebugStreamName, + session, + &omap_data->omap_from)) { + return false; + } + if (!FindAndLoadOmapTable(kOmapToDebugStreamName, + session, + &omap_data->omap_to)) { + return false; + } + + // Get the lengths of the address spaces. + if (!GetOriginalImageLength(session, &omap_data->length_original)) + return false; + + return true; +} + +void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) { + assert(image_map != NULL); + + BuildMapping(omap_data, &image_map->mapping); + BuildEndpointIndexMap(image_map); +} + +void MapAddressRange(const ImageMap& image_map, + const AddressRange& original_range, + AddressRangeVector* mapped_ranges) { + assert(mapped_ranges != NULL); + + const Mapping& map = image_map.mapping; + + // Handle the trivial case of an empty image_map. This means that there is + // no transformation to be applied, and we can simply return the original + // range. + if (map.empty()) { + mapped_ranges->push_back(original_range); + return; + } + + // If we get a query of length 0 we need to handle it by using a non-zero + // query length. + AddressRange query_range(original_range); + if (query_range.length == 0) + query_range.length = 1; + + // Find the range of intervals that can potentially intersect our query range. + size_t imin = 0; + size_t imax = 0; + { + // The index of the earliest possible range that can affect is us done by + // searching through the secondary indexing structure. + const EndpointIndexMap& eim = image_map.endpoint_index_map; + EndpointIndex q1 = { query_range.rva, 0 }; + EndpointIndexMap::const_iterator it1 = std::lower_bound( + eim.begin(), eim.end(), q1, EndpointIndexLess); + if (it1 == eim.end()) { + imin = map.size(); + } else { + // Backup to find the interval that contains our query point. + if (it1 != eim.begin() && query_range.rva < it1->endpoint) + --it1; + imin = it1->index; + } + + // The first range that can't possibly intersect us is found by searching + // through the image map directly as it is already sorted by interval start + // point. + MappedRange q2 = { query_range.end(), 0 }; + Mapping::const_iterator it2 = std::lower_bound( + map.begin(), map.end(), q2, MappedRangeOriginalLess); + imax = it2 - map.begin(); + } + + // Find all intervals that intersect the query range. + Mapping temp_map; + for (size_t i = imin; i < imax; ++i) { + MappedRange mr = map[i]; + ClipMappedRangeOriginal(query_range, &mr); + if (mr.length + mr.injected > 0) + temp_map.push_back(mr); + } + + // If there are no intersecting ranges then the query range has been removed + // from the image in question. + if (temp_map.empty()) + return; + + // Sort based on transformed addresses. + std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess); + + // Zero-length queries can't actually be merged. We simply output the set of + // unique RVAs that correspond to the query RVA. + if (original_range.length == 0) { + mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0)); + for (size_t i = 1; i < temp_map.size(); ++i) { + if (temp_map[i].rva_transformed > mapped_ranges->back().rva) + mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0)); + } + return; + } + + // Merge any ranges that are consecutive in the mapped image. We merge over + // injected content if it makes ranges contiguous, but we ignore any injected + // content at the tail end of a range. This allows us to detect symbols that + // have been lengthened by injecting content in the middle. However, it + // misses the case where content has been injected at the head or the tail. + // The problem is that it doesn't know whether to attribute it to the + // preceding or following symbol. It is up to the author of the transform to + // output explicit OMAP info in these cases to ensure full coverage of the + // transformed address space. + DWORD rva_begin = temp_map[0].rva_transformed; + DWORD rva_cur_content = rva_begin + temp_map[0].length; + DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected; + for (size_t i = 1; i < temp_map.size(); ++i) { + if (rva_cur_injected < temp_map[i].rva_transformed) { + // This marks the end of a continuous range in the image. Output the + // current range and start a new one. + if (rva_begin < rva_cur_content) { + mapped_ranges->push_back( + AddressRange(rva_begin, rva_cur_content - rva_begin)); + } + rva_begin = temp_map[i].rva_transformed; + } + + rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length; + rva_cur_injected = rva_cur_content + temp_map[i].injected; + } + + // Output the range in progress. + if (rva_begin < rva_cur_content) { + mapped_ranges->push_back( + AddressRange(rva_begin, rva_cur_content - rva_begin)); + } + + return; +} + +} // namespace google_breakpad
\ No newline at end of file diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc index 9f4041d5..a3213dc3 100644 --- a/src/common/windows/pdb_source_line_writer.cc +++ b/src/common/windows/pdb_source_line_writer.cc @@ -496,6 +496,8 @@ bool PDBSourceLineWriter::PrintFunctions() { return true; } +#undef max + bool PDBSourceLineWriter::PrintFrameDataUsingPDB() { // It would be nice if it were possible to output frame data alongside the // associated function, as is done with line numbers, but the DIA API diff --git a/src/tools/windows/binaries/symupload.exe b/src/tools/windows/binaries/symupload.exe Binary files differindex 097e4291..ba319b26 100644 --- a/src/tools/windows/binaries/symupload.exe +++ b/src/tools/windows/binaries/symupload.exe diff --git a/src/tools/windows/symupload/symupload.cc b/src/tools/windows/symupload/symupload.cc index eafd9b47..df4bb693 100644 --- a/src/tools/windows/symupload/symupload.cc +++ b/src/tools/windows/symupload/symupload.cc @@ -36,6 +36,7 @@ // debug_identifier: the debug file's identifier, usually consisting of // the guid and age embedded in the pdb, e.g. // "11111111BBBB3333DDDD555555555555F" +// product: the HTTP-friendly product name, e.g. "MyApp" // version: the file version of the module, e.g. "1.2.3.4" // os: the operating system that the module was built for, always // "windows" in this implementation. @@ -154,24 +155,36 @@ static bool DumpSymbolsToTempFile(const wchar_t *file, } __declspec(noreturn) void printUsageAndExit() { - wprintf(L"Usage: symupload [--timeout NN] <file.exe|file.dll> " - L"<symbol upload URL> [...<symbol upload URLs>]\n\n"); - wprintf(L"Timeout is in milliseconds, or can be 0 to be unlimited\n\n"); - wprintf(L"Example:\n\n\tsymupload.exe --timeout 0 chrome.dll " - L"http://no.free.symbol.server.for.you\n"); + wprintf(L"Usage:\n\n" + L" symupload [--timeout NN] [--product product_name] ^\n" + L" <file.exe|file.dll> <symbol upload URL> ^\n" + L" [...<symbol upload URLs>]\n\n"); + wprintf(L" - Timeout is in milliseconds, or can be 0 to be unlimited.\n"); + wprintf(L" - product_name is an HTTP-friendly product name. It must only\n" + L" contain an ascii subset: alphanumeric and punctuation.\n" + L" This string is case-sensitive.\n\n"); + wprintf(L"Example:\n\n" + L" symupload.exe --timeout 0 --product Chrome ^\n" + L" chrome.dll http://no.free.symbol.server.for.you\n"); exit(0); } int wmain(int argc, wchar_t *argv[]) { const wchar_t *module; + const wchar_t *product = nullptr; int timeout = -1; int currentarg = 1; - if (argc > 2) { - if (!wcscmp(L"--timeout", argv[1])) { - timeout = _wtoi(argv[2]); - currentarg = 3; + while (argc > currentarg + 1) { + if (!wcscmp(L"--timeout", argv[currentarg])) { + timeout = _wtoi(argv[currentarg + 1]); + currentarg += 2; + continue; } - } else { - printUsageAndExit(); + if (!wcscmp(L"--product", argv[currentarg])) { + product = argv[currentarg + 1]; + currentarg += 2; + continue; + } + break; } if (argc >= currentarg + 2) @@ -194,6 +207,17 @@ int wmain(int argc, wchar_t *argv[]) { parameters[L"debug_identifier"] = pdb_info.debug_identifier; parameters[L"os"] = L"windows"; // This version of symupload is Windows-only parameters[L"cpu"] = pdb_info.cpu; + + // Don't make a missing product name a hard error. Issue a warning and let + // the server decide whether to reject files without product name. + if (product) { + parameters[L"product"] = product; + } else { + fwprintf( + stderr, + L"Warning: No product name (flag --product) was specified for %s\n", + module); + } // Don't make a missing version a hard error. Issue a warning, and let the // server decide whether to reject files without versions. @@ -207,12 +231,15 @@ int wmain(int argc, wchar_t *argv[]) { bool success = true; while (currentarg < argc) { + int response_code; if (!HTTPUpload::SendRequest(argv[currentarg], parameters, symbol_file, L"symbol_file", timeout == -1 ? NULL : &timeout, - NULL, NULL)) { + nullptr, &response_code)) { success = false; - fwprintf(stderr, L"Symbol file upload to %s failed\n", argv[currentarg]); + fwprintf(stderr, + L"Symbol file upload to %s failed. Response code = %ld\n", + argv[currentarg], response_code); } currentarg++; } diff --git a/src/tools/windows/symupload/symupload.vcproj b/src/tools/windows/symupload/symupload.vcproj deleted file mode 100644 index 50f50b7c..00000000 --- a/src/tools/windows/symupload/symupload.vcproj +++ /dev/null @@ -1,251 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="9.00" - Name="symupload" - ProjectGUID="{E156ED87-9DE9-47C8-94EC-A5A9CDD65E18}" - RootNamespace="symupload" - Keyword="Win32Proj" - TargetFrameworkVersion="131072" - > - <Platforms> - <Platform - Name="Win32" - /> - </Platforms> - <ToolFiles> - </ToolFiles> - <Configurations> - <Configuration - Name="Debug|Win32" - OutputDirectory="Debug" - IntermediateDirectory="Debug" - ConfigurationType="1" - CharacterSet="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - AdditionalIncludeDirectories=""$(VSInstallDir)\DIA SDK\include";..\..\.." - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="3" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="true" - DebugInformationFormat="4" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLinkerTool" - AdditionalDependencies=""$(VSInstallDir)\DIA SDK\lib\diaguids.lib" wininet.lib version.lib imagehlp.lib" - LinkIncremental="2" - GenerateDebugInformation="true" - SubSystem="1" - RandomizedBaseAddress="1" - DataExecutionPrevention="0" - TargetMachine="1" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCManifestTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCAppVerifierTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Release|Win32" - OutputDirectory="Release" - IntermediateDirectory="Release" - ConfigurationType="1" - CharacterSet="1" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories=""$(VSInstallDir)\DIA SDK\include";..\..\.." - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN" - RuntimeLibrary="2" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="true" - DebugInformationFormat="3" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLinkerTool" - AdditionalDependencies=""$(VSInstallDir)\DIA SDK\lib\diaguids.lib" wininet.lib version.lib imagehlp.lib" - LinkIncremental="2" - GenerateDebugInformation="true" - SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - RandomizedBaseAddress="1" - DataExecutionPrevention="0" - TargetMachine="1" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCManifestTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCAppVerifierTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <Filter - Name="Header Files" - Filter="h;hpp;hxx;hm;inl;inc;xsd" - UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" - > - <File - RelativePath="..\..\..\common\windows\dia_util.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\guid_string.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\http_upload.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\omap_internal.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\omap.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\pdb_source_line_writer.h" - > - </File> - <File - RelativePath="..\..\..\common\windows\string_utils-inl.h" - > - </File> - </Filter> - <Filter - Name="Resource Files" - Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" - UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" - > - </Filter> - <Filter - Name="Source Files" - Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" - UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" - > - <File - RelativePath="..\..\..\common\windows\dia_util.cc" - > - </File> - <File - RelativePath="..\..\..\common\windows\guid_string.cc" - > - </File> - <File - RelativePath="..\..\..\common\windows\http_upload.cc" - > - </File> - <File - RelativePath="..\..\..\common\windows\omap.cc" - > - </File> - <File - RelativePath="..\..\..\common\windows\pdb_source_line_writer.cc" - > - </File> - <File - RelativePath="..\..\..\common\windows\string_utils.cc" - > - </File> - <File - RelativePath=".\symupload.cc" - > - </File> - </Filter> - </Files> - <Globals> - </Globals> -</VisualStudioProject> |