1 //===--- HeaderMap.cpp - A file that acts like dir of symlinks ------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements the HeaderMap interface. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Lex/HeaderMap.h" 15 #include "clang/Lex/HeaderMapTypes.h" 16 #include "clang/Basic/CharInfo.h" 17 #include "clang/Basic/FileManager.h" 18 #include "llvm/ADT/SmallString.h" 19 #include "llvm/Support/DataTypes.h" 20 #include "llvm/Support/MathExtras.h" 21 #include "llvm/Support/MemoryBuffer.h" 22 #include "llvm/Support/SwapByteOrder.h" 23 #include "llvm/Support/Debug.h" 24 #include <memory> 25 using namespace clang; 26 27 /// HashHMapKey - This is the 'well known' hash function required by the file 28 /// format, used to look up keys in the hash table. The hash table uses simple 29 /// linear probing based on this function. 30 static inline unsigned HashHMapKey(StringRef Str) { 31 unsigned Result = 0; 32 const char *S = Str.begin(), *End = Str.end(); 33 34 for (; S != End; S++) 35 Result += toLowercase(*S) * 13; 36 return Result; 37 } 38 39 40 41 //===----------------------------------------------------------------------===// 42 // Verification and Construction 43 //===----------------------------------------------------------------------===// 44 45 /// HeaderMap::Create - This attempts to load the specified file as a header 46 /// map. If it doesn't look like a HeaderMap, it gives up and returns null. 47 /// If it looks like a HeaderMap but is obviously corrupted, it puts a reason 48 /// into the string error argument and returns null. 49 const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) { 50 // If the file is too small to be a header map, ignore it. 51 unsigned FileSize = FE->getSize(); 52 if (FileSize <= sizeof(HMapHeader)) return nullptr; 53 54 auto FileBuffer = FM.getBufferForFile(FE); 55 if (!FileBuffer || !*FileBuffer) 56 return nullptr; 57 bool NeedsByteSwap; 58 if (!checkHeader(**FileBuffer, NeedsByteSwap)) 59 return nullptr; 60 return new HeaderMap(std::move(*FileBuffer), NeedsByteSwap); 61 } 62 63 bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File, 64 bool &NeedsByteSwap) { 65 if (File.getBufferSize() <= sizeof(HMapHeader)) 66 return false; 67 const char *FileStart = File.getBufferStart(); 68 69 // We know the file is at least as big as the header, check it now. 70 const HMapHeader *Header = reinterpret_cast<const HMapHeader*>(FileStart); 71 72 // Sniff it to see if it's a headermap by checking the magic number and 73 // version. 74 if (Header->Magic == HMAP_HeaderMagicNumber && 75 Header->Version == HMAP_HeaderVersion) 76 NeedsByteSwap = false; 77 else if (Header->Magic == llvm::ByteSwap_32(HMAP_HeaderMagicNumber) && 78 Header->Version == llvm::ByteSwap_16(HMAP_HeaderVersion)) 79 NeedsByteSwap = true; // Mixed endianness headermap. 80 else 81 return false; // Not a header map. 82 83 if (Header->Reserved != 0) 84 return false; 85 86 // Check the number of buckets. It should be a power of two, and there 87 // should be enough space in the file for all of them. 88 auto NumBuckets = NeedsByteSwap 89 ? llvm::sys::getSwappedBytes(Header->NumBuckets) 90 : Header->NumBuckets; 91 if (NumBuckets & (NumBuckets - 1)) 92 return false; 93 if (File.getBufferSize() < 94 sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets) 95 return false; 96 97 // Okay, everything looks good. 98 return true; 99 } 100 101 //===----------------------------------------------------------------------===// 102 // Utility Methods 103 //===----------------------------------------------------------------------===// 104 105 106 /// getFileName - Return the filename of the headermap. 107 const char *HeaderMapImpl::getFileName() const { 108 return FileBuffer->getBufferIdentifier(); 109 } 110 111 unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const { 112 if (!NeedsBSwap) return X; 113 return llvm::ByteSwap_32(X); 114 } 115 116 /// getHeader - Return a reference to the file header, in unbyte-swapped form. 117 /// This method cannot fail. 118 const HMapHeader &HeaderMapImpl::getHeader() const { 119 // We know the file is at least as big as the header. Return it. 120 return *reinterpret_cast<const HMapHeader*>(FileBuffer->getBufferStart()); 121 } 122 123 /// getBucket - Return the specified hash table bucket from the header map, 124 /// bswap'ing its fields as appropriate. If the bucket number is not valid, 125 /// this return a bucket with an empty key (0). 126 HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { 127 assert(FileBuffer->getBufferSize() >= 128 sizeof(HMapHeader) + sizeof(HMapBucket) * BucketNo && 129 "Expected bucket to be in range"); 130 131 HMapBucket Result; 132 Result.Key = HMAP_EmptyBucketKey; 133 134 const HMapBucket *BucketArray = 135 reinterpret_cast<const HMapBucket*>(FileBuffer->getBufferStart() + 136 sizeof(HMapHeader)); 137 const HMapBucket *BucketPtr = BucketArray+BucketNo; 138 139 // Load the values, bswapping as needed. 140 Result.Key = getEndianAdjustedWord(BucketPtr->Key); 141 Result.Prefix = getEndianAdjustedWord(BucketPtr->Prefix); 142 Result.Suffix = getEndianAdjustedWord(BucketPtr->Suffix); 143 return Result; 144 } 145 146 /// getString - Look up the specified string in the string table. If the string 147 /// index is not valid, it returns an empty string. 148 StringRef HeaderMapImpl::getString(unsigned StrTabIdx) const { 149 // Add the start of the string table to the idx. 150 StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset); 151 152 // Check for invalid index. 153 if (StrTabIdx >= FileBuffer->getBufferSize()) 154 return nullptr; 155 156 // Otherwise, we have a valid pointer into the file. Just return it. We know 157 // that the "string" can not overrun the end of the file, because the buffer 158 // is nul terminated by virtue of being a MemoryBuffer. 159 return FileBuffer->getBufferStart()+StrTabIdx; 160 } 161 162 //===----------------------------------------------------------------------===// 163 // The Main Drivers 164 //===----------------------------------------------------------------------===// 165 166 /// dump - Print the contents of this headermap to stderr. 167 LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { 168 const HMapHeader &Hdr = getHeader(); 169 unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); 170 171 llvm::dbgs() << "Header Map " << getFileName() << ":\n " << NumBuckets 172 << ", " << getEndianAdjustedWord(Hdr.NumEntries) << "\n"; 173 174 for (unsigned i = 0; i != NumBuckets; ++i) { 175 HMapBucket B = getBucket(i); 176 if (B.Key == HMAP_EmptyBucketKey) continue; 177 178 StringRef Key = getString(B.Key); 179 StringRef Prefix = getString(B.Prefix); 180 StringRef Suffix = getString(B.Suffix); 181 llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '" 182 << Suffix << "'\n"; 183 } 184 } 185 186 /// LookupFile - Check to see if the specified relative filename is located in 187 /// this HeaderMap. If so, open it and return its FileEntry. 188 const FileEntry *HeaderMap::LookupFile( 189 StringRef Filename, FileManager &FM) const { 190 191 SmallString<1024> Path; 192 StringRef Dest = HeaderMapImpl::lookupFilename(Filename, Path); 193 if (Dest.empty()) 194 return nullptr; 195 196 return FM.getFile(Dest); 197 } 198 199 StringRef HeaderMapImpl::lookupFilename(StringRef Filename, 200 SmallVectorImpl<char> &DestPath) const { 201 const HMapHeader &Hdr = getHeader(); 202 unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); 203 204 // Don't probe infinitely. This should be checked before constructing. 205 assert(!(NumBuckets & (NumBuckets - 1)) && "Expected power of 2"); 206 207 // Linearly probe the hash table. 208 for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) { 209 HMapBucket B = getBucket(Bucket & (NumBuckets-1)); 210 if (B.Key == HMAP_EmptyBucketKey) return StringRef(); // Hash miss. 211 212 // See if the key matches. If not, probe on. 213 if (!Filename.equals_lower(getString(B.Key))) 214 continue; 215 216 // If so, we have a match in the hash table. Construct the destination 217 // path. 218 StringRef Prefix = getString(B.Prefix); 219 StringRef Suffix = getString(B.Suffix); 220 DestPath.clear(); 221 DestPath.append(Prefix.begin(), Prefix.end()); 222 DestPath.append(Suffix.begin(), Suffix.end()); 223 return StringRef(DestPath.begin(), DestPath.size()); 224 } 225 } 226