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