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