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/coff_decomposer.h"
16 :
17 : #include "gtest/gtest.h"
18 : #include "syzygy/block_graph/basic_block_decomposer.h"
19 : #include "syzygy/block_graph/block_builder.h"
20 : #include "syzygy/block_graph/typed_block.h"
21 : #include "syzygy/core/unittest_util.h"
22 : #include "syzygy/pe/unittest_util.h"
23 :
24 : namespace pe {
25 :
26 : namespace {
27 :
28 : using block_graph::BlockGraph;
29 : using block_graph::ConstTypedBlock;
30 : using core::RelativeAddress;
31 :
32 : const size_t kPointerSize = BlockGraph::Reference::kMaximumSize;
33 :
34 : // test_dll.coff_obj-specific constants. Adjust to match current code in
35 : // test_dll.cc.
36 : const size_t kNumDataSections = 2;
37 : const size_t kNumRDataSections = 14; // Includes .rdata$r sections.
38 :
39 : // The VS2015 release builds contain an extra .bss section caused by the
40 : // use of snprintf in test_dll.cc.
41 : const size_t kNumBssSections = 2;
42 : const size_t kNumTextSections = 30;
43 : const size_t kNumDebugSections = 32; // Includes .debug$S and .debug$T.
44 :
45 : const size_t kNumFunctions = 14;
46 : const size_t kNumJumpLabelsInDllMain = 3;
47 : const size_t kNumCaseLabelsInDllMain = 2;
48 :
49 : class CoffDecomposerTest : public testing::Test {
50 : public:
51 E : virtual void SetUp() override {
52 E : testing::Test::SetUp();
53 :
54 E : test_dll_obj_path_ =
55 : testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
56 E : ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
57 E : }
58 :
59 : base::FilePath test_dll_obj_path_;
60 : CoffFile image_file_;
61 : };
62 :
63 : } // namespace
64 :
65 E : TEST_F(CoffDecomposerTest, Decompose) {
66 : // Decompose the test image.
67 E : CoffDecomposer decomposer(image_file_);
68 :
69 E : BlockGraph block_graph;
70 E : ImageLayout image_layout(&block_graph);
71 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
72 :
73 E : EXPECT_EQ(BlockGraph::COFF_IMAGE, block_graph.image_format());
74 :
75 : // Retrieve the COFF file header.
76 : BlockGraph::Block* file_header_block =
77 E : image_layout.blocks.GetBlockByAddress(RelativeAddress(0));
78 E : ASSERT_TRUE(file_header_block != NULL);
79 :
80 : // There should be some blocks in the graph and in the layout, and the
81 : // same number in the block graph and image layout.
82 E : EXPECT_LT(0u, block_graph.blocks().size());
83 E : EXPECT_LT(0u, image_layout.blocks.size());
84 E : EXPECT_EQ(block_graph.blocks().size(),
85 E : image_layout.blocks.size() + kNumBssSections);
86 :
87 : // We expect the ImageLayout sections to agree with the BlockGraph
88 : // sections in number, id, name and characteristics.
89 E : EXPECT_EQ(block_graph.sections().size(), image_layout.sections.size());
90 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
91 E : const BlockGraph::Section* section = block_graph.GetSectionById(i);
92 E : ASSERT_TRUE(section != NULL);
93 E : EXPECT_EQ(section->id(), i);
94 E : EXPECT_EQ(section->name(), image_layout.sections[i].name);
95 E : EXPECT_EQ(section->characteristics(),
96 E : image_layout.sections[i].characteristics);
97 E : }
98 :
99 : // Count symbols.
100 E : size_t num_internal_symbols = 0;
101 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
102 E : for (size_t i = 0; i < num_symbols; ++i) {
103 E : const IMAGE_SYMBOL* symbol = image_file_.symbol(i);
104 E : i += symbol->NumberOfAuxSymbols;
105 E : if (symbol->SectionNumber > 0)
106 E : ++num_internal_symbols;
107 E : }
108 :
109 : // Check that the number of sections, blocks and references match
110 : // expectations.
111 E : size_t num_code_blocks = 0;
112 E : size_t num_section_blocks = 0;
113 E : size_t num_section_blocks_with_references = 0;
114 E : size_t num_debug_section_blocks = 0;
115 E : size_t num_non_section_blocks = 0;
116 E : size_t num_references_in_symbol_table = 0;
117 : BlockGraph::BlockMap::const_iterator it =
118 E : block_graph.blocks().begin();
119 E : for (; it != block_graph.blocks().end(); ++it) {
120 E : const BlockGraph::Block& block = it->second;
121 :
122 E : if (block.type() == BlockGraph::CODE_BLOCK)
123 E : ++num_code_blocks;
124 :
125 E : if (block.section() == BlockGraph::kInvalidSectionId) {
126 E : ++num_non_section_blocks;
127 :
128 E : if ((block.attributes() & BlockGraph::COFF_SYMBOL_TABLE) != 0)
129 E : num_references_in_symbol_table = block.references().size();
130 E : } else {
131 : // If this is not a header block, it should refer to a valid
132 : // section index.
133 E : EXPECT_LT(block.section(), block_graph.sections().size());
134 E : ++num_section_blocks;
135 :
136 : BlockGraph::Section* section =
137 E : block_graph.GetSectionById(block.section());
138 E : DCHECK(section != NULL);
139 E : if (section->name() == ".debug$S")
140 E : ++num_debug_section_blocks;
141 :
142 : size_t num_relocs =
143 E : image_file_.section_header(block.section())->NumberOfRelocations;
144 : // Debug sections also have hard-coded references in addition to
145 : // relocation references, so the numbers will not match exactly, but
146 : // it will be at least that many.
147 E : if (image_file_.GetSectionName(block.section()) == ".debug$S") {
148 E : EXPECT_LE(num_relocs, block.references().size());
149 E : } else {
150 E : EXPECT_EQ(num_relocs, block.references().size());
151 : }
152 E : if (block.references().size() > 0)
153 E : ++num_section_blocks_with_references;
154 : }
155 E : }
156 E : EXPECT_EQ(num_section_blocks + num_non_section_blocks,
157 E : block_graph.blocks().size());
158 :
159 : // Each symbol has one section and one section offset reference; plus,
160 : // each associative COMDAT section definition must have one additional
161 : // reference. In test_dll.obj, only .debug$S sections should be COMDAT
162 : // associative, except the global .debug$S section.
163 E : EXPECT_EQ(2 * num_internal_symbols + num_debug_section_blocks - 1,
164 E : num_references_in_symbol_table);
165 :
166 : // There should be at least as many code blocks as there are functions in
167 : // test_dll.cc.
168 E : EXPECT_LE(kNumFunctions, num_code_blocks);
169 :
170 : // There should be exactly one block per section (including BSS sections)
171 : // in an object file with function-level linking.
172 E : EXPECT_EQ(image_file_.file_header()->NumberOfSections, num_section_blocks);
173 :
174 : // Non-section blocks should be: the header block, the symbol and string
175 : // tables, and the per-section relocations tables.
176 E : EXPECT_EQ(num_section_blocks_with_references + 3, num_non_section_blocks);
177 E : }
178 :
179 E : TEST_F(CoffDecomposerTest, FunctionsAndLabels) {
180 : // Decompose the test image and look at the result.
181 E : CoffDecomposer decomposer(image_file_);
182 E : BlockGraph block_graph;
183 E : ImageLayout image_layout(&block_graph);
184 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
185 :
186 : // Locate various specific function blocks in the block graph, while
187 : // validating the number of blocks matching each section name (.text,
188 : // .data, etc.).
189 E : size_t num_text_blocks = 0;
190 E : size_t num_data_blocks = 0;
191 E : size_t num_rdata_blocks = 0;
192 E : size_t num_debug_blocks = 0;
193 E : size_t num_bss_blocks = 0;
194 E : const BlockGraph::Block* dll_main_block = NULL;
195 E : const BlockGraph::Block* func_with_inl_asm_block = NULL;
196 :
197 : BlockGraph::BlockMap::const_iterator block_it =
198 E : block_graph.blocks().begin();
199 E : for (; block_it != block_graph.blocks().end(); ++block_it) {
200 E : const BlockGraph::Block* block = &block_it->second;
201 E : const std::string& name = block->name();
202 :
203 E : if (name.find(".text") != std::string::npos)
204 E : ++num_text_blocks;
205 E : else if (name.find(".data") != std::string::npos)
206 E : ++num_data_blocks;
207 E : else if (name.find(".rdata") != std::string::npos)
208 E : ++num_rdata_blocks;
209 E : else if (name.find(".debug") != std::string::npos)
210 E : ++num_debug_blocks;
211 E : else if (name.find(".bss") != std::string::npos)
212 E : ++num_bss_blocks;
213 :
214 E : if (block->type() == BlockGraph::CODE_BLOCK) {
215 E : if (name.find("DllMain") != std::string::npos) {
216 E : EXPECT_TRUE(dll_main_block == NULL);
217 E : dll_main_block = &block_it->second;
218 E : } else if (name.find("FunctionWithInlineAssembly") != std::string::npos) {
219 E : EXPECT_TRUE(func_with_inl_asm_block == NULL);
220 E : func_with_inl_asm_block = &block_it->second;
221 : }
222 : }
223 E : }
224 :
225 E : EXPECT_EQ(kNumTextSections, num_text_blocks);
226 E : EXPECT_EQ(kNumDataSections, num_data_blocks);
227 E : EXPECT_EQ(kNumRDataSections, num_rdata_blocks);
228 E : EXPECT_EQ(kNumDebugSections, num_debug_blocks);
229 E : EXPECT_EQ(kNumBssSections, num_bss_blocks);
230 :
231 E : EXPECT_TRUE(dll_main_block != NULL);
232 E : EXPECT_TRUE(func_with_inl_asm_block != NULL);
233 :
234 : // TODO(lenh): Check for HAS_INLINE_ASSEMBLY attribute when we are able to
235 : // parse CV debug information to tag functions with inline assembly
236 : // properly.
237 :
238 : // Validate that the DllMain block has the expected population of labels.
239 : // These numbers should match those for the PE decomposer.
240 E : std::map<BlockGraph::LabelAttributes, size_t> label_attr_counts;
241 : BlockGraph::Block::LabelMap::const_iterator label_it =
242 E : dll_main_block->labels().begin();
243 E : for (; label_it != dll_main_block->labels().end(); ++label_it) {
244 E : BlockGraph::LabelAttributes attr_mask = 1;
245 E : for (; attr_mask != BlockGraph::LABEL_ATTRIBUTES_MAX; attr_mask <<= 1) {
246 E : if (label_it->second.has_attributes(attr_mask))
247 E : ++label_attr_counts[attr_mask];
248 E : }
249 E : }
250 :
251 E : EXPECT_EQ(kNumJumpLabelsInDllMain,
252 E : label_attr_counts[BlockGraph::JUMP_TABLE_LABEL]);
253 E : EXPECT_EQ(kNumCaseLabelsInDllMain,
254 E : label_attr_counts[BlockGraph::CASE_TABLE_LABEL]);
255 E : }
256 :
257 : // NOTE: This test ensures that COFF parsed blocks interact well with the basic
258 : // block decomposer and the block builder. This is really a test of those two
259 : // pieces of code, but due to the necessity of first decomposing the COFF file
260 : // the tests can't reside in block_graph_unittests. Consider this more of an
261 : // integration test.
262 E : TEST_F(CoffDecomposerTest, RoundTripBasicBlockTest) {
263 : // Decompose the test image and look at the result.
264 E : CoffDecomposer decomposer(image_file_);
265 E : BlockGraph block_graph;
266 E : ImageLayout image_layout(&block_graph);
267 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
268 :
269 E : size_t last_block_id = block_graph.blocks().rbegin()->first;
270 E : BlockGraph::BlockMap::iterator block_it;
271 : BlockGraph::BlockMap::iterator next_block_it =
272 E : block_graph.blocks_mutable().begin();
273 E : while (true) {
274 E : block_it = next_block_it;
275 E : if (block_it == block_graph.blocks_mutable().end())
276 i : break;
277 E : ++next_block_it;
278 :
279 E : BlockGraph::Block* old_block = &block_it->second;
280 E : if (old_block->type() != BlockGraph::CODE_BLOCK)
281 E : continue;
282 :
283 : // We stop iterating when we've hit the rebuilt version of the first block.
284 : // This works because the BlockGraph guarantees monotically increasing
285 : // IDs, thus all rebuilt blocks will have IDs greater than all original
286 : // blocks.
287 E : if (old_block->id() > last_block_id)
288 E : break;
289 :
290 : // Decomposition should work.
291 E : block_graph::BasicBlockSubGraph bbsg;
292 E : block_graph::BasicBlockDecomposer bbdecomp(old_block, &bbsg);
293 E : ASSERT_TRUE(bbdecomp.Decompose());
294 :
295 E : size_t old_reference_count = old_block->references().size();
296 E : size_t old_referrer_count = old_block->referrers().size();
297 E : size_t old_label_count = old_block->labels().size();
298 E : size_t old_size = old_block->size();
299 :
300 : // Grab a copy of the pertinent structures of the old block.
301 : // This aids debugging.
302 : BlockGraph::Block::ReferenceMap old_references =
303 E : old_block->references();
304 : BlockGraph::Block::ReferrerSet old_referrers =
305 E : old_block->referrers();
306 : BlockGraph::Block::LabelMap old_labels =
307 E : old_block->labels();
308 :
309 : // Rebuilding a block should work.
310 E : block_graph::BlockBuilder builder(&block_graph);
311 E : ASSERT_TRUE(builder.Merge(&bbsg));
312 E : EXPECT_EQ(1u, builder.new_blocks().size());
313 E : BlockGraph::Block* new_block = builder.new_blocks()[0];
314 :
315 : // Throw away any PC relative self-references. These aren't produced
316 : // by the decomposer, but *are* produced by the block builder.
317 E : BlockGraph::Block::ReferenceMap::const_iterator ref_it;
318 : BlockGraph::Block::ReferenceMap::const_iterator next_ref_it =
319 E : new_block->references().begin();
320 E : while (true) {
321 E : ref_it = next_ref_it;
322 E : if (ref_it == new_block->references().end())
323 E : break;
324 E : ++next_ref_it;
325 :
326 E : if (ref_it->second.type() == BlockGraph::PC_RELATIVE_REF &&
327 : ref_it->second.referenced() == new_block) {
328 E : new_block->RemoveReference(ref_it->first);
329 : }
330 E : }
331 :
332 E : size_t new_reference_count = new_block->references().size();
333 E : size_t new_referrer_count = new_block->referrers().size();
334 E : size_t new_label_count = new_block->labels().size();
335 E : size_t new_size = new_block->size();
336 :
337 E : EXPECT_EQ(old_reference_count, new_reference_count);
338 E : EXPECT_EQ(old_referrer_count, new_referrer_count);
339 E : EXPECT_EQ(old_label_count, new_label_count);
340 E : EXPECT_EQ(old_size, new_size);
341 E : }
342 E : }
343 :
344 E : TEST(SimpleCoffDecomposerTest, DecomposeCodeView2Symbols) {
345 E : base::FilePath path = testing::GetSrcRelativePath(testing::kCodeView2Name);
346 E : CoffFile file;
347 E : ASSERT_TRUE(file.Init(path));
348 E : CoffDecomposer decomposer(file);
349 E : BlockGraph block_graph;
350 E : ImageLayout image_layout(&block_graph);
351 E : EXPECT_TRUE(decomposer.Decompose(&image_layout));
352 E : EXPECT_EQ(BlockGraph::COFF_IMAGE, block_graph.image_format());
353 E : }
354 :
355 E : TEST(SimpleCoffDecomposerTest, DecomposeEmptyStringTable) {
356 E : base::FilePath path = testing::GetSrcRelativePath(
357 : testing::kEmptyStringTableCoffName);
358 E : pe::CoffFile coff_file;
359 E : ASSERT_TRUE(coff_file.Init(path));
360 E : CoffDecomposer decomposer(coff_file);
361 E : BlockGraph block_graph;
362 E : ImageLayout image_layout(&block_graph);
363 E : EXPECT_TRUE(decomposer.Decompose(&image_layout));
364 E : EXPECT_EQ(BlockGraph::COFF_IMAGE, block_graph.image_format());
365 E : }
366 :
367 : } // namespace pe
|