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/logging.h"
22 : #include "base/path_service.h"
23 : #include "base/files/file_util.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[] = L"randomized_test_dll.dll";
197 : const wchar_t kRandomizedTestDllPdbName[] = L"randomized_test_dll.dll.pdb";
198 : const wchar_t kSignedTestDllName[] = L"signed_test_dll.dll";
199 :
200 : const wchar_t* kBBEntryTraceFiles[4] = {
201 : L"basic_block_entry_traces\\trace-1.bin",
202 : L"basic_block_entry_traces\\trace-2.bin",
203 : L"basic_block_entry_traces\\trace-3.bin",
204 : L"basic_block_entry_traces\\trace-4.bin",
205 : };
206 :
207 : const wchar_t* kBranchTraceFiles[4] = {
208 : L"branch_traces\\trace-1.bin",
209 : L"branch_traces\\trace-2.bin",
210 : L"branch_traces\\trace-3.bin",
211 : L"branch_traces\\trace-4.bin",
212 : };
213 :
214 : const wchar_t* kCallTraceTraceFiles[4] = {
215 : L"call_trace_traces\\trace-1.bin",
216 : L"call_trace_traces\\trace-2.bin",
217 : L"call_trace_traces\\trace-3.bin",
218 : L"call_trace_traces\\trace-4.bin",
219 : };
220 :
221 : const wchar_t* kCoverageTraceFiles[4] = {
222 : L"coverage_traces\\trace-1.bin",
223 : L"coverage_traces\\trace-2.bin",
224 : L"coverage_traces\\trace-3.bin",
225 : L"coverage_traces\\trace-4.bin",
226 : };
227 :
228 : const wchar_t kMemProfTraceFile[] = L"memprof_traces\\trace-1.bin";
229 :
230 : const wchar_t* kProfileTraceFiles[4] = {
231 : L"profile_traces\\trace-1.bin",
232 : L"profile_traces\\trace-2.bin",
233 : L"profile_traces\\trace-3.bin",
234 : L"profile_traces\\trace-4.bin",
235 : };
236 :
237 E : ScopedHMODULE::ScopedHMODULE() : value_(0) {
238 E : }
239 :
240 : ScopedHMODULE::ScopedHMODULE(HMODULE v) : value_(v) {
241 : }
242 :
243 E : void ScopedHMODULE::Reset(HMODULE value) {
244 E : if (value_ != value) {
245 E : Release();
246 E : value_ = value;
247 : }
248 E : }
249 :
250 E : void ScopedHMODULE::Release() {
251 E : if (value_) {
252 E : ::FreeLibrary(value_);
253 E : value_ = 0;
254 : }
255 E : }
256 :
257 E : ScopedHMODULE::~ScopedHMODULE() {
258 E : Release();
259 E : }
260 :
261 E : void TwiddlePdbGuidAndPath(BlockGraph::Block* dos_header_block) {
262 E : ASSERT_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block);
263 :
264 E : DosHeader dos_header;
265 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block));
266 :
267 E : NtHeaders nt_headers;
268 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
269 :
270 : const IMAGE_DATA_DIRECTORY& debug_dir_info =
271 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
272 E : ImageDebugDirectory debug_dir;
273 : ASSERT_TRUE(nt_headers.Dereference(debug_dir_info.VirtualAddress,
274 E : &debug_dir));
275 :
276 : // Find the codeview debug directory entry.
277 E : int32 index = -1;
278 E : for (size_t i = 0; i < debug_dir.ElementCount(); ++i) {
279 E : if (debug_dir[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
280 E : index = i;
281 E : break;
282 : }
283 i : }
284 E : ASSERT_NE(-1, index);
285 :
286 E : CvInfoPdb cv_info_pdb;
287 : ASSERT_TRUE(debug_dir.Dereference(debug_dir[index].PointerToRawData,
288 E : &cv_info_pdb));
289 :
290 : // Modify the GUID.
291 E : cv_info_pdb->signature.Data1 ^= 0xFFFFFFFF;
292 :
293 : // Write a nonsense name using a simple encoding.
294 E : size_t block_size = cv_info_pdb.block()->size();
295 E : size_t string_start = cv_info_pdb.OffsetOf(cv_info_pdb->pdb_file_name);
296 E : ASSERT_LT(string_start, block_size);
297 E : size_t string_len = block_size - string_start;
298 E : for (size_t i = 0; i < string_len && cv_info_pdb->pdb_file_name[i] != 0;
299 E : ++i) {
300 E : char& c = cv_info_pdb->pdb_file_name[i];
301 E : if (c >= 'a' && c <= 'z') {
302 E : c = 'z' - (c - 'a');
303 E : } else if (c >= 'A' && c <= 'Z') {
304 E : c = 'Z' - (c - 'A');
305 E : } else if (c >= '0' && c <= '9') {
306 i : c = '9' - (c - '0');
307 : }
308 E : }
309 E : }
310 :
311 : void PELibUnitTest::LoadTestDll(const base::FilePath& path,
312 E : ScopedHMODULE* module) {
313 E : DCHECK(module != NULL);
314 :
315 E : LOADED_IMAGE loaded_image = {};
316 : BOOL success = ::MapAndLoad(base::WideToUTF8(path.value()).c_str(),
317 : NULL,
318 : &loaded_image,
319 : FALSE,
320 E : TRUE);
321 E : EXPECT_EQ(ERROR_SUCCESS, ::GetLastError());
322 E : ASSERT_TRUE(success);
323 E : EXPECT_TRUE(::UnMapAndLoad(&loaded_image));
324 :
325 E : module->Reset(::LoadLibrary(path.value().c_str()));
326 E : if (*module == NULL) {
327 i : DWORD error = ::GetLastError();
328 i : LOG(ERROR) << "LoadLibrary failed: " << common::LogWe(error);
329 : }
330 E : ASSERT_TRUE(module != NULL);
331 E : }
332 :
333 : void PELibUnitTest::DecomposeTestDll(pe::PEFile* pe_file,
334 E : pe::ImageLayout* image_layout) {
335 E : ASSERT_TRUE(pe_file != NULL);
336 E : ASSERT_TRUE(image_layout != NULL);
337 :
338 E : base::FilePath test_dll = GetOutputRelativePath(kTestDllName);
339 E : ASSERT_TRUE(pe_file->Init(test_dll));
340 :
341 E : pe::Decomposer decomposer(*pe_file);
342 E : ASSERT_TRUE(decomposer.Decompose(image_layout));
343 E : }
344 :
345 E : void PELibUnitTest::CheckTestDll(const base::FilePath& path) {
346 E : ScopedHMODULE module;
347 E : LoadTestDll(path, &module);
348 E : CheckLoadedTestDll(module);
349 E : }
350 :
351 E : void CoffUnitTest::SetUp() {
352 E : testing::PELibUnitTest::SetUp();
353 :
354 : test_dll_obj_path_ =
355 E : testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
356 E : ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_path_));
357 E : new_test_dll_obj_path_ = temp_dir_path_.Append(L"test_dll.obj");
358 E : }
359 :
360 E : void CoffUnitTest::DecomposeOriginal() {
361 E : ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
362 E : pe::CoffDecomposer decomposer(image_file_);
363 E : ASSERT_TRUE(decomposer.Decompose(&image_layout_));
364 :
365 E : headers_block_ = image_layout_.blocks.GetBlockByAddress(RelativeAddress(0));
366 E : ASSERT_TRUE(headers_block_ != NULL);
367 E : }
368 :
369 : void CoffUnitTest::LayoutAndWriteNew(
370 E : block_graph::BlockGraphOrdererInterface* orderer) {
371 E : ASSERT_TRUE(orderer != NULL);
372 :
373 : // Cast headers block.
374 E : ConstTypedBlock<IMAGE_FILE_HEADER> file_header;
375 E : ASSERT_TRUE(file_header.Init(0, headers_block_));
376 :
377 : // Reorder using the specified ordering.
378 E : OrderedBlockGraph ordered_graph(&block_graph_);
379 E : ASSERT_TRUE(orderer->OrderBlockGraph(&ordered_graph, headers_block_));
380 :
381 : // Wipe references from headers, so we can remove relocation blocks
382 : // during laying out.
383 E : ASSERT_TRUE(headers_block_->RemoveAllReferences());
384 :
385 : // Lay out new image.
386 E : pe::ImageLayout new_image_layout(&block_graph_);
387 E : pe::CoffImageLayoutBuilder layout_builder(&new_image_layout);
388 E : ASSERT_TRUE(layout_builder.LayoutImage(ordered_graph));
389 :
390 : // Write temporary image file.
391 E : pe::CoffFileWriter writer(&new_image_layout);
392 E : ASSERT_TRUE(writer.WriteImage(new_test_dll_obj_path_));
393 E : }
394 :
395 E : void CoffUnitTest::TestRoundTrip() {
396 : // Rewrite file and parse new symbol table.
397 E : block_graph::orderers::OriginalOrderer orig_orderer;
398 E : ASSERT_NO_FATAL_FAILURE(LayoutAndWriteNew(&orig_orderer));
399 E : pe::CoffFile image_file;
400 E : ASSERT_TRUE(image_file.Init(new_test_dll_obj_path_));
401 E : }
402 :
403 : } // namespace testing
|