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 : #include "syzygy/pe/unittest_util.h"
16 :
17 : #include <imagehlp.h>
18 : #include <functional>
19 :
20 : #include "base/command_line.h"
21 : #include "base/file_util.h"
22 : #include "base/logging.h"
23 : #include "base/path_service.h"
24 : #include "base/process/launch.h"
25 : #include "base/strings/stringprintf.h"
26 : #include "base/strings/utf_string_conversions.h"
27 : #include "base/win/pe_image.h"
28 : #include "gmock/gmock.h"
29 : #include "gtest/gtest.h"
30 : #include "syzygy/block_graph/typed_block.h"
31 : #include "syzygy/block_graph/orderers/original_orderer.h"
32 : #include "syzygy/common/com_utils.h"
33 : #include "syzygy/core/unittest_util.h"
34 : #include "syzygy/pe/coff_decomposer.h"
35 : #include "syzygy/pe/coff_file.h"
36 : #include "syzygy/pe/coff_file_writer.h"
37 : #include "syzygy/pe/coff_image_layout_builder.h"
38 : #include "syzygy/pe/decomposer.h"
39 : #include "syzygy/pe/pe_data.h"
40 :
41 : namespace testing {
42 :
43 : namespace {
44 :
45 : using block_graph::BlockGraph;
46 : using block_graph::ConstTypedBlock;
47 : using block_graph::OrderedBlockGraph;
48 : using block_graph::TypedBlock;
49 : using core::RelativeAddress;
50 : using pe::CvInfoPdb70;
51 :
52 : typedef TypedBlock<CvInfoPdb70> CvInfoPdb;
53 : typedef TypedBlock<IMAGE_DOS_HEADER> DosHeader;
54 : typedef TypedBlock<IMAGE_NT_HEADERS> NtHeaders;
55 : typedef TypedBlock<IMAGE_DEBUG_DIRECTORY> ImageDebugDirectory;
56 :
57 : bool EnumImportsProc(const base::win::PEImage &image,
58 : const char* module,
59 : DWORD ordinal,
60 : const char* name,
61 : DWORD hint,
62 : IMAGE_THUNK_DATA* iat,
63 E : void* cookie) {
64 E : DCHECK(module != NULL);
65 E : DCHECK(iat != NULL);
66 E : DCHECK(cookie != NULL);
67 :
68 : std::set<std::string>* export_dll_imports =
69 E : reinterpret_cast<std::set<std::string>*>(cookie);
70 :
71 E : if (strcmp(module, "export_dll.dll") == 0) {
72 E : if (name != NULL) {
73 E : EXPECT_TRUE(export_dll_imports->insert(name).second);
74 E : } else {
75 E : std::string ordinal_name(base::StringPrintf("#%d", ordinal));
76 E : EXPECT_TRUE(export_dll_imports->insert(ordinal_name).second);
77 E : }
78 : }
79 :
80 E : return true;
81 E : }
82 :
83 E : void CheckLoadedDllHasSortedSafeSehTable(HMODULE module) {
84 : // Verify that the Safe SEH Table is sorted.
85 : // http://code.google.com/p/sawbuck/issues/detail?id=42
86 E : ASSERT_TRUE(module != NULL);
87 E : base::win::PEImage image(module);
88 :
89 : // Locate the load config directory.
90 : PIMAGE_LOAD_CONFIG_DIRECTORY load_config_directory =
91 : reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY>(
92 E : image.GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG));
93 E : ASSERT_TRUE(load_config_directory != NULL);
94 :
95 : // Find the bounds of the Safe SEH Table.
96 : DWORD* seh_table_begin =
97 E : reinterpret_cast<DWORD*>(load_config_directory->SEHandlerTable);
98 E : size_t seh_table_size = load_config_directory->SEHandlerCount;
99 E : DWORD* seh_table_end = seh_table_begin + seh_table_size;
100 :
101 : // Unfortunately, std::is_sorted is an extension pre-c++0x. An equivalent
102 : // test is to see if there are any adjacent elements such that the first
103 : // is greater than its successor. So, let's look for the first element for
104 : // which this is true, and if we get to the end, then there were no such
105 : // elements.
106 : DWORD* out_of_order_iter = std::adjacent_find(seh_table_begin,
107 : seh_table_end,
108 E : std::greater<DWORD>());
109 E : ASSERT_TRUE(out_of_order_iter == seh_table_end)
110 E : << "The Safe SEH Table must be sorted.";
111 E : }
112 :
113 E : void CheckLoadedTestDll(HMODULE module) {
114 : // Validate that the DLL is properly constructed.
115 E : CheckLoadedDllHasSortedSafeSehTable(module);
116 :
117 : // Load the exported TestExport function and invoke it.
118 : typedef DWORD (WINAPI* TestExportFunc)(size_t buf_len, char* buf);
119 : TestExportFunc test_func = reinterpret_cast<TestExportFunc>(
120 E : ::GetProcAddress(module, "TestExport"));
121 E : ASSERT_TRUE(test_func != NULL);
122 :
123 E : char buffer[1024] = { 0 };
124 E : EXPECT_EQ(0, test_func(arraysize(buffer), buffer));
125 E : EXPECT_STREQ("The quick brown fox jumped over the lazy dog", buffer);
126 :
127 : // Load the exported TestUnusedFunc function and invoke it.
128 : typedef void (CALLBACK* TestUnusedFuncs)(HWND, HINSTANCE, LPSTR, int);
129 : TestUnusedFuncs test_func2 = reinterpret_cast<TestUnusedFuncs>(
130 E : ::GetProcAddress(module, "TestUnusedFuncs"));
131 E : ASSERT_TRUE(test_func2 != NULL);
132 E : test_func2(0, 0, 0, 0);
133 :
134 : // Check the image file for sanity.
135 E : base::win::PEImage image(module);
136 E : ASSERT_TRUE(image.VerifyMagic());
137 :
138 E : std::set<std::string> export_dll_imports;
139 : // Verify all the imports from export_dll.
140 E : ASSERT_TRUE(image.EnumAllImports(EnumImportsProc, &export_dll_imports));
141 :
142 E : std::set<std::string> expected_imports;
143 E : expected_imports.insert("kExportedData");
144 E : expected_imports.insert("function1");
145 E : expected_imports.insert("#7");
146 E : expected_imports.insert("function3");
147 E : EXPECT_THAT(expected_imports, testing::ContainerEq(export_dll_imports));
148 E : }
149 :
150 : } // namespace
151 :
152 : const wchar_t testing::kTestDllName[] = L"test_dll.dll";
153 : const wchar_t testing::kTestDllPdbName[] = L"test_dll.dll.pdb";
154 :
155 : const wchar_t testing::kTestDllName64[] = L"test_dll_x64.dll";
156 : const wchar_t testing::kTestDllPdbName64[] = L"test_dll_x64.dll.pdb";
157 :
158 : const wchar_t testing::kIntegrationTestsDllName[] =
159 : L"integration_tests_dll.dll";
160 : const wchar_t testing::kIntegrationTestsDllPdbName[] =
161 : L"integration_tests_dll.dll.pdb";
162 :
163 : const wchar_t kNoExportsDllName[] = L"no_exports_dll.dll";
164 : const wchar_t kNoExportsDllPdbName[] = L"no_exports_dll.dll.pdb";
165 :
166 : const wchar_t testing::kTestDllCoffObjName[] = L"test_dll.coff_obj";
167 : const wchar_t testing::kTestDllLtcgObjName[] = L"test_dll.ltcg_obj";
168 : const wchar_t testing::kTestDllCoffObjPdbName[] = L"test_dll.coff_obj.pdb";
169 : const wchar_t testing::kTestDllLtcgObjPdbName[] = L"test_dll.ltcg_obj.pdb";
170 :
171 : const wchar_t testing::kCodeView2Name[] =
172 : L"syzygy\\pe\\test_data\\codeview2.obj";
173 : const wchar_t testing::kEmptyStringTableCoffName[] =
174 : L"syzygy\\pe\\test_data\\empty_string_table.obj";
175 :
176 : const wchar_t kAsanInstrumentedTestDllName[] =
177 : L"asan_instrumented_test_dll.dll";
178 : const wchar_t kAsanInstrumentedTestDllPdbName[] =
179 : L"asan_instrumented_test_dll.dll.pdb";
180 : const wchar_t kBBEntryInstrumentedTestDllName[] =
181 : L"basic_block_entry_instrumented_test_dll.dll";
182 : const wchar_t kBBEntryInstrumentedTestDllPdbName[] =
183 : L"basic_block_entry_instrumented_test_dll.dll.pdb";
184 : const wchar_t kCallTraceInstrumentedTestDllName[] =
185 : L"call_trace_instrumented_test_dll.dll";
186 : const wchar_t kCallTraceInstrumentedTestDllPdbName[] =
187 : L"call_trace_instrumented_test_dll.dll.pdb";
188 : const wchar_t kCoverageInstrumentedTestDllName[] =
189 : L"coverage_instrumented_test_dll.dll";
190 : const wchar_t kCoverageInstrumentedTestDllPdbName[] =
191 : L"coverage_instrumented_test_dll.dll.pdb";
192 : const wchar_t kProfileInstrumentedTestDllName[] =
193 : L"profile_instrumented_test_dll.dll";
194 : const wchar_t kProfileInstrumentedTestDllPdbName[] =
195 : L"profile_instrumented_test_dll.dll.pdb";
196 : const wchar_t kRandomizedTestDllName[] =
197 : L"randomized_test_dll.dll";
198 : const wchar_t kRandomizedTestDllPdbName[] =
199 : L"randomized_test_dll.dll.pdb";
200 :
201 : const wchar_t *kBBEntryTraceFiles[4] = {
202 : L"basic_block_entry_traces\\trace-1.bin",
203 : L"basic_block_entry_traces\\trace-2.bin",
204 : L"basic_block_entry_traces\\trace-3.bin",
205 : L"basic_block_entry_traces\\trace-4.bin",
206 : };
207 :
208 : const wchar_t *kBranchTraceFiles[4] = {
209 : L"branch_traces\\trace-1.bin",
210 : L"branch_traces\\trace-2.bin",
211 : L"branch_traces\\trace-3.bin",
212 : L"branch_traces\\trace-4.bin",
213 : };
214 :
215 : const wchar_t *kCallTraceTraceFiles[4] = {
216 : L"call_trace_traces\\trace-1.bin",
217 : L"call_trace_traces\\trace-2.bin",
218 : L"call_trace_traces\\trace-3.bin",
219 : L"call_trace_traces\\trace-4.bin",
220 : };
221 :
222 : const wchar_t *kCoverageTraceFiles[4] = {
223 : L"coverage_traces\\trace-1.bin",
224 : L"coverage_traces\\trace-2.bin",
225 : L"coverage_traces\\trace-3.bin",
226 : L"coverage_traces\\trace-4.bin",
227 : };
228 :
229 : const wchar_t *kProfileTraceFiles[4] = {
230 : L"profile_traces\\trace-1.bin",
231 : L"profile_traces\\trace-2.bin",
232 : L"profile_traces\\trace-3.bin",
233 : L"profile_traces\\trace-4.bin",
234 : };
235 :
236 E : ScopedHMODULE::ScopedHMODULE() : value_(0) {
237 E : }
238 :
239 : ScopedHMODULE::ScopedHMODULE(HMODULE v) : value_(v) {
240 : }
241 :
242 E : void ScopedHMODULE::Reset(HMODULE value) {
243 E : if (value_ != value) {
244 E : Release();
245 E : value_ = value;
246 : }
247 E : }
248 :
249 E : void ScopedHMODULE::Release() {
250 E : if (value_) {
251 E : ::FreeLibrary(value_);
252 E : value_ = 0;
253 : }
254 E : }
255 :
256 E : ScopedHMODULE::~ScopedHMODULE() {
257 E : Release();
258 E : }
259 :
260 E : void TwiddlePdbGuidAndPath(BlockGraph::Block* dos_header_block) {
261 E : ASSERT_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block);
262 :
263 E : DosHeader dos_header;
264 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block));
265 :
266 E : NtHeaders nt_headers;
267 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
268 :
269 : const IMAGE_DATA_DIRECTORY& debug_dir_info =
270 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
271 E : ImageDebugDirectory debug_dir;
272 : ASSERT_TRUE(nt_headers.Dereference(debug_dir_info.VirtualAddress,
273 E : &debug_dir));
274 :
275 : // Find the codeview debug directory entry.
276 E : int32 index = -1;
277 E : for (size_t i = 0; i < debug_dir.ElementCount(); ++i) {
278 E : if (debug_dir[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
279 E : index = i;
280 E : break;
281 : }
282 i : }
283 E : ASSERT_NE(-1, index);
284 :
285 E : CvInfoPdb cv_info_pdb;
286 : ASSERT_TRUE(debug_dir.Dereference(debug_dir[index].PointerToRawData,
287 E : &cv_info_pdb));
288 :
289 : // Modify the GUID.
290 E : cv_info_pdb->signature.Data1 ^= 0xFFFFFFFF;
291 :
292 : // Write a nonsense name using a simple encoding.
293 E : size_t block_size = cv_info_pdb.block()->size();
294 E : size_t string_start = cv_info_pdb.OffsetOf(cv_info_pdb->pdb_file_name);
295 E : ASSERT_LT(string_start, block_size);
296 E : size_t string_len = block_size - string_start;
297 E : for (size_t i = 0; i < string_len && cv_info_pdb->pdb_file_name[i] != 0;
298 E : ++i) {
299 E : char& c = cv_info_pdb->pdb_file_name[i];
300 E : if (c >= 'a' && c <= 'z') {
301 E : c = 'z' - (c - 'a');
302 E : } else if (c >= 'A' && c <= 'Z') {
303 E : c = 'Z' - (c - 'A');
304 E : } else if (c >= '0' && c <= '9') {
305 i : c = '9' - (c - '0');
306 : }
307 E : }
308 E : }
309 :
310 : void PELibUnitTest::LoadTestDll(const base::FilePath& path,
311 E : ScopedHMODULE* module) {
312 E : DCHECK(module != NULL);
313 :
314 E : LOADED_IMAGE loaded_image = {};
315 : BOOL success = ::MapAndLoad(base::WideToUTF8(path.value()).c_str(),
316 : NULL,
317 : &loaded_image,
318 : FALSE,
319 E : TRUE);
320 E : EXPECT_EQ(ERROR_SUCCESS, ::GetLastError());
321 E : ASSERT_TRUE(success);
322 E : EXPECT_TRUE(::UnMapAndLoad(&loaded_image));
323 :
324 E : module->Reset(::LoadLibrary(path.value().c_str()));
325 E : if (*module == NULL) {
326 i : DWORD error = ::GetLastError();
327 i : LOG(ERROR) << "LoadLibrary failed: " << common::LogWe(error);
328 : }
329 E : ASSERT_TRUE(module != NULL);
330 E : }
331 :
332 : void PELibUnitTest::DecomposeTestDll(pe::PEFile* pe_file,
333 E : pe::ImageLayout* image_layout) {
334 E : ASSERT_TRUE(pe_file != NULL);
335 E : ASSERT_TRUE(image_layout != NULL);
336 :
337 E : base::FilePath test_dll = GetOutputRelativePath(kTestDllName);
338 E : ASSERT_TRUE(pe_file->Init(test_dll));
339 :
340 E : pe::Decomposer decomposer(*pe_file);
341 E : ASSERT_TRUE(decomposer.Decompose(image_layout));
342 E : }
343 :
344 E : void PELibUnitTest::CheckTestDll(const base::FilePath& path) {
345 E : ScopedHMODULE module;
346 E : LoadTestDll(path, &module);
347 E : CheckLoadedTestDll(module);
348 E : }
349 :
350 E : void CoffUnitTest::SetUp() {
351 E : testing::PELibUnitTest::SetUp();
352 :
353 : test_dll_obj_path_ =
354 E : testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
355 E : ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_path_));
356 E : new_test_dll_obj_path_ = temp_dir_path_.Append(L"test_dll.obj");
357 E : }
358 :
359 E : void CoffUnitTest::DecomposeOriginal() {
360 E : ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
361 E : pe::CoffDecomposer decomposer(image_file_);
362 E : ASSERT_TRUE(decomposer.Decompose(&image_layout_));
363 :
364 E : headers_block_ = image_layout_.blocks.GetBlockByAddress(RelativeAddress(0));
365 E : ASSERT_TRUE(headers_block_ != NULL);
366 E : }
367 :
368 : void CoffUnitTest::LayoutAndWriteNew(
369 E : block_graph::BlockGraphOrdererInterface* orderer) {
370 E : ASSERT_TRUE(orderer != NULL);
371 :
372 : // Cast headers block.
373 E : ConstTypedBlock<IMAGE_FILE_HEADER> file_header;
374 E : ASSERT_TRUE(file_header.Init(0, headers_block_));
375 :
376 : // Reorder using the specified ordering.
377 E : OrderedBlockGraph ordered_graph(&block_graph_);
378 E : ASSERT_TRUE(orderer->OrderBlockGraph(&ordered_graph, headers_block_));
379 :
380 : // Wipe references from headers, so we can remove relocation blocks
381 : // during laying out.
382 E : ASSERT_TRUE(headers_block_->RemoveAllReferences());
383 :
384 : // Lay out new image.
385 E : pe::ImageLayout new_image_layout(&block_graph_);
386 E : pe::CoffImageLayoutBuilder layout_builder(&new_image_layout);
387 E : ASSERT_TRUE(layout_builder.LayoutImage(ordered_graph));
388 :
389 : // Write temporary image file.
390 E : pe::CoffFileWriter writer(&new_image_layout);
391 E : ASSERT_TRUE(writer.WriteImage(new_test_dll_obj_path_));
392 E : }
393 :
394 E : void CoffUnitTest::TestRoundTrip() {
395 : // Rewrite file and parse new symbol table.
396 E : block_graph::orderers::OriginalOrderer orig_orderer;
397 E : ASSERT_NO_FATAL_FAILURE(LayoutAndWriteNew(&orig_orderer));
398 E : pe::CoffFile image_file;
399 E : ASSERT_TRUE(image_file.Init(new_test_dll_obj_path_));
400 E : }
401 :
402 : } // namespace testing
|