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 : #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 base::FilePath& module_path,
28 : const base::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_ = base::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 base::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 : }
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 : base::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 E : BlockGraph* block_graph = image_->blocks.graph();
154 E : ImageLayout image(block_graph);
155 :
156 : // Decompose the DLL to be reordered. This will let us map call-trace events
157 : // to actual Blocks.
158 E : LOG(INFO) << "Decomposing input image: " << module_path_.value();
159 E : Decomposer decomposer(*pe_file_);
160 E : if (!decomposer.Decompose(&image)) {
161 i : LOG(ERROR) << "Unable to decompose input image: " << module_path_.value();
162 i : return false;
163 : }
164 :
165 : // Make a copy of the image layout without padding blocks, which are
166 : // completely unnecessary in a playback.
167 E : LOG(INFO) << "Removing padding blocks.";
168 E : if (!pe::CopyImageLayoutWithoutPadding(image, image_)) {
169 i : LOG(ERROR) << "Failed to remove padding blocks.";
170 i : return false;
171 : }
172 :
173 E : return true;
174 E : }
175 :
176 : bool Playback::ValidateInstrumentedModuleAndParseSignature(
177 E : pe::PEFile::Signature* orig_signature) {
178 E : DCHECK(orig_signature != NULL);
179 :
180 E : pe::PEFile pe_file;
181 E : if (!pe_file.Init(instrumented_path_)) {
182 i : LOG(ERROR) << "Unable to parse instrumented module: "
183 : << instrumented_path_.value();
184 i : return false;
185 : }
186 E : pe_file.GetSignature(&instr_signature_);
187 :
188 : // Load the metadata from the PE file. Validate the toolchain version and
189 : // return the original module signature.
190 E : pe::Metadata metadata;
191 E : if (!metadata.LoadFromPE(pe_file))
192 i : return false;
193 E : *orig_signature = metadata.module_signature();
194 :
195 E : if (!version::kSyzygyVersion.IsCompatible(metadata.toolchain_version())) {
196 i : LOG(ERROR) << "Module was instrumented with an incompatible version of "
197 : << "the toolchain: " << instrumented_path_.value();
198 i : return false;
199 : }
200 :
201 E : return true;
202 E : }
203 :
204 : bool Playback::MatchesInstrumentedModuleSignature(
205 E : const ModuleInformation& module_info) const {
206 : // On Windows XP gathered traces, only the module size is non-zero.
207 : if (module_info.module_checksum == 0 &&
208 E : module_info.module_time_date_stamp == 0) {
209 : // If the size matches, then check that the names fit.
210 i : if (instr_signature_.module_size != module_info.module_size)
211 i : return false;
212 :
213 i : base::FilePath base_name = instrumented_path_.BaseName();
214 : return (module_info.path.rfind(base_name.value()) !=
215 i : std::wstring::npos);
216 i : } else {
217 : // On Vista and greater, we can check the full module signature.
218 : return (instr_signature_.module_checksum == module_info.module_checksum &&
219 : instr_signature_.module_size == module_info.module_size &&
220 : instr_signature_.module_time_date_stamp ==
221 E : module_info.module_time_date_stamp);
222 : }
223 E : }
224 :
225 : const Playback::BlockGraph::Block* Playback::FindFunctionBlock(
226 E : DWORD process_id, FuncAddr function, bool* error) {
227 E : DCHECK(parser_ != NULL);
228 E : DCHECK(image_ != NULL);
229 E : DCHECK(error != NULL);
230 :
231 E : *error = false;
232 :
233 : AbsoluteAddress64 abs_address =
234 E : reinterpret_cast<AbsoluteAddress64>(function);
235 :
236 : // Resolve the module in which the called function resides.
237 : const ModuleInformation* module_info =
238 E : parser_->GetModuleInformation(process_id, abs_address);
239 :
240 : // We should be able to resolve the instrumented module.
241 E : if (module_info == NULL) {
242 E : LOG(ERROR) << "Failed to resolve module for entry event (pid="
243 : << process_id << ", addr=0x" << function << ").";
244 E : *error = true;
245 E : return NULL;
246 : }
247 :
248 : // Ignore events not belonging to the instrumented module of interest.
249 E : if (!MatchesInstrumentedModuleSignature(*module_info)) {
250 E : return NULL;
251 : }
252 :
253 : // Convert the address to an RVA. We can only instrument 32-bit DLLs, so we're
254 : // sure that the following address conversion is safe.
255 : core::RelativeAddress rva(
256 E : static_cast<uint32>(abs_address - module_info->base_address.value()));
257 :
258 : // Convert the address from one in the instrumented module to one in the
259 : // original module using the OMAP data.
260 E : rva = pdb::TranslateAddressViaOmap(omap_to(), rva);
261 :
262 : // Get the block that this function call refers to.
263 E : const BlockGraph::Block* block = image_->blocks.GetBlockByAddress(rva);
264 E : if (block == NULL) {
265 i : LOG(ERROR) << "Unable to map " << rva << " to a block.";
266 i : *error = true;
267 i : return NULL;
268 : }
269 E : if (block->type() != BlockGraph::CODE_BLOCK) {
270 E : LOG(ERROR) << rva << " maps to a non-code block (" << block->name()
271 : << " in " << module_info->path << ").";
272 E : *error = true;
273 E : return NULL;
274 : }
275 :
276 E : return block;
277 E : }
278 :
279 : } // namespace playback
|