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/stringprintf.h"
18 : #include "gmock/gmock.h"
19 : #include "gtest/gtest.h"
20 : #include "syzygy/block_graph/unittest_util.h"
21 : #include "syzygy/common/unittest_util.h"
22 : #include "syzygy/core/unittest_util.h"
23 : #include "syzygy/pe/pe_utils.h"
24 : #include "syzygy/pe/unittest_util.h"
25 :
26 : namespace relink {
27 :
28 : using block_graph::BlockGraph;
29 : using common::Application;
30 : using core::RelativeAddress;
31 : using ::testing::ScopedLogLevelSaver;
32 :
33 : namespace {
34 :
35 : class TestRelinkApp : public RelinkApp {
36 : public:
37 : using RelinkApp::input_dll_path_;
38 : using RelinkApp::input_pdb_path_;
39 : using RelinkApp::output_dll_path_;
40 : using RelinkApp::output_pdb_path_;
41 : using RelinkApp::order_file_path_;
42 : using RelinkApp::seed_;
43 : using RelinkApp::padding_;
44 : using RelinkApp::no_augment_pdb_;
45 : using RelinkApp::compress_pdb_;
46 : using RelinkApp::no_strip_strings_;
47 : using RelinkApp::output_metadata_;
48 : using RelinkApp::overwrite_;
49 : };
50 :
51 : typedef common::Application<TestRelinkApp> TestApp;
52 :
53 : class RelinkAppTest : public testing::PELibUnitTest {
54 : public:
55 : typedef testing::PELibUnitTest Super;
56 :
57 : RelinkAppTest()
58 : : cmd_line_(FilePath(L"relink.exe")),
59 : test_impl_(test_app_.implementation()),
60 : seed_(1234567),
61 : padding_(32),
62 : no_augment_pdb_(false),
63 : compress_pdb_(false),
64 : no_strip_strings_(false),
65 : output_metadata_(false),
66 E : overwrite_(false) {
67 E : }
68 :
69 E : void SetUp() {
70 E : Super::SetUp();
71 :
72 : // Several of the tests generate progress and (deliberate) error messages
73 : // that would otherwise clutter the unittest output.
74 E : logging::SetMinLogLevel(logging::LOG_FATAL);
75 :
76 : // Setup the IO streams.
77 E : CreateTemporaryDir(&temp_dir_);
78 E : stdin_path_ = temp_dir_.Append(L"NUL");
79 E : stdout_path_ = temp_dir_.Append(L"stdout.txt");
80 E : stderr_path_ = temp_dir_.Append(L"stderr.txt");
81 E : InitStreams(stdin_path_, stdout_path_, stderr_path_);
82 :
83 : // Initialize the (potential) input and output path values.
84 E : abs_input_dll_path_ = testing::GetExeRelativePath(kDllName);
85 E : input_dll_path_ = testing::GetRelativePath(abs_input_dll_path_);
86 E : abs_input_pdb_path_ = testing::GetExeRelativePath(kDllPdbName);
87 E : input_pdb_path_ = testing::GetRelativePath(abs_input_pdb_path_);
88 E : output_dll_path_ = temp_dir_.Append(input_dll_path_.BaseName());
89 E : output_pdb_path_ = temp_dir_.Append(input_pdb_path_.BaseName());
90 E : order_file_path_ = temp_dir_.Append(L"order.json");
91 :
92 : // Point the application at the test's command-line and IO streams.
93 E : test_app_.set_command_line(&cmd_line_);
94 E : test_app_.set_in(in());
95 E : test_app_.set_out(out());
96 E : test_app_.set_err(err());
97 E : }
98 :
99 : // Stashes the current log-level before each test instance and restores it
100 : // after each test completes.
101 : ScopedLogLevelSaver log_level_saver;
102 :
103 : // @name The application under test.
104 : // @{
105 : TestApp test_app_;
106 : TestApp::Implementation& test_impl_;
107 : FilePath temp_dir_;
108 : FilePath stdin_path_;
109 : FilePath stdout_path_;
110 : FilePath stderr_path_;
111 : // @}
112 :
113 : // @name Command-line and parameters.
114 : // @{
115 : CommandLine cmd_line_;
116 : FilePath input_dll_path_;
117 : FilePath input_pdb_path_;
118 : FilePath output_dll_path_;
119 : FilePath output_pdb_path_;
120 : FilePath order_file_path_;
121 : uint32 seed_;
122 : size_t padding_;
123 : bool no_augment_pdb_;
124 : bool compress_pdb_;
125 : bool no_strip_strings_;
126 : bool output_metadata_;
127 : bool overwrite_;
128 : // @}
129 :
130 : // @name Expected final values of input parameters.
131 : // @{
132 : FilePath abs_input_dll_path_;
133 : FilePath abs_input_pdb_path_;
134 : // @}
135 : };
136 :
137 : } // namespace
138 :
139 E : TEST_F(RelinkAppTest, GetHelp) {
140 E : cmd_line_.AppendSwitch("help");
141 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
142 E : }
143 :
144 E : TEST_F(RelinkAppTest, EmptyCommandLineFails) {
145 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
146 E : }
147 :
148 E : TEST_F(RelinkAppTest, ParseWithNeitherInputNorOrderFails) {
149 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
150 :
151 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
152 E : }
153 :
154 E : TEST_F(RelinkAppTest, ParseWithSeedAndOrderFails) {
155 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
156 E : cmd_line_.AppendSwitchASCII("seed", base::StringPrintf("%d", seed_));
157 E : cmd_line_.AppendSwitchPath("order_file", order_file_path_);
158 :
159 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
160 E : }
161 :
162 E : TEST_F(RelinkAppTest, ParseWithEmptySeedFails) {
163 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
164 E : cmd_line_.AppendSwitch("seed");
165 :
166 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
167 E : }
168 :
169 E : TEST_F(RelinkAppTest, ParseWithInvalidSeedFails) {
170 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
171 E : cmd_line_.AppendSwitchASCII("seed", "hello");
172 :
173 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
174 E : }
175 :
176 E : TEST_F(RelinkAppTest, ParseWithEmptyPaddingFails) {
177 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
178 E : cmd_line_.AppendSwitch("padding");
179 :
180 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
181 E : }
182 :
183 E : TEST_F(RelinkAppTest, ParseWithInvalidPaddingFails) {
184 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
185 E : cmd_line_.AppendSwitchASCII("padding", "hello");
186 :
187 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
188 E : }
189 :
190 E : TEST_F(RelinkAppTest, ParseMinimalCommandLineWithInputDll) {
191 E : cmd_line_.AppendSwitchPath("input-dll", input_dll_path_);
192 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
193 :
194 E : EXPECT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
195 E : EXPECT_TRUE(test_impl_.SetUp());
196 E : }
197 :
198 E : TEST_F(RelinkAppTest, ParseMinimalCommandLineWithOrderFile) {
199 : // The order file doesn't actually exist, so setup should fail to infer the
200 : // input dll.
201 E : cmd_line_.AppendSwitchPath("order-file", order_file_path_);
202 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
203 :
204 E : EXPECT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
205 E : EXPECT_FALSE(test_impl_.SetUp());
206 E : }
207 :
208 E : TEST_F(RelinkAppTest, ParseFullCommandLineWithOrderFile) {
209 : // Note that we specify the no-metadata flag, so we expect false below
210 : // for the output_metadata_ member. Also note that neither seed nor padding
211 : // are given, and should default to 0.
212 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
213 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
214 E : cmd_line_.AppendSwitchPath("order-file", order_file_path_);
215 E : cmd_line_.AppendSwitch("no-augment-pdb");
216 E : cmd_line_.AppendSwitch("compress-pdb");
217 E : cmd_line_.AppendSwitch("no-strip-strings");
218 E : cmd_line_.AppendSwitch("no-metadata");
219 E : cmd_line_.AppendSwitch("overwrite");
220 :
221 E : EXPECT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
222 E : EXPECT_TRUE(test_impl_.input_dll_path_.empty());
223 E : EXPECT_TRUE(test_impl_.input_pdb_path_.empty());
224 E : EXPECT_EQ(output_dll_path_, test_impl_.output_dll_path_);
225 E : EXPECT_EQ(output_pdb_path_, test_impl_.output_pdb_path_);
226 E : EXPECT_EQ(order_file_path_, test_impl_.order_file_path_);
227 E : EXPECT_EQ(0, test_impl_.seed_);
228 E : EXPECT_EQ(0, test_impl_.padding_);
229 E : EXPECT_TRUE(test_impl_.no_augment_pdb_);
230 E : EXPECT_TRUE(test_impl_.compress_pdb_);
231 E : EXPECT_TRUE(test_impl_.no_strip_strings_);
232 E : EXPECT_FALSE(test_impl_.output_metadata_);
233 E : EXPECT_TRUE(test_impl_.overwrite_);
234 :
235 : // The order file doesn't actually exist, so setup should fail to infer the
236 : // input dll.
237 E : EXPECT_FALSE(test_impl_.SetUp());
238 E : }
239 :
240 E : TEST_F(RelinkAppTest, ParseFullCommandLineWithInputSeedAndMetadata) {
241 : // Note that we omit the no-metadata flag, so we expect true below for the
242 : // output_metadata_ member.
243 E : cmd_line_.AppendSwitchPath("input-dll", input_dll_path_);
244 E : cmd_line_.AppendSwitchPath("input-pdb", input_pdb_path_);
245 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
246 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
247 E : cmd_line_.AppendSwitchASCII("seed", base::StringPrintf("%d", seed_));
248 E : cmd_line_.AppendSwitchASCII("padding", base::StringPrintf("%d", padding_));
249 E : cmd_line_.AppendSwitch("no-augment-pdb");
250 E : cmd_line_.AppendSwitch("compress-pdb");
251 E : cmd_line_.AppendSwitch("no-strip-strings");
252 E : cmd_line_.AppendSwitch("overwrite");
253 :
254 E : EXPECT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
255 E : EXPECT_EQ(abs_input_dll_path_, test_impl_.input_dll_path_);
256 E : EXPECT_EQ(abs_input_pdb_path_, test_impl_.input_pdb_path_);
257 E : EXPECT_EQ(output_dll_path_, test_impl_.output_dll_path_);
258 E : EXPECT_EQ(output_pdb_path_, test_impl_.output_pdb_path_);
259 E : EXPECT_TRUE(test_impl_.order_file_path_.empty());
260 E : EXPECT_EQ(seed_, test_impl_.seed_);
261 E : EXPECT_EQ(padding_, test_impl_.padding_);
262 E : EXPECT_TRUE(test_impl_.no_augment_pdb_);
263 E : EXPECT_TRUE(test_impl_.compress_pdb_);
264 E : EXPECT_TRUE(test_impl_.no_strip_strings_);
265 E : EXPECT_TRUE(test_impl_.output_metadata_);
266 E : EXPECT_TRUE(test_impl_.overwrite_);
267 :
268 : // SetUp() has nothing else to infer so it should succeed.
269 E : EXPECT_TRUE(test_impl_.SetUp());
270 E : }
271 :
272 E : TEST_F(RelinkAppTest, RandomRelink) {
273 E : cmd_line_.AppendSwitchPath("input-dll", input_dll_path_);
274 E : cmd_line_.AppendSwitchPath("input-pdb", input_pdb_path_);
275 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
276 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
277 E : cmd_line_.AppendSwitchASCII("seed", base::StringPrintf("%d", seed_));
278 E : cmd_line_.AppendSwitchASCII("padding", base::StringPrintf("%d", padding_));
279 E : cmd_line_.AppendSwitch("overwrite");
280 :
281 E : ASSERT_EQ(0, test_app_.Run());
282 E : ASSERT_NO_FATAL_FAILURE(CheckTestDll(output_dll_path_));
283 E : }
284 :
285 E : TEST_F(RelinkAppTest, RandomRelinkBasicBlocks) {
286 E : cmd_line_.AppendSwitchPath("input-dll", input_dll_path_);
287 E : cmd_line_.AppendSwitchPath("input-pdb", input_pdb_path_);
288 E : cmd_line_.AppendSwitchPath("output-dll", output_dll_path_);
289 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
290 E : cmd_line_.AppendSwitchASCII("seed", base::StringPrintf("%d", seed_));
291 E : cmd_line_.AppendSwitchASCII("padding", base::StringPrintf("%d", padding_));
292 E : cmd_line_.AppendSwitch("overwrite");
293 E : cmd_line_.AppendSwitch("basic-blocks");
294 E : cmd_line_.AppendSwitch("exclude-bb-padding");
295 :
296 E : ASSERT_EQ(0, test_app_.Run());
297 E : ASSERT_NO_FATAL_FAILURE(CheckTestDll(output_dll_path_));
298 E : }
299 :
300 : } // namespace pe
|