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