1 : // Copyright 2012 Google Inc. All Rights Reserved.
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 : // Implementation of utility functions for module/PDB search and discovery.
16 : // Leverages the debughlp back-end so that behaviour is consistent with those
17 : // tools.
18 :
19 : #include "syzygy/pe/find.h"
20 :
21 : #include <dbghelp.h>
22 : #include <winnt.h>
23 :
24 : #include "base/environment.h"
25 : #include "base/file_util.h"
26 : #include "base/logging.h"
27 : #include "base/utf_string_conversions.h"
28 : #include "base/strings/string_split.h"
29 : #include "sawbuck/common/com_utils.h"
30 : #include "syzygy/pdb/pdb_util.h"
31 : #include "syzygy/pe/pdb_info.h"
32 : #include "syzygy/pe/pe_data.h"
33 : #include "syzygy/pe/pe_file.h"
34 :
35 : namespace pe {
36 :
37 : namespace {
38 :
39 E : bool GetEnvVar(const char* name, std::wstring* value) {
40 E : DCHECK(name != NULL);
41 E : DCHECK(value != NULL);
42 E : value->clear();
43 :
44 E : scoped_ptr<base::Environment> env(base::Environment::Create());
45 E : if (env.get() == NULL) {
46 i : LOG(ERROR) << "base::Environment::Create returned NULL.";
47 i : return false;
48 : }
49 :
50 : // If this fails, the environment variable simply does not exist.
51 E : std::string var;
52 E : if (!env->GetVar(name, &var))
53 E : return true;
54 :
55 : if (!UTF8ToWide(var.c_str(),
56 : var.size(),
57 E : value)) {
58 i : LOG(ERROR) << "UTF8ToWide(\"" << var << "\" failed.";
59 i : return false;
60 : }
61 :
62 E : return true;
63 E : }
64 :
65 : // Return TRUE to continue searching, FALSE if we want the search to stop.
66 E : BOOL CALLBACK FindPdbFileCallback(PCTSTR path, PVOID context) {
67 E : DCHECK(path != NULL);
68 E : DCHECK(context != NULL);
69 :
70 E : base::FilePath pdb_path(path);
71 E : const PdbInfo* pdb_info = static_cast<PdbInfo*>(context);
72 :
73 : pdb::PdbInfoHeader70 pdb_header;
74 E : if (!pdb::ReadPdbHeader(pdb_path, &pdb_header))
75 i : return TRUE;
76 E : if (!pdb_info->IsConsistent(pdb_header))
77 E : return TRUE;
78 :
79 E : return FALSE;
80 E : }
81 :
82 : // Return TRUE to continue searching, FALSE if we want the search to stop.
83 E : BOOL CALLBACK FindPeFileCallback(PCTSTR path, PVOID context) {
84 E : DCHECK(path != NULL);
85 E : DCHECK(context != NULL);
86 :
87 E : base::FilePath pe_path(path);
88 E : const PEFile::Signature* pe_info = static_cast<PEFile::Signature*>(context);
89 :
90 E : PEFile pe_file;
91 E : if (!pe_file.Init(pe_path))
92 i : return TRUE;
93 E : PEFile::Signature pe_sig;
94 E : pe_file.GetSignature(&pe_sig);
95 :
96 : // We don't care about the base address or the path.
97 : if (pe_sig.module_checksum != pe_info->module_checksum ||
98 : pe_sig.module_size != pe_info->module_size ||
99 E : pe_sig.module_time_date_stamp != pe_info->module_time_date_stamp) {
100 i : return TRUE;
101 : }
102 :
103 E : return FALSE;
104 E : }
105 :
106 : bool FindFile(const base::FilePath& file_path,
107 : const base::StringPiece16& search_paths,
108 : const void* id,
109 : uint32 data,
110 : uint32 flags,
111 : PFINDFILEINPATHCALLBACKW callback,
112 : void* callback_context,
113 E : base::FilePath* found_file) {
114 E : DCHECK(found_file != NULL);
115 :
116 E : found_file->clear();
117 :
118 E : HANDLE handle = ::GetCurrentProcess();
119 :
120 E : BOOL result = ::SymInitialize(handle, NULL, FALSE);
121 E : if (result == FALSE) {
122 i : DWORD error = ::GetLastError();
123 i : LOG(ERROR) << "SymInitialize failed: " << com::LogWe(error);
124 i : return false;
125 : }
126 :
127 E : base::FilePath dir = file_path.DirName();
128 E : std::wstring basename = file_path.BaseName().value();
129 :
130 : // Augment the search paths with the directory of file_path and the
131 : // current working directory.
132 E : std::wstring paths;
133 E : if (file_util::PathExists(dir)) {
134 E : paths.append(dir.value());
135 E : paths.push_back(L';');
136 : }
137 E : paths.append(L".;");
138 E : paths.append(search_paths.begin(), search_paths.end());
139 :
140 : // Search for the file.
141 : wchar_t buffer[MAX_PATH];
142 : result = ::SymFindFileInPathW(handle,
143 : paths.c_str(),
144 : basename.c_str(),
145 : const_cast<void*>(id),
146 : data,
147 : 0,
148 : flags,
149 : &buffer[0],
150 : callback,
151 E : callback_context);
152 E : if (::SymCleanup(handle) == FALSE) {
153 i : DWORD error = ::GetLastError();
154 i : LOG(ERROR) << "SymCleanup failed: " << com::LogWe(error);
155 i : return false;
156 : }
157 E : if (!result) {
158 : // If there is a zero error code, this simply means that the search failed
159 : // to find anything, which is not an error.
160 E : DWORD error = ::GetLastError();
161 E : if (error == 0)
162 E : return true;
163 :
164 i : LOG(ERROR) << "SymFindFileInPath(\"" << file_path.value() << "\") failed: "
165 : << com::LogWe(error);
166 i : return false;
167 : }
168 :
169 E : *found_file = base::FilePath(buffer);
170 :
171 E : return true;
172 E : }
173 :
174 : } // namespace
175 :
176 : bool PeAndPdbAreMatched(const base::FilePath& pe_path,
177 E : const base::FilePath& pdb_path) {
178 E : pe::PdbInfo pe_pdb_info;
179 E : if (!pe_pdb_info.Init(pe_path))
180 E : return false;
181 : pdb::PdbInfoHeader70 pdb_info;
182 E : if (!pdb::ReadPdbHeader(pdb_path, &pdb_info))
183 E : return false;
184 E : if (!pe_pdb_info.IsConsistent(pdb_info))
185 E : return false;
186 E : return true;
187 E : }
188 :
189 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
190 : const base::StringPiece16& search_paths,
191 E : base::FilePath* module_path) {
192 E : DCHECK(module_path != NULL);
193 :
194 E : std::vector<base::FilePath> candidate_paths;
195 E : if (!module_path->empty())
196 E : candidate_paths.push_back(*module_path);
197 E : candidate_paths.push_back(base::FilePath(module_signature.path));
198 :
199 : const void* id =
200 E : reinterpret_cast<void*>(module_signature.module_time_date_stamp);
201 :
202 : // Try a search based on each of the candidate paths.
203 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
204 E : const base::FilePath& path = candidate_paths[i];
205 :
206 : if (!FindFile(path,
207 : search_paths,
208 : id,
209 : module_signature.module_size,
210 : SSRVOPT_DWORD,
211 : FindPeFileCallback,
212 : const_cast<PEFile::Signature*>(&module_signature),
213 E : module_path)) {
214 i : return false;
215 : }
216 :
217 : // If the search was successful we can terminate early.
218 E : if (!module_path->empty())
219 E : return true;
220 i : }
221 i : DCHECK(module_path->empty());
222 :
223 i : return true;
224 E : }
225 :
226 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
227 E : base::FilePath* module_path) {
228 E : DCHECK(module_path != NULL);
229 :
230 E : std::wstring search_paths;
231 E : if (!GetEnvVar("PATH", &search_paths))
232 i : return false;
233 :
234 : return FindModuleBySignature(module_signature,
235 : search_paths.c_str(),
236 E : module_path);
237 E : }
238 :
239 : bool FindPdbForModule(const base::FilePath& module_path,
240 : const base::StringPiece16& search_paths,
241 E : base::FilePath* pdb_path) {
242 E : DCHECK(pdb_path != NULL);
243 :
244 E : std::vector<base::FilePath> candidate_paths;
245 E : if (!pdb_path->empty())
246 E : candidate_paths.push_back(*pdb_path);
247 :
248 E : PdbInfo pdb_info;
249 E : if (!pdb_info.Init(module_path))
250 E : return false;
251 E : candidate_paths.push_back(pdb_info.pdb_file_name());
252 :
253 : // Prepend the module path to the symbol path.
254 E : std::wstring search_path(module_path.DirName().value());
255 E : search_path.append(L";");
256 E : search_path.append(search_paths.begin(), search_paths.end());
257 :
258 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
259 E : const base::FilePath& path = candidate_paths[i];
260 :
261 : if (!FindFile(path,
262 : search_path.c_str(),
263 : &pdb_info.signature(),
264 : pdb_info.pdb_age(),
265 : SSRVOPT_GUIDPTR,
266 : FindPdbFileCallback,
267 : &pdb_info,
268 E : pdb_path)) {
269 i : return false;
270 : }
271 :
272 : // If the search was successful we can terminate early.
273 E : if (!pdb_path->empty())
274 E : return true;
275 E : }
276 E : DCHECK(pdb_path->empty());
277 :
278 E : return true;
279 E : }
280 :
281 : bool FindPdbForModule(const base::FilePath& module_path,
282 E : base::FilePath* pdb_path) {
283 E : DCHECK(pdb_path != NULL);
284 :
285 E : std::wstring search_paths;
286 E : if (!GetEnvVar("_NT_SYMBOL_PATH", &search_paths))
287 i : return false;
288 :
289 : return FindPdbForModule(module_path,
290 : search_paths.c_str(),
291 E : pdb_path);
292 E : }
293 :
294 : } // namespace pe
|