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 : // Declares PERelinker. Relinking can be seen as decomposing an input image,
16 : // applying a sequence of block-graph transforms (some applied implicitly, and
17 : // others provided by the user), followed by a sequence of orderers (again, some
18 : // implicit, some provided by the user), laying-out, finalizing and finally
19 : // writing a new image. After writing the image a similar transformation
20 : // workflow is applied to the corresponding PDB file, consisting of applying
21 : // any user defined PDB mutations, followed by 2-3 (depending on PeRelinker
22 : // configuration) internal mutations (updating the GUID/age, adding the history
23 : // stream and adding the serialized block-graph stream). PERelinker encapsulates
24 : // this workflow.
25 : //
26 : // It is intended to be used as follows:
27 : //
28 : // PERelinker relinker;
29 : // relinker.set_input_path(...); // Required.
30 : // relinker.set_output_path(...); // Required.
31 : // relinker.set_input_pdb_path(...); // Optional.
32 : // relinker.set_output_pdb_path(...); // Optional.
33 : // relinker.Init(); // Check the return value!
34 : //
35 : // // At this point, the following accessors are valid:
36 : // relinker.input_pe_file();
37 : // relinker.input_image_layout();
38 : // relinker.block_graph();
39 : // relinker.dos_header_block();
40 : // relinker.output_guid();
41 : //
42 : // relinker.AppendTransform(...); // May be called repeatedly.
43 : // relinker.AppendOrderer(...); // May be called repeatedly.
44 : // relinker.AppendPdbMutator(...); // May be called repeatedly.
45 : //
46 : // relinker.Relink(); // Check the return value!
47 : //
48 : // NOTE: This split workflow is only necessary as a workaround to deal with
49 : // transforms and orderers built around legacy code. Intermediate
50 : // representations of serialized data-structures should be stored in such
51 : // a way so as not to explicitly require access to the untransformed image.
52 : // Additionally, for checking validity a transform or orderer should require
53 : // no more than the PESignature associated with the original module, and/or
54 : // the toolchain metadata present in the module, if there was any.
55 : //
56 : // TODO(chrisha): Resimplify this API once Reorderer has been reworked to move
57 : // away from Block pointers.
58 :
59 : #ifndef SYZYGY_PE_PE_RELINKER_H_
60 : #define SYZYGY_PE_PE_RELINKER_H_
61 :
62 : #include <vector>
63 :
64 : #include "base/files/file_path.h"
65 : #include "syzygy/block_graph/orderer.h"
66 : #include "syzygy/block_graph/transform.h"
67 : #include "syzygy/pdb/pdb_mutator.h"
68 : #include "syzygy/pe/image_layout_builder.h"
69 : #include "syzygy/pe/pe_file.h"
70 :
71 : namespace pe {
72 :
73 : // Embodies a transformation on a PE image, from decomposing an original image
74 : // to applying some transform(s) to it, to generating the layout and finally
75 : // writing the image and accompanying PDB to disk.
76 : //
77 : // Creating a PERelinker and not changing its default configuration yields an
78 : // identity relinker that will produce an identical (nearly, except for cosmetic
79 : // differences in some headers) image to the input.
80 : //
81 : // The workflow is as follows:
82 : //
83 : // 1. Relinker created with an input image. The PDB file is found automatically
84 : // and the image is decomposed. Optionally the PDB may be directly specified.
85 : // 2. The image is transformed:
86 : // a) Transforms provided by the user are applied.
87 : // b) AddMetadataTransform is conditionally applied.
88 : // c) AddPdbInfoTransform is applied.
89 : // d) PrepareHeadersTransform is applied.
90 : // 3. The image is ordered:
91 : // a) Orderers provided by the user are applied.
92 : // b) PEOrderer is applied.
93 : // 4. ImageLayoutBuilder is used to convert the OrderedBlockGraph to an
94 : // ImageLayout.
95 : // 5. Image and accompanying PDB file are written. (Filenames are inferred from
96 : // input filenames or directly specified.)
97 : class PERelinker {
98 : public:
99 : typedef block_graph::BlockGraph BlockGraph;
100 : typedef block_graph::BlockGraphOrdererInterface Orderer;
101 : typedef block_graph::BlockGraphTransformInterface Transform;
102 :
103 : PERelinker();
104 :
105 : // @name Accessors.
106 : // @{
107 E : const base::FilePath& input_path() const { return input_path_; }
108 E : const base::FilePath& input_pdb_path() const { return input_pdb_path_; }
109 E : const base::FilePath& output_path() const { return output_path_; }
110 E : const base::FilePath& output_pdb_path() const { return output_pdb_path_; }
111 E : bool add_metadata() const { return add_metadata_; }
112 E : bool allow_overwrite() const { return allow_overwrite_; }
113 E : bool augment_pdb() const { return augment_pdb_; }
114 E : bool compress_pdb() const { return compress_pdb_; }
115 E : bool parse_debug_info() const { return parse_debug_info_; }
116 E : bool strip_strings() const { return strip_strings_; }
117 E : bool use_new_decomposer() const { return use_new_decomposer_; }
118 E : size_t padding() const { return padding_; }
119 : // @}
120 :
121 : // @name Mutators for controlling relinker behaviour.
122 : // @{
123 E : void set_input_path(const base::FilePath& input_path) {
124 E : input_path_ = input_path;
125 E : }
126 E : void set_input_pdb_path(const base::FilePath& input_pdb_path) {
127 E : input_pdb_path_ = input_pdb_path;
128 E : }
129 E : void set_output_path(const base::FilePath& output_path) {
130 E : output_path_ = output_path;
131 E : }
132 E : void set_output_pdb_path(const base::FilePath& output_pdb_path) {
133 E : output_pdb_path_ = output_pdb_path;
134 E : }
135 E : void set_add_metadata(bool add_metadata) {
136 E : add_metadata_ = add_metadata;
137 E : }
138 E : void set_allow_overwrite(bool allow_overwrite) {
139 E : allow_overwrite_ = allow_overwrite;
140 E : }
141 E : void set_augment_pdb(bool augment_pdb) {
142 E : augment_pdb_ = augment_pdb;
143 E : }
144 E : void set_compress_pdb(bool compress_pdb) {
145 E : compress_pdb_ = compress_pdb;
146 E : }
147 E : void set_parse_debug_info(bool parse_debug_info) {
148 E : parse_debug_info_ = parse_debug_info;
149 E : }
150 E : void set_strip_strings(bool strip_strings) {
151 E : strip_strings_ = strip_strings;
152 E : }
153 E : void set_use_new_decomposer(bool use_new_decomposer) {
154 E : use_new_decomposer_ = use_new_decomposer;
155 E : }
156 E : void set_padding(size_t padding) {
157 E : padding_ = padding;
158 E : }
159 : // @}
160 :
161 : // Appends a transform to be applied by this relinker. If no transforms are
162 : // specified, none will be applied and the transform is effectively the
163 : // identity transform. Each transform will be applied in the order added
164 : // to the relinker, assuming all earlier transforms have succeeded.
165 : //
166 : // @param transform the transform to append to the list of transforms to
167 : // apply. The pointer must remain valid for the lifespan of the relinker.
168 : void AppendTransform(Transform* transform);
169 :
170 : // Appends a list of transforms to be applied by this relinker. Each transform
171 : // will be applied in the order added to the relinker, assuming all earlier
172 : // transforms have succeeded.
173 : //
174 : // @param transforms a vector of transforms to be applied to the input image.
175 : // The pointers must remain valid for the lifespan of the relinker.
176 : void AppendTransforms(const std::vector<Transform*>& transforms);
177 :
178 : // Appends an orderer to be applied by this relinker.
179 : //
180 : // If no orderers are specified the default orderer will be applied. If no
181 : // transforms have been applied this makes the entire relinker an identity
182 : // relinker. Each orderer will be applied in the order added to the relinker,
183 : // assuming all earlier orderers have succeeded.
184 : //
185 : // @param orderer a orderer to be applied to the input image. The pointer must
186 : // remain valid for the lifespan of the relinker.
187 : void AppendOrderer(Orderer* orderer);
188 :
189 : // Appends a list of orderers to be applied by this relinker.
190 : //
191 : // If no orderers are specified the default orderer will be applied. If no
192 : // transforms have been applied this makes the entire relinker an identity
193 : // relinker. Each orderer will be applied in the order added to the relinker,
194 : // assuming all earlier orderers have succeeded.
195 : //
196 : // @param orderers a vector of orderers to be applied to the input image.
197 : // The pointers must remain valid for the lifespan of the relinker.
198 : void AppendOrderers(const std::vector<Orderer*>& orderers);
199 :
200 : // Appends a PDB mutator to be applied by this relinker.
201 : //
202 : // Each mutator will be applied in the order added to the relinker,
203 : // assuming all earlier mutators have succeeded.
204 : //
205 : // @param pdb_mutator a PDB mutator to be applied to the input image. The
206 : // pointer must remain valid for the lifespan of the relinker.
207 : void AppendPdbMutator(pdb::PdbMutatorInterface* pdb_mutator);
208 :
209 : // Appends a list of PDB mutators to be applied by this relinker.
210 : //
211 : // Each mutator will be applied in the order added to the relinker,
212 : // assuming all earlier mutators have succeeded.
213 : //
214 : // @param pdb_mutators a vector of mutators to be applied to the input image.
215 : // The pointers must remain valid for the lifespan of the relinker.
216 : void AppendPdbMutators(
217 : const std::vector<pdb::PdbMutatorInterface*>& pdb_mutators);
218 :
219 : // Runs the initialization phase of the relinker. This consists of decomposing
220 : // the input image, after which the intermediate data accessors declared below
221 : // become valid. This should typically be followed by a call to Relink.
222 : //
223 : // @returns true on success, false otherwise.
224 : // @pre input_path and output_path must be set prior to calling this.
225 : // input_pdb_path and output_pdb_path may optionally have been set prior
226 : // to calling this.
227 : // @post input_pe_file and input_image_layout may be called after this.
228 : // @note This entrypoint is virtual for unittest/mocking purposes.
229 : virtual bool Init();
230 :
231 : // Runs the relinker, generating an output image and PDB.
232 : //
233 : // @returns true on success, false otherwise.
234 : // @pre Init must have been called successfully.
235 : // @note This entrypoint is virtual for unittest/mocking purposes.
236 : virtual bool Relink();
237 :
238 : // @name Intermediate data accessors.
239 : // @{
240 : // These accessors only return meaningful data after Init has been called. By
241 : // the time any transforms or orderers are being called, these will contain
242 : // valid data.
243 : //
244 : // TODO(chrisha): Clean these up as part of the API simplification after
245 : // all legacy code has been refactored.
246 : //
247 : // @pre Init has been successfully called.
248 E : const PEFile& input_pe_file() const { return input_pe_file_; }
249 E : const ImageLayout& input_image_layout() const { return input_image_layout_; }
250 E : const BlockGraph& block_graph() const { return block_graph_; }
251 E : const BlockGraph::Block* dos_header_block() const {
252 E : return dos_header_block_; }
253 : const GUID& output_guid() const { return output_guid_; }
254 : // @}
255 :
256 : protected:
257 : base::FilePath input_path_;
258 : base::FilePath input_pdb_path_;
259 : base::FilePath output_path_;
260 : base::FilePath output_pdb_path_;
261 :
262 : // If true, metadata will be added to the output image. Defaults to true.
263 : bool add_metadata_;
264 : // If true, allow the relinker to rewrite the input files in place. Defaults
265 : // to false.
266 : bool allow_overwrite_;
267 : // If true, the PDB will be augmented with a serialized block-graph and
268 : // image layout. Defaults to true.
269 : bool augment_pdb_;
270 : // If true, then the augmented PDB stream will be compressed as it is written.
271 : // Defaults to false.
272 : bool compress_pdb_;
273 : // If true, then the decomposition will parse full symbol information.
274 : // Defaults to true.
275 : bool parse_debug_info_;
276 : // If true, strings associated with a block-graph will not be serialized into
277 : // the PDB. Defaults to false.
278 : bool strip_strings_;
279 : // If true we will use the new decomposer. Defaults to false.
280 : bool use_new_decomposer_;
281 : // Indicates the amount of padding to be added between blocks. Zero is the
282 : // default value and indicates no padding will be added.
283 : size_t padding_;
284 :
285 : // The vectors of user supplied transforms, orderers and mutators to be
286 : // applied.
287 : std::vector<Transform*> transforms_;
288 : std::vector<Orderer*> orderers_;
289 : std::vector<pdb::PdbMutatorInterface*> pdb_mutators_;
290 :
291 : // The internal state of the relinker.
292 : bool inited_;
293 :
294 : // Intermediate variables that are initialized and used by Relink. They are
295 : // made externally accessible so that transforms and orderers may make use
296 : // of them if necessary.
297 :
298 : // These refer to the original image, and don't change after init.
299 : PEFile input_pe_file_;
300 : ImageLayout input_image_layout_;
301 :
302 : // These refer to the image the whole way through the process. They may
303 : // evolve.
304 : BlockGraph block_graph_;
305 : BlockGraph::Block* dos_header_block_;
306 :
307 : // These are for the new image that will be produced at the end of Relink.
308 : GUID output_guid_;
309 : };
310 :
311 : } // namespace pe
312 :
313 : #endif // SYZYGY_PE_PE_RELINKER_H_
|