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