1 : // Copyright 2012 Google Inc.
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 : // Unittests for iteration primitives.
16 :
17 : #include "syzygy/instrument/transforms/entry_thunk_transform.h"
18 :
19 : #include <vector>
20 :
21 : #include "gmock/gmock.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/block_graph/typed_block.h"
24 : #include "syzygy/pe/pe_utils.h"
25 :
26 : namespace instrument {
27 : namespace transforms {
28 :
29 : namespace {
30 :
31 : using block_graph::BlockGraph;
32 : using block_graph::ConstBlockVector;
33 : using block_graph::TypedBlock;
34 : using core::AbsoluteAddress;
35 : using testing::_;
36 : using testing::Return;
37 :
38 : // Expose protected members for testing.
39 : class TestEntryThunkTransform : public EntryThunkTransform {
40 : public:
41 : using EntryThunkTransform::Thunk;
42 : };
43 :
44 : class EntryThunkTransformTest : public testing::Test {
45 : public:
46 : EntryThunkTransformTest()
47 : : dos_header_block_(NULL),
48 : nt_headers_block_(NULL),
49 : foo_(NULL),
50 : bar_(NULL),
51 E : array_(NULL) {
52 E : }
53 :
54 E : virtual void SetUp() {
55 : // TODO(siggi): We have a lot of code that does this sort of thing, maybe
56 : // it should be concentrated in a test fixture in pe someplace.
57 : // Create the DOS/NT headers.
58 : dos_header_block_ = bg_.AddBlock(BlockGraph::DATA_BLOCK,
59 : sizeof(IMAGE_DOS_HEADER),
60 E : "DOS Header");
61 E : ASSERT_TRUE(
62 : dos_header_block_->AllocateData(dos_header_block_->size()) != NULL);
63 :
64 : nt_headers_block_ = bg_.AddBlock(BlockGraph::DATA_BLOCK,
65 : sizeof(IMAGE_NT_HEADERS),
66 E : "NT Headers");
67 :
68 E : ASSERT_TRUE(
69 : nt_headers_block_->AllocateData(nt_headers_block_->size()) != NULL);
70 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
71 E : ASSERT_TRUE(nt_headers.Init(0, nt_headers_block_));
72 E : nt_headers->Signature = IMAGE_NT_SIGNATURE;
73 E : nt_headers->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
74 E : nt_headers->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
75 :
76 E : TypedBlock<IMAGE_DOS_HEADER> dos_header;
77 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block_));
78 E : ASSERT_TRUE(dos_header.SetReference(BlockGraph::RELATIVE_REF,
79 : dos_header->e_lfanew,
80 : nt_headers));
81 :
82 : // Make the DOS header valid just for giggles.
83 E : ASSERT_TRUE(pe::UpdateDosHeader(dos_header_block_));
84 :
85 : // Get the .text section.
86 : BlockGraph::Section* text =
87 E : bg_.FindOrAddSection(pe::kCodeSectionName, pe::kCodeCharacteristics);
88 :
89 : // Create a couple of code blocks for "functions".
90 E : foo_ = bg_.AddBlock(BlockGraph::CODE_BLOCK, 20, "foo");
91 E : foo_->set_section(text->id());
92 : foo_->source_ranges().Push(BlockGraph::Block::DataRange(0, 20),
93 E : BlockGraph::Block::SourceRange(core::RelativeAddress(0x1000), 20));
94 :
95 E : bar_ = bg_.AddBlock(BlockGraph::CODE_BLOCK, 20, "bar");
96 E : bar_->set_section(text->id());
97 : bar_->source_ranges().Push(BlockGraph::Block::DataRange(0, 20),
98 E : BlockGraph::Block::SourceRange(core::RelativeAddress(0x1020), 20));
99 :
100 : // Get the .rdata section.
101 : BlockGraph::Section* rdata =
102 : bg_.FindOrAddSection(pe::kReadOnlyDataSectionName,
103 E : pe::kReadOnlyDataCharacteristics);
104 :
105 : // Create a data array block.
106 : array_ = bg_.AddBlock(BlockGraph::DATA_BLOCK,
107 : 30 * sizeof(AbsoluteAddress),
108 E : "array");
109 E : array_->set_section(rdata->id());
110 :
111 : // foo() refers to the start of bar() with a PC-relative reference.
112 : foo_->SetReference(5, BlockGraph::Reference(BlockGraph::PC_RELATIVE_REF,
113 : sizeof(AbsoluteAddress),
114 : bar_,
115 E : 0, 0));
116 : // foo() is self-referential.
117 : foo_->SetReference(10, BlockGraph::Reference(BlockGraph::PC_RELATIVE_REF,
118 : sizeof(AbsoluteAddress),
119 : foo_,
120 E : 0, 0));
121 :
122 : // bar() refers to foo() five bytes in.
123 : bar_->SetReference(5, BlockGraph::Reference(BlockGraph::PC_RELATIVE_REF,
124 : sizeof(AbsoluteAddress),
125 : foo_,
126 E : 5, 5));
127 :
128 : // The array refers to the start of both foo() and bar().
129 : array_->SetReference(0, BlockGraph::Reference(BlockGraph::ABSOLUTE_REF,
130 : sizeof(AbsoluteAddress),
131 : foo_,
132 E : 0, 0));
133 :
134 : array_->SetReference(4, BlockGraph::Reference(BlockGraph::ABSOLUTE_REF,
135 : sizeof(AbsoluteAddress),
136 : bar_,
137 E : 0, 0));
138 :
139 : // And the array refers 5 bytes into foo().
140 : array_->SetReference(8, BlockGraph::Reference(BlockGraph::ABSOLUTE_REF,
141 : sizeof(AbsoluteAddress),
142 : foo_,
143 E : 5, 5));
144 :
145 E : num_sections_pre_transform_ = bg_.sections().size();
146 :
147 : // No thunks so far.
148 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(0, 0, 0));
149 E : }
150 :
151 : // Retrieves the thunks.
152 E : void FindThunks(ConstBlockVector* ret) {
153 E : ASSERT_TRUE(ret != NULL);
154 E : EXPECT_TRUE(ret->empty());
155 E : BlockGraph::Section* thunk_section = bg_.FindSection(".thunks");
156 E : if (thunk_section == NULL)
157 E : return;
158 :
159 E : BlockGraph::BlockMap::const_iterator it = bg_.blocks().begin();
160 E : for (; it != bg_.blocks().end(); ++it) {
161 E : const BlockGraph::Block& block = it->second;
162 E : if (block.section() == thunk_section->id()) {
163 E : EXPECT_EQ(BlockGraph::CODE_BLOCK, block.type());
164 E : EXPECT_EQ(2, block.references().size());
165 :
166 : // It's a thunk.
167 E : ret->push_back(&block);
168 : }
169 E : }
170 E : }
171 :
172 E : size_t CountDestinations(const ConstBlockVector& blocks) {
173 : typedef std::set<std::pair<BlockGraph::Block*, BlockGraph::Offset>>
174 : ReferenceMap;
175 E : ReferenceMap destinations;
176 E : for (size_t i = 0; i < blocks.size(); ++i) {
177 : // Lookup and record the destination.
178 E : BlockGraph::Reference ref;
179 E : EXPECT_TRUE(blocks[i]->GetReference(
180 : offsetof(TestEntryThunkTransform::Thunk, func_addr), &ref));
181 E : EXPECT_EQ(BlockGraph::ABSOLUTE_REF, ref.type());
182 E : destinations.insert(std::make_pair(ref.referenced(), ref.offset()));
183 E : }
184 E : return destinations.size();
185 E : }
186 :
187 E : size_t CountEntryPoints(const ConstBlockVector& blocks) {
188 : typedef std::set<std::pair<BlockGraph::Block*, BlockGraph::Offset>>
189 : ReferenceMap;
190 E : ReferenceMap entrypoints;
191 E : for (size_t i = 0; i < blocks.size(); ++i) {
192 : // Lookup and record the entrypoint.
193 E : BlockGraph::Reference ref;
194 E : EXPECT_TRUE(blocks[i]->GetReference(
195 : offsetof(TestEntryThunkTransform::Thunk, hook_addr), &ref));
196 E : EXPECT_EQ(BlockGraph::ABSOLUTE_REF, ref.type());
197 E : entrypoints.insert(std::make_pair(ref.referenced(), ref.offset()));
198 E : }
199 E : return entrypoints.size();
200 E : }
201 :
202 E : void VerifySourceRanges(const ConstBlockVector& thunks) {
203 E : for (size_t i = 0; i < thunks.size(); ++i) {
204 : // Test the source ranges on the thunk.
205 E : ASSERT_EQ(1, thunks[i]->source_ranges().size());
206 : BlockGraph::Block::SourceRanges::RangePair r =
207 E : thunks[i]->source_ranges().range_pairs()[0];
208 E : ASSERT_EQ(0, r.first.start());
209 E : ASSERT_EQ(sizeof(TestEntryThunkTransform::Thunk), r.first.size());
210 :
211 E : BlockGraph::Reference ref;
212 E : EXPECT_TRUE(thunks[i]->GetReference(
213 : offsetof(TestEntryThunkTransform::Thunk, func_addr), &ref));
214 :
215 : // Retrieve the referenced block's source ranges to calculate
216 : // the destination start address.
217 E : EXPECT_EQ(1, ref.referenced()->source_ranges().size());
218 : BlockGraph::Block::SourceRanges::RangePair o =
219 E : ref.referenced()->source_ranges().range_pairs()[0];
220 :
221 : // The thunk's destination should be the block's start, plus the
222 : // reference offset.
223 E : EXPECT_EQ(o.second.start() + ref.offset(), r.second.start());
224 E : EXPECT_EQ(sizeof(TestEntryThunkTransform::Thunk), r.second.size());
225 E : }
226 E : }
227 :
228 : // Verifies that there are num_thunks thunks in the image, and that they
229 : // have the expected properties.
230 : void VerifyThunks(size_t expected_thunks,
231 : size_t expected_destinations,
232 E : size_t expected_entrypoints) {
233 E : ConstBlockVector thunks;
234 E : ASSERT_NO_FATAL_FAILURE(FindThunks(&thunks));
235 :
236 E : EXPECT_EQ(expected_thunks, thunks.size());
237 E : EXPECT_EQ(expected_destinations, CountDestinations(thunks));
238 E : EXPECT_EQ(expected_entrypoints, CountEntryPoints(thunks));
239 E : }
240 :
241 : enum ImageType {
242 : DLL_IMAGE,
243 : EXE_IMAGE,
244 : };
245 :
246 E : void SetEmptyDllEntryPoint() {
247 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
248 E : ASSERT_TRUE(nt_headers.Init(0, nt_headers_block_));
249 E : nt_headers->FileHeader.Characteristics |= IMAGE_FILE_DLL;
250 E : }
251 :
252 : // Sets the image entrypoint and sets or clears the DLL flag
253 : // in the NT headers.
254 E : void SetEntryPoint(BlockGraph::Block* entrypoint, ImageType image_type) {
255 : // Set the image entrypoint.
256 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
257 E : ASSERT_TRUE(nt_headers.Init(0, nt_headers_block_));
258 E : ASSERT_TRUE(
259 : nt_headers.SetReference(
260 : BlockGraph::RELATIVE_REF,
261 : nt_headers->OptionalHeader.AddressOfEntryPoint,
262 : entrypoint,
263 : 0, 0));
264 :
265 : // Set or clear the DLL flag.
266 E : if (image_type == DLL_IMAGE)
267 E : nt_headers->FileHeader.Characteristics |= IMAGE_FILE_DLL;
268 E : else
269 E : nt_headers->FileHeader.Characteristics &= ~IMAGE_FILE_DLL;
270 E : }
271 :
272 : // Creates a TLS directory with the given block for entrypoint, and sets or
273 : // clears the DLL flag in the NT headers.
274 E : void SetTLSEntryPoint(BlockGraph::Block* entrypoint, ImageType image_type) {
275 : // Set the image entrypoint.
276 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
277 E : ASSERT_TRUE(nt_headers.Init(0, nt_headers_block_));
278 : IMAGE_DATA_DIRECTORY& data_dir =
279 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
280 E : ASSERT_EQ(0, data_dir.Size);
281 :
282 : // Create the TLS directory block.
283 : BlockGraph::Block* tls_dir_block = bg_.AddBlock(BlockGraph::DATA_BLOCK,
284 : sizeof(IMAGE_TLS_DIRECTORY),
285 E : "TLS Directory");
286 E : ASSERT_TRUE(tls_dir_block != NULL);
287 E : ASSERT_TRUE(tls_dir_block->AllocateData(tls_dir_block->size()));
288 :
289 : // Hook the TLS dir up to the NT headers.
290 E : ASSERT_TRUE(nt_headers.SetReference(BlockGraph::ABSOLUTE_REF,
291 : data_dir.VirtualAddress,
292 : tls_dir_block,
293 : 0, 0));
294 E : data_dir.Size = tls_dir_block->size();
295 :
296 E : TypedBlock<IMAGE_TLS_DIRECTORY> tls_dir;
297 E : ASSERT_TRUE(tls_dir.Init(0, tls_dir_block));
298 :
299 : BlockGraph::Block* tls_callbacks = bg_.AddBlock(BlockGraph::DATA_BLOCK,
300 : 2 * sizeof(AbsoluteAddress),
301 E : "TLS Callbacks");
302 E : ASSERT_TRUE(tls_callbacks != NULL);
303 E : ASSERT_TRUE(tls_callbacks->AllocateData(tls_callbacks->size()) != NULL);
304 E : ASSERT_TRUE(tls_dir.SetReference(BlockGraph::ABSOLUTE_REF,
305 : tls_dir->AddressOfCallBacks,
306 : tls_callbacks,
307 : 0, 0));
308 :
309 E : ASSERT_TRUE(tls_callbacks->SetReference(0,
310 : BlockGraph::Reference(BlockGraph::ABSOLUTE_REF,
311 : sizeof(AbsoluteAddress),
312 : entrypoint,
313 : 0, 0)));
314 :
315 : // Set or clear the DLL flag.
316 E : if (image_type == DLL_IMAGE)
317 E : nt_headers->FileHeader.Characteristics |= IMAGE_FILE_DLL;
318 E : else
319 E : nt_headers->FileHeader.Characteristics &= ~IMAGE_FILE_DLL;
320 E : }
321 :
322 : protected:
323 : size_t num_sections_pre_transform_;
324 :
325 : BlockGraph bg_;
326 : BlockGraph::Block* dos_header_block_;
327 : BlockGraph::Block* nt_headers_block_;
328 :
329 : BlockGraph::Block* foo_;
330 : BlockGraph::Block* bar_;
331 : BlockGraph::Block* array_;
332 : };
333 :
334 : } // namespace
335 :
336 E : TEST_F(EntryThunkTransformTest, InstrumentAll) {
337 E : EntryThunkTransform transform;
338 E : ASSERT_NO_FATAL_FAILURE(SetEmptyDllEntryPoint());
339 :
340 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
341 :
342 : // We should have three thunks - one each for the start of foo() and bar(),
343 : // and one for the middle of foo().
344 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(3, 3, 1));
345 :
346 : // The .thunks section should have been added.
347 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
348 E : }
349 :
350 E : TEST_F(EntryThunkTransformTest, InstrumentModuleEntriesOnlyNone) {
351 E : EntryThunkTransform transform;
352 E : ASSERT_NO_FATAL_FAILURE(SetEmptyDllEntryPoint());
353 E : transform.set_only_instrument_module_entry(true);
354 :
355 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
356 :
357 : // We should have no thunks.
358 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(0, 0, 0));
359 :
360 : // The .thunks section should not have been added, as there are no hooks
361 : // added.
362 E : EXPECT_EQ(num_sections_pre_transform_, bg_.sections().size());
363 E : }
364 :
365 E : TEST_F(EntryThunkTransformTest, InstrumentModuleEntriesOnlyDllMainOnly) {
366 E : EntryThunkTransform transform;
367 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, DLL_IMAGE));
368 E : transform.set_only_instrument_module_entry(true);
369 :
370 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
371 :
372 : // We should have one thunk, for the DLL main entry point to the start of
373 : // foo_.
374 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(1, 1, 1));
375 :
376 : // The .thunks section should have been added.
377 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
378 E : }
379 :
380 E : TEST_F(EntryThunkTransformTest, InstrumentModuleEntriesOnlyDllMainAndTls) {
381 E : EntryThunkTransform transform;
382 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, DLL_IMAGE));
383 E : ASSERT_NO_FATAL_FAILURE(SetTLSEntryPoint(bar_, DLL_IMAGE));
384 E : transform.set_only_instrument_module_entry(true);
385 :
386 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
387 :
388 : // We should have two thunk, for the DLL main entry point and another for the
389 : // TLS. One is to foo_ and one is to bar_.
390 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(2, 2, 1));
391 :
392 : // The .thunks section should have been added.
393 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
394 E : }
395 :
396 E : TEST_F(EntryThunkTransformTest, InstrumentModuleEntriesOnlyExeMainAndTls) {
397 E : EntryThunkTransform transform;
398 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, EXE_IMAGE));
399 E : ASSERT_NO_FATAL_FAILURE(SetTLSEntryPoint(bar_, EXE_IMAGE));
400 E : transform.set_only_instrument_module_entry(true);
401 :
402 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
403 :
404 : // We should have one TLS thunk and an EXE entry thunk.
405 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(2, 2, 2));
406 :
407 : // The .thunks section should have been added.
408 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
409 E : }
410 :
411 E : TEST_F(EntryThunkTransformTest, InstrumentAllDebugFriendly) {
412 E : EntryThunkTransform transform;
413 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, EXE_IMAGE));
414 E : transform.set_src_ranges_for_thunks(true);
415 :
416 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
417 :
418 : // Verify the source ranges on the thunks.
419 E : ConstBlockVector thunks;
420 E : ASSERT_NO_FATAL_FAILURE(FindThunks(&thunks));
421 E : VerifySourceRanges(thunks);
422 E : }
423 :
424 E : TEST_F(EntryThunkTransformTest, InstrumentNoUnsafe) {
425 E : EntryThunkTransform transform;
426 E : ASSERT_NO_FATAL_FAILURE(SetEmptyDllEntryPoint());
427 :
428 : // No unsafe reference instrumentation.
429 E : transform.set_instrument_unsafe_references(false);
430 :
431 : // Tag both foo and bar with unsafe attributes.
432 E : foo_->set_attribute(BlockGraph::HAS_INLINE_ASSEMBLY);
433 E : bar_->set_attribute(BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER);
434 :
435 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
436 :
437 : // We should have two thunks - one each for the start of foo() and bar().
438 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(2, 2, 1));
439 :
440 : // The foo->bar reference should not have been thunked.
441 E : BlockGraph::Reference ref;
442 E : ASSERT_TRUE(foo_->GetReference(5, &ref));
443 E : ASSERT_EQ(bar_, ref.referenced());
444 :
445 : // The .thunks section should have been added.
446 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
447 E : }
448 :
449 E : TEST_F(EntryThunkTransformTest, InstrumentDllEntrypoint) {
450 E : EntryThunkTransform transform;
451 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, DLL_IMAGE));
452 :
453 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
454 :
455 : // We should have three thunks - one each for the start of foo() and bar().
456 : // One of the thunks should use the DllMain entrypoint.
457 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(3, 3, 2));
458 :
459 : // The .thunks section should have been added.
460 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
461 E : }
462 :
463 E : TEST_F(EntryThunkTransformTest, InstrumentExeEntrypoint) {
464 E : EntryThunkTransform transform;
465 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, EXE_IMAGE));
466 :
467 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
468 :
469 : // We should have three thunks - one each for the start of foo() and bar().
470 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(3, 3, 2));
471 :
472 : // The .thunks section should have been added.
473 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
474 E : }
475 :
476 E : TEST_F(EntryThunkTransformTest, InstrumentDllTLSEntrypoint) {
477 E : EntryThunkTransform transform;
478 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, DLL_IMAGE));
479 E : ASSERT_NO_FATAL_FAILURE(SetTLSEntryPoint(bar_, DLL_IMAGE));
480 :
481 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
482 :
483 : // We should have three thunks - one each for the start of foo() and bar().
484 : // One of the thunks should use the DllMain entrypoint.
485 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(3, 3, 2));
486 :
487 : // The .thunks section should have been added.
488 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
489 E : }
490 :
491 E : TEST_F(EntryThunkTransformTest, InstrumentExeTLSEntrypoint) {
492 E : EntryThunkTransform transform;
493 E : ASSERT_NO_FATAL_FAILURE(SetEntryPoint(foo_, EXE_IMAGE));
494 E : ASSERT_NO_FATAL_FAILURE(SetTLSEntryPoint(bar_, EXE_IMAGE));
495 :
496 E : ASSERT_TRUE(ApplyBlockGraphTransform(&transform, &bg_, dos_header_block_));
497 :
498 : // We should have three thunks - one each for the start of foo() and bar().
499 E : ASSERT_NO_FATAL_FAILURE(VerifyThunks(3, 3, 3));
500 :
501 : // The .thunks section should have been added.
502 E : EXPECT_EQ(num_sections_pre_transform_ + 1, bg_.sections().size());
503 E : }
504 :
505 : } // namespace transforms
506 : } // namespace instrument
|