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/reorder/reorderer.h"
16 :
17 : #include "base/values.h"
18 : #include "base/files/file_util.h"
19 : #include "base/json/json_reader.h"
20 : #include "base/json/string_escape.h"
21 : #include "base/strings/stringprintf.h"
22 : #include "base/strings/utf_string_conversions.h"
23 : #include "syzygy/block_graph/block_graph.h"
24 : #include "syzygy/common/defs.h"
25 : #include "syzygy/core/json_file_writer.h"
26 : #include "syzygy/core/serialization.h"
27 : #include "syzygy/pdb/omap.h"
28 : #include "syzygy/pe/find.h"
29 : #include "syzygy/pe/metadata.h"
30 : #include "syzygy/pe/pe_file.h"
31 : #include "syzygy/pe/pe_utils.h"
32 : #include "syzygy/version/syzygy_version.h"
33 :
34 : namespace reorder {
35 :
36 : using block_graph::BlockGraph;
37 : using trace::parser::Parser;
38 : using base::ListValue;
39 : using base::DictionaryValue;
40 : using base::Value;
41 :
42 : namespace {
43 :
44 : const char kCommentKey[] = "comment";
45 : const char kMetadataKey[] = "metadata";
46 : const char kSectionsKey[] = "sections";
47 : const char kSectionIdKey[] = "id";
48 : const char kSectionNameKey[] = "name";
49 : const char kSectionCharacteristicsKey[] = "characteristics";
50 : const char kBlocksKey[] = "blocks";
51 :
52 : bool OutputTrailingBlockComment(const BlockGraph::Block* block,
53 E : core::JSONFileWriter* json_file) {
54 E : DCHECK(block != NULL);
55 E : DCHECK(json_file != NULL);
56 :
57 E : if (!json_file->pretty_print())
58 E : return true;
59 :
60 : std::string comment = base::StringPrintf(
61 : "%s(%s)",
62 : BlockGraph::BlockTypeToString(block->type()),
63 E : block->name().c_str());
64 :
65 E : if (!json_file->OutputTrailingComment(comment))
66 i : return false;
67 :
68 E : return true;
69 E : }
70 :
71 : bool OutputBlockSpec(const Reorderer::Order::BlockSpec& block_spec,
72 E : core::JSONFileWriter* json_file) {
73 : // TODO(rogerm): Stop referring to block->addr() and take the address space
74 : // as an input parameter.
75 E : DCHECK(json_file != NULL);
76 : // TODO(rogerm): Flesh out support for synthesizing new blocks.
77 E : DCHECK(block_spec.block != NULL);
78 :
79 : // If no basic-block RVAs are given then the entire block is to be
80 : // used and we can just just the block address.
81 E : if (block_spec.basic_block_offsets.empty()) {
82 E : if (!json_file->OutputInteger(block_spec.block->addr().value()))
83 i : return false;
84 E : if (!OutputTrailingBlockComment(block_spec.block, json_file))
85 i : return false;
86 E : return true;
87 : }
88 :
89 : // Otherwise, we output a pair (two element list) comprising the block
90 : // address and the list of basic-block RVAs.
91 :
92 : // Open the outer list.
93 E : if (!json_file->OpenList())
94 i : return false;
95 :
96 : // Output the block address.
97 : if (!json_file->OutputInteger(block_spec.block->addr().value()) ||
98 E : !OutputTrailingBlockComment(block_spec.block, json_file)) {
99 i : return false;
100 : }
101 :
102 : // Open the inner list.
103 E : if (!json_file->OpenList())
104 i : return false;
105 :
106 : // Output the basic block RVAs.
107 : Reorderer::Order::OffsetVector::const_iterator it =
108 E : block_spec.basic_block_offsets.begin();
109 E : for (; it != block_spec.basic_block_offsets.end(); ++it) {
110 E : if (!json_file->OutputInteger(*it))
111 i : return false;
112 E : }
113 :
114 : // Close the inner list.
115 E : if (!json_file->CloseList())
116 i : return false;
117 :
118 : // Close the outer list.
119 E : if (!json_file->CloseList())
120 i : return false;
121 :
122 E : return true;
123 E : }
124 :
125 : // Serializes a block list to JSON.
126 : bool OutputSectionSpec(const Reorderer::Order::SectionSpec& section_spec,
127 E : core::JSONFileWriter* json_file) {
128 E : DCHECK(json_file != NULL);
129 :
130 : // Open the section specification dictionary.
131 E : if (!json_file->OpenDict())
132 i : return false;
133 :
134 : // If the section has an ID in the original image, output the ID.
135 E : if (section_spec.id != Reorderer::Order::SectionSpec::kNewSectionId) {
136 : if (!json_file->OutputKey(kSectionIdKey) ||
137 E : !json_file->OutputInteger(section_spec.id)) {
138 i : return false;
139 : }
140 : }
141 :
142 : // Output the section metadata.
143 : if (!json_file->OutputKey(kSectionNameKey) ||
144 : !json_file->OutputString(section_spec.name) ||
145 : !json_file->OutputKey(kSectionCharacteristicsKey) ||
146 E : !json_file->OutputInteger(section_spec.characteristics)) {
147 i : return false;
148 : }
149 :
150 : // Open the block spec list.
151 : if (!json_file->OutputKey(kBlocksKey) ||
152 E : !json_file->OpenList()) {
153 i : return false;
154 : }
155 :
156 : // Output each of the block specifications.
157 : Reorderer::Order::BlockSpecVector::const_iterator it =
158 E : section_spec.blocks.begin();
159 E : for (; it != section_spec.blocks.end(); ++it) {
160 E : if (!OutputBlockSpec(*it, json_file))
161 i : return false;
162 E : }
163 :
164 : // Close the block spec list.
165 E : if (!json_file->CloseList())
166 i : return false;
167 :
168 : // Close the section spec dictionary.
169 E : if (!json_file->CloseDict())
170 i : return false;
171 :
172 E : return true;
173 E : }
174 :
175 : bool LoadBlockSpec(const pe::ImageLayout& image,
176 : const Value* block_value,
177 E : Reorderer::Order::BlockSpec* block_spec) {
178 E : DCHECK(block_value != NULL);
179 E : DCHECK(block_spec != NULL);
180 :
181 E : block_spec->block = NULL;
182 E : block_spec->basic_block_offsets.clear();
183 :
184 : // If the block value is a single integer, then we use the entire block.
185 : // Otherwise, we evaluate the value as a pair (represented as a list)
186 : // where the first element is the address and the second element is a list
187 : // of basic-block RVAs.
188 E : int address = 0;
189 E : const ListValue* rva_list = NULL;
190 E : if (!block_value->GetAsInteger(&address)) {
191 E : const ListValue* pair = NULL;
192 : if (!block_value->GetAsList(&pair) ||
193 : !pair->GetInteger(0, &address) ||
194 E : !pair->GetList(1, &rva_list)) {
195 i : LOG(ERROR) << "Invalid entry for block specification.";
196 i : return false;
197 : }
198 : }
199 :
200 : // Resolve the referenced block.
201 E : core::RelativeAddress rva(address);
202 E : const BlockGraph::Block* block = image.blocks.GetBlockByAddress(rva);
203 E : if (block == NULL) {
204 i : LOG(ERROR) << "Block address not found in decomposed image: "
205 : << address;
206 i : return false;
207 : }
208 :
209 : // Read in the basic_block offsets.
210 E : bool seen_end_block = false;
211 E : if (rva_list != NULL && !rva_list->empty()) {
212 E : block_spec->basic_block_offsets.reserve(rva_list->GetSize());
213 E : for (size_t i = 0; i < rva_list->GetSize(); ++i) {
214 E : int offset = 0;
215 E : if (!rva_list->GetInteger(i, &offset)) {
216 i : LOG(ERROR) << "Unexpected value for basic-block offset #" << i
217 : << " of " << address << " [" << block->name() << "].";
218 i : block_spec->basic_block_offsets.clear();
219 i : return false;
220 : }
221 : if (offset < 0 ||
222 E : static_cast<BlockGraph::Size>(offset) > block->size()) {
223 i : LOG(ERROR) << "Offset " << offset << " falls outside block range [0-"
224 : << block->size() << "] for " << block->name();
225 i : return false;
226 : }
227 :
228 : // The basic-end block must be last in the block specification. The
229 : // block builder will catch this error but we can meaningfully catch this
230 : // earlier and avoid a lot of computation for nothing.
231 E : if (static_cast<BlockGraph::Size>(offset) == block->size()) {
232 E : seen_end_block = true;
233 E : } else if (seen_end_block) {
234 i : LOG(ERROR) << "Encountered basic-end block that is not last in the "
235 : << "specified ordering.";
236 i : return false;
237 : }
238 E : block_spec->basic_block_offsets.push_back(offset);
239 E : }
240 : }
241 :
242 E : block_spec->block = block;
243 E : return true;
244 E : }
245 :
246 : bool LoadSectionSpec(const pe::ImageLayout& image,
247 : const DictionaryValue* section_value,
248 : Reorderer::Order::SectionSpec* section_spec,
249 E : std::set<size_t>* seen_section_ids) {
250 E : DCHECK(section_value != NULL);
251 E : DCHECK(section_spec != NULL);
252 E : DCHECK(seen_section_ids != NULL);
253 :
254 : // Some keys we'll refer to multiple times below.
255 E : const std::string section_id_key(kSectionIdKey);
256 E : const std::string section_name_key(kSectionNameKey);
257 E : const std::string section_characteristics_key(kSectionCharacteristicsKey);
258 E : const std::string blocks_key(kBlocksKey);
259 :
260 : // Get the section id, if given.
261 E : int tmp_section_id = Reorderer::Order::SectionSpec::kNewSectionId;
262 : if (section_value->HasKey(section_id_key) &&
263 E : !section_value->GetInteger(section_id_key, &tmp_section_id)) {
264 i : LOG(ERROR) << "Invalid value for " << section_id_key << ".";
265 i : return false;
266 : }
267 :
268 : // Lookup the original section by id, if the id was given. Populate the
269 : // section metadata based on the original section info. The other keys
270 : // will be inspected below to see if any of the metadata needs to be
271 : // over-ridden.
272 E : section_spec->id = tmp_section_id;
273 E : if (section_spec->id != Reorderer::Order::SectionSpec::kNewSectionId) {
274 : // Lookup the section in the original image layout.
275 E : if (section_spec->id < 0 || section_spec->id > image.sections.size()) {
276 i : LOG(ERROR) << "Invalid section id: " << section_spec->id << ".";
277 i : return false;
278 : }
279 :
280 : // Make sure this section id does not already exist.
281 E : if (!seen_section_ids->insert(section_spec->id).second) {
282 i : LOG(ERROR) << "Section ID " << section_spec->id << " redefined.";
283 i : return false;
284 : }
285 :
286 : // Copy the metadata into the section spec.
287 E : section_spec->name = image.sections[section_spec->id].name;
288 : section_spec->characteristics =
289 E : image.sections[section_spec->id].characteristics;
290 : }
291 :
292 : // Possibly over-ride the section name.
293 : if (section_value->HasKey(section_name_key) &&
294 E : !section_value->GetString(section_name_key, §ion_spec->name)) {
295 i : LOG(ERROR) << "Invalid value for " << section_name_key << ".";
296 i : return false;
297 : }
298 :
299 : // Make sure we've got a section name. This may have come from either the
300 : // the original image (via the section id) or explicitly from the section
301 : // name key.
302 E : if (section_spec->name.empty()) {
303 i : LOG(ERROR) << "Missing a value for the section name. Either a valid "
304 : << section_id_key << " or valid " << section_name_key
305 : << " is required.";
306 i : return false;
307 : }
308 :
309 : // Possibly over-ride the section characteristics. The characteristics are
310 : // required if the section was not given by id.
311 : if (section_spec->id == Reorderer::Order::SectionSpec::kNewSectionId ||
312 E : section_value->HasKey(section_characteristics_key)) {
313 E : int tmp_characteristics = 0;
314 : if (!section_value->GetInteger(section_characteristics_key,
315 E : &tmp_characteristics)) {
316 i : LOG(ERROR) << "Missing or invalid value for "
317 : << section_characteristics_key << ".";
318 i : return false;
319 : }
320 E : section_spec->characteristics = tmp_characteristics;
321 : }
322 :
323 : // Get the list of block specifications.
324 E : const ListValue* blocks = NULL;
325 E : if (!section_value->GetList(blocks_key, &blocks)) {
326 i : LOG(ERROR) << "Invalid or missing value for " << blocks_key << ".";
327 i : return false;
328 : }
329 :
330 E : if (!blocks->empty()) {
331 : // Populate the block spec vector.
332 E : section_spec->blocks.resize(blocks->GetSize());
333 E : for (size_t block_idx = 0; block_idx != blocks->GetSize(); ++block_idx) {
334 E : const Value* block_value = NULL;
335 E : if (!blocks->Get(block_idx, &block_value)) {
336 i : LOG(ERROR) << "Failed to access item " << block_idx << ".";
337 i : return false;
338 : }
339 :
340 E : if (!LoadBlockSpec(image, block_value, §ion_spec->blocks[block_idx]))
341 i : return false;
342 E : }
343 : }
344 :
345 E : return true;
346 E : }
347 :
348 : } // namespace
349 :
350 : const size_t Reorderer::Order::SectionSpec::kNewSectionId = ~1U;
351 :
352 : Reorderer::Reorderer(const base::FilePath& module_path,
353 : const base::FilePath& instrumented_path,
354 : const TraceFileList& trace_files,
355 : Flags flags)
356 : : playback_(module_path, instrumented_path, trace_files),
357 : flags_(flags),
358 : code_block_entry_events_(0),
359 E : order_generator_(NULL) {
360 E : }
361 :
362 E : Reorderer::~Reorderer() {
363 E : }
364 :
365 : bool Reorderer::Reorder(OrderGenerator* order_generator,
366 : Order* order,
367 : PEFile* pe_file,
368 E : ImageLayout* image) {
369 E : DCHECK(order_generator != NULL);
370 E : DCHECK(order != NULL);
371 :
372 E : DCHECK(order_generator_ == NULL);
373 E : order_generator_ = order_generator;
374 :
375 E : bool success = ReorderImpl(order, pe_file, image);
376 :
377 E : order_generator_ = NULL;
378 :
379 E : return success;
380 E : }
381 :
382 : bool Reorderer::ReorderImpl(Order* order,
383 : PEFile* pe_file,
384 E : ImageLayout* image) {
385 E : DCHECK(order != NULL);
386 E : DCHECK(order_generator_ != NULL);
387 :
388 E : if (!parser_.Init(this)) {
389 i : LOG(ERROR) << "Failed to initialize call trace parser.";
390 i : return false;
391 : }
392 :
393 E : if (!playback_.Init(pe_file, image, &parser_))
394 i : return false;
395 :
396 E : if (playback_.trace_files().size() > 0) {
397 E : LOG(INFO) << "Processing trace events.";
398 E : if (!parser_.Consume())
399 i : return false;
400 :
401 E : if (code_block_entry_events_ == 0) {
402 i : LOG(ERROR) << "No events originated from the given instrumented DLL.";
403 i : return false;
404 : }
405 : }
406 :
407 E : if (!CalculateReordering(order))
408 i : return false;
409 :
410 E : return true;
411 E : }
412 :
413 E : bool Reorderer::CalculateReordering(Order* order) {
414 E : DCHECK(order != NULL);
415 E : DCHECK(order_generator_ != NULL);
416 :
417 E : LOG(INFO) << "Calculating new order.";
418 : if (!order_generator_->CalculateReordering(*playback_.pe_file(),
419 : *playback_.image(),
420 : (flags_ & kFlagReorderCode) != 0,
421 : (flags_ & kFlagReorderData) != 0,
422 E : order))
423 i : return false;
424 :
425 : order->comment = base::StringPrintf("Generated using the %s.",
426 E : order_generator_->name().c_str());
427 :
428 E : return true;
429 E : }
430 :
431 : void Reorderer::OnProcessStarted(
432 E : base::Time time, DWORD process_id, const TraceSystemInfo* data) {
433 E : UniqueTime entry_time(time);
434 :
435 E : if (!order_generator_->OnProcessStarted(process_id, entry_time)) {
436 i : parser_.set_error_occurred(true);
437 : return;
438 : }
439 E : }
440 :
441 E : void Reorderer::OnProcessEnded(base::Time time, DWORD process_id) {
442 : // Notify the order generator.
443 E : if (!order_generator_->OnProcessEnded(process_id, UniqueTime(time))) {
444 i : parser_.set_error_occurred(true);
445 : return;
446 : }
447 E : }
448 :
449 : // CallTraceEvents implementation.
450 : void Reorderer::OnFunctionEntry(base::Time time,
451 : DWORD process_id,
452 : DWORD thread_id,
453 E : const TraceEnterExitEventData* data) {
454 E : DCHECK(data != NULL);
455 :
456 E : bool error = false;
457 : const BlockGraph::Block* block = playback_.FindFunctionBlock(process_id,
458 : data->function,
459 E : &error);
460 :
461 : // Handle the error if any occurred.
462 E : if (error) {
463 i : LOG(ERROR) << "Playback::FindFunctionBlock failed.";
464 i : parser_.set_error_occurred(true);
465 i : return;
466 : }
467 :
468 : // If no block was found then we simply ignore the event.
469 E : if (block == NULL)
470 i : return;
471 :
472 : // Get the time of the call. Since batched function calls come in with the
473 : // same time stamp, we rely on their relative ordering and UniqueTime's
474 : // incrementing ID to maintain relative order.
475 E : UniqueTime entry_time(time);
476 :
477 E : ++code_block_entry_events_;
478 : if (!order_generator_->OnCodeBlockEntry(block,
479 : block->addr(),
480 : process_id,
481 : thread_id,
482 E : entry_time)) {
483 i : LOG(ERROR) << order_generator_->name() << "::OnCodeBlockEntry failed.";
484 i : parser_.set_error_occurred(true);
485 : return;
486 : }
487 E : }
488 :
489 : void Reorderer::OnBatchFunctionEntry(base::Time time,
490 : DWORD process_id,
491 : DWORD thread_id,
492 E : const TraceBatchEnterData* data) {
493 : // Explode the batch event into individual function entry events.
494 E : TraceEnterExitEventData new_data = {};
495 E : for (size_t i = 0; i < data->num_calls; ++i) {
496 E : new_data.function = data->calls[i].function;
497 E : OnFunctionEntry(time, process_id, thread_id, &new_data);
498 E : }
499 E : }
500 :
501 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
502 : const base::FilePath &path,
503 E : bool pretty_print) const {
504 E : base::ScopedFILE file(base::OpenFile(path, "wb"));
505 E : if (file.get() == NULL)
506 i : return false;
507 E : core::JSONFileWriter json_file(file.get(), pretty_print);
508 E : return SerializeToJSON(pe, &json_file);
509 E : }
510 :
511 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
512 E : core::JSONFileWriter* json_file) const {
513 E : DCHECK(json_file != NULL);
514 :
515 : // Open the top-level dictionary and the metadata dictionary.
516 E : if (!json_file->OpenDict())
517 i : return false;
518 :
519 : // Output the filecomment.
520 : if (!json_file->OutputKey(kCommentKey) ||
521 E : !json_file->OutputString(comment)) {
522 i : return false;
523 : }
524 :
525 : // Output metadata.
526 E : PEFile::Signature orig_sig;
527 E : pe.GetSignature(&orig_sig);
528 E : pe::Metadata metadata;
529 : if (!metadata.Init(orig_sig) ||
530 : !json_file->OutputKey(kMetadataKey) ||
531 E : !metadata.SaveToJSON(json_file)) {
532 i : return false;
533 : }
534 :
535 : // Open list of sections.
536 : if (!json_file->OutputKey(kSectionsKey) ||
537 E : !json_file->OpenList()) {
538 i : return false;
539 : }
540 :
541 : // Output the individual block lists.
542 E : SectionSpecVector::const_iterator it = sections.begin();
543 E : for (; it != sections.end(); ++it) {
544 E : const SectionSpec& section_spec = *it;
545 E : if (section_spec.blocks.empty())
546 i : continue;
547 :
548 E : if (!OutputSectionSpec(section_spec, json_file))
549 i : return false;
550 E : }
551 :
552 : // Close the list of sections.
553 E : if (!json_file->CloseList())
554 i : return false;
555 :
556 : // Close the outermost dictionary.
557 E : if (!json_file->CloseDict())
558 i : return false;
559 :
560 E : return true;
561 E : }
562 :
563 : bool Reorderer::Order::LoadFromJSON(const PEFile& pe,
564 : const ImageLayout& image,
565 E : const base::FilePath& path) {
566 E : std::string file_string;
567 E : if (!base::ReadFileToString(path, &file_string)) {
568 i : LOG(ERROR) << "Unable to read order file to string";
569 i : return false;
570 : }
571 :
572 : // Read in the JSON file. It should be a dictionary.
573 E : const DictionaryValue* outer_dict = NULL;
574 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
575 E : if (value.get() == NULL || !value->GetAsDictionary(&outer_dict)) {
576 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
577 i : return false;
578 : }
579 :
580 : // Load the metadata from the order file, and ensure it is consistent with
581 : // the signature of the module the ordering is being applied to.
582 E : pe::Metadata metadata;
583 E : PEFile::Signature pe_sig;
584 E : pe.GetSignature(&pe_sig);
585 E : const DictionaryValue* metadata_dict = NULL;
586 : if (!outer_dict->GetDictionary(kMetadataKey, &metadata_dict) ||
587 : !metadata.LoadFromJSON(*metadata_dict) ||
588 E : !metadata.IsConsistent(pe_sig)) {
589 i : LOG(ERROR) << "Missing, invalid, or inconsistent " << kMetadataKey << ".";
590 i : return false;
591 : }
592 :
593 : // Load the comments field.
594 : if (outer_dict->HasKey(kCommentKey) &&
595 E : !outer_dict->GetString(kCommentKey, &comment)) {
596 i : LOG(ERROR) << "Invalid " << kCommentKey << " value. Must be a string.";
597 i : return false;
598 : }
599 :
600 : // Grab the sections list.
601 E : const ListValue* order = NULL;
602 E : if (!outer_dict->GetList(kSectionsKey, &order)) {
603 i : LOG(ERROR) << "Missing or invalid " << kSectionsKey << ".";
604 i : return false;
605 : }
606 :
607 : // Allocate the expected number of sections.
608 E : sections.clear();
609 :
610 : // Iterate through the elements of the list. They should each be dictionaries
611 : // representing a single section. We'll also track the sections descriptions
612 : // we have already seen.
613 E : std::set<size_t> seen_section_ids;
614 E : sections.resize(order->GetSize());
615 E : for (size_t index = 0; index < order->GetSize(); ++index) {
616 E : const DictionaryValue* section = NULL;
617 E : if (!order->GetDictionary(index, §ion)) {
618 i : LOG(ERROR) << "Item " << index << "of " << kSectionsKey
619 : << " list is not a dictionary.";
620 i : return false;
621 : }
622 :
623 E : if (!LoadSectionSpec(image, section, §ions[index], &seen_section_ids))
624 i : return false;
625 E : }
626 :
627 E : return true;
628 E : }
629 :
630 : bool Reorderer::Order::GetOriginalModulePath(const base::FilePath& path,
631 E : base::FilePath* module) {
632 E : std::string file_string;
633 E : if (!base::ReadFileToString(path, &file_string)) {
634 E : LOG(ERROR) << "Unable to read order file to string.";
635 E : return false;
636 : }
637 :
638 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
639 E : if (value.get() == NULL || value->GetType() != Value::TYPE_DICTIONARY) {
640 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
641 i : return false;
642 : }
643 : const DictionaryValue* outer_dict =
644 E : reinterpret_cast<const DictionaryValue*>(value.get());
645 :
646 E : std::string metadata_key("metadata");
647 E : const DictionaryValue* metadata_dict = NULL;
648 E : if (!outer_dict->GetDictionary(metadata_key, &metadata_dict)) {
649 i : LOG(ERROR) << "Order dictionary must contain 'metadata'.";
650 i : return false;
651 : }
652 :
653 E : pe::Metadata metadata;
654 E : if (!metadata.LoadFromJSON(*metadata_dict))
655 i : return false;
656 :
657 E : *module = base::FilePath(metadata.module_signature().path);
658 :
659 E : return true;
660 E : }
661 :
662 : Reorderer::UniqueTime::UniqueTime()
663 : : time_(),
664 : id_(0) {
665 : }
666 :
667 : Reorderer::UniqueTime::UniqueTime(const UniqueTime& other)
668 : : time_(other.time_),
669 E : id_(other.id_) {
670 E : }
671 :
672 : Reorderer::UniqueTime::UniqueTime(const base::Time& time)
673 : : time_(time),
674 E : id_(next_id_++) {
675 E : }
676 :
677 E : Reorderer::UniqueTime& Reorderer::UniqueTime::operator=(const UniqueTime& rhs) {
678 E : time_ = rhs.time_;
679 E : id_ = rhs.id_;
680 E : return *this;
681 E : }
682 :
683 E : int Reorderer::UniqueTime::compare(const UniqueTime& rhs) const {
684 E : if (time_ < rhs.time_)
685 E : return -1;
686 E : if (time_ > rhs.time_)
687 E : return 1;
688 E : if (id_ < rhs.id_)
689 E : return -1;
690 E : if (id_ > rhs.id_)
691 E : return 1;
692 E : return 0;
693 E : }
694 :
695 : size_t Reorderer::UniqueTime::next_id_ = 0;
696 :
697 : } // namespace reorder
|