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 (!common::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 E : if (module_info.image_checksum == 0 && module_info.time_date_stamp == 0) {
208 : // If the size matches, then check that the names fit.
209 i : if (instr_signature_.module_size != module_info.module_size)
210 i : return false;
211 :
212 i : base::FilePath base_name = instrumented_path_.BaseName();
213 : return (module_info.image_file_name.rfind(base_name.value()) !=
214 i : std::wstring::npos);
215 i : } else {
216 : // On Vista and greater, we can check the full module signature.
217 : return (instr_signature_.module_checksum == module_info.image_checksum &&
218 : instr_signature_.module_size == module_info.module_size &&
219 E : instr_signature_.module_time_date_stamp == module_info.time_date_stamp);
220 : }
221 E : }
222 :
223 : const Playback::BlockGraph::Block* Playback::FindFunctionBlock(
224 E : DWORD process_id, FuncAddr function) {
225 E : DCHECK(parser_ != NULL);
226 E : DCHECK(image_ != NULL);
227 :
228 : AbsoluteAddress64 abs_address =
229 E : reinterpret_cast<AbsoluteAddress64>(function);
230 :
231 : // Resolve the module in which the called function resides.
232 : const ModuleInformation* module_info =
233 E : parser_->GetModuleInformation(process_id, abs_address);
234 :
235 : // We should be able to resolve the instrumented module.
236 E : if (module_info == NULL) {
237 i : LOG(ERROR) << "Failed to resolve module for entry event (pid="
238 : << process_id << ", addr=0x" << function << ").";
239 i : return NULL;
240 : }
241 :
242 : // Ignore events not belonging to the instrumented module of interest.
243 E : if (!MatchesInstrumentedModuleSignature(*module_info)) {
244 i : return NULL;
245 : }
246 :
247 : // Convert the address to an RVA. We can only instrument 32-bit DLLs, so we're
248 : // sure that the following address conversion is safe.
249 : core::RelativeAddress rva(
250 E : static_cast<uint32>(abs_address - module_info->base_address));
251 :
252 : // Convert the address from one in the instrumented module to one in the
253 : // original module using the OMAP data.
254 E : rva = pdb::TranslateAddressViaOmap(omap_to(), rva);
255 :
256 : // Get the block that this function call refers to.
257 E : const BlockGraph::Block* block = image_->blocks.GetBlockByAddress(rva);
258 E : if (block == NULL) {
259 i : LOG(ERROR) << "Unable to map " << rva << " to a block.";
260 i : return NULL;
261 : }
262 E : if (block->type() != BlockGraph::CODE_BLOCK) {
263 i : LOG(ERROR) << rva << " maps to a non-code block (" << block->name()
264 : << " in " << module_info->image_file_name << ").";
265 i : return NULL;
266 : }
267 :
268 E : return block;
269 E : }
270 :
271 : } // namespace playback
|