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 cursor = sizeof(uint32);
422 E : if (!ParseDebugSymbols(cci::C11, cursor, block->size() - cursor, block))
423 i : return false;
424 :
425 E : return true;
426 E : }
427 :
428 : } // namespace
429 :
430 : const char CoffDecomposer::kSectionComdatSep[] = "; COMDAT=";
431 :
432 : CoffDecomposer::CoffDecomposer(const CoffFile& image_file)
433 : : image_file_(image_file),
434 : image_layout_(NULL),
435 E : image_(NULL) {
436 E : }
437 :
438 E : bool CoffDecomposer::Decompose(ImageLayout* image_layout) {
439 E : DCHECK_NE(reinterpret_cast<ImageLayout*>(NULL), image_layout);
440 :
441 : // Internal temporaries.
442 E : DCHECK_EQ(reinterpret_cast<ImageLayout*>(NULL), image_layout_);
443 E : DCHECK_EQ(reinterpret_cast<BlockGraph::AddressSpace*>(NULL),image_);
444 E : AutoReset<ImageLayout*> auto_reset_image_layout(&image_layout_, image_layout);
445 : AutoReset<BlockGraph::AddressSpace*> auto_reset_image(&image_,
446 E : &image_layout->blocks);
447 :
448 : // Set the image format.
449 E : image_layout->blocks.graph()->set_image_format(BlockGraph::COFF_IMAGE);
450 :
451 : // Copy the image headers to the layout.
452 : CopySectionHeadersToImageLayout(
453 : image_file_.file_header()->NumberOfSections,
454 : image_file_.section_headers(),
455 E : &image_layout_->sections);
456 :
457 E : if (!CopySectionInfoToBlockGraph(image_file_, image_->graph()))
458 i : return false;
459 :
460 E : if (!CreateBlocksFromSections())
461 i : return false;
462 :
463 E : if (!CreateBlocksAndReferencesFromNonSections())
464 i : return false;
465 :
466 E : if (!CreateReferencesFromRelocations())
467 i : return false;
468 :
469 E : if (!CreateReferencesFromDebugInfo())
470 i : return false;
471 :
472 E : if (!CreateLabelsFromSymbols())
473 i : return false;
474 :
475 E : return true;
476 E : }
477 :
478 E : bool CoffDecomposer::CreateBlocksAndReferencesFromNonSections() {
479 E : DCHECK(image_ != NULL);
480 :
481 E : if (!CreateBlocksAndReferencesFromSymbolAndStringTables())
482 i : return false;
483 :
484 E : if (!CreateBlocksFromRelocationTables())
485 i : return false;
486 :
487 E : if (!CreateBlocksAndReferencesFromHeaders())
488 i : return false;
489 :
490 E : return true;
491 E : }
492 :
493 E : bool CoffDecomposer::CreateBlocksAndReferencesFromHeaders() {
494 E : const IMAGE_FILE_HEADER* file_header = image_file_.file_header();
495 E : DCHECK(file_header != NULL);
496 :
497 : // Create a block for COFF and section headers.
498 : size_t headers_size =
499 : sizeof(*file_header) +
500 E : file_header->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
501 : Block* block = CreateBlock(BlockGraph::DATA_BLOCK, FileOffsetAddress(0),
502 E : headers_size, kHeadersBlockName);
503 E : if (block == NULL) {
504 i : LOG(ERROR) << "Unable to create block for headers.";
505 i : return false;
506 : }
507 E : block->set_attribute(BlockGraph::COFF_HEADERS);
508 :
509 : // Create a reference for the symbol table pointer.
510 : FileOffsetAddress symbols_ptr_addr(
511 E : offsetof(IMAGE_FILE_HEADER, PointerToSymbolTable));
512 : if (!CreateFileOffsetReference(
513 : symbols_ptr_addr,
514 : BlockGraph::FILE_OFFSET_REF,
515 : sizeof(file_header->PointerToSymbolTable),
516 E : FileOffsetAddress(file_header->PointerToSymbolTable))) {
517 i : return false;
518 : }
519 :
520 : // Create a reference for the section and relocation pointers in each section
521 : // header.
522 : FileOffsetAddress section_headers_start(
523 E : sizeof(*file_header) + file_header->SizeOfOptionalHeader);
524 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
525 E : for (size_t i = 0; i < num_sections; ++i) {
526 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
527 E : DCHECK(header != NULL);
528 E : FileOffsetAddress start(section_headers_start + i * sizeof(*header));
529 :
530 : FileOffsetAddress data_ptr_addr(
531 E : start + offsetof(IMAGE_SECTION_HEADER, PointerToRawData));
532 : if (!CreateFileOffsetReference(
533 : data_ptr_addr,
534 : BlockGraph::FILE_OFFSET_REF,
535 : sizeof(header->PointerToRawData),
536 E : FileOffsetAddress(header->PointerToRawData))) {
537 i : return false;
538 : }
539 :
540 : FileOffsetAddress relocs_ptr_addr(
541 E : start + offsetof(IMAGE_SECTION_HEADER, PointerToRelocations));
542 : if (!CreateFileOffsetReference(
543 : relocs_ptr_addr,
544 : BlockGraph::FILE_OFFSET_REF,
545 : sizeof(header->PointerToRelocations),
546 E : FileOffsetAddress(header->PointerToRelocations))) {
547 i : return false;
548 : }
549 E : }
550 :
551 E : return true;
552 E : }
553 :
554 E : bool CoffDecomposer::CreateBlocksAndReferencesFromSymbolAndStringTables() {
555 : // Create a block for the symbol table.
556 E : FileOffsetAddress symbols_start(image_file_.symbols_address());
557 E : size_t symbols_size = image_file_.symbols_size();
558 : Block* block = CreateBlock(BlockGraph::DATA_BLOCK,
559 E : symbols_start, symbols_size, kSymbolsBlockName);
560 E : if (block == NULL) {
561 i : LOG(ERROR) << "Unable to create block for symbols.";
562 i : return false;
563 : }
564 E : block->set_attribute(BlockGraph::COFF_SYMBOL_TABLE);
565 :
566 : // Create a block for the strings table that follows.
567 E : FileOffsetAddress strings_start(image_file_.strings_address());
568 E : size_t strings_size = image_file_.strings_size();
569 : block = CreateBlock(BlockGraph::DATA_BLOCK,
570 E : strings_start, strings_size, kStringsBlockName);
571 E : if (block == NULL) {
572 i : LOG(ERROR) << "Unable to create block for strings.";
573 i : return false;
574 : }
575 E : block->set_attribute(BlockGraph::COFF_STRING_TABLE);
576 :
577 : // Add references.
578 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
579 E : const IMAGE_SYMBOL* symbol = NULL;
580 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
581 E : symbol = image_file_.symbol(i);
582 :
583 : // Ignore external symbols (no references to blocks) and other kinds of
584 : // non-reference symbols.
585 E : if (symbol->SectionNumber <= 0)
586 E : continue;
587 :
588 E : FileOffsetAddress start(symbols_start + i * sizeof(*symbol));
589 :
590 : // Symbols with section storage class simply provide the characteristics
591 : // of the section in the symbol value. Other symbols store an actual offset
592 : // into a section in the value field.
593 E : if (symbol->StorageClass != IMAGE_SYM_CLASS_SECTION) {
594 E : FileOffsetAddress value_addr(start + offsetof(IMAGE_SYMBOL, Value));
595 : if (!CreateSymbolOffsetReference(
596 : value_addr,
597 : BlockGraph::SECTION_OFFSET_REF,
598 : sizeof(symbol->Value),
599 : symbol,
600 E : symbol->Value)) {
601 i : return false;
602 : }
603 : }
604 :
605 : FileOffsetAddress section_addr(
606 E : start + offsetof(IMAGE_SYMBOL, SectionNumber));
607 : if (!CreateSymbolOffsetReference(
608 : section_addr,
609 : BlockGraph::SECTION_REF,
610 : sizeof(symbol->SectionNumber),
611 : symbol,
612 E : 0)) {
613 i : return false;
614 : }
615 :
616 : // Section definitions for associative COMDAT sections require an additional
617 : // section reference within the auxiliary symbol.
618 : if (symbol->StorageClass == IMAGE_SYM_CLASS_STATIC &&
619 : symbol->Type >> 4 != IMAGE_SYM_DTYPE_FUNCTION &&
620 E : symbol->NumberOfAuxSymbols == 1) {
621 : const IMAGE_AUX_SYMBOL* aux =
622 E : reinterpret_cast<const IMAGE_AUX_SYMBOL*>(image_file_.symbol(i + 1));
623 E : DCHECK(aux != NULL);
624 E : if (aux->Section.Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
625 : FileOffsetAddress number_addr(
626 : start + sizeof(IMAGE_SYMBOL) +
627 E : offsetof(IMAGE_AUX_SYMBOL, Section.Number));
628 : if (!CreateSectionOffsetReference(
629 : number_addr,
630 : BlockGraph::SECTION_REF,
631 : sizeof(short),
632 : aux->Section.Number - 1,
633 E : 0)) {
634 i : return false;
635 : }
636 : }
637 : }
638 E : }
639 :
640 E : return true;
641 E : }
642 :
643 E : bool CoffDecomposer::CreateBlocksFromRelocationTables() {
644 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
645 E : for (size_t i = 0; i < num_sections; ++i) {
646 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
647 E : DCHECK(header != NULL);
648 E : if (header->NumberOfRelocations == 0)
649 E : continue;
650 :
651 E : FileOffsetAddress relocs_start(header->PointerToRelocations);
652 E : size_t relocs_size(header->NumberOfRelocations * sizeof(IMAGE_RELOCATION));
653 :
654 : // Create a block for this relocation table.
655 : Block* block =
656 : CreateBlock(BlockGraph::DATA_BLOCK,
657 E : relocs_start, relocs_size, kRelocsBlockName);
658 E : if (block == NULL)
659 i : return false;
660 E : block->set_attribute(BlockGraph::COFF_RELOC_DATA);
661 E : }
662 E : return true;
663 E : }
664 :
665 E : bool CoffDecomposer::CreateBlocksFromSections() {
666 E : DCHECK(image_ != NULL);
667 :
668 : // Build COMDAT symbol map, which associates each COMDAT section
669 : // with the COMDAT (secondary) symbol. When compiling with
670 : // function-level linking (/Gy for MSVC), all data and code lives in
671 : // COMDAT sections. Each COMDAT section is associated with at least
672 : // one symbol in the symbol table (the primary symbol), but usually
673 : // two or more.
674 : //
675 : // The primary symbol must always be the section symbol, which
676 : // indicates which final executable section the COMDAT section will
677 : // need to be merged into (e.g., .text or .data).
678 : //
679 : // The secondary symbol, when it exists, is the first symbol bound
680 : // to the COMDAT section that comes after the primary (usually but
681 : // not necessarily right after). With function-level linking, the
682 : // secondary symbol is always the name of the function or variable
683 : // defined in the section.
684 : //
685 : // The COFF decomposer assumes functions live in their own sections,
686 : // which is guaranteed by the MSVC compiler documentation for /Gy,
687 : // but is more forgiving when it comes to variables, which may be
688 : // grouped together in one or multiple data sections.
689 E : ComdatMap comdat_map;
690 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
691 E : const IMAGE_SYMBOL* symbol = NULL;
692 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
693 E : symbol = image_file_.symbol(i);
694 E : DCHECK(symbol != NULL);
695 E : if (symbol->SectionNumber <= 0)
696 E : continue;
697 E : size_t section_index = symbol->SectionNumber - 1;
698 :
699 : // Skip non-COMDAT sections.
700 : const IMAGE_SECTION_HEADER* header =
701 E : image_file_.section_header(section_index);
702 E : DCHECK(header != NULL);
703 E : if ((header->Characteristics & IMAGE_SCN_LNK_COMDAT) == 0)
704 E : continue;
705 :
706 : // Skip primary section symbols.
707 E : ComdatMap::iterator it = comdat_map.find(section_index);
708 E : if (it == comdat_map.end()) {
709 : comdat_map.insert(std::make_pair(section_index,
710 E : static_cast<const char*>(0)));
711 E : } else {
712 : // Skip symbols after the second one.
713 E : if (it->second != NULL)
714 E : continue;
715 :
716 : // This should be the second symbol (assuming the first one is
717 : // the section symbol, as mandated by the specifications), that
718 : // is, the COMDAT symbol.
719 E : it->second = image_file_.GetSymbolName(i);
720 : }
721 E : }
722 :
723 : // Build a block for each data or code section.
724 E : size_t num_sections = image_file_.file_header()->NumberOfSections;
725 E : for (size_t i = 0; i < num_sections; ++i) {
726 E : const IMAGE_SECTION_HEADER* header = image_file_.section_header(i);
727 E : DCHECK(header != NULL);
728 : BlockType block_type = GetSectionType(*header) == kSectionCode ?
729 E : BlockGraph::CODE_BLOCK : BlockGraph::DATA_BLOCK;
730 :
731 : // Retrieve or make up a suitable name for the block.
732 E : std::string name(image_file_.GetSectionName(*header));
733 E : ComdatMap::iterator it = comdat_map.find(i);
734 E : if (it != comdat_map.end()) {
735 E : name.append(kSectionComdatSep);
736 E : if (it->second != NULL)
737 E : name.append(it->second);
738 : }
739 :
740 : // Compute the address of the block; when using function-level linking,
741 : // each function begins at offset zero. Unmapped sections (BSS) get an
742 : // unmapped block with an invalid address.
743 E : FileOffsetAddress addr(FileOffsetAddress::kInvalidAddress);
744 E : if (image_file_.IsSectionMapped(i)) {
745 E : CHECK(image_file_.SectionOffsetToFileOffset(i, 0, &addr));
746 : }
747 :
748 : // Put everything together into a block.
749 : Block* block = CreateBlock(block_type,
750 E : addr, header->SizeOfRawData, name.c_str());
751 E : if (block == NULL) {
752 i : LOG(ERROR) << "Unable to create block for section " << i << " \""
753 : << name << "\".";
754 i : return false;
755 : }
756 :
757 : // Assuming block graph section IDs match those of the image file.
758 E : block->set_section(i);
759 : block->set_attribute(image_file_.IsSectionMapped(i) ?
760 E : BlockGraph::SECTION_CONTRIB : BlockGraph::COFF_BSS);
761 :
762 : // Add to section-block map so we can find it later.
763 E : section_block_map_.insert(std::make_pair(i, block));
764 E : }
765 :
766 E : return true;
767 E : }
768 :
769 E : bool CoffDecomposer::CreateReferencesFromDebugInfo() {
770 E : DCHECK(image_ != NULL);
771 :
772 : // Read debug data directly from the block graph, since debug section
773 : // blocks have already been inserted.
774 E : BlockGraph::BlockMap& blocks = image_->graph()->blocks_mutable();
775 E : BlockGraph::BlockMap::iterator it = blocks.begin();
776 E : for (; it != blocks.end(); ++it) {
777 E : size_t section_index = it->second.section();
778 : BlockGraph::Section* section =
779 E : image_->graph()->GetSectionById(section_index);
780 E : if (section == NULL || section->name() != ".debug$S")
781 E : continue;
782 :
783 E : Block* block = &it->second;
784 E : ConstTypedBlock<uint32> magic;
785 E : if (!magic.Init(0, block)) {
786 i : LOG(ERROR) << "Unable to read magic number from .debug$S section "
787 : << section_index << ".";
788 i : return false;
789 : }
790 :
791 : // Parse subsections.
792 E : switch (*magic) {
793 : case cci::C11: {
794 E : if (!ParseDebugSubsections2(block))
795 i : return false;
796 E : break;
797 : }
798 :
799 : case cci::C13: {
800 E : if (!ParseDebugSubsections4(block))
801 i : return false;
802 E : break;
803 : }
804 :
805 : default: {
806 i : LOG(ERROR) << "Unsupported CV version " << *magic
807 : << " in .debug$S section " << section_index << ".";
808 i : return false;
809 : }
810 : }
811 E : }
812 E : return true;
813 E : }
814 :
815 E : bool CoffDecomposer::CreateReferencesFromRelocations() {
816 E : DCHECK(image_ != NULL);
817 :
818 E : CoffFile::RelocMap reloc_map;
819 E : image_file_.DecodeRelocs(&reloc_map);
820 :
821 E : CoffFile::RelocMap::iterator it = reloc_map.begin();
822 E : for (; it != reloc_map.end(); ++it) {
823 E : DCHECK(it->second != NULL);
824 : const IMAGE_SYMBOL* symbol =
825 E : image_file_.symbol(it->second->SymbolTableIndex);
826 E : DCHECK(symbol != NULL);
827 :
828 : // Compute reference attributes.
829 E : ReferenceType ref_type = BlockGraph::REFERENCE_TYPE_MAX;
830 E : BlockGraph::Size ref_size = 0;
831 E : if (!GetRelocationTypeAndSize(*it->second, &ref_type, &ref_size))
832 i : continue;
833 E : DCHECK_LT(ref_type, BlockGraph::REFERENCE_TYPE_MAX);
834 E : DCHECK_GT(ref_size, 0u);
835 :
836 : // Add reference.
837 E : size_t offset = symbol->SectionNumber == 0 ? 0 : symbol->Value;
838 : if (!CreateSymbolOffsetReference(it->first, ref_type, ref_size,
839 E : symbol, offset)) {
840 i : return false;
841 : }
842 E : }
843 :
844 E : return true;
845 E : }
846 :
847 E : bool CoffDecomposer::CreateLabelsFromSymbols() {
848 E : DCHECK(image_ != NULL);
849 :
850 E : size_t num_symbols = image_file_.file_header()->NumberOfSymbols;
851 : const IMAGE_SYMBOL* symbol;
852 E : for (size_t i = 0; i < num_symbols; i += 1 + symbol->NumberOfAuxSymbols) {
853 E : symbol = image_file_.symbol(i);
854 :
855 : // Data labels should reference a valid section, have storage
856 : // class STATIC, a non-function type (contrary to static
857 : // functions), and no auxiliary record (contrary to section
858 : // definitions). Skip the rest.
859 : //
860 : // MSVC records section descriptions in the symbol table as STATIC
861 : // data symbols; hence a section symbol and the first data symbol
862 : // at offset zero will have the same storage class and offset;
863 : // data symbols, however, occupy a single entry in the table,
864 : // whereas section symbols take two records (hence one auxiliary
865 : // record with class-specific data in addition of the main
866 : // record).
867 : if (!(symbol->SectionNumber > 0 &&
868 : symbol->StorageClass == IMAGE_SYM_CLASS_STATIC &&
869 : symbol->Type >> 4 != IMAGE_SYM_DTYPE_FUNCTION &&
870 E : symbol->NumberOfAuxSymbols == 0)) {
871 E : continue;
872 : }
873 E : size_t section_index = symbol->SectionNumber - 1;
874 :
875 : // Skip labels in non-code sections.
876 : const IMAGE_SECTION_HEADER* header =
877 E : image_file_.section_header(section_index);
878 E : DCHECK(header != NULL);
879 E : if (GetSectionType(*header) != kSectionCode)
880 E : continue;
881 :
882 : // Get block and offset.
883 E : SectionBlockMap::iterator it = section_block_map_.find(section_index);
884 E : DCHECK(it != section_block_map_.end());
885 E : Block* block = it->second;
886 E : DCHECK(block != NULL);
887 E : BlockGraph::Offset offset = symbol->Value;
888 :
889 : // Tables only appear in code blocks; ignore others.
890 E : if (block->type() != BlockGraph::CODE_BLOCK)
891 i : continue;
892 :
893 : // Compute label attributes. Jump tables are always an array of
894 : // pointers, thus they coincide exactly with a reference. Case
895 : // tables are simple arrays of integer values, thus do not
896 : // coincide with a reference.
897 E : BlockGraph::LabelAttributes attrs = BlockGraph::DATA_LABEL;
898 E : if (block->references().find(offset) != block->references().end()) {
899 E : attrs |= BlockGraph::JUMP_TABLE_LABEL;
900 E : } else {
901 E : attrs |= BlockGraph::CASE_TABLE_LABEL;
902 : }
903 :
904 : // Add label.
905 E : const char* name = image_file_.GetSymbolName(i);
906 E : if (!AddLabelToBlock(offset, name, attrs, block))
907 i : return false;
908 E : }
909 E : return true;
910 E : }
911 :
912 : Block* CoffDecomposer::CreateBlock(BlockType type,
913 : FileOffsetAddress addr,
914 : BlockGraph::Size size,
915 E : const base::StringPiece& name) {
916 E : DCHECK(image_ != NULL);
917 :
918 E : if (addr == FileOffsetAddress::kInvalidAddress) {
919 : // Unmapped block.
920 E : Block* block = image_->graph()->AddBlock(type, size, name);
921 E : if (block == NULL) {
922 i : LOG(ERROR) << "Unable to add unmapped block \"" << name.as_string()
923 : << "\" with size " << size << ".";
924 i : return NULL;
925 : }
926 E : return block;
927 : }
928 :
929 : // Otherwise, we have a normal mapped block.
930 E : BlockGraphAddress block_addr(FileOffsetToBlockGraphAddress(addr));
931 E : Block* block = image_->AddBlock(type, block_addr, size, name);
932 E : if (block == NULL) {
933 i : LOG(ERROR) << "Unable to add block \"" << name.as_string() << "\" at "
934 : << block_addr << " with size " << size << ".";
935 i : return NULL;
936 : }
937 :
938 : // Mark the source range from whence this block originates.
939 E : if (size > 0) {
940 : bool pushed = block->source_ranges().Push(
941 : Block::DataRange(0, size),
942 E : Block::SourceRange(block_addr, size));
943 E : DCHECK(pushed);
944 : }
945 :
946 E : const uint8* data = image_file_.GetImageData(addr, size);
947 E : if (data != NULL)
948 E : block->SetData(data, size);
949 :
950 E : return block;
951 E : }
952 :
953 : bool CoffDecomposer::CreateReference(FileOffsetAddress src_addr,
954 : ReferenceType ref_type,
955 : BlockGraph::Size ref_size,
956 : Block* target,
957 E : BlockGraph::Offset offset) {
958 E : DCHECK(image_ != NULL);
959 :
960 : // Get source block and offset.
961 E : Block* source = NULL;
962 E : BlockGraph::Offset src_offset = -1;
963 E : if (!FileOffsetToBlockOffset(src_addr, &source, &src_offset))
964 i : return false;
965 E : DCHECK(source != NULL);
966 E : DCHECK_GE(src_offset, 0);
967 :
968 : // Read additional offset for relocations.
969 E : BlockGraph::Offset extra_offset = 0;
970 E : if ((ref_type & BlockGraph::RELOC_REF_BIT) != 0) {
971 E : switch (ref_size) {
972 : case sizeof(uint32):
973 E : if (!ReadRelocationValue<uint32>(source, src_offset, &extra_offset))
974 i : return false;
975 E : break;
976 : case sizeof(uint16):
977 E : if (!ReadRelocationValue<uint16>(source, src_offset, &extra_offset))
978 i : return false;
979 E : break;
980 : case sizeof(uint8):
981 : // TODO(chrisha): This is really a special 7-bit relocation; we do
982 : // not touch these, for now.
983 i : break;
984 : default:
985 i : LOG(ERROR) << "Unsupported relocation value size (" << ref_size << ").";
986 i : return false;
987 : }
988 : }
989 :
990 : // Find an existing reference, or insert a new one.
991 E : Reference ref(ref_type, ref_size, target, offset + extra_offset, offset);
992 : Block::ReferenceMap::const_iterator ref_it =
993 E : source->references().find(src_offset);
994 E : if (ref_it == source->references().end()) {
995 : // New reference.
996 E : CHECK(source->SetReference(src_offset, ref));
997 E : } else {
998 : // Collisions are only allowed if the references are identical.
999 i : if (!(ref == ref_it->second)) {
1000 i : LOG(ERROR) << "Block \"" << source->name() << "\" has a conflicting "
1001 : << "reference at offset " << src_offset << ".";
1002 i : return false;
1003 : }
1004 : }
1005 :
1006 E : return true;
1007 E : }
1008 :
1009 : bool CoffDecomposer::CreateFileOffsetReference(FileOffsetAddress src_addr,
1010 : ReferenceType ref_type,
1011 : BlockGraph::Size ref_size,
1012 E : FileOffsetAddress dst_addr) {
1013 E : DCHECK(image_ != NULL);
1014 :
1015 : // Get target section and offset.
1016 E : Block* target = NULL;
1017 E : BlockGraph::Offset offset = -1;
1018 E : if (!FileOffsetToBlockOffset(dst_addr, &target, &offset))
1019 i : return false;
1020 E : DCHECK(target != NULL);
1021 E : DCHECK_GE(offset, 0);
1022 :
1023 : // Add reference.
1024 E : if (!CreateReference(src_addr, ref_type, ref_size, target, offset))
1025 i : return false;
1026 :
1027 E : return true;
1028 E : }
1029 :
1030 : bool CoffDecomposer::CreateSectionOffsetReference(FileOffsetAddress src_addr,
1031 : ReferenceType ref_type,
1032 : BlockGraph::Size ref_size,
1033 : size_t section_index,
1034 E : size_t section_offset) {
1035 E : DCHECK(image_ != NULL);
1036 :
1037 : // Get target section and offset.
1038 E : Block* target = NULL;
1039 E : BlockGraph::Offset offset = -1;
1040 : if (!SectionOffsetToBlockOffset(section_index, section_offset,
1041 E : &target, &offset)) {
1042 i : return false;
1043 : }
1044 E : DCHECK(target != NULL);
1045 E : DCHECK_GE(offset, 0);
1046 :
1047 : // Add reference.
1048 E : if (!CreateReference(src_addr, ref_type, ref_size, target, offset))
1049 i : return false;
1050 :
1051 E : return true;
1052 E : }
1053 :
1054 : bool CoffDecomposer::CreateSymbolOffsetReference(FileOffsetAddress src_addr,
1055 : ReferenceType ref_type,
1056 : BlockGraph::Size ref_size,
1057 : const IMAGE_SYMBOL* symbol,
1058 E : size_t offset) {
1059 E : DCHECK(image_ != NULL);
1060 E : DCHECK(symbol != NULL);
1061 :
1062 E : if (symbol->SectionNumber < 0) {
1063 i : LOG(ERROR) << "Symbol cannot be converted to a reference.";
1064 i : return false;
1065 : }
1066 :
1067 E : if (symbol->SectionNumber != 0) {
1068 : // Section symbol.
1069 : return CreateSectionOffsetReference(src_addr, ref_type, ref_size,
1070 E : symbol->SectionNumber - 1, offset);
1071 i : } else {
1072 : // External symbol. As a convention, we use a reference to the symbol
1073 : // table, since there is no corresponding block. The offset is ignored
1074 : // (will be inferred from the symbol value and reference type).
1075 E : size_t symbol_index = symbol - image_file_.symbols();
1076 : return CreateFileOffsetReference(
1077 : src_addr, ref_type, ref_size,
1078 E : image_file_.symbols_address() + symbol_index * sizeof(*symbol));
1079 : }
1080 E : }
1081 :
1082 : bool CoffDecomposer::FileOffsetToBlockOffset(FileOffsetAddress addr,
1083 : Block** block,
1084 E : BlockGraph::Offset* offset) {
1085 E : DCHECK(image_ != NULL);
1086 E : DCHECK(block != NULL);
1087 E : DCHECK(offset != NULL);
1088 :
1089 : // Get block and offset.
1090 E : BlockGraphAddress actual_addr(FileOffsetToBlockGraphAddress(addr));
1091 E : Block* containing_block = image_->GetBlockByAddress(actual_addr);
1092 E : if (containing_block == NULL) {
1093 i : LOG(ERROR) << "File offset " << addr << " does not lie within a block.";
1094 i : return false;
1095 : }
1096 E : BlockGraphAddress block_addr;
1097 E : CHECK(image_->GetAddressOf(containing_block, &block_addr));
1098 :
1099 E : *block = containing_block;
1100 E : *offset = actual_addr - block_addr;
1101 E : return true;
1102 E : }
1103 :
1104 : bool CoffDecomposer::SectionOffsetToBlockOffset(size_t section_index,
1105 : size_t section_offset,
1106 : Block** block,
1107 E : BlockGraph::Offset* offset) {
1108 E : DCHECK(image_ != NULL);
1109 E : DCHECK_NE(BlockGraph::kInvalidSectionId, section_index);
1110 E : DCHECK_LT(section_index, image_file_.file_header()->NumberOfSections);
1111 : DCHECK_LE(section_offset,
1112 E : image_file_.section_header(section_index)->SizeOfRawData);
1113 E : DCHECK(block != NULL);
1114 E : DCHECK(offset != NULL);
1115 :
1116 : // Get block and offset.
1117 E : SectionBlockMap::iterator it = section_block_map_.find(section_index);
1118 E : if (it == section_block_map_.end()) {
1119 i : LOG(ERROR) << "Section " << section_index << " is not mapped to a block.";
1120 i : return false;
1121 : }
1122 E : DCHECK(it->second != NULL);
1123 E : DCHECK_LE(section_offset, it->second->size());
1124 :
1125 E : *block = it->second;
1126 E : *offset = section_offset;
1127 E : return true;
1128 E : }
1129 :
1130 : CoffDecomposer::BlockGraphAddress CoffDecomposer::FileOffsetToBlockGraphAddress(
1131 E : FileOffsetAddress addr) {
1132 E : return BlockGraphAddress(addr.value());
1133 E : }
1134 :
1135 : } // namespace pe
|