1 : // Copyright 2014 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 : // Instrumentation adapter that adds archive support to any existing
16 : // instrumenter. Takes care of instantiating a new instance of the
17 : // underlying instrumenter for each file in the archive. When not processing
18 : // an archive simply passes through the original instrumenter.
19 :
20 : #include "syzygy/instrument/instrumenters/archive_instrumenter.h"
21 :
22 : #include "base/bind.h"
23 : #include "base/file_util.h"
24 : #include "syzygy/ar/ar_transform.h"
25 : #include "syzygy/core/file_util.h"
26 :
27 : namespace instrument {
28 : namespace instrumenters {
29 :
30 : namespace {
31 :
32 : const char kInputImage[] = "input-image";
33 : const char kOutputImage[] = "output-image";
34 :
35 : } // namespace
36 :
37 : ArchiveInstrumenter::ArchiveInstrumenter()
38 : : factory_(NULL), overwrite_(false) {
39 : }
40 :
41 : ArchiveInstrumenter::ArchiveInstrumenter(InstrumenterFactoryFunction factory)
42 E : : factory_(factory), overwrite_(false) {
43 E : DCHECK_NE(reinterpret_cast<InstrumenterFactoryFunction>(NULL), factory);
44 E : }
45 :
46 E : bool ArchiveInstrumenter::ParseCommandLine(const CommandLine* command_line) {
47 E : DCHECK_NE(reinterpret_cast<CommandLine*>(NULL), command_line);
48 :
49 : // Create a copy of the command-line.
50 E : command_line_.reset(new CommandLine(*command_line));
51 :
52 : // Parse the few parameters that we care about.
53 E : input_image_ = command_line_->GetSwitchValuePath(kInputImage);
54 E : output_image_ = command_line_->GetSwitchValuePath(kOutputImage);
55 E : overwrite_ = command_line_->HasSwitch("overwrite");
56 :
57 E : return true;
58 E : }
59 :
60 E : bool ArchiveInstrumenter::Instrument() {
61 E : DCHECK_NE(reinterpret_cast<InstrumenterFactoryFunction>(NULL), factory_);
62 :
63 E : if (ProcessingArchive()) {
64 E : if (!InstrumentArchive())
65 i : return false;
66 E : } else {
67 E : if (!InstrumentPassthrough())
68 i : return false;
69 : }
70 :
71 E : return true;
72 E : }
73 :
74 E : bool ArchiveInstrumenter::ProcessingArchive() const {
75 E : if (input_image_.empty() || output_image_.empty())
76 i : return false;
77 :
78 E : if (!base::PathExists(input_image_))
79 i : return false;
80 :
81 E : core::FileType file_type = core::kUnknownFileType;
82 E : if (!core::GuessFileType(input_image_, &file_type))
83 i : return false;
84 :
85 E : if (file_type == core::kArchiveFileType)
86 E : return true;
87 :
88 E : return false;
89 E : }
90 :
91 E : bool ArchiveInstrumenter::InstrumentPassthrough() {
92 E : DCHECK_NE(reinterpret_cast<InstrumenterFactoryFunction>(NULL), factory_);
93 :
94 E : scoped_ptr<InstrumenterInterface> instrumenter(factory_());
95 : DCHECK_NE(reinterpret_cast<InstrumenterInterface*>(NULL),
96 E : instrumenter.get());
97 :
98 E : if (!instrumenter->ParseCommandLine(command_line_.get()))
99 i : return false;
100 :
101 E : if (!instrumenter->Instrument())
102 i : return false;
103 :
104 E : return true;
105 E : }
106 :
107 E : bool ArchiveInstrumenter::InstrumentArchive() {
108 : // Ensure we're not accidentally going to be overwriting the output.
109 E : if (!overwrite_ && base::PathExists(output_image_)) {
110 i : LOG(ERROR) << "Output path exists. Did you want to specify --overwrite?";
111 i : return false;
112 : }
113 :
114 E : LOG(INFO) << "Instrumenting archive: " << input_image_.value();
115 :
116 : // Configure and run an archive transform.
117 : ar::OnDiskArTransformAdapter::TransformFileOnDiskCallback callback =
118 E : base::Bind(&ArchiveInstrumenter::InstrumentFile, base::Unretained(this));
119 E : ar::OnDiskArTransformAdapter on_disk_adapter(callback);
120 E : ar::ArTransform ar_transform;
121 E : ar_transform.set_callback(on_disk_adapter.outer_callback());
122 E : ar_transform.set_input_archive(input_image_);
123 E : ar_transform.set_output_archive(output_image_);
124 E : if (!ar_transform.Transform())
125 i : return false;
126 :
127 E : return true;
128 E : }
129 :
130 : bool ArchiveInstrumenter::InstrumentFile(const base::FilePath& input_path,
131 : const base::FilePath& output_path,
132 : ar::ParsedArFileHeader* header,
133 E : bool* remove) {
134 E : DCHECK_NE(reinterpret_cast<InstrumenterFactoryFunction>(NULL), factory_);
135 E : DCHECK_NE(reinterpret_cast<ar::ParsedArFileHeader*>(NULL), header);
136 E : DCHECK_NE(reinterpret_cast<bool*>(NULL), remove);
137 :
138 : // We don't want to delete the file from the archive.
139 E : *remove = false;
140 :
141 : // Filter anything that isn't a known and recognized COFF file.
142 E : core::FileType file_type = core::kUnknownFileType;
143 E : if (!core::GuessFileType(input_path, &file_type)) {
144 i : LOG(ERROR) << "Unable to determine file type.";
145 i : return false;
146 : }
147 E : if (file_type != core::kCoffFileType) {
148 i : LOG(INFO) << "Not processing non-object file.";
149 i : if (!base::CopyFile(input_path, output_path)) {
150 i : LOG(ERROR) << "Unable to write output file: " << output_path.value();
151 i : return false;
152 : }
153 i : return true;
154 : }
155 :
156 : // Create the command-line for the child instrumenter.
157 E : CommandLine command_line(*command_line_.get());
158 E : command_line.AppendSwitchPath(kInputImage, input_path);
159 E : command_line.AppendSwitchPath(kOutputImage, output_path);
160 :
161 : // Create, initialize and run an instrumenter.
162 E : scoped_ptr<InstrumenterInterface> instrumenter(factory_());
163 : DCHECK_NE(reinterpret_cast<InstrumenterInterface*>(NULL),
164 E : instrumenter.get());
165 E : if (!instrumenter->ParseCommandLine(&command_line))
166 i : return false;
167 E : if (!instrumenter->Instrument())
168 i : return false;
169 :
170 E : return true;
171 E : }
172 :
173 : } // namespace instrumenters
174 : } // namespace instrument
|