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/pe/unittest_util.h"
16 :
17 : #include <imagehlp.h>
18 :
19 : #include <algorithm>
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/pe/pe_data.h"
33 :
34 : using pe::CvInfoPdb70;
35 :
36 : namespace {
37 :
38 : // This class wraps an HMODULE and ensures that ::FreeLibrary is called when it
39 : // goes out of scope.
40 : class ScopedHMODULE {
41 : public:
42 E : explicit ScopedHMODULE(HMODULE v): value_(v) {
43 E : }
44 :
45 E : ~ScopedHMODULE() {
46 E : if (value_) {
47 E : ::FreeLibrary(value_);
48 : }
49 E : }
50 :
51 E : operator HMODULE() const {
52 E : return value_;
53 E : }
54 :
55 : private:
56 : HMODULE value_;
57 : };
58 :
59 : bool EnumImportsProc(const base::win::PEImage &image,
60 : const char* module,
61 : DWORD ordinal,
62 : const char* name,
63 : DWORD hint,
64 : IMAGE_THUNK_DATA* iat,
65 E : void* cookie) {
66 E : DCHECK(module != NULL);
67 E : DCHECK(iat != NULL);
68 E : DCHECK(cookie != NULL);
69 :
70 : std::set<std::string>* export_dll_imports =
71 E : reinterpret_cast<std::set<std::string>*>(cookie);
72 :
73 E : if (strcmp(module, "export_dll.dll") == 0) {
74 E : if (name != NULL) {
75 E : EXPECT_TRUE(export_dll_imports->insert(name).second);
76 E : } else {
77 E : std::string ordinal_name(base::StringPrintf("#%d", ordinal));
78 E : EXPECT_TRUE(export_dll_imports->insert(ordinal_name).second);
79 E : }
80 : }
81 :
82 E : return true;
83 E : }
84 :
85 E : void CheckLoadedDllHasSortedSafeSehTable(HMODULE module) {
86 : // Verify that the Safe SEH Table is sorted.
87 : // http://code.google.com/p/sawbuck/issues/detail?id=42
88 E : ASSERT_TRUE(module != NULL);
89 E : base::win::PEImage image(module);
90 :
91 : // Locate the load config directory.
92 : PIMAGE_LOAD_CONFIG_DIRECTORY load_config_directory =
93 : reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY>(
94 E : image.GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG));
95 E : ASSERT_TRUE(load_config_directory != NULL);
96 :
97 : // Find the bounds of the Safe SEH Table.
98 : DWORD* seh_table_begin =
99 E : reinterpret_cast<DWORD*>(load_config_directory->SEHandlerTable);
100 E : size_t seh_table_size = load_config_directory->SEHandlerCount;
101 E : DWORD* seh_table_end = seh_table_begin + seh_table_size;
102 :
103 : // Unfortunately, std::is_sorted is an extension pre-c++0x. An equivalent
104 : // test is to see if there are any adjacent elements such that the first
105 : // is greater than its successor. So, let's look for the first element for
106 : // which this is true, and if we get to the end, then there were no such
107 : // elements.
108 : DWORD* out_of_order_iter = std::adjacent_find(seh_table_begin,
109 : seh_table_end,
110 E : std::greater<DWORD>());
111 E : ASSERT_TRUE(out_of_order_iter == seh_table_end)
112 E : << "The Safe SEH Table must be sorted.";
113 E : }
114 :
115 E : void CheckLoadedTestDll(HMODULE module) {
116 : // Validate that the DLL is properly constructed.
117 E : CheckLoadedDllHasSortedSafeSehTable(module);
118 :
119 : // Load the exported TestExport function and invoke it.
120 : typedef DWORD (WINAPI* TestExportFunc)(size_t buf_len, char* buf);
121 : TestExportFunc test_func = reinterpret_cast<TestExportFunc>(
122 E : ::GetProcAddress(module, "TestExport"));
123 E : ASSERT_TRUE(test_func != NULL);
124 :
125 E : char buffer[1024] = { 0 };
126 E : EXPECT_EQ(0, test_func(arraysize(buffer), buffer));
127 E : EXPECT_STREQ("The quick brown fox jumped over the lazy dog", buffer);
128 :
129 : // Load the exported TestUnusedFunc function and invoke it.
130 : typedef void (CALLBACK* TestUnusedFuncs)(HWND, HINSTANCE, LPSTR, int);
131 : TestUnusedFuncs test_func2 = reinterpret_cast<TestUnusedFuncs>(
132 E : ::GetProcAddress(module, "TestUnusedFuncs"));
133 E : ASSERT_TRUE(test_func2 != NULL);
134 E : test_func2(0, 0, 0, 0);
135 :
136 : // Check the image file for sanity.
137 E : base::win::PEImage image(module);
138 E : ASSERT_TRUE(image.VerifyMagic());
139 :
140 E : std::set<std::string> export_dll_imports;
141 : // Verify all the imports from export_dll.
142 E : ASSERT_TRUE(image.EnumAllImports(EnumImportsProc, &export_dll_imports));
143 :
144 E : std::set<std::string> expected_imports;
145 E : expected_imports.insert("function1");
146 E : expected_imports.insert("#7");
147 E : expected_imports.insert("function3");
148 E : EXPECT_THAT(expected_imports, testing::ContainerEq(export_dll_imports));
149 E : }
150 :
151 : } // namespace
152 :
153 : namespace testing {
154 :
155 : const wchar_t PELibUnitTest::kDllName[] = L"test_dll.dll";
156 : const wchar_t PELibUnitTest::kDllPdbName[] = L"test_dll.pdb";
157 : const wchar_t PELibUnitTest::kRandomizedTestDllName[] =
158 : L"randomized_test_dll.dll";
159 : const wchar_t PELibUnitTest::kRandomizedTestDllPdbName[] =
160 : L"randomized_test_dll.pdb";
161 : const wchar_t PELibUnitTest::kRpcInstrumentedDllName[] =
162 : L"rpc_instrumented_test_dll.dll";
163 : const wchar_t PELibUnitTest::kRpcInstrumentedDllPdbName[] =
164 : L"rpc_instrumented_test_dll.pdb";
165 :
166 E : void PELibUnitTest::CreateTemporaryDir(FilePath* temp_dir) {
167 E : ASSERT_TRUE(file_util::CreateNewTempDirectory(L"", temp_dir));
168 E : temp_dirs_.push_back(*temp_dir);
169 E : }
170 :
171 E : void PELibUnitTest::TearDown() {
172 E : DirList::const_iterator iter;
173 E : for (iter = temp_dirs_.begin(); iter != temp_dirs_.end(); ++iter) {
174 E : file_util::Delete(*iter, true);
175 E : }
176 :
177 E : Super::TearDown();
178 E : }
179 :
180 E : void PELibUnitTest::CheckTestDll(const FilePath& path) {
181 E : LOADED_IMAGE loaded_image = {};
182 : BOOL success = ::MapAndLoad(WideToUTF8(path.value()).c_str(),
183 : NULL,
184 : &loaded_image,
185 : FALSE,
186 E : FALSE);
187 E : EXPECT_EQ(ERROR_SUCCESS, ::GetLastError());
188 E : ASSERT_TRUE(success);
189 E : EXPECT_TRUE(::UnMapAndLoad(&loaded_image));
190 :
191 E : ScopedHMODULE module(::LoadLibrary(path.value().c_str()));
192 E : if (module == NULL) {
193 i : DWORD error = ::GetLastError();
194 i : LOG(ERROR) << "LoadLibrary failed: " << com::LogWe(error);
195 : }
196 E : ASSERT_TRUE(module != NULL);
197 E : CheckLoadedTestDll(module);
198 E : }
199 :
200 : } // namespace testing
|