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 : // 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 i : 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 wchar_t* 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(search_paths != NULL);
115 E : DCHECK(found_file != NULL);
116 :
117 E : found_file->clear();
118 :
119 E : HANDLE handle = ::GetCurrentProcess();
120 :
121 E : BOOL result = ::SymInitialize(handle, NULL, FALSE);
122 E : if (result == FALSE) {
123 i : DWORD error = ::GetLastError();
124 i : LOG(ERROR) << "SymInitialize failed: " << com::LogWe(error);
125 i : return false;
126 : }
127 :
128 E : FilePath dir = file_path.DirName();
129 E : std::wstring basename = file_path.BaseName().value();
130 :
131 : // Augment the search paths with the directory of file_path and the
132 : // current working directory.
133 E : std::wstring paths;
134 E : if (file_util::PathExists(dir)) {
135 E : paths.append(dir.value());
136 E : paths.push_back(L';');
137 : }
138 E : paths.append(L".;");
139 E : paths.append(search_paths);
140 :
141 : // Search for the file.
142 : wchar_t buffer[MAX_PATH];
143 : result = ::SymFindFileInPathW(handle,
144 : paths.c_str(),
145 : basename.c_str(),
146 : const_cast<void*>(id),
147 : data,
148 : 0,
149 : flags,
150 : &buffer[0],
151 : callback,
152 E : callback_context);
153 E : if (::SymCleanup(handle) == FALSE) {
154 i : DWORD error = ::GetLastError();
155 i : LOG(ERROR) << "SymCleanup failed: " << com::LogWe(error);
156 i : return false;
157 : }
158 E : if (!result) {
159 : // If there is a zero error code, this simply means that the search failed
160 : // to find anything, which is not an error.
161 E : DWORD error = ::GetLastError();
162 E : if (error == 0)
163 E : return true;
164 :
165 i : LOG(ERROR) << "SymFindFileInPath(\"" << file_path.value() << "\") failed: "
166 : << com::LogWe(error);
167 i : return false;
168 : }
169 :
170 E : *found_file = FilePath(buffer);
171 :
172 E : return true;
173 E : }
174 :
175 : } // namespace
176 :
177 E : bool PeAndPdbAreMatched(const FilePath& pe_path, const 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 i : 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 wchar_t* search_paths,
191 E : FilePath* module_path) {
192 E : DCHECK(search_paths != NULL);
193 E : DCHECK(module_path != NULL);
194 :
195 E : FilePath path(module_signature.path);
196 : const void* id =
197 E : reinterpret_cast<void*>(module_signature.module_time_date_stamp);
198 : return FindFile(path,
199 : search_paths,
200 : id,
201 : module_signature.module_size,
202 : SSRVOPT_DWORD,
203 : FindPeFileCallback,
204 : const_cast<PEFile::Signature*>(&module_signature),
205 E : module_path);
206 E : }
207 :
208 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
209 E : FilePath* module_path) {
210 E : DCHECK(module_path != NULL);
211 :
212 E : std::wstring search_paths;
213 E : if (!GetEnvVar("PATH", &search_paths))
214 i : return false;
215 :
216 : return FindModuleBySignature(module_signature,
217 : search_paths.c_str(),
218 E : module_path);
219 E : }
220 :
221 : bool FindPdbForModule(const FilePath& module_path,
222 : const wchar_t* search_paths,
223 E : FilePath* pdb_path) {
224 E : DCHECK(search_paths != NULL);
225 E : DCHECK(pdb_path != NULL);
226 :
227 E : PdbInfo pdb_info;
228 E : if (!pdb_info.Init(module_path))
229 i : return false;
230 :
231 : // Prepend the module path to the symbol path.
232 E : std::wstring search_path(module_path.DirName().value());
233 E : search_path.append(L";");
234 E : search_path.append(search_paths);
235 :
236 : return FindFile(pdb_info.pdb_file_name(),
237 : search_path.c_str(),
238 : &pdb_info.signature(),
239 : pdb_info.pdb_age(),
240 : SSRVOPT_GUIDPTR,
241 : FindPdbFileCallback,
242 : &pdb_info,
243 E : pdb_path);
244 E : }
245 :
246 : bool FindPdbForModule(const FilePath& module_path,
247 E : FilePath* pdb_path) {
248 E : DCHECK(pdb_path != NULL);
249 :
250 E : std::wstring search_paths;
251 E : if (!GetEnvVar("_NT_SYMBOL_PATH", &search_paths))
252 i : return false;
253 :
254 : return FindPdbForModule(module_path,
255 : search_paths.c_str(),
256 E : pdb_path);
257 E : }
258 :
259 : } // namespace pe
|