1 : // Copyright 2012 Google Inc.
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/relink/relink_app.h"
16 :
17 : #include "base/logging_win.h"
18 : #include "base/string_number_conversions.h"
19 : #include "syzygy/block_graph/orderers/random_orderer.h"
20 : #include "syzygy/pe/pe_relinker.h"
21 : #include "syzygy/pe/transforms/explode_basic_blocks_transform.h"
22 : #include "syzygy/reorder/orderers/explicit_orderer.h"
23 :
24 : namespace relink {
25 :
26 : namespace {
27 :
28 : const char kUsageFormatStr[] =
29 : "Usage: %ls [options]\n"
30 : " Required Options:\n"
31 : " --input-dll=<path> The input DLL to relink.\n"
32 : " --output-dll=<path> Output path for the rewritten DLL.\n"
33 : " Optional Options:\n"
34 : " --basic-blocks Reorder at the basic-block level. At present,\n"
35 : " this is only supported for random reorderings.\n"
36 : " --compress-pdb If --no-augment-pdb is specified, causes the\n"
37 : " augmented PDB stream to be compressed.\n"
38 : " --exclude-bb-padding When randomly reordering basic blocks, exclude\n"
39 : " padding and unreachable code from the relinked\n"
40 : " output binary.\n"
41 : " --input-pdb=<path> The PDB file associated with the input DLL.\n"
42 : " Default is inferred from input-dll.\n"
43 : " --no-augment-pdb Indicates that the relinker should not augment\n"
44 : " the PDB with roundtrip decomposition info.\n"
45 : " --no-metadata Prevents the relinker from adding metadata\n"
46 : " to the output DLL.\n"
47 : " --no-strip-strings Causes strings to be output in the augmented\n"
48 : " PDB stream. The default is to omit these to\n"
49 : " make smaller PDBs.\n"
50 : " --order-file=<path> Reorder based on a JSON ordering file.\n"
51 : " --output-pdb=<path> Output path for the rewritten PDB file.\n"
52 : " Default is inferred from output-dll.\n"
53 : " --overwrite Allow output files to be overwritten.\n"
54 : " --padding=<integer> Add bytes of padding between blocks.\n"
55 : " --seed=<integer> Randomly reorder based on the given seed.\n"
56 : " Notes:\n"
57 : " * The --seed and --order-file options are mutually exclusive\n"
58 : " * If --order-file is specified, --input-dll is optional.\n"
59 : " * The --compress-pdb and --no-strip-strings options are only\n"
60 : " effective if --no-augment-pdb is not specified.\n"
61 : " * The --exclude-bb-padding option is only effective if\n"
62 : " --basic-blocks is specified.\n";
63 :
64 E : bool ParsePadding(const std::wstring& value_str, size_t* out_value) {
65 E : DCHECK(out_value != NULL);
66 :
67 : int temp;
68 E : if (!base::StringToInt(value_str, &temp) || temp < 0) {
69 i : return false;
70 : }
71 :
72 E : *out_value = static_cast<size_t>(temp);
73 E : return true;
74 E : }
75 :
76 E : bool ParseUInt32(const std::wstring& value_str, uint32* out_value) {
77 E : DCHECK(out_value != NULL);
78 E : return base::StringToInt(value_str, reinterpret_cast<int*>(out_value));
79 E : }
80 :
81 : void GuessPdbPath(const FilePath& module_path, FilePath* pdb_path) {
82 : DCHECK(pdb_path != NULL);
83 : *pdb_path = module_path.ReplaceExtension(L"pdb");
84 : }
85 :
86 : } // namespace
87 :
88 E : bool RelinkApp::ParseCommandLine(const CommandLine* cmd_line) {
89 E : input_dll_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-dll"));
90 E : input_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-pdb"));
91 E : output_dll_path_ = cmd_line->GetSwitchValuePath("output-dll");
92 E : output_pdb_path_ = cmd_line->GetSwitchValuePath("output-pdb");
93 E : order_file_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("order-file"));
94 E : no_augment_pdb_ = cmd_line->HasSwitch("no-augment-pdb");
95 E : compress_pdb_ = cmd_line->HasSwitch("compress-pdb");
96 E : no_strip_strings_ = cmd_line->HasSwitch("no-strip-strings");
97 E : output_metadata_ = !cmd_line->HasSwitch("no-metadata");
98 E : overwrite_ = cmd_line->HasSwitch("overwrite");
99 E : basic_blocks_ = cmd_line->HasSwitch("basic-blocks");
100 E : exclude_bb_padding_ = cmd_line->HasSwitch("exclude-bb-padding");
101 :
102 : // The --output-dll argument is required.
103 E : if (output_dll_path_.empty()) {
104 E : return Usage(cmd_line, "You must specify --output-dll.");
105 : }
106 :
107 : // Ensure that we have an input-dll, either explicity specified, or to be
108 : // taken from an order file.
109 E : if (input_dll_path_.empty() && order_file_path_.empty()) {
110 : return Usage(
111 : cmd_line,
112 E : "You must specify --input-dll if --order-file is not given.");
113 : }
114 :
115 : // Parse the random seed, if given. Note that the --seed and --order-file
116 : // arguments are mutually exclusive.
117 E : if (cmd_line->HasSwitch("seed")) {
118 E : if (cmd_line->HasSwitch("order-file")) {
119 : return Usage(cmd_line,
120 i : "The seed and order-file arguments are mutually exclusive");
121 : }
122 E : std::wstring seed_str(cmd_line->GetSwitchValueNative("seed"));
123 E : if (!ParseUInt32(seed_str, &seed_))
124 i : return Usage(cmd_line, "Invalid seed value.");
125 E : }
126 :
127 : // Parse the padding argument.
128 E : if (cmd_line->HasSwitch("padding")) {
129 E : std::wstring padding_str(cmd_line->GetSwitchValueNative("padding"));
130 E : if (!ParsePadding(padding_str, &padding_))
131 i : return Usage(cmd_line, "Invalid padding value.");
132 E : }
133 :
134 E : return true;
135 E : }
136 :
137 E : bool RelinkApp::SetUp() {
138 E : if (input_dll_path_.empty()) {
139 E : DCHECK(!order_file_path_.empty());
140 : if (!reorder::Reorderer::Order::GetOriginalModulePath(order_file_path_,
141 E : &input_dll_path_)) {
142 E : LOG(ERROR) << "Unable to infer input-dll.";
143 E : return false;
144 : }
145 :
146 i : LOG(INFO) << "Inferring input DLL path from order file: "
147 : << input_dll_path_.value();
148 : }
149 :
150 E : DCHECK(!input_dll_path_.empty());
151 E : DCHECK(!output_dll_path_.empty());
152 E : DCHECK(order_file_path_.empty() || seed_ == 0);
153 :
154 E : return true;
155 E : }
156 :
157 E : int RelinkApp::Run() {
158 E : pe::PERelinker relinker;
159 E : relinker.set_input_path(input_dll_path_);
160 E : relinker.set_input_pdb_path(input_pdb_path_);
161 E : relinker.set_output_path(output_dll_path_);
162 E : relinker.set_output_pdb_path(output_pdb_path_);
163 E : relinker.set_padding(padding_);
164 E : relinker.set_add_metadata(output_metadata_);
165 E : relinker.set_allow_overwrite(overwrite_);
166 E : relinker.set_augment_pdb(!no_augment_pdb_);
167 E : relinker.set_compress_pdb(compress_pdb_);
168 E : relinker.set_strip_strings(!no_strip_strings_);
169 :
170 : // Initialize the relinker. This does the decomposition, etc.
171 E : if (!relinker.Init()) {
172 i : LOG(ERROR) << "Failed to initialize relinker.";
173 i : return 1;
174 : }
175 :
176 : // Set up the orderer.
177 E : scoped_ptr<pe::transforms::ExplodeBasicBlocksTransform> bb_explode;
178 E : scoped_ptr<block_graph::BlockGraphOrdererInterface> orderer;
179 E : scoped_ptr<reorder::Reorderer::Order> order;
180 E : if (!order_file_path_.empty()) {
181 i : order.reset(new reorder::Reorderer::Order());
182 : if (!order->LoadFromJSON(relinker.input_pe_file(),
183 : relinker.input_image_layout(),
184 i : order_file_path_)) {
185 i : LOG(ERROR) << "Failed to load order file: " << order_file_path_.value();
186 i : return 1;
187 : }
188 :
189 i : orderer.reset(new reorder::orderers::ExplicitOrderer(order.get()));
190 i : } else {
191 E : orderer.reset(new block_graph::orderers::RandomOrderer(true, seed_));
192 E : if (basic_blocks_) {
193 E : bb_explode.reset(new pe::transforms::ExplodeBasicBlocksTransform());
194 E : bb_explode->set_exclude_padding(exclude_bb_padding_);
195 E : relinker.AppendTransform(bb_explode.get());
196 : }
197 : }
198 :
199 : // Append the orderer to the relinker.
200 E : relinker.AppendOrderer(orderer.get());
201 :
202 : // Perform the actual relink.
203 E : if (!relinker.Relink()) {
204 i : LOG(ERROR) << "Unable to relink input image.";
205 i : return 1;
206 : }
207 :
208 E : return 0;
209 E : }
210 :
211 : bool RelinkApp::Usage(const CommandLine* cmd_line,
212 E : const base::StringPiece& message) const {
213 E : if (!message.empty()) {
214 E : ::fwrite(message.data(), 1, message.length(), err());
215 E : ::fprintf(err(), "\n\n");
216 : }
217 :
218 : ::fprintf(err(),
219 : kUsageFormatStr,
220 E : cmd_line->GetProgram().BaseName().value().c_str());
221 :
222 E : return false;
223 E : }
224 :
225 : } // namespace relink
|