1 : // Copyright 2013 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/optimize/optimize_app.h"
16 :
17 : #include "syzygy/block_graph/block_graph.h"
18 : #include "syzygy/block_graph/transforms/fuzzing_transform.h"
19 : #include "syzygy/block_graph/transforms/named_transform.h"
20 : #include "syzygy/common/indexed_frequency_data.h"
21 : #include "syzygy/grinder/basic_block_util.h"
22 : #include "syzygy/optimize/application_profile.h"
23 : #include "syzygy/optimize/transforms/basic_block_reordering_transform.h"
24 : #include "syzygy/optimize/transforms/block_alignment_transform.h"
25 : #include "syzygy/optimize/transforms/chained_subgraph_transforms.h"
26 : #include "syzygy/optimize/transforms/inlining_transform.h"
27 : #include "syzygy/optimize/transforms/peephole_transform.h"
28 : #include "syzygy/optimize/transforms/unreachable_block_transform.h"
29 : #include "syzygy/pe/pe_relinker.h"
30 : #include "syzygy/pe/pe_transform_policy.h"
31 :
32 : namespace optimize {
33 :
34 : namespace {
35 :
36 : using block_graph::transforms::FuzzingTransform;
37 : using common::IndexedFrequencyData;
38 : using grinder::basic_block_util::IndexedFrequencyMap;
39 : using grinder::basic_block_util::LoadBranchStatisticsFromFile;
40 : using optimize::transforms::BasicBlockReorderingTransform;
41 : using optimize::transforms::BlockAlignmentTransform;
42 : using optimize::transforms::ChainedSubgraphTransforms;
43 : using optimize::transforms::InliningTransform;
44 : using optimize::transforms::PeepholeTransform;
45 : using optimize::transforms::UnreachableBlockTransform;
46 :
47 : const char kUsageFormatStr[] =
48 : "Usage: %ls [options]\n"
49 : " Required Options:\n"
50 : " --input-image=<path> The input image file to optimize.\n"
51 : " --output-image=<path> Output path for the rewritten image file.\n"
52 : "\n"
53 : " Options:\n"
54 : " --branch-file=<path> Branch statistics in JSON format.\n"
55 : " --input-pdb=<path> The PDB file associated with the input DLL.\n"
56 : " Default is inferred from input-image.\n"
57 : " --output-pdb=<path> Output path for the rewritten PDB file.\n"
58 : " Default is inferred from output-image.\n"
59 : " --overwrite Allow output files to be overwritten.\n"
60 : "\n"
61 : " Optimization Options:\n"
62 : " --all Enable all optimizations.\n"
63 : " --allow-inline-assembly\n"
64 : " Enable the decomposition of inline assembly\n"
65 : " blocks.\n"
66 : " --basic-block-reorder Enable basic block reodering.\n"
67 : " --block-alignment Enable block realignment.\n"
68 : " --inlining Enable function inlining.\n"
69 : " --peephole Enable peephole optimization.\n"
70 : " --unreachable-block Enable unreachable block optimization.\n"
71 : "\n"
72 : " Unreachable block options:\n"
73 : " --dump-unreachable-graph=<path>\n"
74 : " Dump the unreachable graph.\n"
75 : "\n"
76 : " Testing Options:\n"
77 : " --fuzz Fuzz the binary.\n"
78 : "\n";
79 :
80 : } // namespace
81 :
82 E : bool OptimizeApp::ParseCommandLine(const CommandLine* cmd_line) {
83 :
84 E : if (cmd_line->HasSwitch("help"))
85 E : return Usage(cmd_line, "");
86 :
87 E : input_image_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-image"));
88 E : output_image_path_ = cmd_line->GetSwitchValuePath("output-image");
89 E : input_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-pdb"));
90 E : output_pdb_path_ = cmd_line->GetSwitchValuePath("output-pdb");
91 E : branch_file_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("branch-file"));
92 :
93 E : basic_block_reorder_ = cmd_line->HasSwitch("basic-block-reorder");
94 E : block_alignment_ = cmd_line->HasSwitch("block-alignment");
95 E : fuzz_ = cmd_line->HasSwitch("fuzz");
96 E : inlining_ = cmd_line->HasSwitch("inlining");
97 E : allow_inline_assembly_ = cmd_line->HasSwitch("allow-inline-assembly");
98 E : peephole_ = cmd_line->HasSwitch("peephole");
99 E : unreachable_block_ = cmd_line->HasSwitch("unreachable-block");
100 E : overwrite_ = cmd_line->HasSwitch("overwrite");
101 :
102 : // Enable all optimization transforms.
103 E : if (cmd_line->HasSwitch("all")) {
104 E : basic_block_reorder_ = true;
105 E : block_alignment_ = true;
106 E : inlining_ = true;
107 E : peephole_ = true;
108 E : unreachable_block_ = true;
109 : }
110 :
111 : unreachable_graph_path_ =
112 E : AbsolutePath(cmd_line->GetSwitchValuePath("dump-unreachable-graph"));
113 :
114 : // The --input-image argument is required.
115 E : if (input_image_path_.empty())
116 E : return Usage(cmd_line, "You must specify --input-image.");
117 :
118 : // The --output-image argument is required.
119 E : if (output_image_path_.empty())
120 E : return Usage(cmd_line, "You must specify --output-image.");
121 :
122 E : return true;
123 E : }
124 :
125 E : bool OptimizeApp::SetUp() {
126 E : DCHECK(!input_image_path_.empty());
127 E : DCHECK(!output_image_path_.empty());
128 E : return true;
129 E : }
130 :
131 E : int OptimizeApp::Run() {
132 E : pe::PETransformPolicy policy;
133 E : policy.set_allow_inline_assembly(allow_inline_assembly_);
134 E : pe::PERelinker relinker(&policy);
135 E : relinker.set_input_path(input_image_path_);
136 E : relinker.set_input_pdb_path(input_pdb_path_);
137 E : relinker.set_output_path(output_image_path_);
138 E : relinker.set_output_pdb_path(output_pdb_path_);
139 E : relinker.set_allow_overwrite(overwrite_);
140 :
141 : // Initialize the relinker. This does the decomposition, etc.
142 E : if (!relinker.Init()) {
143 i : LOG(ERROR) << "Failed to initialize relinker.";
144 i : return 1;
145 : }
146 :
147 : // Get module signature and layout.
148 E : pe::PEFile::Signature signature;
149 E : relinker.input_pe_file().GetSignature(&signature);
150 E : const pe::ImageLayout& image_layout = relinker.input_image_layout();
151 :
152 : // Load profile information from file.
153 E : ApplicationProfile profile(&image_layout);
154 E : if (!branch_file_path_.empty()) {
155 i : IndexedFrequencyMap frequencies;
156 : if (!LoadBranchStatisticsFromFile(branch_file_path_,
157 : signature,
158 i : &frequencies)) {
159 i : LOG(ERROR) << "Unable to load profile information.";
160 i : return 1;
161 : }
162 i : if (!profile.ImportFrequencies(frequencies)) {
163 i : LOG(ERROR) << "Could not import metrics for '"
164 : << branch_file_path_.value() << "'.";
165 i : return false;
166 : }
167 i : }
168 :
169 : // Compute global profile information for the current block graph.
170 E : if (!profile.ComputeGlobalProfile()) {
171 i : LOG(ERROR) << "Unable to build profile information.";
172 i : return 1;
173 : }
174 :
175 : // Construct a chain of basic block transforms.
176 E : ChainedSubgraphTransforms chains(&profile);
177 :
178 : // Declare transforms we may apply.
179 E : scoped_ptr<BasicBlockReorderingTransform> basic_block_reordering_transform;
180 E : scoped_ptr<BlockAlignmentTransform> block_alignment_transform;
181 E : scoped_ptr<FuzzingTransform> fuzzing_transform;
182 E : scoped_ptr<InliningTransform> inlining_transform;
183 E : scoped_ptr<PeepholeTransform> peephole_transform;
184 E : scoped_ptr<UnreachableBlockTransform> unreachable_block_transform;
185 :
186 : // If block block reordering is enabled, add it to the chain.
187 E : if (peephole_) {
188 i : peephole_transform.reset(new PeepholeTransform());
189 i : chains.AppendTransform(peephole_transform.get());
190 : }
191 :
192 : // If inlining is enabled, add it to the chain.
193 E : if (inlining_) {
194 i : inlining_transform.reset(new InliningTransform());
195 i : chains.AppendTransform(inlining_transform.get());
196 : }
197 :
198 : // If block block reordering is enabled, add it to the chain.
199 E : if (basic_block_reorder_) {
200 i : basic_block_reordering_transform.reset(new BasicBlockReorderingTransform());
201 i : chains.AppendTransform(basic_block_reordering_transform.get());
202 : }
203 :
204 : // If block alignment is enabled, add it to the chain.
205 E : if (block_alignment_) {
206 i : block_alignment_transform.reset(new BlockAlignmentTransform());
207 i : chains.AppendTransform(block_alignment_transform.get());
208 : }
209 :
210 : // Append the chain to the relinker.
211 E : if (!relinker.AppendTransform(&chains))
212 i : return false;
213 :
214 : // If unreachable-block is enabled, add it to the relinker.
215 E : if (unreachable_block_) {
216 i : unreachable_block_transform.reset(new UnreachableBlockTransform());
217 : unreachable_block_transform->set_unreachable_graph_path(
218 i : unreachable_graph_path_);
219 i : relinker.AppendTransform(unreachable_block_transform.get());
220 : }
221 :
222 : // If fuzzing is enabled, add it to the relinker.
223 E : if (fuzz_) {
224 i : fuzzing_transform.reset(new block_graph::transforms::FuzzingTransform);
225 i : relinker.AppendTransform(fuzzing_transform.get());
226 : }
227 :
228 : // Perform the actual relink.
229 E : if (!relinker.Relink()) {
230 i : LOG(ERROR) << "Unable to relink input image.";
231 i : return 1;
232 : }
233 :
234 E : return 0;
235 E : }
236 :
237 : bool OptimizeApp::Usage(const CommandLine* cmd_line,
238 E : const base::StringPiece& message) const {
239 E : if (!message.empty()) {
240 E : ::fwrite(message.data(), 1, message.length(), err());
241 E : ::fprintf(err(), "\n\n");
242 : }
243 :
244 : ::fprintf(err(),
245 : kUsageFormatStr,
246 E : cmd_line->GetProgram().BaseName().value().c_str());
247 :
248 E : return false;
249 E : }
250 :
251 : } // namespace optimize
|