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