1 : // Copyright 2012 Google Inc.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/grinder/line_info.h"
16 :
17 : #include <dia2.h>
18 : #include <algorithm>
19 :
20 : #include "base/utf_string_conversions.h"
21 : #include "base/win/scoped_bstr.h"
22 : #include "base/win/scoped_comptr.h"
23 : #include "sawbuck/common/com_utils.h"
24 : #include "syzygy/core/address_space.h"
25 :
26 : namespace grinder {
27 :
28 : namespace {
29 :
30 : using base::win::ScopedBstr;
31 : using base::win::ScopedComPtr;
32 :
33 : typedef core::AddressRange<core::RelativeAddress, size_t> RelativeAddressRange;
34 : typedef std::map<DWORD, const std::string*> SourceFileMap;
35 :
36 : bool GetDiaSessionForPdb(const FilePath& pdb_path,
37 : IDiaDataSource* source,
38 E : IDiaSession** session) {
39 E : DCHECK(source != NULL);
40 E : DCHECK(session != NULL);
41 :
42 E : HRESULT hr = source->loadDataFromPdb(pdb_path.value().c_str());
43 E : if (FAILED(hr)) {
44 i : LOG(ERROR) << "Failure in loadDataFromPdb: " << com::LogHr(hr) << ".";
45 i : return false;
46 : }
47 :
48 E : hr = source->openSession(session);
49 E : if (FAILED(hr)) {
50 i : LOG(ERROR) << "Failure in openSession: " << com::LogHr(hr) << ".";
51 i : return false;
52 : }
53 :
54 E : return true;
55 E : }
56 :
57 :
58 E : bool DisableOmapTranslation(IDiaSession* session) {
59 E : DCHECK(session != NULL);
60 :
61 E : ScopedComPtr<IDiaAddressMap> addr_map;
62 : HRESULT hr = session->QueryInterface(IID_IDiaAddressMap,
63 E : addr_map.ReceiveVoid());
64 E : if (FAILED(hr)) {
65 i : LOG(ERROR) << "Failure in QueryInterface: " << com::LogHr(hr) << ".";
66 i : return false;
67 : }
68 E : hr = addr_map->put_addressMapEnabled(FALSE);
69 E : if (FAILED(hr)) {
70 i : LOG(ERROR) << "Failure in put_addressMapEnabled: " << com::LogHr(hr) << ".";
71 i : return false;
72 : }
73 :
74 E : return true;
75 E : }
76 :
77 : const std::string* GetSourceFileName(DWORD source_file_id,
78 : IDiaLineNumber* line_number,
79 : LineInfo::SourceFileSet* source_files,
80 E : SourceFileMap* source_file_map) {
81 E : DCHECK(line_number != NULL);
82 E : DCHECK(source_files != NULL);
83 E : DCHECK(source_file_map != NULL);
84 :
85 E : SourceFileMap::const_iterator map_it = source_file_map->find(source_file_id);
86 :
87 E : if (map_it != source_file_map->end())
88 E : return map_it->second;
89 :
90 E : ScopedComPtr<IDiaSourceFile> source_file;
91 E : HRESULT hr = line_number->get_sourceFile(source_file.Receive());
92 E : if (FAILED(hr)) {
93 i : LOG(ERROR) << "Failure in get_sourceFile: " << com::LogHr(hr) << ".";
94 i : return false;
95 : }
96 :
97 E : ScopedBstr source_file_path_bstr;
98 E : hr = source_file->get_fileName(source_file_path_bstr.Receive());
99 E : if (FAILED(hr)) {
100 i : LOG(ERROR) << "Failure in get_fileName: " << com::LogHr(hr) << ".";
101 i : return false;
102 : }
103 :
104 E : std::string source_file_path;
105 : if (!WideToUTF8(com::ToString(source_file_path_bstr),
106 : source_file_path_bstr.Length(),
107 E : &source_file_path)) {
108 i : LOG(ERROR) << "WideToUTF8 failed for path \""
109 : << com::ToString(source_file_path_bstr) << "\".";
110 i : return false;
111 : }
112 :
113 : LineInfo::SourceFileSet::const_iterator source_file_it =
114 E : source_files->insert(source_file_path).first;
115 E : const std::string* source_file_name = &(*source_file_it);
116 : source_file_map->insert(std::make_pair(source_file_id,
117 E : source_file_name));
118 :
119 E : return source_file_name;
120 E : }
121 :
122 : // Used for comparing the ranges covered by two source lines.
123 : struct SourceLineAddressComparator {
124 : bool operator()(const LineInfo::SourceLine& sl1,
125 E : const LineInfo::SourceLine& sl2) const {
126 E : return sl1.address + sl1.size <= sl2.address;
127 E : }
128 : };
129 :
130 : } // namespace
131 :
132 E : bool LineInfo::Init(const FilePath& pdb_path) {
133 E : ScopedComPtr<IDiaDataSource> source;
134 E : HRESULT hr = source.CreateInstance(CLSID_DiaSource);
135 E : if (FAILED(hr)) {
136 i : LOG(ERROR) << "Failed to create DiaSource: " << com::LogHr(hr) << ".";
137 i : return false;
138 : }
139 :
140 E : ScopedComPtr<IDiaSession> session;
141 E : if (!GetDiaSessionForPdb(pdb_path, source.get(), session.Receive()))
142 i : return false;
143 :
144 : // We want original module addresses so we disable OMAP translation.
145 E : if (!DisableOmapTranslation(session.get()))
146 i : return false;
147 :
148 : // Get the line number enumeration.
149 E : ScopedComPtr<IDiaEnumLineNumbers> line_number_enum;
150 E : hr = session->findLinesByRVA(0, 0xFFFFFF, line_number_enum.Receive());
151 E : if (FAILED(hr)) {
152 i : LOG(ERROR) << "Failure in findLinesByRVA: " << com::LogHr(hr) << ".";
153 i : return false;
154 : }
155 :
156 : // A map of source file IDs we've already seen, mapping back to the source
157 : // file path. We use this as a cache so we're not constantly doing source-file
158 : // lookups while iterating.
159 E : SourceFileMap source_file_map;
160 :
161 : // Get the line info count and reserve space.
162 E : LONG line_number_count = 0;
163 E : hr = line_number_enum->get_Count(&line_number_count);
164 E : if (FAILED(hr)) {
165 i : LOG(ERROR) << "Failure in get_Count: " << com::LogHr(hr) << ".";
166 i : return false;
167 : }
168 E : source_lines_.reserve(line_number_count);
169 :
170 : // Iterate over the source line information.
171 E : DWORD old_source_file_id = -1;
172 E : DWORD old_rva = 0;
173 E : const std::string* source_file_name = NULL;
174 E : while (true) {
175 E : ScopedComPtr<IDiaLineNumber> line_number;
176 E : ULONG fetched = 0;
177 E : hr = line_number_enum->Next(1, line_number.Receive(), &fetched);
178 E : if (hr != S_OK || fetched != 1)
179 E : break;
180 :
181 E : DWORD source_file_id = 0;
182 E : hr = line_number->get_sourceFileId(&source_file_id);
183 E : if (FAILED(hr)) {
184 i : LOG(ERROR) << "Failure in get_sourceFileId: " << com::LogHr(hr) << ".";
185 i : return false;
186 : }
187 :
188 : // Look for the source file by ID. Since we most often see successive
189 : // lines from the same file we have a shortcut to avoid extra processing in
190 : // this case.
191 E : if (source_file_id != old_source_file_id) {
192 : source_file_name = GetSourceFileName(source_file_id,
193 : line_number.get(),
194 : &source_files_,
195 E : &source_file_map);
196 : }
197 E : old_source_file_id = source_file_id;
198 E : DCHECK(source_file_name != NULL);
199 :
200 E : DWORD line = 0;
201 E : DWORD rva = 0;
202 E : DWORD length = 0;
203 : if (FAILED(line_number->get_lineNumber(&line)) ||
204 : FAILED(line_number->get_relativeVirtualAddress(&rva)) ||
205 E : FAILED(line_number->get_length(&length))) {
206 i : LOG(ERROR) << "Failed to get line number properties.";
207 i : return false;
208 : }
209 :
210 : // We rely on the enumeration returning us lines in order of increasing
211 : // address, as they are stored originally in the PDB. This is required for
212 : // the following zero-length fixing mechanism to work as intended.
213 E : DCHECK_LE(old_rva, rva);
214 E : old_rva = rva;
215 :
216 : // Is this a non-zero length? Back up and make any zero-length ranges
217 : // with the same start address the same length as us. This makes them
218 : // simply look like repeated entries in the array and makes searching for
219 : // them with lower_bound/upper_bound work as expected.
220 E : if (length != 0) {
221 E : SourceLines::reverse_iterator it = source_lines_.rbegin();
222 E : for (; it != source_lines_.rend(); ++it) {
223 E : if (it->size != 0)
224 E : break;
225 E : if (it->address.value() != rva) {
226 i : LOG(ERROR) << "Encountered zero-length line number with "
227 : << "inconsistent address.";
228 i : return false;
229 : }
230 E : it->size = length;
231 E : }
232 E : }
233 :
234 : source_lines_.push_back(SourceLine(source_file_name,
235 : line,
236 : core::RelativeAddress(rva),
237 E : length));
238 E : }
239 :
240 E : return true;
241 E : }
242 :
243 E : bool LineInfo::Visit(core::RelativeAddress address, size_t size) {
244 : // Visiting a range of size zero is a nop.
245 E : if (size == 0)
246 i : return true;
247 :
248 : // Create a dummy 'source line' for the search.
249 E : SourceLine visit_source_line(NULL, 0, address, size);
250 :
251 : SourceLines::iterator begin_it =
252 : std::lower_bound(source_lines_.begin(),
253 : source_lines_.end(),
254 : visit_source_line,
255 E : SourceLineAddressComparator());
256 :
257 : SourceLines::iterator end_it =
258 : std::upper_bound(source_lines_.begin(),
259 : source_lines_.end(),
260 : visit_source_line,
261 E : SourceLineAddressComparator());
262 :
263 E : SourceLines::iterator it = begin_it;
264 E : RelativeAddressRange visit(address, size);
265 E : for (; it != end_it; ++it) {
266 E : RelativeAddressRange range(it->address, it->size);
267 E : if (visit.Intersects(range))
268 E : it->visited = true;
269 E : }
270 :
271 E : return true;
272 E : }
273 :
274 : } // namespace grinder
|