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/typed_block.h"
19 : #include "syzygy/core/unittest_util.h"
20 : #include "syzygy/pe/unittest_util.h"
21 :
22 : namespace pe {
23 :
24 : namespace {
25 :
26 : using block_graph::BlockGraph;
27 : using block_graph::ConstTypedBlock;
28 : using core::RelativeAddress;
29 :
30 : const size_t kPointerSize = BlockGraph::Reference::kMaximumSize;
31 :
32 : // test_dll.coff_obj-specific constants. Adjust to match current code in
33 : // test_dll.cc.
34 : const size_t kNumTextSections = 25;
35 : const size_t kNumDataSections = 2;
36 : const size_t kNumRDataSections = 14; // Includes .rdata$r sections.
37 : const size_t kNumDebugSections = 27; // Includes .debug$S and .debug$T.
38 : const size_t kNumBssSections = 1;
39 :
40 : const size_t kNumFunctions = 14;
41 : const size_t kNumJumpLabelsInDllMain = 3;
42 : const size_t kNumCaseLabelsInDllMain = 2;
43 :
44 : class CoffDecomposerTest : public testing::Test {
45 : public:
46 E : virtual void SetUp() OVERRIDE {
47 E : testing::Test::SetUp();
48 :
49 : test_dll_obj_path_ =
50 E : testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
51 E : ASSERT_TRUE(image_file_.Init(test_dll_obj_path_));
52 E : }
53 :
54 : base::FilePath test_dll_obj_path_;
55 : CoffFile image_file_;
56 : };
57 :
58 : } // namespace
59 :
60 E : TEST_F(CoffDecomposerTest, Decompose) {
61 : // Decompose the test image.
62 E : CoffDecomposer decomposer(image_file_);
63 :
64 E : BlockGraph block_graph;
65 E : ImageLayout image_layout(&block_graph);
66 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
67 :
68 : // Retrieve the COFF file header.
69 : BlockGraph::Block* file_header_block =
70 E : image_layout.blocks.GetBlockByAddress(RelativeAddress(0));
71 E : ASSERT_TRUE(file_header_block != NULL);
72 :
73 : // There should be some blocks in the graph and in the layout, and the
74 : // same number in the block graph and image layout.
75 E : EXPECT_LT(0u, block_graph.blocks().size());
76 E : EXPECT_LT(0u, image_layout.blocks.size());
77 : EXPECT_EQ(block_graph.blocks().size(),
78 E : image_layout.blocks.size() + kNumBssSections);
79 :
80 : // We expect the ImageLayout sections to agree with the BlockGraph
81 : // sections in number, id, name and characteristics.
82 E : EXPECT_EQ(block_graph.sections().size(), image_layout.sections.size());
83 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
84 E : const BlockGraph::Section* section = block_graph.GetSectionById(i);
85 E : ASSERT_TRUE(section != NULL);
86 E : EXPECT_EQ(section->id(), i);
87 E : EXPECT_EQ(section->name(), image_layout.sections[i].name);
88 : EXPECT_EQ(section->characteristics(),
89 E : image_layout.sections[i].characteristics);
90 E : }
91 :
92 : // Count symbols.
93 E : size_t num_internal_symbols = 0;
94 E : size_t num_assoc_comdat_sections = 0;
95 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
96 E : for (size_t i = 0; i < num_symbols; ++i) {
97 E : const IMAGE_SYMBOL* symbol = image_file_.symbol(i);
98 E : i += symbol->NumberOfAuxSymbols;
99 E : if (symbol->SectionNumber > 0)
100 E : ++num_internal_symbols;
101 E : }
102 :
103 : // Check that the number of sections, blocks and references match
104 : // expectations.
105 E : size_t num_code_blocks = 0;
106 E : size_t num_section_blocks = 0;
107 E : size_t num_section_blocks_with_references = 0;
108 E : size_t num_debug_section_blocks = 0;
109 E : size_t num_non_section_blocks = 0;
110 E : size_t num_references_in_symbol_table = 0;
111 : BlockGraph::BlockMap::const_iterator it =
112 E : block_graph.blocks().begin();
113 E : for (; it != block_graph.blocks().end(); ++it) {
114 E : const BlockGraph::Block& block = it->second;
115 :
116 E : if (block.type() == BlockGraph::CODE_BLOCK)
117 E : ++num_code_blocks;
118 :
119 E : if (block.section() == BlockGraph::kInvalidSectionId) {
120 E : ++num_non_section_blocks;
121 :
122 E : if ((block.attributes() & BlockGraph::COFF_SYMBOL_TABLE) != 0)
123 E : num_references_in_symbol_table = block.references().size();
124 E : } else {
125 : // If this is not a header block, it should refer to a valid
126 : // section index.
127 E : EXPECT_LT(block.section(), block_graph.sections().size());
128 E : ++num_section_blocks;
129 :
130 : BlockGraph::Section* section =
131 E : block_graph.GetSectionById(block.section());
132 E : DCHECK(section != NULL);
133 E : if (section->name() == ".debug$S")
134 E : ++num_debug_section_blocks;
135 :
136 : size_t num_relocs =
137 E : image_file_.section_header(block.section())->NumberOfRelocations;
138 : // Debug sections also have hard-coded references in addition to
139 : // relocation references, so the numbers will not match exactly, but
140 : // it will be at least that many.
141 E : if (image_file_.GetSectionName(block.section()) == ".debug$S") {
142 E : EXPECT_LE(num_relocs, block.references().size());
143 E : } else {
144 E : EXPECT_EQ(num_relocs, block.references().size());
145 : }
146 E : if (block.references().size() > 0)
147 E : ++num_section_blocks_with_references;
148 : }
149 E : }
150 : EXPECT_EQ(num_section_blocks + num_non_section_blocks,
151 E : block_graph.blocks().size());
152 :
153 : // Each symbol has one section and one section offset reference; plus,
154 : // each associative COMDAT section definition must have one additional
155 : // reference. In test_dll.obj, only .debug$S sections should be COMDAT
156 : // associative, except the global .debug$S section.
157 : EXPECT_EQ(2 * num_internal_symbols + num_debug_section_blocks - 1,
158 E : num_references_in_symbol_table);
159 :
160 : // There should be at least as many code blocks as there are functions in
161 : // test_dll.cc.
162 E : EXPECT_LE(kNumFunctions, num_code_blocks);
163 :
164 : // There should be exactly one block per section (including BSS sections)
165 : // in an object file with function-level linking.
166 E : EXPECT_EQ(image_file_.file_header()->NumberOfSections, num_section_blocks);
167 :
168 : // Non-section blocks should be: the header block, the symbol and string
169 : // tables, and the per-section relocations tables.
170 E : EXPECT_EQ(num_section_blocks_with_references + 3, num_non_section_blocks);
171 E : }
172 :
173 E : TEST_F(CoffDecomposerTest, FunctionsAndLabels) {
174 : // Decompose the test image and look at the result.
175 E : CoffDecomposer decomposer(image_file_);
176 E : BlockGraph block_graph;
177 E : ImageLayout image_layout(&block_graph);
178 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
179 :
180 : // Locate various specific function blocks in the block graph, while
181 : // validating the number of blocks matching each section name (.text,
182 : // .data, etc.).
183 E : size_t num_text_blocks = 0;
184 E : size_t num_data_blocks = 0;
185 E : size_t num_rdata_blocks = 0;
186 E : size_t num_debug_blocks = 0;
187 E : size_t num_bss_blocks = 0;
188 E : const BlockGraph::Block* dll_main_block = NULL;
189 E : const BlockGraph::Block* func_with_inl_asm_block = NULL;
190 :
191 : BlockGraph::BlockMap::const_iterator block_it =
192 E : block_graph.blocks().begin();
193 E : for (; block_it != block_graph.blocks().end(); ++block_it) {
194 E : const BlockGraph::Block* block = &block_it->second;
195 E : const std::string& name = block->name();
196 :
197 E : if (name.find(".text") != std::string::npos)
198 E : ++num_text_blocks;
199 E : else if (name.find(".data") != std::string::npos)
200 E : ++num_data_blocks;
201 E : else if (name.find(".rdata") != std::string::npos)
202 E : ++num_rdata_blocks;
203 E : else if (name.find(".debug") != std::string::npos)
204 E : ++num_debug_blocks;
205 E : else if (name.find(".bss") != std::string::npos)
206 E : ++num_bss_blocks;
207 :
208 E : if (block->type() == BlockGraph::CODE_BLOCK) {
209 E : if (name.find("DllMain") != std::string::npos) {
210 E : EXPECT_TRUE(dll_main_block == NULL);
211 E : dll_main_block = &block_it->second;
212 E : } else if (name.find("FunctionWithInlineAssembly") != std::string::npos) {
213 E : EXPECT_TRUE(func_with_inl_asm_block == NULL);
214 E : func_with_inl_asm_block = &block_it->second;
215 : }
216 : }
217 E : }
218 :
219 E : EXPECT_EQ(kNumTextSections, num_text_blocks);
220 E : EXPECT_EQ(kNumDataSections, num_data_blocks);
221 E : EXPECT_EQ(kNumRDataSections, num_rdata_blocks);
222 E : EXPECT_EQ(kNumDebugSections, num_debug_blocks);
223 E : EXPECT_EQ(kNumBssSections, num_bss_blocks);
224 :
225 E : EXPECT_TRUE(dll_main_block != NULL);
226 E : EXPECT_TRUE(func_with_inl_asm_block != NULL);
227 :
228 : // TODO(lenh): Check for HAS_INLINE_ASSEMBLY attribute when we are able to
229 : // parse CV debug information to tag functions with inline assembly
230 : // properly.
231 :
232 : // Validate that the DllMain block has the expected population of labels.
233 : // These numbers should match those for the PE decomposer.
234 E : std::map<BlockGraph::LabelAttributes, size_t> label_attr_counts;
235 : BlockGraph::Block::LabelMap::const_iterator label_it =
236 E : dll_main_block->labels().begin();
237 E : for (; label_it != dll_main_block->labels().end(); ++label_it) {
238 E : BlockGraph::LabelAttributes attr_mask = 1;
239 E : for (; attr_mask != BlockGraph::LABEL_ATTRIBUTES_MAX; attr_mask <<= 1) {
240 E : if (label_it->second.has_attributes(attr_mask))
241 E : ++label_attr_counts[attr_mask];
242 E : }
243 E : }
244 :
245 : EXPECT_EQ(kNumJumpLabelsInDllMain,
246 E : label_attr_counts[BlockGraph::JUMP_TABLE_LABEL]);
247 : EXPECT_EQ(kNumCaseLabelsInDllMain,
248 E : label_attr_counts[BlockGraph::CASE_TABLE_LABEL]);
249 E : }
250 :
251 : } // namespace pe
|