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