1 //===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "SymbolMap.h" 10 #include "DebugMap.h" 11 #include "MachOUtils.h" 12 13 #include "llvm/Support/FileSystem.h" 14 #include "llvm/Support/Path.h" 15 #include "llvm/Support/WithColor.h" 16 17 #ifdef __APPLE__ 18 #include <CoreFoundation/CoreFoundation.h> 19 #include <uuid/uuid.h> 20 #endif 21 22 namespace llvm { 23 namespace dsymutil { 24 25 StringRef SymbolMapTranslator::operator()(StringRef Input) { 26 if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) 27 return Input; 28 29 bool MightNeedUnderscore = false; 30 StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); 31 if (Line[0] == '#') { 32 Line = Line.drop_front(); 33 MightNeedUnderscore = true; 34 } 35 36 std::size_t LineNumber = std::numeric_limits<std::size_t>::max(); 37 Line.split('_').first.getAsInteger(10, LineNumber); 38 if (LineNumber >= UnobfuscatedStrings.size()) { 39 WithColor::warning() << "reference to a unexisting unobfuscated string " 40 << Input << ": symbol map mismatch?\n" 41 << Line << '\n'; 42 return Input; 43 } 44 45 const std::string &Translation = UnobfuscatedStrings[LineNumber]; 46 if (!MightNeedUnderscore || !MangleNames) 47 return Translation; 48 49 // Objective-C symbols for the MachO symbol table start with a \1. Please see 50 // `CGObjCCommonMac::GetNameForMethod` in clang. 51 if (Translation[0] == 1) 52 return StringRef(Translation).drop_front(); 53 54 // We need permanent storage for the string we are about to create. Just 55 // append it to the vector containing translations. This should only happen 56 // during MachO symbol table translation, thus there should be no risk on 57 // exponential growth. 58 UnobfuscatedStrings.emplace_back("_" + Translation); 59 return UnobfuscatedStrings.back(); 60 } 61 62 SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile, 63 const DebugMap &Map) const { 64 if (SymbolMap.empty()) 65 return {}; 66 67 std::string SymbolMapPath = SymbolMap; 68 69 #if __APPLE__ 70 // Look through the UUID Map. 71 if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { 72 uuid_string_t UUIDString; 73 uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); 74 75 SmallString<256> PlistPath( 76 sys::path::parent_path(sys::path::parent_path(InputFile))); 77 sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); 78 79 CFStringRef plistFile = CFStringCreateWithCString( 80 kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); 81 CFURLRef fileURL = CFURLCreateWithFileSystemPath( 82 kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); 83 CFReadStreamRef resourceData = 84 CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); 85 if (resourceData) { 86 CFReadStreamOpen(resourceData); 87 CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( 88 kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, 89 nullptr, nullptr); 90 91 if (plist) { 92 if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { 93 CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( 94 plist, CFSTR("DBGOriginalUUID")); 95 96 StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); 97 SmallString<256> BCSymbolMapPath(SymbolMapPath); 98 sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); 99 SymbolMapPath = std::string(BCSymbolMapPath); 100 } 101 CFRelease(plist); 102 } 103 CFReadStreamClose(resourceData); 104 CFRelease(resourceData); 105 } 106 CFRelease(fileURL); 107 CFRelease(plistFile); 108 } 109 #endif 110 111 if (sys::fs::is_directory(SymbolMapPath)) { 112 SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + 113 MachOUtils::getArchName(Map.getTriple().getArchName()) + 114 ".bcsymbolmap") 115 .str(); 116 } 117 118 auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); 119 if (auto EC = ErrOrMemBuffer.getError()) { 120 WithColor::warning() << SymbolMapPath << ": " << EC.message() 121 << ": not unobfuscating.\n"; 122 return {}; 123 } 124 125 std::vector<std::string> UnobfuscatedStrings; 126 auto &MemBuf = **ErrOrMemBuffer; 127 StringRef Data(MemBuf.getBufferStart(), 128 MemBuf.getBufferEnd() - MemBuf.getBufferStart()); 129 StringRef LHS; 130 std::tie(LHS, Data) = Data.split('\n'); 131 bool MangleNames = false; 132 133 // Check version string first. 134 if (!LHS.startswith("BCSymbolMap Version:")) { 135 // Version string not present, warns but try to parse it. 136 WithColor::warning() << SymbolMapPath 137 << " is missing version string: assuming 1.0.\n"; 138 UnobfuscatedStrings.emplace_back(LHS); 139 } else if (LHS.equals("BCSymbolMap Version: 1.0")) { 140 MangleNames = true; 141 } else if (LHS.equals("BCSymbolMap Version: 2.0")) { 142 MangleNames = false; 143 } else { 144 StringRef VersionNum; 145 std::tie(LHS, VersionNum) = LHS.split(':'); 146 WithColor::warning() << SymbolMapPath 147 << " has unsupported symbol map version" << VersionNum 148 << ": not unobfuscating.\n"; 149 return {}; 150 } 151 152 while (!Data.empty()) { 153 std::tie(LHS, Data) = Data.split('\n'); 154 UnobfuscatedStrings.emplace_back(LHS); 155 } 156 157 return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); 158 } 159 160 } // namespace dsymutil 161 } // namespace llvm 162