1 : // Copyright 2012 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/block_graph/block_graph_serializer.h"
16 :
17 : #include "base/bind.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/block_graph/unittest_util.h"
20 : #include "syzygy/core/serialization.h"
21 :
22 : namespace block_graph {
23 :
24 : namespace {
25 :
26 : class TestBlockGraphSerializer : public BlockGraphSerializer {
27 : public:
28 : using BlockGraphSerializer::SaveUint32;
29 : using BlockGraphSerializer::LoadUint32;
30 : using BlockGraphSerializer::SaveInt32;
31 : using BlockGraphSerializer::LoadInt32;
32 : };
33 :
34 : class BlockGraphSerializerTest : public ::testing::Test {
35 : public:
36 E : BlockGraphSerializerTest() : block_data_loaded_by_callback_(0) { }
37 :
38 E : virtual void SetUp() OVERRIDE {
39 E : }
40 :
41 E : void InitOutArchive() {
42 E : v_.clear();
43 E : os_.reset(core::CreateByteOutStream(std::back_inserter(v_)));
44 : oa_.reset(new core::NativeBinaryOutArchive(
45 E : os_.get()));
46 E : }
47 :
48 E : void InitInArchive() {
49 E : is_.reset(core::CreateByteInStream(v_.begin(), v_.end()));
50 : ia_.reset(new core::NativeBinaryInArchive(
51 E : is_.get()));
52 E : }
53 :
54 E : void InitBlockGraph() {
55 E : BlockGraph::Section* text = bg_.AddSection(".text", 1 | 4);
56 E : BlockGraph::Section* data = bg_.AddSection(".data", 2);
57 E : BlockGraph::Section* rdata = bg_.AddSection(".rdata", 2 | 4);
58 :
59 E : BlockGraph::Block* c1 = bg_.AddBlock(BlockGraph::CODE_BLOCK, 20, "code1");
60 E : BlockGraph::Block* c2 = bg_.AddBlock(BlockGraph::CODE_BLOCK, 16, "code2");
61 E : BlockGraph::Block* d1 = bg_.AddBlock(BlockGraph::DATA_BLOCK, 20, "data1");
62 E : BlockGraph::Block* rd1 = bg_.AddBlock(BlockGraph::DATA_BLOCK, 16, "rdata1");
63 E : BlockGraph::Block* rd2 = bg_.AddBlock(BlockGraph::DATA_BLOCK, 16, "rdata2");
64 :
65 E : c1->set_section(text->id());
66 E : c2->set_section(text->id());
67 E : d1->set_section(data->id());
68 E : rd1->set_section(rdata->id());
69 E : rd2->set_section(rdata->id());
70 :
71 : // Set compiland name.
72 E : c1->set_compiland_name("c.o");
73 E : c2->set_compiland_name("c.o");
74 E : d1->set_compiland_name("d.o");
75 E : rd1->set_compiland_name("d.o");
76 E : rd2->set_compiland_name("d.o");
77 :
78 : // Set up alignments and paddings.
79 E : c2->set_alignment(16);
80 E : c2->set_alignment_offset(-4);
81 E : c2->set_padding_before(1);
82 E : d1->set_alignment(16);
83 E : rd1->set_alignment(16);
84 E : rd1->set_alignment(16);
85 :
86 : // Some of the blocks own their own data, some don't. One has no data at
87 : // all.
88 E : c1->SetData(kCode1Data, sizeof(kCode1Data));
89 E : c2->CopyData(sizeof(kCode2Data), kCode2Data);
90 E : d1->SetData(kData1Data, sizeof(kData1Data));
91 E : rd1->CopyData(sizeof(kRdata1Data), kRdata1Data);
92 :
93 : // Given them all source ranges.
94 : c1->source_ranges().Push(BlockGraph::Block::DataRange(0, 20),
95 E : BlockGraph::Block::SourceRange(core::RelativeAddress(0), 20));
96 : c2->source_ranges().Push(BlockGraph::Block::DataRange(0, 16),
97 E : BlockGraph::Block::SourceRange(core::RelativeAddress(36), 48));
98 : d1->source_ranges().Push(BlockGraph::Block::DataRange(0, 20),
99 E : BlockGraph::Block::SourceRange(core::RelativeAddress(512), 532));
100 : rd1->source_ranges().Push(BlockGraph::Block::DataRange(0, 16),
101 E : BlockGraph::Block::SourceRange(core::RelativeAddress(1024), 1040));
102 : rd2->source_ranges().Push(BlockGraph::Block::DataRange(0, 16),
103 E : BlockGraph::Block::SourceRange(core::RelativeAddress(1040), 1056));
104 :
105 : // Set up labels.
106 : c1->SetLabel(0, BlockGraph::Label("code1",
107 E : BlockGraph::CODE_LABEL | BlockGraph::DEBUG_START_LABEL));
108 E : c1->SetLabel(8, BlockGraph::Label("label", BlockGraph::CODE_LABEL));
109 E : c1->SetLabel(11, BlockGraph::Label("debug", BlockGraph::DEBUG_END_LABEL));
110 : c1->SetLabel(12, BlockGraph::Label("jump",
111 E : BlockGraph::DATA_LABEL | BlockGraph::JUMP_TABLE_LABEL));
112 E : c2->SetLabel(0, BlockGraph::Label("code1", BlockGraph::CODE_LABEL));
113 : c2->SetLabel(8, BlockGraph::Label("jump",
114 E : BlockGraph::DATA_LABEL | BlockGraph::JUMP_TABLE_LABEL));
115 : c2->SetLabel(12, BlockGraph::Label("case",
116 E : BlockGraph::DATA_LABEL | BlockGraph::CASE_TABLE_LABEL));
117 E : d1->SetLabel(0, BlockGraph::Label("data", BlockGraph::DATA_LABEL));
118 :
119 : // Set up some references.
120 : c1->SetReference(4, BlockGraph::Reference(
121 E : BlockGraph::ABSOLUTE_REF, 4, d1, 0, 0));
122 : c1->SetReference(12, BlockGraph::Reference(
123 E : BlockGraph::ABSOLUTE_REF, 4, c2, 0, 0));
124 : c2->SetReference(8, BlockGraph::Reference(
125 E : BlockGraph::ABSOLUTE_REF, 4, c1, 0, 0));
126 : d1->SetReference(0, BlockGraph::Reference(
127 E : BlockGraph::ABSOLUTE_REF, 4, rd1, 0, 0));
128 : rd1->SetReference(0, BlockGraph::Reference(
129 E : BlockGraph::ABSOLUTE_REF, 4, rd2, 0, 0));
130 E : }
131 :
132 E : void InitBlockDataCallbacks1() {
133 : s_.set_load_block_data_callback(
134 : base::Bind(&BlockGraphSerializerTest::LoadBlockDataCallback1,
135 E : base::Unretained(this)));
136 E : }
137 :
138 E : void InitBlockDataCallbacks2() {
139 : s_.set_save_block_data_callback(
140 : base::Bind(&BlockGraphSerializerTest::SaveBlockDataCallback2,
141 E : base::Unretained(this)));
142 : s_.set_load_block_data_callback(
143 : base::Bind(&BlockGraphSerializerTest::LoadBlockDataCallback2,
144 E : base::Unretained(this)));
145 E : }
146 :
147 : bool LoadBlockDataCallback1(bool need_to_set_data,
148 : size_t size,
149 : BlockGraph::Block* block,
150 E : core::InArchive* in_archive) {
151 E : DCHECK(block != NULL);
152 E : DCHECK(in_archive != NULL);
153 :
154 : // We only have work to do if the data is not explicitly saved.
155 E : if (!need_to_set_data)
156 E : return true;
157 :
158 E : block_data_loaded_by_callback_++;
159 :
160 E : EXPECT_LT(0u, size);
161 E : if (size == 0)
162 i : return false;
163 :
164 E : EXPECT_EQ(1u, block->source_ranges().size());
165 E : if (block->source_ranges().size() != 1)
166 i : return false;
167 :
168 : // We use the source range to determine which block gets which data, as the
169 : // name is not always present.
170 E : size_t data_size = 0;
171 E : const uint8* data = NULL;
172 E : switch (block->source_ranges().range_pairs()[0].second.start().value()) {
173 : case 0:
174 E : data = kCode1Data;
175 E : data_size = sizeof(kCode1Data);
176 E : break;
177 :
178 : case 36:
179 E : data = kCode2Data;
180 E : data_size = sizeof(kCode2Data);
181 E : break;
182 :
183 : case 512:
184 E : data = kData1Data;
185 E : data_size = sizeof(kData1Data);
186 E : break;
187 :
188 : case 1024:
189 E : data = kRdata1Data;
190 E : data_size = sizeof(kRdata1Data);
191 : break;
192 :
193 : default:
194 : break;
195 : }
196 :
197 E : EXPECT_TRUE(data != NULL);
198 E : EXPECT_EQ(data_size, size);
199 E : if (data == NULL || data_size != size)
200 i : return false;
201 :
202 E : block->SetData(data, data_size);
203 E : return true;
204 E : }
205 :
206 : bool SaveBlockDataCallback2(bool data_already_saved,
207 : const BlockGraph::Block& block,
208 E : core::OutArchive* out_archive) {
209 E : DCHECK(out_archive != NULL);
210 :
211 : // If the data is already saved, do nothing.
212 E : if (data_already_saved)
213 E : return true;
214 :
215 E : EXPECT_LT(0u, block.data_size());
216 E : if (block.data_size() == 0)
217 i : return false;
218 :
219 : // Determine which data buffer the block holds, and save an index value
220 : // representing it.
221 E : char data_index = -1;
222 E : if (memcmp(kCode1Data, block.data(), block.data_size()) == 0)
223 E : data_index = 0;
224 E : else if (memcmp(kCode2Data, block.data(), block.data_size()) == 0)
225 E : data_index = 1;
226 E : else if (memcmp(kData1Data, block.data(), block.data_size()) == 0)
227 E : data_index = 2;
228 E : else if (memcmp(kRdata1Data, block.data(), block.data_size()) == 0)
229 E : data_index = 3;
230 :
231 E : EXPECT_NE(-1, data_index);
232 E : if (data_index == -1)
233 i : return false;
234 :
235 E : if (!out_archive->Save(data_index))
236 i : return false;
237 :
238 E : return true;
239 E : }
240 :
241 : bool LoadBlockDataCallback2(bool need_to_set_data,
242 : size_t size,
243 : BlockGraph::Block* block,
244 E : core::InArchive* in_archive) {
245 E : DCHECK(block != NULL);
246 E : DCHECK(in_archive != NULL);
247 :
248 : // We only have work to do if the data is not explicitly saved.
249 E : if (!need_to_set_data)
250 E : return true;
251 :
252 E : block_data_loaded_by_callback_++;
253 :
254 E : EXPECT_LT(0u, size);
255 E : if (size == 0)
256 i : return false;
257 :
258 E : char data_index = -1;
259 E : if (!in_archive->Load(&data_index))
260 i : return false;
261 :
262 E : EXPECT_LE(0, data_index);
263 E : EXPECT_GT(4, data_index);
264 :
265 : static const uint8* kData[] = {
266 : kCode1Data, kCode2Data, kData1Data, kRdata1Data };
267 E : block->SetData(kData[data_index], size);
268 :
269 E : return true;
270 E : }
271 :
272 : enum InitCallbacksType {
273 : eNoBlockDataCallbacks,
274 : eInitBlockDataCallbacks1,
275 : eInitBlockDataCallbacks2
276 : };
277 :
278 : void TestRoundTrip(BlockGraphSerializer::DataMode data_mode,
279 : BlockGraphSerializer::Attributes attributes,
280 : InitCallbacksType init_callback,
281 E : size_t expected_block_data_loaded_by_callback) {
282 E : InitBlockGraph();
283 E : InitOutArchive();
284 :
285 E : s_.set_data_mode(data_mode);
286 E : s_.set_attributes(attributes);
287 :
288 : // Initialize the callbacks.
289 E : switch (init_callback) {
290 : case eInitBlockDataCallbacks1: {
291 E : InitBlockDataCallbacks1();
292 E : break;
293 : }
294 :
295 : case eInitBlockDataCallbacks2: {
296 E : InitBlockDataCallbacks2();
297 : break;
298 : }
299 :
300 : case eNoBlockDataCallbacks:
301 : default:
302 : // Do nothing.
303 : break;
304 : }
305 :
306 E : ASSERT_TRUE(s_.Save(bg_, oa_.get()));
307 E : ASSERT_LT(0u, v_.size());
308 :
309 E : InitInArchive();
310 :
311 E : BlockGraph bg;
312 E : ASSERT_TRUE(s_.Load(&bg, ia_.get()));
313 E : ASSERT_EQ(data_mode, s_.data_mode());
314 E : ASSERT_EQ(attributes, s_.attributes());
315 E : ASSERT_EQ(expected_block_data_loaded_by_callback,
316 : block_data_loaded_by_callback_);
317 :
318 E : ASSERT_TRUE(testing::BlockGraphsEqual(bg_, bg, s_));
319 E : }
320 :
321 : TestBlockGraphSerializer s_;
322 :
323 : // A block-graph.
324 : BlockGraph bg_;
325 :
326 : // Streams and archives.
327 : std::vector<uint8> v_;
328 : scoped_ptr<core::OutStream> os_;
329 : scoped_ptr<core::InStream> is_;
330 : scoped_ptr<core::OutArchive> oa_;
331 : scoped_ptr<core::InArchive> ia_;
332 :
333 : static const uint8 kCode1Data[16];
334 : static const uint8 kCode2Data[16];
335 : static const uint8 kData1Data[16];
336 : static const uint8 kRdata1Data[16];
337 :
338 : size_t block_data_loaded_by_callback_;
339 : };
340 :
341 : const uint8 BlockGraphSerializerTest::kCode1Data[16] = {
342 : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
343 : const uint8 BlockGraphSerializerTest::kCode2Data[16] = {
344 : 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5 };
345 : const uint8 BlockGraphSerializerTest::kData1Data[16] = {
346 : 10, 30, 45, 63, 20, 23, 67, 20, 32, 40, 50, 10, 15, 10, 18, 19 };
347 : const uint8 BlockGraphSerializerTest::kRdata1Data[16] = {
348 : 28, 28, 29, 30, 56, 28, 23, 78, 19, 99, 10, 10, 23, 54, 54, 12 };
349 :
350 : } // namespace
351 :
352 E : TEST_F(BlockGraphSerializerTest, Construction) {
353 E : ASSERT_EQ(BlockGraphSerializer::DEFAULT_DATA_MODE, s_.data_mode());
354 E : ASSERT_EQ(BlockGraphSerializer::DEFAULT_ATTRIBUTES, s_.data_mode());
355 E : }
356 :
357 E : TEST_F(BlockGraphSerializerTest, SetDataMode) {
358 E : ASSERT_EQ(BlockGraphSerializer::DEFAULT_DATA_MODE, s_.data_mode());
359 :
360 E : s_.set_data_mode(BlockGraphSerializer::OUTPUT_NO_DATA);
361 E : ASSERT_EQ(BlockGraphSerializer::OUTPUT_NO_DATA, s_.data_mode());
362 :
363 E : s_.set_data_mode(BlockGraphSerializer::OUTPUT_ALL_DATA);
364 E : ASSERT_EQ(BlockGraphSerializer::OUTPUT_ALL_DATA, s_.data_mode());
365 E : }
366 :
367 E : TEST_F(BlockGraphSerializerTest, AddAttributes) {
368 E : ASSERT_EQ(0u, s_.attributes());
369 :
370 E : s_.add_attributes(1);
371 E : ASSERT_EQ(1u, s_.attributes());
372 :
373 E : s_.add_attributes(2 | 4);
374 E : ASSERT_EQ(1u | 2u | 4u, s_.attributes());
375 E : }
376 :
377 E : TEST_F(BlockGraphSerializerTest, ClearAttributes) {
378 E : ASSERT_EQ(0u, s_.attributes());
379 :
380 E : s_.add_attributes(1 | 2);
381 E : ASSERT_EQ(1u | 2u, s_.attributes());
382 :
383 E : s_.clear_attributes(2);
384 E : ASSERT_EQ(1u, s_.attributes());
385 E : }
386 :
387 E : TEST_F(BlockGraphSerializerTest, SetAttributes) {
388 E : ASSERT_EQ(0u, s_.attributes());
389 :
390 E : s_.set_attributes(1 | 2);
391 E : ASSERT_EQ(1u | 2u, s_.attributes());
392 :
393 E : s_.set_attributes(4 | 8);
394 E : ASSERT_EQ(4u | 8u, s_.attributes());
395 E : }
396 :
397 E : TEST_F(BlockGraphSerializerTest, HasAttributes) {
398 E : ASSERT_EQ(0u, s_.attributes());
399 :
400 E : s_.set_attributes(1 | 2);
401 E : ASSERT_EQ(1u | 2u, s_.attributes());
402 :
403 E : ASSERT_TRUE(s_.has_attributes(1));
404 E : ASSERT_TRUE(s_.has_attributes(2));
405 E : ASSERT_TRUE(s_.has_attributes(1 | 2));
406 E : ASSERT_FALSE(s_.has_attributes(1 | 2 | 4));
407 E : }
408 :
409 E : TEST_F(BlockGraphSerializerTest, HasAnyAttributes) {
410 E : ASSERT_EQ(0u, s_.attributes());
411 :
412 E : s_.set_attributes(1 | 2);
413 E : ASSERT_EQ(1u | 2u, s_.attributes());
414 :
415 E : ASSERT_TRUE(s_.has_any_attributes(1));
416 E : ASSERT_TRUE(s_.has_any_attributes(2));
417 E : ASSERT_TRUE(s_.has_any_attributes(1 | 2 | 4));
418 E : ASSERT_FALSE(s_.has_any_attributes(4 | 8));
419 E : }
420 :
421 E : TEST_F(BlockGraphSerializerTest, VariableLengthUint32Encoding) {
422 : const uint32 kTestValues[] = {
423 : // 5-bit values (< 32) that map to 1 byte.
424 : 1, 27, 31,
425 : // 13-bit values (< 8,192) that map to 2 bytes.
426 : 32, 1034, 8191,
427 : // 21-bit values (< 2,097,152) that map to 3 bytes.
428 : 8192, 1023847, 2097151,
429 : // 29-bit values (< 536,870,912) that map to 4 bytes.
430 : 2097152, 38274285, 536870911,
431 : // 32-bit values (< 4,294,967,296) that map to 5 bytes.
432 E : 536870912, 1610612736, 4294967295 };
433 :
434 E : for (size_t i = 0; i < arraysize(kTestValues); ++i) {
435 E : InitOutArchive();
436 E : ASSERT_TRUE(s_.SaveUint32(kTestValues[i], oa_.get()));
437 E : ASSERT_EQ((i / 3) + 1, v_.size());
438 :
439 E : InitInArchive();
440 E : uint32 value = 0;
441 E : ASSERT_TRUE(s_.LoadUint32(&value, ia_.get()));
442 :
443 E : ASSERT_EQ(kTestValues[i], value);
444 E : }
445 E : }
446 :
447 E : TEST_F(BlockGraphSerializerTest, VariableLengthInt32Encoding) {
448 : const int32 kTestValues[] = {
449 : // 4-bit values (< 16) that map to 1 byte.
450 : 1, 9, 15,
451 : // 12-bit values (< 4,096) that map to 2 bytes.
452 : 16, 1034, 4095,
453 : // 20-bit values (< 1,048,576) that map to 3 bytes.
454 : 4096, 815632, 1048575,
455 : // 28-bit values (< 268,435,456) that map to 4 bytes.
456 : 1048576, 38274285, 268435455,
457 : // 31-bit values (< 2,147,483,648) that map to 5 bytes.
458 E : 268435456, 805306368, 2147483647 };
459 :
460 E : for (size_t i = 0; i < arraysize(kTestValues); ++i) {
461 : // We try the value in a negative and positive format.
462 E : for (int32 j = -1; j <= 1; j += 2) {
463 E : int32 expected_value = kTestValues[i] * j;
464 :
465 E : InitOutArchive();
466 E : ASSERT_TRUE(s_.SaveInt32(expected_value, oa_.get()));
467 E : ASSERT_EQ((i / 3) + 1, v_.size());
468 :
469 E : InitInArchive();
470 E : int32 value = 0;
471 E : ASSERT_TRUE(s_.LoadInt32(&value, ia_.get()));
472 :
473 E : ASSERT_EQ(expected_value, value);
474 E : }
475 E : }
476 E : }
477 :
478 E : TEST_F(BlockGraphSerializerTest, FailsToLoadWrongVersion) {
479 : // Serialize an empty block-graph.
480 E : InitOutArchive();
481 E : ASSERT_TRUE(s_.Save(bg_, oa_.get()));
482 :
483 : // The first 4 bytes of the stream are the version. We change it so it is
484 : // invalid.
485 E : v_[0] += 1;
486 :
487 : // Deserialization should fail.
488 E : InitInArchive();
489 E : ASSERT_FALSE(s_.Load(&bg_, ia_.get()));
490 E : }
491 :
492 E : TEST_F(BlockGraphSerializerTest, RoundTripNoData) {
493 : ASSERT_NO_FATAL_FAILURE(TestRoundTrip(
494 : BlockGraphSerializer::OUTPUT_NO_DATA,
495 : BlockGraphSerializer::DEFAULT_ATTRIBUTES,
496 E : eInitBlockDataCallbacks1, 4));
497 E : }
498 :
499 E : TEST_F(BlockGraphSerializerTest, RoundTripNoDataCustomRepresentation) {
500 : ASSERT_NO_FATAL_FAILURE(TestRoundTrip(
501 : BlockGraphSerializer::OUTPUT_NO_DATA,
502 : BlockGraphSerializer::DEFAULT_ATTRIBUTES,
503 E : eInitBlockDataCallbacks2, 4));
504 E : }
505 :
506 E : TEST_F(BlockGraphSerializerTest, RoundTripOwnedData) {
507 : ASSERT_NO_FATAL_FAILURE(TestRoundTrip(
508 : BlockGraphSerializer::OUTPUT_OWNED_DATA,
509 : BlockGraphSerializer::DEFAULT_ATTRIBUTES,
510 E : eInitBlockDataCallbacks1, 2));
511 E : }
512 :
513 E : TEST_F(BlockGraphSerializerTest, RoundTripOwnedDataCustomRepresentation) {
514 : ASSERT_NO_FATAL_FAILURE(TestRoundTrip(
515 : BlockGraphSerializer::OUTPUT_OWNED_DATA,
516 : BlockGraphSerializer::DEFAULT_ATTRIBUTES,
517 E : eInitBlockDataCallbacks2, 2));
518 E : }
519 :
520 E : TEST_F(BlockGraphSerializerTest, RoundTripAllData) {
521 : ASSERT_NO_FATAL_FAILURE(TestRoundTrip(
522 : BlockGraphSerializer::OUTPUT_ALL_DATA,
523 : BlockGraphSerializer::DEFAULT_ATTRIBUTES,
524 E : eNoBlockDataCallbacks, 0));
525 E : }
526 :
527 : // TODO(chrisha): Do a heck of a lot more testing of protected member functions.
528 :
529 : } // namespace block_graph
|