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/string_split.h"
28 : #include "base/utf_string_conversions.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 : 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 : 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 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 : 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 : 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 = FilePath(buffer);
170 :
171 E : return true;
172 E : }
173 :
174 : } // namespace
175 :
176 E : bool PeAndPdbAreMatched(const FilePath& pe_path, const FilePath& pdb_path) {
177 E : pe::PdbInfo pe_pdb_info;
178 E : if (!pe_pdb_info.Init(pe_path))
179 E : return false;
180 : pdb::PdbInfoHeader70 pdb_info;
181 E : if (!pdb::ReadPdbHeader(pdb_path, &pdb_info))
182 E : return false;
183 E : if (!pe_pdb_info.IsConsistent(pdb_info))
184 E : return false;
185 E : return true;
186 E : }
187 :
188 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
189 : const base::StringPiece16& search_paths,
190 E : FilePath* module_path) {
191 E : DCHECK(module_path != NULL);
192 :
193 E : std::vector<FilePath> candidate_paths;
194 E : if (!module_path->empty())
195 E : candidate_paths.push_back(*module_path);
196 E : candidate_paths.push_back(FilePath(module_signature.path));
197 :
198 : const void* id =
199 E : reinterpret_cast<void*>(module_signature.module_time_date_stamp);
200 :
201 : // Try a search based on each of the candidate paths.
202 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
203 E : const FilePath& path = candidate_paths[i];
204 :
205 : if (!FindFile(path,
206 : search_paths,
207 : id,
208 : module_signature.module_size,
209 : SSRVOPT_DWORD,
210 : FindPeFileCallback,
211 : const_cast<PEFile::Signature*>(&module_signature),
212 E : module_path)) {
213 i : return false;
214 : }
215 :
216 : // If the search was successful we can terminate early.
217 E : if (!module_path->empty())
218 E : return true;
219 i : }
220 i : DCHECK(module_path->empty());
221 :
222 i : return true;
223 E : }
224 :
225 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
226 E : FilePath* module_path) {
227 E : DCHECK(module_path != NULL);
228 :
229 E : std::wstring search_paths;
230 E : if (!GetEnvVar("PATH", &search_paths))
231 i : return false;
232 :
233 : return FindModuleBySignature(module_signature,
234 : search_paths.c_str(),
235 E : module_path);
236 E : }
237 :
238 : bool FindPdbForModule(const FilePath& module_path,
239 : const base::StringPiece16& search_paths,
240 E : FilePath* pdb_path) {
241 E : DCHECK(pdb_path != NULL);
242 :
243 E : std::vector<FilePath> candidate_paths;
244 E : if (!pdb_path->empty())
245 E : candidate_paths.push_back(*pdb_path);
246 :
247 E : PdbInfo pdb_info;
248 E : if (!pdb_info.Init(module_path))
249 E : return false;
250 E : candidate_paths.push_back(pdb_info.pdb_file_name());
251 :
252 : // Prepend the module path to the symbol path.
253 E : std::wstring search_path(module_path.DirName().value());
254 E : search_path.append(L";");
255 E : search_path.append(search_paths.begin(), search_paths.end());
256 :
257 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
258 E : const FilePath& path = candidate_paths[i];
259 :
260 : if (!FindFile(path,
261 : search_path.c_str(),
262 : &pdb_info.signature(),
263 : pdb_info.pdb_age(),
264 : SSRVOPT_GUIDPTR,
265 : FindPdbFileCallback,
266 : &pdb_info,
267 E : pdb_path)) {
268 i : return false;
269 : }
270 :
271 : // If the search was successful we can terminate early.
272 E : if (!pdb_path->empty())
273 E : return true;
274 E : }
275 E : DCHECK(pdb_path->empty());
276 :
277 E : return true;
278 E : }
279 :
280 : bool FindPdbForModule(const FilePath& module_path,
281 E : FilePath* pdb_path) {
282 E : DCHECK(pdb_path != NULL);
283 :
284 E : std::wstring search_paths;
285 E : if (!GetEnvVar("_NT_SYMBOL_PATH", &search_paths))
286 i : return false;
287 :
288 : return FindPdbForModule(module_path,
289 : search_paths.c_str(),
290 E : pdb_path);
291 E : }
292 :
293 : } // namespace pe
|