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/playback/playback.h"
16 :
17 : #include "syzygy/core/address.h"
18 : #include "syzygy/pdb/omap.h"
19 : #include "syzygy/pe/find.h"
20 : #include "syzygy/pe/metadata.h"
21 : #include "syzygy/pe/pe_file.h"
22 :
23 : namespace playback {
24 :
25 : using trace::parser::Parser;
26 :
27 : Playback::Playback(const FilePath& module_path,
28 : const FilePath& instrumented_path,
29 : const TraceFileList& trace_files)
30 : : module_path_(module_path),
31 : instrumented_path_(instrumented_path),
32 : trace_files_(trace_files),
33 : pe_file_(NULL),
34 : image_(NULL),
35 E : parser_(NULL) {
36 E : }
37 :
38 E : Playback::~Playback() {
39 E : pe_file_ = NULL;
40 E : image_ = NULL;
41 E : parser_ = NULL;
42 E : }
43 :
44 E : bool Playback::Init(PEFile* pe_file, ImageLayout* image, Parser* parser) {
45 : // Fail if the function was already initialized,
46 : // or if the parameters aren't.
47 E : DCHECK(pe_file != NULL);
48 E : DCHECK(image != NULL);
49 E : DCHECK(parser != NULL);
50 :
51 E : DCHECK(pe_file_ == NULL);
52 E : DCHECK(image_ == NULL);
53 E : DCHECK(parser_ == NULL);
54 :
55 E : pe_file_ = pe_file;
56 E : image_ = image;
57 E : parser_ = parser;
58 :
59 : // Load and decompose the module.
60 E : if (!LoadModuleInformation())
61 E : return false;
62 E : if (!InitializeParser())
63 E : return false;
64 E : if (!LoadInstrumentedOmap())
65 i : return false;
66 E : if (!DecomposeImage())
67 i : return false;
68 :
69 E : return true;
70 E : }
71 :
72 E : bool Playback::LoadModuleInformation() {
73 E : DCHECK(pe_file_ != NULL);
74 E : DCHECK(image_ != NULL);
75 :
76 : // Validate the instrumented module, and extract the signature of the original
77 : // module it was built from.
78 E : pe::PEFile::Signature orig_signature;
79 E : if (!ValidateInstrumentedModuleAndParseSignature(&orig_signature))
80 i : return false;
81 :
82 : // If the input DLL path is empty, use the inferred one from the
83 : // instrumented module.
84 E : if (module_path_.empty()) {
85 i : LOG(INFO) << "Inferring input DLL path from instrumented module: "
86 : << orig_signature.path;
87 i : module_path_ = FilePath(orig_signature.path);
88 : }
89 :
90 : // Try to read the input DLL.
91 E : LOG(INFO) << "Reading input DLL.";
92 E : if (!pe_file_->Init(module_path_)) {
93 i : LOG(ERROR) << "Unable to read input image: " << module_path_.value();
94 i : return false;
95 : }
96 E : pe::PEFile::Signature input_signature;
97 E : pe_file_->GetSignature(&input_signature);
98 :
99 : // Validate that the input DLL signature matches the original signature
100 : // extracted from the instrumented module.
101 E : if (!orig_signature.IsConsistent(input_signature)) {
102 E : LOG(ERROR) << "Instrumented module metadata does not match input module.";
103 E : return false;
104 : }
105 :
106 E : return true;
107 E : }
108 :
109 E : bool Playback::InitializeParser() {
110 : // Open the log files. We do this before running the decomposer as if these
111 : // fail we'll have wasted a lot of time!
112 :
113 E : for (TraceFileIter i = trace_files_.begin(); i < trace_files_.end(); ++i) {
114 E : const FilePath& trace_path = *i;
115 E : LOG(INFO) << "Opening '" << trace_path.BaseName().value() << "'.";
116 E : if (!parser_->OpenTraceFile(trace_path)) {
117 E : LOG(ERROR) << "Unable to open trace log: " << trace_path.value();
118 E : return false;
119 E : }
120 E : }
121 :
122 E : return true;
123 E : }
124 :
125 E : bool Playback::LoadInstrumentedOmap() {
126 : // Find the PDB file for the instrumented module.
127 E : FilePath instrumented_pdb;
128 : if (!pe::FindPdbForModule(instrumented_path_, &instrumented_pdb) ||
129 E : instrumented_pdb.empty()) {
130 i : LOG(ERROR) << "Unable to find PDB for instrumented image \""
131 : << instrumented_path_.value() << "\".";
132 i : return false;
133 : }
134 E : LOG(INFO) << "Found PDB for instrumented module: \""
135 : << instrumented_pdb.value() << "\".";
136 :
137 : // Load the OMAPTO table from the instrumented PDB. This will allow us to map
138 : // call-trace event addresses to addresses in the original image.
139 E : if (!pdb::ReadOmapsFromPdbFile(instrumented_pdb, &omap_to_, &omap_from_)) {
140 i : LOG(ERROR) << "Failed to read OMAPTO vector from PDB \""
141 : << instrumented_pdb.value() << "\".";
142 i : return false;
143 : }
144 E : LOG(INFO) << "Read OMAP data from instrumented module PDB.";
145 :
146 E : return true;
147 E : }
148 :
149 E : bool Playback::DecomposeImage() {
150 E : DCHECK(pe_file_ != NULL);
151 E : DCHECK(image_ != NULL);
152 :
153 : // Decompose the DLL to be reordered. This will let us map call-trace events
154 : // to actual Blocks.
155 E : LOG(INFO) << "Decomposing input image.";
156 E : Decomposer decomposer(*pe_file_);
157 E : if (!decomposer.Decompose(image_)) {
158 i : LOG(ERROR) << "Unable to decompose input image: " << module_path_.value();
159 i : return false;
160 : }
161 :
162 E : return true;
163 E : }
164 :
165 : bool Playback::ValidateInstrumentedModuleAndParseSignature(
166 E : pe::PEFile::Signature* orig_signature) {
167 E : DCHECK(orig_signature != NULL);
168 :
169 E : pe::PEFile pe_file;
170 E : if (!pe_file.Init(instrumented_path_)) {
171 i : LOG(ERROR) << "Unable to parse instrumented module: "
172 : << instrumented_path_.value();
173 i : return false;
174 : }
175 E : pe_file.GetSignature(&instr_signature_);
176 :
177 : // Load the metadata from the PE file. Validate the toolchain version and
178 : // return the original module signature.
179 E : pe::Metadata metadata;
180 E : if (!metadata.LoadFromPE(pe_file))
181 i : return false;
182 E : *orig_signature = metadata.module_signature();
183 :
184 E : if (!common::kSyzygyVersion.IsCompatible(metadata.toolchain_version())) {
185 i : LOG(ERROR) << "Module was instrumented with an incompatible version of "
186 : << "the toolchain: " << instrumented_path_.value();
187 i : return false;
188 : }
189 :
190 E : return true;
191 E : }
192 :
193 : bool Playback::MatchesInstrumentedModuleSignature(
194 E : const ModuleInformation& module_info) const {
195 : // On Windows XP gathered traces, only the module size is non-zero.
196 E : if (module_info.image_checksum == 0 && module_info.time_date_stamp == 0) {
197 : // If the size matches, then check that the names fit.
198 i : if (instr_signature_.module_size != module_info.module_size)
199 i : return false;
200 :
201 i : FilePath base_name = instrumented_path_.BaseName();
202 : return (module_info.image_file_name.rfind(base_name.value()) !=
203 i : std::wstring::npos);
204 : } else {
205 : // On Vista and greater, we can check the full module signature.
206 : return (instr_signature_.module_checksum == module_info.image_checksum &&
207 : instr_signature_.module_size == module_info.module_size &&
208 E : instr_signature_.module_time_date_stamp == module_info.time_date_stamp);
209 : }
210 E : }
211 :
212 : const Playback::BlockGraph::Block* Playback::FindFunctionBlock(
213 E : DWORD process_id, FuncAddr function) {
214 E : DCHECK(parser_ != NULL);
215 E : DCHECK(image_ != NULL);
216 :
217 : AbsoluteAddress64 abs_address =
218 E : reinterpret_cast<AbsoluteAddress64>(function);
219 :
220 : // Resolve the module in which the called function resides.
221 : const ModuleInformation* module_info =
222 E : parser_->GetModuleInformation(process_id, abs_address);
223 :
224 : // We should be able to resolve the instrumented module.
225 E : if (module_info == NULL) {
226 i : LOG(ERROR) << "Failed to resolve module for entry event (pid="
227 : << process_id << ", addr=0x" << function << ").";
228 i : return NULL;
229 : }
230 :
231 : // Ignore events not belonging to the instrumented module of interest.
232 E : if (!MatchesInstrumentedModuleSignature(*module_info)) {
233 i : return NULL;
234 : }
235 :
236 : // Convert the address to an RVA. We can only instrument 32-bit DLLs, so we're
237 : // sure that the following address conversion is safe.
238 : core::RelativeAddress rva(
239 E : static_cast<uint32>(abs_address - module_info->base_address));
240 :
241 : // Convert the address from one in the instrumented module to one in the
242 : // original module using the OMAP data.
243 E : rva = pdb::TranslateAddressViaOmap(omap_to(), rva);
244 :
245 : // Get the block that this function call refers to.
246 E : const BlockGraph::Block* block = image_->blocks.GetBlockByAddress(rva);
247 E : if (block == NULL) {
248 i : LOG(ERROR) << "Unable to map " << rva << " to a block.";
249 i : return NULL;
250 : }
251 E : if (block->type() != BlockGraph::CODE_BLOCK) {
252 i : LOG(ERROR) << rva << " maps to a non-code block (" << block->name()
253 : << " in " << module_info->image_file_name << ").";
254 i : return NULL;
255 : }
256 :
257 E : return block;
258 E : }
259 :
260 : } // namespace playback
|