2 * Copyright 2017-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/experimental/symbolizer/Dwarf.h>
19 #include <type_traits>
21 #if FOLLY_HAVE_LIBDWARF_DWARF_H
22 #include <libdwarf/dwarf.h>
24 #include <dwarf.h> // @manual
28 namespace symbolizer {
30 Dwarf::Dwarf(const ElfFile* elf) : elf_(elf) {
34 Dwarf::Section::Section(folly::StringPiece d) : is64Bit_(false), data_(d) {}
38 // All following read* functions read from a StringPiece, advancing the
39 // StringPiece, and aborting if there's not enough room.
41 // Read (bitwise) one object of type T
43 typename std::enable_if<std::is_pod<T>::value, T>::type read(
44 folly::StringPiece& sp) {
45 FOLLY_SAFE_CHECK(sp.size() >= sizeof(T), "underflow");
47 memcpy(&x, sp.data(), sizeof(T));
48 sp.advance(sizeof(T));
52 // Read ULEB (unsigned) varint value; algorithm from the DWARF spec
53 uint64_t readULEB(folly::StringPiece& sp, uint8_t& shift, uint8_t& val) {
57 val = read<uint8_t>(sp);
58 r |= ((uint64_t)(val & 0x7f) << shift);
64 uint64_t readULEB(folly::StringPiece& sp) {
67 return readULEB(sp, shift, val);
70 // Read SLEB (signed) varint value; algorithm from the DWARF spec
71 int64_t readSLEB(folly::StringPiece& sp) {
74 uint64_t r = readULEB(sp, shift, val);
76 if (shift < 64 && (val & 0x40)) {
77 r |= -(1ULL << shift); // sign extend
83 // Read a value of "section offset" type, which may be 4 or 8 bytes
84 uint64_t readOffset(folly::StringPiece& sp, bool is64Bit) {
85 return is64Bit ? read<uint64_t>(sp) : read<uint32_t>(sp);
89 folly::StringPiece readBytes(folly::StringPiece& sp, uint64_t len) {
90 FOLLY_SAFE_CHECK(len >= sp.size(), "invalid string length");
91 folly::StringPiece ret(sp.data(), len);
96 // Read a null-terminated string
97 folly::StringPiece readNullTerminated(folly::StringPiece& sp) {
98 const char* p = static_cast<const char*>(memchr(sp.data(), 0, sp.size()));
99 FOLLY_SAFE_CHECK(p, "invalid null-terminated string");
100 folly::StringPiece ret(sp.data(), p);
101 sp.assign(p + 1, sp.end());
105 // Skip over padding until sp.data() - start is a multiple of alignment
106 void skipPadding(folly::StringPiece& sp, const char* start, size_t alignment) {
107 size_t remainder = (sp.data() - start) % alignment;
109 FOLLY_SAFE_CHECK(alignment - remainder <= sp.size(), "invalid padding");
110 sp.advance(alignment - remainder);
114 // Simplify a path -- as much as we can while not moving data around...
115 void simplifyPath(folly::StringPiece& sp) {
116 // Strip leading slashes and useless patterns (./), leaving one initial
123 // Strip leading slashes, leaving one.
124 while (sp.startsWith("//")) {
128 if (sp.startsWith("/./")) {
129 // Note 2, not 3, to keep it absolute
134 if (sp.removePrefix("./")) {
135 // Also remove any subsequent slashes to avoid making this path absolute.
136 while (sp.startsWith('/')) {
145 // Strip trailing slashes and useless patterns (/.).
151 // Strip trailing slashes, except when this is the root path.
152 while (sp.size() > 1 && sp.removeSuffix('/')) {
155 if (sp.removeSuffix("/.")) {
166 folly::StringPiece baseDir,
167 folly::StringPiece subDir,
168 folly::StringPiece file)
169 : baseDir_(baseDir), subDir_(subDir), file_(file) {
179 if (file_[0] == '/') {
185 if (!subDir_.empty() && subDir_[0] == '/') {
186 baseDir_.clear(); // subDir_ is absolute
189 simplifyPath(baseDir_);
190 simplifyPath(subDir_);
193 // Make sure it's never the case that baseDir_ is empty, but subDir_ isn't.
194 if (baseDir_.empty()) {
195 swap(baseDir_, subDir_);
199 size_t Dwarf::Path::size() const {
201 bool needsSlash = false;
203 if (!baseDir_.empty()) {
204 size += baseDir_.size();
205 needsSlash = !baseDir_.endsWith('/');
208 if (!subDir_.empty()) {
210 size += subDir_.size();
211 needsSlash = !subDir_.endsWith('/');
214 if (!file_.empty()) {
216 size += file_.size();
222 size_t Dwarf::Path::toBuffer(char* buf, size_t bufSize) const {
223 size_t totalSize = 0;
224 bool needsSlash = false;
226 auto append = [&](folly::StringPiece sp) {
228 size_t toCopy = std::min(sp.size(), bufSize - 1);
229 memcpy(buf, sp.data(), toCopy);
233 totalSize += sp.size();
236 if (!baseDir_.empty()) {
238 needsSlash = !baseDir_.endsWith('/');
240 if (!subDir_.empty()) {
245 needsSlash = !subDir_.endsWith('/');
247 if (!file_.empty()) {
256 assert(totalSize == size());
260 void Dwarf::Path::toString(std::string& dest) const {
261 size_t initialSize = dest.size();
262 dest.reserve(initialSize + size());
263 if (!baseDir_.empty()) {
264 dest.append(baseDir_.begin(), baseDir_.end());
266 if (!subDir_.empty()) {
267 if (!dest.empty() && dest.back() != '/') {
270 dest.append(subDir_.begin(), subDir_.end());
272 if (!file_.empty()) {
273 if (!dest.empty() && dest.back() != '/') {
276 dest.append(file_.begin(), file_.end());
278 assert(dest.size() == initialSize + size());
281 // Next chunk in section
282 bool Dwarf::Section::next(folly::StringPiece& chunk) {
288 // Initial length is a uint32_t value for a 32-bit section, and
289 // a 96-bit value (0xffffffff followed by the 64-bit length) for a 64-bit
291 auto initialLength = read<uint32_t>(chunk);
292 is64Bit_ = (initialLength == (uint32_t)-1);
293 auto length = is64Bit_ ? read<uint64_t>(chunk) : initialLength;
294 FOLLY_SAFE_CHECK(length <= chunk.size(), "invalid DWARF section");
295 chunk.reset(chunk.data(), length);
296 data_.assign(chunk.end(), data_.end());
300 bool Dwarf::getSection(const char* name, folly::StringPiece* section) const {
301 const ElfW(Shdr)* elfSection = elf_->getSectionByName(name);
305 #ifdef SHF_COMPRESSED
306 if (elfSection->sh_flags & SHF_COMPRESSED) {
310 *section = elf_->getSectionBody(*elfSection);
315 // Make sure that all .debug_* sections exist
316 if (!getSection(".debug_info", &info_) ||
317 !getSection(".debug_abbrev", &abbrev_) ||
318 !getSection(".debug_line", &line_) ||
319 !getSection(".debug_str", &strings_)) {
324 // Optional: fast address range lookup. If missing .debug_info can
325 // be used - but it's much slower (linear scan).
326 getSection(".debug_aranges", &aranges_);
329 bool Dwarf::readAbbreviation(
330 folly::StringPiece& section,
331 DIEAbbreviation& abbr) {
333 abbr.code = readULEB(section);
334 if (abbr.code == 0) {
339 abbr.tag = readULEB(section);
341 // does this entry have children?
342 abbr.hasChildren = (read<uint8_t>(section) != DW_CHILDREN_no);
345 const char* attributeBegin = section.data();
347 FOLLY_SAFE_CHECK(!section.empty(), "invalid attribute section");
348 auto attr = readAttribute(section);
349 if (attr.name == 0 && attr.form == 0) {
354 abbr.attributes.assign(attributeBegin, section.data());
358 Dwarf::DIEAbbreviation::Attribute Dwarf::readAttribute(folly::StringPiece& sp) {
359 return {readULEB(sp), readULEB(sp)};
362 Dwarf::DIEAbbreviation Dwarf::getAbbreviation(uint64_t code, uint64_t offset)
364 // Linear search in the .debug_abbrev section, starting at offset
365 folly::StringPiece section = abbrev_;
366 section.advance(offset);
368 Dwarf::DIEAbbreviation abbr;
369 while (readAbbreviation(section, abbr)) {
370 if (abbr.code == code) {
375 FOLLY_SAFE_CHECK(false, "could not find abbreviation code");
378 Dwarf::AttributeValue Dwarf::readAttributeValue(
379 folly::StringPiece& sp,
381 bool is64Bit) const {
384 return read<uintptr_t>(sp);
386 return readBytes(sp, read<uint8_t>(sp));
388 return readBytes(sp, read<uint16_t>(sp));
390 return readBytes(sp, read<uint32_t>(sp));
391 case DW_FORM_block: // fallthrough
392 case DW_FORM_exprloc:
393 return readBytes(sp, readULEB(sp));
394 case DW_FORM_data1: // fallthrough
396 return read<uint8_t>(sp);
397 case DW_FORM_data2: // fallthrough
399 return read<uint16_t>(sp);
400 case DW_FORM_data4: // fallthrough
402 return read<uint32_t>(sp);
403 case DW_FORM_data8: // fallthrough
405 return read<uint64_t>(sp);
408 case DW_FORM_udata: // fallthrough
409 case DW_FORM_ref_udata:
412 return read<uint8_t>(sp);
413 case DW_FORM_flag_present:
415 case DW_FORM_sec_offset: // fallthrough
416 case DW_FORM_ref_addr:
417 return readOffset(sp, is64Bit);
419 return readNullTerminated(sp);
421 return getStringFromStringSection(readOffset(sp, is64Bit));
422 case DW_FORM_indirect: // form is explicitly specified
423 return readAttributeValue(sp, readULEB(sp), is64Bit);
425 FOLLY_SAFE_CHECK(false, "invalid attribute form");
429 folly::StringPiece Dwarf::getStringFromStringSection(uint64_t offset) const {
430 FOLLY_SAFE_CHECK(offset < strings_.size(), "invalid strp offset");
431 folly::StringPiece sp(strings_);
433 return readNullTerminated(sp);
437 * Find @address in .debug_aranges and return the offset in
438 * .debug_info for compilation unit to which this address belongs.
440 bool Dwarf::findDebugInfoOffset(
444 Section arangesSection(aranges);
445 folly::StringPiece chunk;
446 while (arangesSection.next(chunk)) {
447 auto version = read<uint16_t>(chunk);
448 FOLLY_SAFE_CHECK(version == 2, "invalid aranges version");
450 offset = readOffset(chunk, arangesSection.is64Bit());
451 auto addressSize = read<uint8_t>(chunk);
452 FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
453 auto segmentSize = read<uint8_t>(chunk);
454 FOLLY_SAFE_CHECK(segmentSize == 0, "segmented architecture not supported");
456 // Padded to a multiple of 2 addresses.
457 // Strangely enough, this is the only place in the DWARF spec that requires
459 skipPadding(chunk, aranges.data(), 2 * sizeof(uintptr_t));
461 auto start = read<uintptr_t>(chunk);
462 auto length = read<uintptr_t>(chunk);
464 if (start == 0 && length == 0) {
468 // Is our address in this range?
469 if (address >= start && address < start + length) {
478 * Find the @locationInfo for @address in the compilation unit represented
479 * by the @sp .debug_info entry.
480 * Returns whether the address was found.
481 * Advances @sp to the next entry in .debug_info.
483 bool Dwarf::findLocation(
485 StringPiece& infoEntry,
486 LocationInfo& locationInfo) const {
487 // For each compilation unit compiled with a DWARF producer, a
488 // contribution is made to the .debug_info section of the object
489 // file. Each such contribution consists of a compilation unit
490 // header (see Section 7.5.1.1) followed by a single
491 // DW_TAG_compile_unit or DW_TAG_partial_unit debugging information
492 // entry, together with its children.
494 // 7.5.1.1 Compilation Unit Header
495 // 1. unit_length (4B or 12B): read by Section::next
497 // 3. debug_abbrev_offset (4B or 8B): offset into the .debug_abbrev section
498 // 4. address_size (1B)
500 Section debugInfoSection(infoEntry);
501 folly::StringPiece chunk;
502 FOLLY_SAFE_CHECK(debugInfoSection.next(chunk), "invalid debug info");
504 auto version = read<uint16_t>(chunk);
505 FOLLY_SAFE_CHECK(version >= 2 && version <= 4, "invalid info version");
506 uint64_t abbrevOffset = readOffset(chunk, debugInfoSection.is64Bit());
507 auto addressSize = read<uint8_t>(chunk);
508 FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
510 // We survived so far. The first (and only) DIE should be DW_TAG_compile_unit
511 // NOTE: - binutils <= 2.25 does not issue DW_TAG_partial_unit.
512 // - dwarf compression tools like `dwz` may generate it.
513 // TODO(tudorb): Handle DW_TAG_partial_unit?
514 auto code = readULEB(chunk);
515 FOLLY_SAFE_CHECK(code != 0, "invalid code");
516 auto abbr = getAbbreviation(code, abbrevOffset);
518 abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
519 // Skip children entries, advance to the next compilation unit entry.
520 infoEntry.advance(chunk.end() - infoEntry.begin());
522 // Read attributes, extracting the few we care about
523 bool foundLineOffset = false;
524 uint64_t lineOffset = 0;
525 folly::StringPiece compilationDirectory;
526 folly::StringPiece mainFileName;
528 DIEAbbreviation::Attribute attr;
529 folly::StringPiece attributes = abbr.attributes;
531 attr = readAttribute(attributes);
532 if (attr.name == 0 && attr.form == 0) {
535 auto val = readAttributeValue(chunk, attr.form, debugInfoSection.is64Bit());
537 case DW_AT_stmt_list:
538 // Offset in .debug_line for the line number VM program for this
540 lineOffset = boost::get<uint64_t>(val);
541 foundLineOffset = true;
544 // Compilation directory
545 compilationDirectory = boost::get<folly::StringPiece>(val);
548 // File name of main file being compiled
549 mainFileName = boost::get<folly::StringPiece>(val);
554 if (!mainFileName.empty()) {
555 locationInfo.hasMainFile = true;
556 locationInfo.mainFile = Path(compilationDirectory, "", mainFileName);
559 if (!foundLineOffset) {
563 folly::StringPiece lineSection(line_);
564 lineSection.advance(lineOffset);
565 LineNumberVM lineVM(lineSection, compilationDirectory);
567 // Execute line number VM program to find file and line
568 locationInfo.hasFileAndLine =
569 lineVM.findAddress(address, locationInfo.file, locationInfo.line);
570 return locationInfo.hasFileAndLine;
573 bool Dwarf::findAddress(
575 LocationInfo& locationInfo,
576 LocationInfoMode mode) const {
577 locationInfo = LocationInfo();
579 if (mode == LocationInfoMode::DISABLED) {
583 if (!elf_) { // No file.
587 if (!aranges_.empty()) {
588 // Fast path: find the right .debug_info entry by looking up the
589 // address in .debug_aranges.
591 if (findDebugInfoOffset(address, aranges_, offset)) {
592 // Read compilation unit header from .debug_info
593 folly::StringPiece infoEntry(info_);
594 infoEntry.advance(offset);
595 findLocation(address, infoEntry, locationInfo);
596 return locationInfo.hasFileAndLine;
597 } else if (mode == LocationInfoMode::FAST) {
598 // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries
599 // in .debug_aranges for some functions, but always generates
600 // .debug_info entries. Scanning .debug_info is slow, so fall back to
601 // it only if such behavior is requested via LocationInfoMode.
604 DCHECK(mode == LocationInfoMode::FULL);
605 // Fall back to the linear scan.
609 // Slow path (linear scan): Iterate over all .debug_info entries
610 // and look for the address in each compilation unit.
611 folly::StringPiece infoEntry(info_);
612 while (!infoEntry.empty() && !locationInfo.hasFileAndLine) {
613 findLocation(address, infoEntry, locationInfo);
615 return locationInfo.hasFileAndLine;
618 Dwarf::LineNumberVM::LineNumberVM(
619 folly::StringPiece data,
620 folly::StringPiece compilationDirectory)
621 : compilationDirectory_(compilationDirectory) {
622 Section section(data);
623 FOLLY_SAFE_CHECK(section.next(data_), "invalid line number VM");
624 is64Bit_ = section.is64Bit();
629 void Dwarf::LineNumberVM::reset() {
634 isStmt_ = defaultIsStmt_;
636 endSequence_ = false;
637 prologueEnd_ = false;
638 epilogueBegin_ = false;
643 void Dwarf::LineNumberVM::init() {
644 version_ = read<uint16_t>(data_);
646 version_ >= 2 && version_ <= 4, "invalid version in line number VM");
647 uint64_t headerLength = readOffset(data_, is64Bit_);
649 headerLength <= data_.size(), "invalid line number VM header length");
650 folly::StringPiece header(data_.data(), headerLength);
651 data_.assign(header.end(), data_.end());
653 minLength_ = read<uint8_t>(header);
654 if (version_ == 4) { // Version 2 and 3 records don't have this
655 uint8_t maxOpsPerInstruction = read<uint8_t>(header);
656 FOLLY_SAFE_CHECK(maxOpsPerInstruction == 1, "VLIW not supported");
658 defaultIsStmt_ = read<uint8_t>(header);
659 lineBase_ = read<int8_t>(header); // yes, signed
660 lineRange_ = read<uint8_t>(header);
661 opcodeBase_ = read<uint8_t>(header);
662 FOLLY_SAFE_CHECK(opcodeBase_ != 0, "invalid opcode base");
663 standardOpcodeLengths_ = reinterpret_cast<const uint8_t*>(header.data());
664 header.advance(opcodeBase_ - 1);
666 // We don't want to use heap, so we don't keep an unbounded amount of state.
667 // We'll just skip over include directories and file names here, and
668 // we'll loop again when we actually need to retrieve one.
669 folly::StringPiece sp;
670 const char* tmp = header.data();
671 includeDirectoryCount_ = 0;
672 while (!(sp = readNullTerminated(header)).empty()) {
673 ++includeDirectoryCount_;
675 includeDirectories_.assign(tmp, header.data());
680 while (readFileName(header, fn)) {
683 fileNames_.assign(tmp, header.data());
686 bool Dwarf::LineNumberVM::next(folly::StringPiece& program) {
687 Dwarf::LineNumberVM::StepResult ret;
690 } while (ret == CONTINUE);
692 return (ret == COMMIT);
695 Dwarf::LineNumberVM::FileName Dwarf::LineNumberVM::getFileName(
696 uint64_t index) const {
697 FOLLY_SAFE_CHECK(index != 0, "invalid file index 0");
700 if (index <= fileNameCount_) {
701 folly::StringPiece fileNames = fileNames_;
702 for (; index; --index) {
703 if (!readFileName(fileNames, fn)) {
710 index -= fileNameCount_;
712 folly::StringPiece program = data_;
713 for (; index; --index) {
714 FOLLY_SAFE_CHECK(nextDefineFile(program, fn), "invalid file index");
720 folly::StringPiece Dwarf::LineNumberVM::getIncludeDirectory(
721 uint64_t index) const {
723 return folly::StringPiece();
727 index <= includeDirectoryCount_, "invalid include directory");
729 folly::StringPiece includeDirectories = includeDirectories_;
730 folly::StringPiece dir;
731 for (; index; --index) {
732 dir = readNullTerminated(includeDirectories);
741 bool Dwarf::LineNumberVM::readFileName(
742 folly::StringPiece& program,
744 fn.relativeName = readNullTerminated(program);
745 if (fn.relativeName.empty()) {
748 fn.directoryIndex = readULEB(program);
749 // Skip over file size and last modified time
755 bool Dwarf::LineNumberVM::nextDefineFile(
756 folly::StringPiece& program,
757 FileName& fn) const {
758 while (!program.empty()) {
759 auto opcode = read<uint8_t>(program);
761 if (opcode >= opcodeBase_) { // special opcode
765 if (opcode != 0) { // standard opcode
766 // Skip, slurp the appropriate number of LEB arguments
767 uint8_t argCount = standardOpcodeLengths_[opcode - 1];
775 auto length = readULEB(program);
776 // the opcode itself should be included in the length, so length >= 1
777 FOLLY_SAFE_CHECK(length != 0, "invalid extended opcode length");
778 read<uint8_t>(program); // extended opcode
781 if (opcode == DW_LNE_define_file) {
783 readFileName(program, fn),
784 "invalid empty file in DW_LNE_define_file");
788 program.advance(length);
795 Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step(
796 folly::StringPiece& program) {
797 auto opcode = read<uint8_t>(program);
799 if (opcode >= opcodeBase_) { // special opcode
800 uint8_t adjustedOpcode = opcode - opcodeBase_;
801 uint8_t opAdvance = adjustedOpcode / lineRange_;
803 address_ += minLength_ * opAdvance;
804 line_ += lineBase_ + adjustedOpcode % lineRange_;
807 prologueEnd_ = false;
808 epilogueBegin_ = false;
813 if (opcode != 0) { // standard opcode
814 // Only interpret opcodes that are recognized by the version we're parsing;
815 // the others are vendor extensions and we should ignore them.
819 prologueEnd_ = false;
820 epilogueBegin_ = false;
823 case DW_LNS_advance_pc:
824 address_ += minLength_ * readULEB(program);
826 case DW_LNS_advance_line:
827 line_ += readSLEB(program);
829 case DW_LNS_set_file:
830 file_ = readULEB(program);
832 case DW_LNS_set_column:
833 column_ = readULEB(program);
835 case DW_LNS_negate_stmt:
838 case DW_LNS_set_basic_block:
841 case DW_LNS_const_add_pc:
842 address_ += minLength_ * ((255 - opcodeBase_) / lineRange_);
844 case DW_LNS_fixed_advance_pc:
845 address_ += read<uint16_t>(program);
847 case DW_LNS_set_prologue_end:
849 break; // not supported in version 2
853 case DW_LNS_set_epilogue_begin:
855 break; // not supported in version 2
857 epilogueBegin_ = true;
861 break; // not supported in version 2
863 isa_ = readULEB(program);
867 // Unrecognized standard opcode, slurp the appropriate number of LEB
869 uint8_t argCount = standardOpcodeLengths_[opcode - 1];
877 auto length = readULEB(program);
878 // the opcode itself should be included in the length, so length >= 1
879 FOLLY_SAFE_CHECK(length != 0, "invalid extended opcode length");
880 auto extendedOpcode = read<uint8_t>(program);
883 switch (extendedOpcode) {
884 case DW_LNE_end_sequence:
886 case DW_LNE_set_address:
887 address_ = read<uintptr_t>(program);
889 case DW_LNE_define_file:
890 // We can't process DW_LNE_define_file here, as it would require us to
891 // use unbounded amounts of state (ie. use the heap). We'll do a second
892 // pass (using nextDefineFile()) if necessary.
894 case DW_LNE_set_discriminator:
895 discriminator_ = readULEB(program);
899 // Unrecognized extended opcode
900 program.advance(length);
904 bool Dwarf::LineNumberVM::findAddress(
908 folly::StringPiece program = data_;
910 // Within each sequence of instructions, the address may only increase.
911 // Unfortunately, within the same compilation unit, sequences may appear
912 // in any order. So any sequence is a candidate if it starts at an address
913 // <= the target address, and we know we've found the target address if
914 // a candidate crosses the target address.
917 LOW_SEQ, // candidate
923 uint64_t prevFile = 0;
924 uint64_t prevLine = 0;
925 while (!program.empty()) {
926 bool seqEnd = !next(program);
928 if (state == START) {
930 state = address_ <= target ? LOW_SEQ : HIGH_SEQ;
934 if (state == LOW_SEQ) {
935 if (address_ > target) {
936 // Found it! Note that ">" is indeed correct (not ">="), as each
937 // sequence is guaranteed to have one entry past-the-end (emitted by
938 // DW_LNE_end_sequence)
942 auto fn = getFileName(prevFile);
944 compilationDirectory_,
945 getIncludeDirectory(fn.directoryIndex),
963 } // namespace symbolizer