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 "base/auto_reset.h"
18 : #include "base/strings/string_split.h"
19 : #include "syzygy/block_graph/typed_block.h"
20 : #include "syzygy/common/align.h"
21 : #include "syzygy/pe/cvinfo_ext.h"
22 : #include "syzygy/pe/pe_utils.h"
23 :
24 : namespace pe {
25 :
26 : namespace {
27 :
28 : namespace cci = Microsoft_Cci_Pdb;
29 :
30 : using base::AutoReset;
31 : using block_graph::BlockGraph;
32 : using block_graph::ConstTypedBlock;
33 : using core::AbsoluteAddress;
34 : using core::RelativeAddress;
35 :
36 : typedef BlockGraph::Block Block;
37 : typedef BlockGraph::BlockType BlockType;
38 : typedef BlockGraph::Reference Reference;
39 : typedef BlockGraph::ReferenceType ReferenceType;
40 :
41 : typedef std::map<size_t, const char*> ComdatMap;
42 :
43 : const char kHeadersBlockName[] = "<headers>";
44 : const char kSymbolsBlockName[] = "<symbols>";
45 : const char kStringsBlockName[] = "<strings>";
46 : const char kRelocsBlockName[] = "<relocs>";
47 :
48 : const size_t kDebugSubsectionAlignment = 4;
49 :
50 : // Retrieve the relocation type and size for the specified COFF relocation.
51 : //
52 : // @param reloc the relocation.
53 : // @param ref_type where to store the resulting reference type.
54 : // @param ref_size where to store the resulting reference size.
55 : // @returns true on success, or false if the information cannot be determined
56 : // for the specified relocation.
57 : bool GetRelocationTypeAndSize(const IMAGE_RELOCATION& reloc,
58 : ReferenceType* ref_type,
59 E : BlockGraph::Size* ref_size) {
60 E : DCHECK(ref_type != NULL);
61 E : DCHECK(ref_size != NULL);
62 :
63 E : switch (reloc.Type) {
64 : case IMAGE_REL_I386_ABSOLUTE:
65 : // Ignored, as per the specifications.
66 i : return false;
67 : case IMAGE_REL_I386_DIR32:
68 E : *ref_type = BlockGraph::RELOC_ABSOLUTE_REF;
69 E : *ref_size = sizeof(uint32);
70 E : return true;
71 : case IMAGE_REL_I386_DIR32NB:
72 E : *ref_type = BlockGraph::RELOC_RELATIVE_REF;
73 E : *ref_size = sizeof(uint32);
74 E : return true;
75 : case IMAGE_REL_I386_SECTION:
76 E : *ref_type = BlockGraph::RELOC_SECTION_REF;
77 E : *ref_size = sizeof(uint16);
78 E : return true;
79 : case IMAGE_REL_I386_SECREL:
80 E : *ref_type = BlockGraph::RELOC_SECTION_OFFSET_REF;
81 E : *ref_size = sizeof(uint32);
82 E : return true;
83 : case IMAGE_REL_I386_SECREL7:
84 i : *ref_type = BlockGraph::RELOC_SECTION_OFFSET_REF;
85 : // TODO(chrisha): This is actually a 7-bit offset;
86 : // BlockGraph::Reference only represents byte sizes. We pass as a 1-byte
87 : // reference as there are no actual 8-bit references in COFF files.
88 i : *ref_size = 1;
89 i : return true;
90 : case IMAGE_REL_I386_REL32:
91 E : *ref_type = BlockGraph::RELOC_PC_RELATIVE_REF;
92 E : *ref_size = sizeof(uint32);
93 E : return true;
94 : default:
95 : // Ignore other types; they are either explicitly mentioned as unsupported
96 : // in the specifications, or for managed code.
97 i : LOG(WARNING) << "Unexpected COFF relocation type.";
98 i : return false;
99 : }
100 E : }
101 :
102 : // Retrieve the relocation encoded value at the relocated location.
103 : //
104 : // @tparam ValueType the type of data to read.
105 : // @param source the source block.
106 : // @param src_offset the offset of the relocated location.
107 : // @param extra_offset where to place the additional offset read.
108 : // @returns true on success, or false on failure.
109 : template <typename ValueType>
110 : bool ReadRelocationValue(Block* source,
111 : BlockGraph::Offset src_offset,
112 E : BlockGraph::Offset* extra_offset) {
113 E : ConstTypedBlock<ValueType> value;
114 E : if (!value.Init(src_offset, source)) {
115 i : LOG(ERROR) << "Unable to read relocation location value.";
116 i : return false;
117 : }
118 E : *extra_offset = *value;
119 E : return true;
120 E : }
121 :
122 : // Parse a CodeView debug symbol subsection, adding references and attributes as
123 : // needed to @p block.
124 : //
125 : // @param version The CodeView version to use in parsing the symbol stream.
126 : // @param start the offset to the beginning of the contents (excluding type and
127 : // length) of the subsection inside @p block.
128 : // @param size the size of the subsection.
129 : // @param block the debug section block.
130 : // @returns true on success, or false on failure.
131 : bool ParseDebugSymbols(
132 E : cci::CV_SIGNATURE version, size_t start, size_t size, Block* block) {
133 E : DCHECK(version == cci::C11 || version == cci::C13);
134 E : DCHECK_NE(reinterpret_cast<Block*>(NULL), block);
135 E : DCHECK_GT(block->data_size(), start);
136 E : DCHECK_GE(block->data_size(), start + size);
137 :
138 : // We assume that functions do not nest, hence dependent debug symbols should
139 : // all refer to the last function symbol, whose block is stored in
140 : // current_func.
141 E : size_t section_index = block->section();
142 E : Block* current_func = NULL;
143 E : size_t cursor = start;
144 E : while (cursor < size) {
145 E : ConstTypedBlock<cci::SYMTYPE> dsym;
146 E : if (!dsym.Init(cursor, block)) {
147 i : LOG(ERROR) << "Unable to read debug symbol header at offset "
148 : << cursor << " in .debug$S section " << section_index << ".";
149 i : return false;
150 : }
151 E : cursor += sizeof(*dsym);
152 :
153 E : switch (dsym->rectyp) {
154 : case cci::S_GPROC32:
155 : case cci::S_LPROC32:
156 : case cci::S_GPROC32_VS2013:
157 : case cci::S_LPROC32_VS2013: {
158 E : ConstTypedBlock<cci::ProcSym32> proc;
159 E : if (!proc.Init(cursor, block)) {
160 i : LOG(ERROR) << "Unable to read debug procedure (" << dsym->rectyp
161 : << ") symbol at offset " << cursor
162 : << " in .debug$S section " << section_index << ".";
163 i : return false;
164 : }
165 :
166 : // Get the existing relocation reference that points to the correct
167 : // function block.
168 E : Reference reloc_ref;
169 : if (!block->GetReference(cursor + offsetof(cci::ProcSym32, off),
170 E : &reloc_ref)) {
171 i : LOG(ERROR) << "No relocation reference in ProcSym32 "
172 : << "(missing COFF relocation?) at offset "
173 : << cursor + offsetof(cci::ProcSym32, off)
174 : << " in .debug$S section " << section_index << ".";
175 i : return false;
176 : }
177 E : current_func = reloc_ref.referenced();
178 :
179 : // These are function-relative offsets; we can use section offsets as we
180 : // assume function-level linking.
181 : if (!block->SetReference(cursor + offsetof(cci::ProcSym32, dbgStart),
182 : Reference(BlockGraph::SECTION_OFFSET_REF,
183 : sizeof(proc->dbgStart),
184 : current_func,
185 E : proc->dbgStart, proc->dbgStart))) {
186 i : LOG(ERROR) << "Unable to create reference at offset "
187 : << cursor + offsetof(cci::ProcSym32, dbgStart)
188 : << " in .debug$S section " << section_index << ".";
189 i : return false;
190 : }
191 : if (!block->SetReference(cursor + offsetof(cci::ProcSym32, dbgEnd),
192 : Reference(BlockGraph::SECTION_OFFSET_REF,
193 : sizeof(proc->dbgEnd),
194 : current_func,
195 E : proc->dbgEnd, proc->dbgEnd))) {
196 i : LOG(ERROR) << "Unable to create reference at offset "
197 : << cursor + offsetof(cci::ProcSym32, dbgEnd)
198 : << " in .debug$S section " << section_index << ".";
199 i : return false;
200 : }
201 E : break;
202 : }
203 :
204 : case cci::S_FRAMEPROC: {
205 E : ConstTypedBlock<cci::FrameProcSym> frame;
206 E : if (!frame.Init(cursor, block)) {
207 i : LOG(ERROR) << "Unable to read debug frame (" << dsym->rectyp
208 : << ") symbol at offset " << cursor
209 : << " in .debug$S section " << section_index << ".";
210 i : return false;
211 : }
212 :
213 E : DCHECK(current_func != NULL);
214 E : if ((frame->flags & cci::fHasInlAsm) != 0)
215 i : current_func->set_attribute(BlockGraph::HAS_INLINE_ASSEMBLY);
216 E : if ((frame->flags & cci::fHasSEH) != 0)
217 i : current_func->set_attribute(BlockGraph::HAS_EXCEPTION_HANDLING);
218 E : break;
219 : }
220 :
221 : case cci::S_ANNOTATION:
222 : case cci::S_BLOCK32:
223 : case cci::S_BPREL32:
224 : case cci::S_CALLSITEINFO:
225 : case cci::S_CONSTANT:
226 : case cci::S_END:
227 : case cci::S_FRAMECOOKIE:
228 : case cci::S_GDATA32:
229 : case cci::S_GDATA32_ST:
230 : case cci::S_GTHREAD32:
231 : case cci::S_LABEL32:
232 : case cci::S_LDATA32:
233 : case cci::S_LDATA32_ST:
234 : case cci::S_LTHREAD32:
235 : case cci::S_OBJNAME:
236 : case cci::S_REGISTER:
237 : case cci::S_REGREL32:
238 : case cci::S_THUNK32:
239 : case cci::S_UDT:
240 : case cci::S_UDT_ST:
241 E : break;
242 :
243 : case cci::S_COMPILE3:
244 : case cci::S_MSTOOLENV_V3:
245 E : break;
246 :
247 : // CodeView2 symbols that we can safely ignore.
248 : case cci::S_COMPILE_CV2:
249 : case cci::S_COMPILE2_ST:
250 : case cci::S_OBJNAME_CV2:
251 E : break;
252 :
253 : // These are unknown symbol types, but currently seen. From inspection
254 : // they don't appear to contain references that need to be parsed.
255 : // TODO(chrisha): Figure out what these symbols are. Many of them appear
256 : // to have been added only as of VS2013.
257 : case 0x113E:
258 : case 0x1141:
259 : case 0x1142:
260 : case 0x1143:
261 : case 0x1144:
262 : case 0x1145:
263 : case 0x114D:
264 : case 0x114E:
265 : case 0x1153:
266 : case 0x115A:
267 i : break;
268 :
269 : default:
270 i : LOG(ERROR) << "Unsupported debug symbol type 0x"
271 : << std::hex << dsym->rectyp << std::dec
272 : << " at offset "
273 : << cursor - sizeof(*dsym) + offsetof(cci::SYMTYPE, rectyp)
274 : << " in .debug$S section " << section_index << ".";
275 i : return false;
276 : }
277 E : cursor += dsym->reclen - sizeof(*dsym) + sizeof(dsym->reclen);
278 E : }
279 E : return true;
280 E : }
281 :
282 : // Parse a CodeView debug line number subsection, adding references as needed
283 : // to @p block.
284 : //
285 : // @param start the offset to the beginning of the contents (excluding type and
286 : // length) of the subsection inside @p block.
287 : // @param size the size of the subsection.
288 : // @param block the debug section block.
289 : // @returns true on success, or false on failure.
290 E : bool ParseDebugLines(size_t start, size_t size, Block* block) {
291 E : DCHECK(block != NULL);
292 :
293 E : size_t section_index = block->section();
294 E : size_t cursor = start;
295 :
296 : // Parse the section info.
297 E : ConstTypedBlock<cci::CV_LineSection> line_section;
298 E : if (!line_section.Init(cursor, block)) {
299 i : LOG(ERROR) << "Unable to read debug line section header at offset "
300 : << cursor << " in .debug$S section " << section_index << ".";
301 i : return false;
302 : }
303 :
304 : // Get the existing relocation reference that points to the function block
305 : // these lines are for.
306 E : Reference reloc_ref;
307 : if (!block->GetReference(cursor + offsetof(cci::CV_LineSection, off),
308 E : &reloc_ref)) {
309 i : LOG(ERROR) << "No relocation reference in CV_LineSection "
310 : << "(missing COFF relocation?) at offset "
311 : << cursor + offsetof(cci::ProcSym32, off)
312 : << " in .debug$S section " << section_index << ".";
313 i : return false;
314 : }
315 E : Block* func = reloc_ref.referenced();
316 E : cursor += sizeof(*line_section);
317 :
318 : // Parse the source info.
319 E : ConstTypedBlock<cci::CV_SourceFile> line_file;
320 E : if (!line_file.Init(cursor, block)) {
321 i : LOG(ERROR) << "Unable to read debug line file header at offset "
322 : << cursor << " in .debug$S section " << section_index << ".";
323 i : return false;
324 : }
325 E : DCHECK_GE(size, line_file->linsiz);
326 E : cursor += sizeof(*line_file);
327 :
328 : // The rest of the subsection is an array of CV_Line structures.
329 E : ConstTypedBlock<cci::CV_Line> lines;
330 E : if (!lines.Init(cursor, block)) {
331 i : LOG(ERROR) << "Unable to read debug line file header at offset "
332 : << cursor << " in .debug$S section " << section_index << ".";
333 i : return false;
334 : }
335 E : DCHECK_GE(lines.ElementCount(), line_file->count);
336 :
337 E : for (size_t i = 0; i < line_file->count; ++i) {
338 : // This should be a function-relative offset; we can use a section offset as
339 : // we assume function-level linking.
340 : Reference ref(BlockGraph::SECTION_OFFSET_REF, sizeof(lines[i].offset),
341 E : func, lines[i].offset, lines[i].offset);
342 E : if (!block->SetReference(cursor + offsetof(cci::CV_Line, offset), ref)) {
343 i : LOG(ERROR) << "Unable to create reference at offset "
344 : << cursor + offsetof(cci::CV_Line, offset)
345 : << " in .debug$S section " << section_index << ".";
346 i : return false;
347 : }
348 E : cursor += sizeof(lines[i]);
349 E : }
350 :
351 E : return true;
352 E : }
353 :
354 : // Parse all CodeView4 debug subsections in the specified debug section block.
355 : //
356 : // @param block the debug section block.
357 : // @returns true on success, or false on failure.
358 E : bool ParseDebugSubsections4(Block* block) {
359 E : DCHECK(block != NULL);
360 :
361 E : size_t section_index = block->section();
362 E : size_t cursor = sizeof(uint32);
363 E : while (cursor < block->data_size()) {
364 E : ConstTypedBlock<uint32> type;
365 E : if (!type.Init(cursor, block)) {
366 i : LOG(ERROR) << "Unable to read debug subsection type at offset "
367 : << cursor << " in .debug$S section " << section_index << ".";
368 i : return false;
369 : }
370 E : cursor += sizeof(*type);
371 :
372 E : ConstTypedBlock<uint32> size;
373 E : if (!size.Init(cursor, block)) {
374 i : LOG(ERROR) << "Unable to read debug subsection size at offset "
375 : << cursor << " in .debug$S section " << section_index << ".";
376 i : return false;
377 : }
378 E : cursor += sizeof(*size);
379 :
380 : // A sentinel bit marks some sections ignored. We parse them even if they
381 : // are ignored.
382 E : switch (*type & ~cci::DEBUG_S_IGNORE) {
383 : case cci::DEBUG_S_SYMBOLS:
384 E : if (!ParseDebugSymbols(cci::C13, cursor, *size, block))
385 i : return false;
386 E : break;
387 : case cci::DEBUG_S_LINES:
388 E : if (!ParseDebugLines(cursor, *size, block))
389 i : return false;
390 E : break;
391 : case cci::DEBUG_S_STRINGTABLE:
392 : case cci::DEBUG_S_FILECHKSMS:
393 : case cci::DEBUG_S_FRAMEDATA:
394 E : break;
395 :
396 : // This is a new debug symbol type as of VS2013.
397 : // TODO(chrisha): Figure out the contents of this subsection type.
398 : case 0xF6:
399 i : break;
400 :
401 : default:
402 i : LOG(ERROR) << "Unsupported debug subsection type " << std::hex << *type
403 : << std::dec << " at offset " << cursor
404 : << " in .debug$S section " << section_index << ".";
405 i : return false;
406 : }
407 E : cursor += common::AlignUp(*size, sizeof(kDebugSubsectionAlignment));
408 E : }
409 E : return true;
410 E : }
411 :
412 : // Parse all CodeView2 debug subsections in the specified debug section block.
413 : // This is simply a raw stream of code view symbols, like a CodeView4
414 : // DEBUG_S_SYMBOLS subsection.
415 : //
416 : // @param block the debug section block.
417 : // @returns true on success, or false on failure.
418 E : bool ParseDebugSubsections2(Block* block) {
419 E : DCHECK(block != NULL);
420 :
421 E : size_t section_index = block->section();
422 E : size_t cursor = sizeof(uint32);
423 E : if (!ParseDebugSymbols(cci::C11, cursor, block->size() - cursor, block))
424 i : return false;
425 :
426 E : return true;
427 E : }
428 :
429 : } // namespace
430 :
431 : const char CoffDecomposer::kSectionComdatSep[] = "; COMDAT=";
432 :
433 : CoffDecomposer::CoffDecomposer(const CoffFile& image_file)
434 : : image_file_(image_file),
435 : image_layout_(NULL),
436 E : image_(NULL) {
437 E : }
438 :
439 E : bool CoffDecomposer::Decompose(ImageLayout* image_layout) {
440 E : DCHECK_NE(reinterpret_cast<ImageLayout*>(NULL), image_layout);
441 :
442 : // Internal temporaries.
443 E : DCHECK_EQ(reinterpret_cast<ImageLayout*>(NULL), image_layout_);
444 E : DCHECK_EQ(reinterpret_cast<BlockGraph::AddressSpace*>(NULL),image_);
445 E : AutoReset<ImageLayout*> auto_reset_image_layout(&image_layout_, image_layout);
446 : AutoReset<BlockGraph::AddressSpace*> auto_reset_image(&image_,
447 E : &image_layout->blocks);
448 :
449 : // Set the image format.
450 E : image_layout->blocks.graph()->set_image_format(BlockGraph::COFF_IMAGE);
451 :
452 : // Copy the image headers to the layout.
453 : CopySectionHeadersToImageLayout(
454 : image_file_.file_header()->NumberOfSections,
455 : image_file_.section_headers(),
456 E : &image_layout_->sections);
457 :
458 E : if (!CopySectionInfoToBlockGraph(image_file_, image_->graph()))
459 i : return false;
460 :
461 E : if (!CreateBlocksFromSections())
462 i : return false;
463 :
464 E : if (!CreateBlocksAndReferencesFromNonSections())
465 i : return false;
466 :
467 E : if (!CreateReferencesFromRelocations())
468 i : return false;
469 :
470 E : if (!CreateReferencesFromDebugInfo())
471 i : return false;
472 :
473 E : if (!CreateLabelsFromSymbols())
474 i : return false;
475 :
476 E : return true;
477 E : }
478 :
479 E : bool CoffDecomposer::CreateBlocksAndReferencesFromNonSections() {
480 E : DCHECK(image_ != NULL);
481 :
482 E : if (!CreateBlocksAndReferencesFromSymbolAndStringTables())
483 i : return false;
484 :
485 E : if (!CreateBlocksFromRelocationTables())
486 i : return false;
487 :
488 E : if (!CreateBlocksAndReferencesFromHeaders())
489 i : return false;
490 :
491 E : return true;
492 E : }
493 :
494 E : bool CoffDecomposer::CreateBlocksAndReferencesFromHeaders() {
495 E : const IMAGE_FILE_HEADER* file_header = image_file_.file_header();
496 E : DCHECK(file_header != NULL);
497 :
498 : // Create a block for COFF and section headers.
499 : size_t headers_size =
500 : sizeof(*file_header) +
501 E : file_header->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
502 : Block* block = CreateBlock(BlockGraph::DATA_BLOCK, FileOffsetAddress(0),
503 E : headers_size, kHeadersBlockName);
504 E : if (block == NULL) {
505 i : LOG(ERROR) << "Unable to create block for headers.";
506 i : return false;
507 : }
508 E : block->set_attribute(BlockGraph::COFF_HEADERS);
509 :
510 : // Create a reference for the symbol table pointer.
511 : FileOffsetAddress symbols_ptr_addr(
512 E : offsetof(IMAGE_FILE_HEADER, PointerToSymbolTable));
513 : if (!CreateFileOffsetReference(
514 : symbols_ptr_addr,
515 : BlockGraph::FILE_OFFSET_REF,
516 : sizeof(file_header->PointerToSymbolTable),
517 E : FileOffsetAddress(file_header->PointerToSymbolTable))) {
518 i : return false;
519 : }
520 :
521 : // Create a reference for the section and relocation pointers in each section
522 : // header.
523 : FileOffsetAddress section_headers_start(
524 E : sizeof(*file_header) + file_header->SizeOfOptionalHeader);
525 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
526 E : for (size_t i = 0; i < num_sections; ++i) {
527 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
528 E : DCHECK(header != NULL);
529 E : FileOffsetAddress start(section_headers_start + i * sizeof(*header));
530 :
531 : FileOffsetAddress data_ptr_addr(
532 E : start + offsetof(IMAGE_SECTION_HEADER, PointerToRawData));
533 : if (!CreateFileOffsetReference(
534 : data_ptr_addr,
535 : BlockGraph::FILE_OFFSET_REF,
536 : sizeof(header->PointerToRawData),
537 E : FileOffsetAddress(header->PointerToRawData))) {
538 i : return false;
539 : }
540 :
541 : FileOffsetAddress relocs_ptr_addr(
542 E : start + offsetof(IMAGE_SECTION_HEADER, PointerToRelocations));
543 : if (!CreateFileOffsetReference(
544 : relocs_ptr_addr,
545 : BlockGraph::FILE_OFFSET_REF,
546 : sizeof(header->PointerToRelocations),
547 E : FileOffsetAddress(header->PointerToRelocations))) {
548 i : return false;
549 : }
550 E : }
551 :
552 E : return true;
553 E : }
554 :
555 E : bool CoffDecomposer::CreateBlocksAndReferencesFromSymbolAndStringTables() {
556 : // Create a block for the symbol table.
557 E : FileOffsetAddress symbols_start(image_file_.symbols_address());
558 E : size_t symbols_size = image_file_.symbols_size();
559 : Block* block = CreateBlock(BlockGraph::DATA_BLOCK,
560 E : symbols_start, symbols_size, kSymbolsBlockName);
561 E : if (block == NULL) {
562 i : LOG(ERROR) << "Unable to create block for symbols.";
563 i : return false;
564 : }
565 E : block->set_attribute(BlockGraph::COFF_SYMBOL_TABLE);
566 :
567 : // Create a block for the strings table that follows.
568 E : FileOffsetAddress strings_start(image_file_.strings_address());
569 E : size_t strings_size = image_file_.strings_size();
570 : block = CreateBlock(BlockGraph::DATA_BLOCK,
571 E : strings_start, strings_size, kStringsBlockName);
572 E : if (block == NULL) {
573 i : LOG(ERROR) << "Unable to create block for strings.";
574 i : return false;
575 : }
576 E : block->set_attribute(BlockGraph::COFF_STRING_TABLE);
577 :
578 : // Add references.
579 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
580 E : const IMAGE_SYMBOL* symbol = NULL;
581 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
582 E : symbol = image_file_.symbol(i);
583 :
584 : // Ignore external symbols (no references to blocks) and other kinds of
585 : // non-reference symbols.
586 E : if (symbol->SectionNumber <= 0)
587 E : continue;
588 :
589 E : FileOffsetAddress start(symbols_start + i * sizeof(*symbol));
590 :
591 : // Symbols with section storage class simply provide the characteristics
592 : // of the section in the symbol value. Other symbols store an actual offset
593 : // into a section in the value field.
594 E : if (symbol->StorageClass != IMAGE_SYM_CLASS_SECTION) {
595 E : FileOffsetAddress value_addr(start + offsetof(IMAGE_SYMBOL, Value));
596 : if (!CreateSymbolOffsetReference(
597 : value_addr,
598 : BlockGraph::SECTION_OFFSET_REF,
599 : sizeof(symbol->Value),
600 : symbol,
601 E : symbol->Value)) {
602 i : return false;
603 : }
604 : }
605 :
606 : FileOffsetAddress section_addr(
607 E : start + offsetof(IMAGE_SYMBOL, SectionNumber));
608 : if (!CreateSymbolOffsetReference(
609 : section_addr,
610 : BlockGraph::SECTION_REF,
611 : sizeof(symbol->SectionNumber),
612 : symbol,
613 E : 0)) {
614 i : return false;
615 : }
616 :
617 : // Section definitions for associative COMDAT sections require an additional
618 : // section reference within the auxiliary symbol.
619 : if (symbol->StorageClass == IMAGE_SYM_CLASS_STATIC &&
620 : symbol->Type >> 4 != IMAGE_SYM_DTYPE_FUNCTION &&
621 E : symbol->NumberOfAuxSymbols == 1) {
622 : const IMAGE_AUX_SYMBOL* aux =
623 E : reinterpret_cast<const IMAGE_AUX_SYMBOL*>(image_file_.symbol(i + 1));
624 E : DCHECK(aux != NULL);
625 E : if (aux->Section.Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
626 : FileOffsetAddress number_addr(
627 : start + sizeof(IMAGE_SYMBOL) +
628 E : offsetof(IMAGE_AUX_SYMBOL, Section.Number));
629 : if (!CreateSectionOffsetReference(
630 : number_addr,
631 : BlockGraph::SECTION_REF,
632 : sizeof(short),
633 : aux->Section.Number - 1,
634 E : 0)) {
635 i : return false;
636 : }
637 : }
638 : }
639 E : }
640 :
641 E : return true;
642 E : }
643 :
644 E : bool CoffDecomposer::CreateBlocksFromRelocationTables() {
645 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
646 E : for (size_t i = 0; i < num_sections; ++i) {
647 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
648 E : DCHECK(header != NULL);
649 E : if (header->NumberOfRelocations == 0)
650 E : continue;
651 :
652 E : FileOffsetAddress relocs_start(header->PointerToRelocations);
653 E : size_t relocs_size(header->NumberOfRelocations * sizeof(IMAGE_RELOCATION));
654 :
655 : // Create a block for this relocation table.
656 : Block* block =
657 : CreateBlock(BlockGraph::DATA_BLOCK,
658 E : relocs_start, relocs_size, kRelocsBlockName);
659 E : if (block == NULL)
660 i : return false;
661 E : block->set_attribute(BlockGraph::COFF_RELOC_DATA);
662 E : }
663 E : return true;
664 E : }
665 :
666 E : bool CoffDecomposer::CreateBlocksFromSections() {
667 E : DCHECK(image_ != NULL);
668 :
669 : // Build COMDAT symbol map, which associates each COMDAT section
670 : // with the COMDAT (secondary) symbol. When compiling with
671 : // function-level linking (/Gy for MSVC), all data and code lives in
672 : // COMDAT sections. Each COMDAT section is associated with at least
673 : // one symbol in the symbol table (the primary symbol), but usually
674 : // two or more.
675 : //
676 : // The primary symbol must always be the section symbol, which
677 : // indicates which final executable section the COMDAT section will
678 : // need to be merged into (e.g., .text or .data).
679 : //
680 : // The secondary symbol, when it exists, is the first symbol bound
681 : // to the COMDAT section that comes after the primary (usually but
682 : // not necessarily right after). With function-level linking, the
683 : // secondary symbol is always the name of the function or variable
684 : // defined in the section.
685 : //
686 : // The COFF decomposer assumes functions live in their own sections,
687 : // which is guaranteed by the MSVC compiler documentation for /Gy,
688 : // but is more forgiving when it comes to variables, which may be
689 : // grouped together in one or multiple data sections.
690 E : ComdatMap comdat_map;
691 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
692 E : const IMAGE_SYMBOL* symbol = NULL;
693 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
694 E : symbol = image_file_.symbol(i);
695 E : DCHECK(symbol != NULL);
696 E : if (symbol->SectionNumber <= 0)
697 E : continue;
698 E : size_t section_index = symbol->SectionNumber - 1;
699 :
700 : // Skip non-COMDAT sections.
701 : const IMAGE_SECTION_HEADER* header =
702 E : image_file_.section_header(section_index);
703 E : DCHECK(header != NULL);
704 E : if ((header->Characteristics & IMAGE_SCN_LNK_COMDAT) == 0)
705 E : continue;
706 :
707 : // Skip primary section symbols.
708 E : ComdatMap::iterator it = comdat_map.find(section_index);
709 E : if (it == comdat_map.end()) {
710 : comdat_map.insert(std::make_pair(section_index,
711 E : static_cast<const char*>(0)));
712 E : } else {
713 : // Skip symbols after the second one.
714 E : if (it->second != NULL)
715 E : continue;
716 :
717 : // This should be the second symbol (assuming the first one is
718 : // the section symbol, as mandated by the specifications), that
719 : // is, the COMDAT symbol.
720 E : it->second = image_file_.GetSymbolName(i);
721 : }
722 E : }
723 :
724 : // Build a block for each data or code section.
725 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
726 E : for (size_t i = 0; i < num_sections; ++i) {
727 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
728 E : DCHECK(header != NULL);
729 : BlockType block_type = GetSectionType(*header) == kSectionCode ?
730 E : BlockGraph::CODE_BLOCK : BlockGraph::DATA_BLOCK;
731 :
732 : // Retrieve or make up a suitable name for the block.
733 E : std::string name(image_file_.GetSectionName(*header));
734 E : ComdatMap::iterator it = comdat_map.find(i);
735 E : if (it != comdat_map.end()) {
736 E : name.append(kSectionComdatSep);
737 E : if (it->second != NULL)
738 E : name.append(it->second);
739 : }
740 :
741 : // Compute the address of the block; when using function-level linking,
742 : // each function begins at offset zero. Unmapped sections (BSS) get an
743 : // unmapped block with an invalid address.
744 E : FileOffsetAddress addr(FileOffsetAddress::kInvalidAddress);
745 E : if (image_file_.IsSectionMapped(i)) {
746 E : CHECK(image_file_.SectionOffsetToFileOffset(i, 0, &addr));
747 : }
748 :
749 : // Put everything together into a block.
750 : Block* block = CreateBlock(block_type,
751 E : addr, header->SizeOfRawData, name.c_str());
752 E : if (block == NULL) {
753 i : LOG(ERROR) << "Unable to create block for section " << i << " \""
754 : << name << "\".";
755 i : return false;
756 : }
757 :
758 : // Assuming block graph section IDs match those of the image file.
759 E : block->set_section(i);
760 : block->set_attribute(image_file_.IsSectionMapped(i) ?
761 E : BlockGraph::SECTION_CONTRIB : BlockGraph::COFF_BSS);
762 :
763 : // Add to section-block map so we can find it later.
764 E : section_block_map_.insert(std::make_pair(i, block));
765 E : }
766 :
767 E : return true;
768 E : }
769 :
770 E : bool CoffDecomposer::CreateReferencesFromDebugInfo() {
771 E : DCHECK(image_ != NULL);
772 :
773 : // Read debug data directly from the block graph, since debug section
774 : // blocks have already been inserted.
775 E : BlockGraph::BlockMap& blocks = image_->graph()->blocks_mutable();
776 E : BlockGraph::BlockMap::iterator it = blocks.begin();
777 E : for (; it != blocks.end(); ++it) {
778 E : size_t section_index = it->second.section();
779 : BlockGraph::Section* section =
780 E : image_->graph()->GetSectionById(section_index);
781 E : if (section == NULL || section->name() != ".debug$S")
782 E : continue;
783 :
784 E : Block* block = &it->second;
785 E : ConstTypedBlock<uint32> magic;
786 E : if (!magic.Init(0, block)) {
787 i : LOG(ERROR) << "Unable to read magic number from .debug$S section "
788 : << section_index << ".";
789 i : return false;
790 : }
791 :
792 : // Parse subsections.
793 E : switch (*magic) {
794 : case cci::C11: {
795 E : if (!ParseDebugSubsections2(block))
796 i : return false;
797 E : break;
798 : }
799 :
800 : case cci::C13: {
801 E : if (!ParseDebugSubsections4(block))
802 i : return false;
803 E : break;
804 : }
805 :
806 : default: {
807 i : LOG(ERROR) << "Unsupported CV version " << *magic
808 : << " in .debug$S section " << section_index << ".";
809 i : return false;
810 : }
811 : }
812 E : }
813 E : return true;
814 E : }
815 :
816 E : bool CoffDecomposer::CreateReferencesFromRelocations() {
817 E : DCHECK(image_ != NULL);
818 :
819 E : CoffFile::RelocMap reloc_map;
820 E : image_file_.DecodeRelocs(&reloc_map);
821 :
822 E : CoffFile::RelocMap::iterator it = reloc_map.begin();
823 E : for (; it != reloc_map.end(); ++it) {
824 E : DCHECK(it->second != NULL);
825 : const IMAGE_SYMBOL* symbol =
826 E : image_file_.symbol(it->second->SymbolTableIndex);
827 E : DCHECK(symbol != NULL);
828 :
829 : // Compute reference attributes.
830 E : ReferenceType ref_type = BlockGraph::REFERENCE_TYPE_MAX;
831 E : BlockGraph::Size ref_size = 0;
832 E : if (!GetRelocationTypeAndSize(*it->second, &ref_type, &ref_size))
833 i : continue;
834 E : DCHECK_LT(ref_type, BlockGraph::REFERENCE_TYPE_MAX);
835 E : DCHECK_GT(ref_size, 0u);
836 :
837 : // Add reference.
838 E : size_t offset = symbol->SectionNumber == 0 ? 0 : symbol->Value;
839 : if (!CreateSymbolOffsetReference(it->first, ref_type, ref_size,
840 E : symbol, offset)) {
841 i : return false;
842 : }
843 E : }
844 :
845 E : return true;
846 E : }
847 :
848 E : bool CoffDecomposer::CreateLabelsFromSymbols() {
849 E : DCHECK(image_ != NULL);
850 :
851 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
852 : const IMAGE_SYMBOL* symbol;
853 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
854 E : symbol = image_file_.symbol(i);
855 :
856 : // Data labels should reference a valid section, have storage
857 : // class STATIC, a non-function type (contrary to static
858 : // functions), and no auxiliary record (contrary to section
859 : // definitions). Skip the rest.
860 : //
861 : // MSVC records section descriptions in the symbol table as STATIC
862 : // data symbols; hence a section symbol and the first data symbol
863 : // at offset zero will have the same storage class and offset;
864 : // data symbols, however, occupy a single entry in the table,
865 : // whereas section symbols take two records (hence one auxiliary
866 : // record with class-specific data in addition of the main
867 : // record).
868 : if (!(symbol->SectionNumber > 0 &&
869 : symbol->StorageClass == IMAGE_SYM_CLASS_STATIC &&
870 : symbol->Type >> 4 != IMAGE_SYM_DTYPE_FUNCTION &&
871 E : symbol->NumberOfAuxSymbols == 0)) {
872 E : continue;
873 : }
874 E : size_t section_index = symbol->SectionNumber - 1;
875 :
876 : // Skip labels in non-code sections.
877 : const IMAGE_SECTION_HEADER* header =
878 E : image_file_.section_header(section_index);
879 E : DCHECK(header != NULL);
880 E : if (GetSectionType(*header) != kSectionCode)
881 E : continue;
882 :
883 : // Get block and offset.
884 E : SectionBlockMap::iterator it = section_block_map_.find(section_index);
885 E : DCHECK(it != section_block_map_.end());
886 E : Block* block = it->second;
887 E : DCHECK(block != NULL);
888 E : BlockGraph::Offset offset = symbol->Value;
889 :
890 : // Tables only appear in code blocks; ignore others.
891 E : if (block->type() != BlockGraph::CODE_BLOCK)
892 i : continue;
893 :
894 : // Compute label attributes. Jump tables are always an array of
895 : // pointers, thus they coincide exactly with a reference. Case
896 : // tables are simple arrays of integer values, thus do not
897 : // coincide with a reference.
898 E : BlockGraph::LabelAttributes attrs = BlockGraph::DATA_LABEL;
899 E : if (block->references().find(offset) != block->references().end()) {
900 E : attrs |= BlockGraph::JUMP_TABLE_LABEL;
901 E : } else {
902 E : attrs |= BlockGraph::CASE_TABLE_LABEL;
903 : }
904 :
905 : // Add label.
906 E : const char* name = image_file_.GetSymbolName(i);
907 E : if (!AddLabelToBlock(offset, name, attrs, block))
908 i : return false;
909 E : }
910 E : return true;
911 E : }
912 :
913 : Block* CoffDecomposer::CreateBlock(BlockType type,
914 : FileOffsetAddress addr,
915 : BlockGraph::Size size,
916 E : const base::StringPiece& name) {
917 E : DCHECK(image_ != NULL);
918 :
919 E : if (addr == FileOffsetAddress::kInvalidAddress) {
920 : // Unmapped block.
921 E : Block* block = image_->graph()->AddBlock(type, size, name);
922 E : if (block == NULL) {
923 i : LOG(ERROR) << "Unable to add unmapped block \"" << name.as_string()
924 : << "\" with size " << size << ".";
925 i : return NULL;
926 : }
927 E : return block;
928 : }
929 :
930 : // Otherwise, we have a normal mapped block.
931 E : BlockGraphAddress block_addr(FileOffsetToBlockGraphAddress(addr));
932 E : Block* block = image_->AddBlock(type, block_addr, size, name);
933 E : if (block == NULL) {
934 i : LOG(ERROR) << "Unable to add block \"" << name.as_string() << "\" at "
935 : << block_addr << " with size " << size << ".";
936 i : return NULL;
937 : }
938 :
939 : // Mark the source range from whence this block originates.
940 E : if (size > 0) {
941 : bool pushed = block->source_ranges().Push(
942 : Block::DataRange(0, size),
943 E : Block::SourceRange(block_addr, size));
944 E : DCHECK(pushed);
945 : }
946 :
947 E : const uint8* data = image_file_.GetImageData(addr, size);
948 E : if (data != NULL)
949 E : block->SetData(data, size);
950 :
951 E : return block;
952 E : }
953 :
954 : bool CoffDecomposer::CreateReference(FileOffsetAddress src_addr,
955 : ReferenceType ref_type,
956 : BlockGraph::Size ref_size,
957 : Block* target,
958 E : BlockGraph::Offset offset) {
959 E : DCHECK(image_ != NULL);
960 :
961 : // Get source block and offset.
962 E : Block* source = NULL;
963 E : BlockGraph::Offset src_offset = -1;
964 E : if (!FileOffsetToBlockOffset(src_addr, &source, &src_offset))
965 i : return false;
966 E : DCHECK(source != NULL);
967 E : DCHECK_GE(src_offset, 0);
968 :
969 : // Read additional offset for relocations.
970 E : BlockGraph::Offset extra_offset = 0;
971 E : if ((ref_type & BlockGraph::RELOC_REF_BIT) != 0) {
972 E : switch (ref_size) {
973 : case sizeof(uint32):
974 E : if (!ReadRelocationValue<uint32>(source, src_offset, &extra_offset))
975 i : return false;
976 E : break;
977 : case sizeof(uint16):
978 E : if (!ReadRelocationValue<uint16>(source, src_offset, &extra_offset))
979 i : return false;
980 E : break;
981 : case sizeof(uint8):
982 : // TODO(chrisha): This is really a special 7-bit relocation; we do
983 : // not touch these, for now.
984 i : break;
985 : default:
986 i : LOG(ERROR) << "Unsupported relocation value size (" << ref_size << ").";
987 i : return false;
988 : }
989 : }
990 :
991 : // Find an existing reference, or insert a new one.
992 E : Reference ref(ref_type, ref_size, target, offset + extra_offset, offset);
993 : Block::ReferenceMap::const_iterator ref_it =
994 E : source->references().find(src_offset);
995 E : if (ref_it == source->references().end()) {
996 : // New reference.
997 E : CHECK(source->SetReference(src_offset, ref));
998 E : } else {
999 : // Collisions are only allowed if the references are identical.
1000 i : if (!(ref == ref_it->second)) {
1001 i : LOG(ERROR) << "Block \"" << source->name() << "\" has a conflicting "
1002 : << "reference at offset " << src_offset << ".";
1003 i : return false;
1004 : }
1005 : }
1006 :
1007 E : return true;
1008 E : }
1009 :
1010 : bool CoffDecomposer::CreateFileOffsetReference(FileOffsetAddress src_addr,
1011 : ReferenceType ref_type,
1012 : BlockGraph::Size ref_size,
1013 E : FileOffsetAddress dst_addr) {
1014 E : DCHECK(image_ != NULL);
1015 :
1016 : // Get target section and offset.
1017 E : Block* target = NULL;
1018 E : BlockGraph::Offset offset = -1;
1019 E : if (!FileOffsetToBlockOffset(dst_addr, &target, &offset))
1020 i : return false;
1021 E : DCHECK(target != NULL);
1022 E : DCHECK_GE(offset, 0);
1023 :
1024 : // Add reference.
1025 E : if (!CreateReference(src_addr, ref_type, ref_size, target, offset))
1026 i : return false;
1027 :
1028 E : return true;
1029 E : }
1030 :
1031 : bool CoffDecomposer::CreateSectionOffsetReference(FileOffsetAddress src_addr,
1032 : ReferenceType ref_type,
1033 : BlockGraph::Size ref_size,
1034 : size_t section_index,
1035 E : size_t section_offset) {
1036 E : DCHECK(image_ != NULL);
1037 :
1038 : // Get target section and offset.
1039 E : Block* target = NULL;
1040 E : BlockGraph::Offset offset = -1;
1041 : if (!SectionOffsetToBlockOffset(section_index, section_offset,
1042 E : &target, &offset)) {
1043 i : return false;
1044 : }
1045 E : DCHECK(target != NULL);
1046 E : DCHECK_GE(offset, 0);
1047 :
1048 : // Add reference.
1049 E : if (!CreateReference(src_addr, ref_type, ref_size, target, offset))
1050 i : return false;
1051 :
1052 E : return true;
1053 E : }
1054 :
1055 : bool CoffDecomposer::CreateSymbolOffsetReference(FileOffsetAddress src_addr,
1056 : ReferenceType ref_type,
1057 : BlockGraph::Size ref_size,
1058 : const IMAGE_SYMBOL* symbol,
1059 E : size_t offset) {
1060 E : DCHECK(image_ != NULL);
1061 E : DCHECK(symbol != NULL);
1062 :
1063 E : if (symbol->SectionNumber < 0) {
1064 i : LOG(ERROR) << "Symbol cannot be converted to a reference.";
1065 i : return false;
1066 : }
1067 :
1068 E : if (symbol->SectionNumber != 0) {
1069 : // Section symbol.
1070 : return CreateSectionOffsetReference(src_addr, ref_type, ref_size,
1071 E : symbol->SectionNumber - 1, offset);
1072 i : } else {
1073 : // External symbol. As a convention, we use a reference to the symbol
1074 : // table, since there is no corresponding block. The offset is ignored
1075 : // (will be inferred from the symbol value and reference type).
1076 E : size_t symbol_index = symbol - image_file_.symbols();
1077 : return CreateFileOffsetReference(
1078 : src_addr, ref_type, ref_size,
1079 E : image_file_.symbols_address() + symbol_index * sizeof(*symbol));
1080 : }
1081 E : }
1082 :
1083 : bool CoffDecomposer::FileOffsetToBlockOffset(FileOffsetAddress addr,
1084 : Block** block,
1085 E : BlockGraph::Offset* offset) {
1086 E : DCHECK(image_ != NULL);
1087 E : DCHECK(block != NULL);
1088 E : DCHECK(offset != NULL);
1089 :
1090 : // Get block and offset.
1091 E : BlockGraphAddress actual_addr(FileOffsetToBlockGraphAddress(addr));
1092 E : Block* containing_block = image_->GetBlockByAddress(actual_addr);
1093 E : if (containing_block == NULL) {
1094 i : LOG(ERROR) << "File offset " << addr << " does not lie within a block.";
1095 i : return false;
1096 : }
1097 E : BlockGraphAddress block_addr;
1098 E : CHECK(image_->GetAddressOf(containing_block, &block_addr));
1099 :
1100 E : *block = containing_block;
1101 E : *offset = actual_addr - block_addr;
1102 E : return true;
1103 E : }
1104 :
1105 : bool CoffDecomposer::SectionOffsetToBlockOffset(size_t section_index,
1106 : size_t section_offset,
1107 : Block** block,
1108 E : BlockGraph::Offset* offset) {
1109 E : DCHECK(image_ != NULL);
1110 E : DCHECK_NE(BlockGraph::kInvalidSectionId, section_index);
1111 E : DCHECK_LT(section_index, image_file_.file_header()->NumberOfSections);
1112 : DCHECK_LE(section_offset,
1113 E : image_file_.section_header(section_index)->SizeOfRawData);
1114 E : DCHECK(block != NULL);
1115 E : DCHECK(offset != NULL);
1116 :
1117 : // Get block and offset.
1118 E : SectionBlockMap::iterator it = section_block_map_.find(section_index);
1119 E : if (it == section_block_map_.end()) {
1120 i : LOG(ERROR) << "Section " << section_index << " is not mapped to a block.";
1121 i : return false;
1122 : }
1123 E : DCHECK(it->second != NULL);
1124 E : DCHECK_LE(section_offset, it->second->size());
1125 :
1126 E : *block = it->second;
1127 E : *offset = section_offset;
1128 E : return true;
1129 E : }
1130 :
1131 : CoffDecomposer::BlockGraphAddress CoffDecomposer::FileOffsetToBlockGraphAddress(
1132 E : FileOffsetAddress addr) {
1133 E : return BlockGraphAddress(addr.value());
1134 E : }
1135 :
1136 : } // namespace pe
|