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