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 <cstdio> 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. 87 auto NumBuckets = NeedsByteSwap 88 ? llvm::sys::getSwappedBytes(Header->NumBuckets) 89 : Header->NumBuckets; 90 91 // If the number of buckets is not a power of two, the headermap is corrupt. 92 if (NumBuckets & (NumBuckets - 1)) 93 return false; 94 95 // Okay, everything looks good. 96 return true; 97 } 98 99 //===----------------------------------------------------------------------===// 100 // Utility Methods 101 //===----------------------------------------------------------------------===// 102 103 104 /// getFileName - Return the filename of the headermap. 105 const char *HeaderMapImpl::getFileName() const { 106 return FileBuffer->getBufferIdentifier(); 107 } 108 109 unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const { 110 if (!NeedsBSwap) return X; 111 return llvm::ByteSwap_32(X); 112 } 113 114 /// getHeader - Return a reference to the file header, in unbyte-swapped form. 115 /// This method cannot fail. 116 const HMapHeader &HeaderMapImpl::getHeader() const { 117 // We know the file is at least as big as the header. Return it. 118 return *reinterpret_cast<const HMapHeader*>(FileBuffer->getBufferStart()); 119 } 120 121 /// getBucket - Return the specified hash table bucket from the header map, 122 /// bswap'ing its fields as appropriate. If the bucket number is not valid, 123 /// this return a bucket with an empty key (0). 124 HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { 125 HMapBucket Result; 126 Result.Key = HMAP_EmptyBucketKey; 127 128 const HMapBucket *BucketArray = 129 reinterpret_cast<const HMapBucket*>(FileBuffer->getBufferStart() + 130 sizeof(HMapHeader)); 131 132 const HMapBucket *BucketPtr = BucketArray+BucketNo; 133 if ((const char*)(BucketPtr+1) > FileBuffer->getBufferEnd()) { 134 Result.Prefix = 0; 135 Result.Suffix = 0; 136 return Result; // Invalid buffer, corrupt hmap. 137 } 138 139 // Otherwise, the bucket is valid. 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 const char *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 fprintf(stderr, "Header Map %s:\n %d buckets, %d entries\n", 172 getFileName(), NumBuckets, 173 getEndianAdjustedWord(Hdr.NumEntries)); 174 175 for (unsigned i = 0; i != NumBuckets; ++i) { 176 HMapBucket B = getBucket(i); 177 if (B.Key == HMAP_EmptyBucketKey) continue; 178 179 const char *Key = getString(B.Key); 180 const char *Prefix = getString(B.Prefix); 181 const char *Suffix = getString(B.Suffix); 182 fprintf(stderr, " %d. %s -> '%s' '%s'\n", i, Key, Prefix, Suffix); 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