1 : // Copyright 2013 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/pe_relinker_util.h"
16 :
17 : #include "base/files/file_util.h"
18 : #include "base/strings/utf_string_conversions.h"
19 : #include "gtest/gtest.h"
20 : #include "syzygy/block_graph/typed_block.h"
21 : #include "syzygy/common/defs.h"
22 : #include "syzygy/core/unittest_util.h"
23 : #include "syzygy/pdb/pdb_reader.h"
24 : #include "syzygy/pdb/pdb_util.h"
25 : #include "syzygy/pe/decomposer.h"
26 : #include "syzygy/pe/pe_data.h"
27 : #include "syzygy/pe/unittest_util.h"
28 :
29 : namespace pe {
30 :
31 : namespace {
32 :
33 : using block_graph::BlockGraph;
34 : using block_graph::ConstTypedBlock;
35 : using block_graph::OrderedBlockGraph;
36 :
37 : typedef ConstTypedBlock<IMAGE_DATA_DIRECTORY> ImageDataDirectory;
38 : typedef ConstTypedBlock<IMAGE_DEBUG_DIRECTORY> ImageDebugDirectory;
39 : typedef ConstTypedBlock<IMAGE_DOS_HEADER> DosHeader;
40 : typedef ConstTypedBlock<IMAGE_NT_HEADERS> NtHeaders;
41 :
42 : class PERelinkerUtilTest : public testing::PELibUnitTest {
43 : typedef testing::PELibUnitTest Super;
44 :
45 : public:
46 E : PERelinkerUtilTest() : image_layout_(&block_graph_) {
47 E : }
48 :
49 E : void SetUp() {
50 E : Super::SetUp();
51 :
52 E : input_dll_ = testing::GetExeRelativePath(testing::kTestDllName);
53 E : input_pdb_ = testing::GetExeRelativePath(testing::kTestDllPdbName);
54 :
55 E : ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_));
56 E : temp_dll_ = temp_dir_.Append(testing::kTestDllName);
57 E : temp_pdb_ = temp_dir_.Append(testing::kTestDllPdbName);
58 E : }
59 :
60 E : void CreateFile(const base::FilePath& file) {
61 E : base::ScopedFILE f(base::OpenFile(file, "wb"));
62 E : }
63 :
64 E : void DecomposeTestDll() {
65 E : ASSERT_TRUE(pe_file_.Init(input_dll_));
66 E : pe::Decomposer decomposer(pe_file_);
67 E : ASSERT_TRUE(decomposer.Decompose(&image_layout_));
68 : dos_header_block_ = image_layout_.blocks.GetBlockByAddress(
69 E : core::RelativeAddress(0));
70 E : ASSERT_TRUE(dos_header_block_ != NULL);
71 E : }
72 :
73 E : bool BlockGraphHasSyzygyMetadataSection() const {
74 : BlockGraph::SectionMap::const_iterator it =
75 E : block_graph_.sections().begin();
76 E : for (; it != block_graph_.sections().end(); ++it) {
77 E : if (it->second.name() == common::kSyzygyMetadataSectionName)
78 E : return true;
79 E : }
80 E : return false;
81 E : }
82 :
83 : void CheckPdbInfo(const base::FilePath& pdb_path,
84 E : const GUID& pdb_guid) {
85 E : DosHeader dos_header;
86 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block_));
87 :
88 E : NtHeaders nt_headers;
89 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
90 :
91 : const IMAGE_DATA_DIRECTORY* data_dir =
92 E : nt_headers->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG;
93 E : ImageDebugDirectory debug_dir;
94 E : ASSERT_TRUE(nt_headers.Dereference(data_dir->VirtualAddress, &debug_dir));
95 :
96 E : bool seen_cv_record = false;
97 E : for (size_t i = 0; i < debug_dir.ElementCount(); ++i) {
98 E : if (debug_dir[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW)
99 E : continue;
100 :
101 E : ASSERT_FALSE(seen_cv_record);
102 E : seen_cv_record = true;
103 :
104 E : ASSERT_LE(sizeof(CvInfoPdb70), debug_dir[i].SizeOfData);
105 E : ConstTypedBlock<CvInfoPdb70> pdb_info;
106 E : ASSERT_TRUE(debug_dir.Dereference(debug_dir[i].AddressOfRawData,
107 : &pdb_info));
108 :
109 E : ASSERT_EQ(pdb_guid, pdb_info->signature);
110 :
111 E : std::wstring pdb_file_name = base::UTF8ToWide(pdb_info->pdb_file_name);
112 E : ASSERT_EQ(pdb_path.value(), pdb_file_name);
113 E : }
114 E : }
115 :
116 : PETransformPolicy policy_;
117 : base::FilePath input_dll_;
118 : base::FilePath input_pdb_;
119 : base::FilePath temp_dir_;
120 : base::FilePath temp_dll_;
121 : base::FilePath temp_pdb_;
122 :
123 : pe::PEFile pe_file_;
124 : ImageLayout image_layout_;
125 : BlockGraph block_graph_;
126 : BlockGraph::Block* dos_header_block_;
127 : };
128 :
129 : } // namespace
130 :
131 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsInferPdbPaths) {
132 E : base::FilePath input_pdb, output_pdb;
133 :
134 : EXPECT_TRUE(ValidateAndInferPaths(
135 E : input_dll_, temp_dll_, false, &input_pdb, &output_pdb));
136 E : EXPECT_FALSE(input_pdb.empty());
137 E : EXPECT_FALSE(output_pdb.empty());
138 :
139 E : input_pdb.clear();
140 E : output_pdb.clear();
141 : EXPECT_TRUE(ValidateAndInferPaths(
142 E : input_dll_, temp_dll_, true, &input_pdb, &output_pdb));
143 E : EXPECT_FALSE(input_pdb.empty());
144 E : EXPECT_FALSE(output_pdb.empty());
145 E : }
146 :
147 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsAllSpecified) {
148 : EXPECT_TRUE(ValidateAndInferPaths(
149 E : input_dll_, temp_dll_, false, &input_pdb_, &temp_pdb_));
150 E : }
151 :
152 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsMissingInputPdb) {
153 E : base::FilePath output_pdb;
154 : EXPECT_FALSE(ValidateAndInferPaths(
155 E : input_dll_, temp_dll_, false, &temp_pdb_, &output_pdb));
156 E : }
157 :
158 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsMismatchedInputPdb) {
159 E : base::FilePath output_pdb;
160 E : CreateFile(temp_pdb_);
161 : EXPECT_FALSE(ValidateAndInferPaths(
162 E : input_dll_, temp_pdb_, false, &input_pdb_, &output_pdb));
163 E : }
164 :
165 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsExistingModule) {
166 E : CreateFile(temp_dll_);
167 :
168 : EXPECT_FALSE(ValidateAndInferPaths(
169 E : input_dll_, temp_dll_, false, &input_pdb_, &temp_pdb_));
170 :
171 : EXPECT_TRUE(ValidateAndInferPaths(
172 E : input_dll_, temp_dll_, true, &input_pdb_, &temp_pdb_));
173 E : }
174 :
175 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsExistingPdb) {
176 E : CreateFile(temp_pdb_);
177 :
178 : EXPECT_FALSE(ValidateAndInferPaths(
179 E : input_dll_, temp_dll_, false, &input_pdb_, &temp_pdb_));
180 :
181 : EXPECT_TRUE(ValidateAndInferPaths(
182 E : input_dll_, temp_dll_, true, &input_pdb_, &temp_pdb_));
183 E : }
184 :
185 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsInPlaceModule) {
186 E : base::FilePath output_dll = input_dll_;
187 : EXPECT_FALSE(ValidateAndInferPaths(
188 E : input_dll_, output_dll, false, &input_pdb_, &temp_pdb_));
189 E : }
190 :
191 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsInPlacePdb) {
192 E : base::FilePath output_pdb = input_pdb_;
193 : EXPECT_FALSE(ValidateAndInferPaths(
194 E : input_dll_, temp_dll_, false, &input_pdb_, &output_pdb));
195 E : }
196 :
197 E : TEST_F(PERelinkerUtilTest, ValidateAndInferPathsBothOutputsSame) {
198 E : base::FilePath output_dll = temp_dll_;
199 E : base::FilePath output_pdb = temp_dll_;
200 : EXPECT_FALSE(ValidateAndInferPaths(
201 E : input_dll_, temp_dll_, false, &output_dll, &output_pdb));
202 E : }
203 :
204 E : TEST_F(PERelinkerUtilTest, FinalizeBlockGraphNoMetadata) {
205 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
206 :
207 E : DosHeader dos_header;
208 E : NtHeaders nt_headers;
209 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block_));
210 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
211 E : size_t header_section_count = nt_headers->FileHeader.NumberOfSections;
212 :
213 E : GUID guid = {};
214 : EXPECT_TRUE(FinalizeBlockGraph(input_dll_, temp_pdb_, guid, false,
215 E : &policy_, &block_graph_, dos_header_block_));
216 E : EXPECT_EQ(header_section_count, nt_headers->FileHeader.NumberOfSections);
217 :
218 E : EXPECT_FALSE(BlockGraphHasSyzygyMetadataSection());
219 E : ASSERT_NO_FATAL_FAILURE(CheckPdbInfo(temp_pdb_, guid));
220 E : }
221 :
222 E : TEST_F(PERelinkerUtilTest, FinalizeBlockGraphMetadata) {
223 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
224 :
225 E : DosHeader dos_header;
226 E : NtHeaders nt_headers;
227 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block_));
228 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
229 E : size_t header_section_count = nt_headers->FileHeader.NumberOfSections;
230 :
231 E : GUID guid = {};
232 : EXPECT_TRUE(FinalizeBlockGraph(input_dll_, temp_pdb_, guid, true,
233 E : &policy_, &block_graph_, dos_header_block_));
234 E : EXPECT_EQ(header_section_count + 1, nt_headers->FileHeader.NumberOfSections);
235 :
236 E : EXPECT_TRUE(BlockGraphHasSyzygyMetadataSection());
237 E : ASSERT_NO_FATAL_FAILURE(CheckPdbInfo(temp_pdb_, guid));
238 E : }
239 :
240 : // This is more of an integration test, but the individual transforms and
241 : // orderers are very thoroughly tested elsewhere.
242 E : TEST_F(PERelinkerUtilTest, FinalizeOrderedBlockGraphAndBuildImageLayout) {
243 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
244 E : OrderedBlockGraph obg(&block_graph_);
245 :
246 : // Move the DOS header block out of place.
247 E : BlockGraph::Section* section = block_graph_.FindSection(".text");
248 E : obg.PlaceAtHead(section);
249 E : obg.PlaceAtHead(section, dos_header_block_);
250 : ASSERT_EQ(dos_header_block_,
251 E : obg.ordered_section(section).ordered_blocks().front());
252 :
253 E : EXPECT_TRUE(FinalizeOrderedBlockGraph(&obg, dos_header_block_));
254 :
255 : // Ensure the DOS header block is no longer first. The section itself should
256 : // still be first.
257 : EXPECT_NE(dos_header_block_,
258 E : obg.ordered_section(section).ordered_blocks().front());
259 :
260 : // Build the layout and ensure it is as expected.
261 E : size_t kPadding = 8;
262 E : size_t kCodeAlign = 16;
263 E : ImageLayout image_layout(&block_graph_);
264 : EXPECT_TRUE(BuildImageLayout(kPadding, kCodeAlign, obg, dos_header_block_,
265 E : &image_layout));
266 :
267 : // Skip over header blocks.
268 E : BlockGraph::AddressSpace::RangeMapConstIter it = image_layout.blocks.begin();
269 : while (it != image_layout.blocks.end() &&
270 E : it->second->section() == BlockGraph::kInvalidSectionId) {
271 E : ++it;
272 E : }
273 :
274 : // We expect there to be blocks left.
275 E : ASSERT_TRUE(it != image_layout.blocks.end());
276 :
277 : // Make sure the rest of all blocks respect the padding and alignment.
278 E : BlockGraph::AddressSpace::RangeMapConstIter prev_it = it;
279 E : ++it;
280 E : while (it != image_layout.blocks.end()) {
281 E : ASSERT_LE(prev_it->first.end() + kPadding, it->first.start());
282 E : if (it->second->type() == BlockGraph::CODE_BLOCK)
283 E : ASSERT_EQ(0u, it->first.start().value() % kCodeAlign);
284 :
285 E : prev_it = it;
286 E : ++it;
287 E : }
288 E : }
289 :
290 : // Again, this is more of an integration test as the individual PDB mutators
291 : // are thoroughly tested elsewhere.
292 E : TEST_F(PERelinkerUtilTest, GetOmapRangeAndFinalizePdb) {
293 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
294 :
295 E : RelativeAddressRange omap_range;
296 E : GetOmapRange(image_layout_.sections, &omap_range);
297 E : EXPECT_FALSE(omap_range.IsEmpty());
298 :
299 E : ASSERT_TRUE(base::CopyFile(input_dll_, temp_dll_));
300 :
301 E : pdb::PdbFile pdb_file;
302 E : pdb::PdbReader pdb_reader;
303 E : ASSERT_TRUE(pdb_reader.Read(input_pdb_, &pdb_file));
304 :
305 E : GUID guid = {};
306 : EXPECT_TRUE(FinalizePdbFile(input_dll_,
307 : temp_dll_,
308 : omap_range,
309 : image_layout_,
310 : guid,
311 : true, // augment_pdb.
312 : false, // strip_strings.
313 : true, // compress_pdb.
314 E : &pdb_file));
315 :
316 : pdb::PdbInfoHeader70 pdb_header;
317 E : pdb::NameStreamMap pdb_name_stream_map;
318 : ASSERT_TRUE(pdb::ReadHeaderInfoStream(
319 E : pdb_file, &pdb_header, &pdb_name_stream_map));
320 :
321 E : EXPECT_EQ(guid, pdb_header.signature);
322 E : }
323 :
324 : } // namespace pe
|