| //===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SymbolMap.h" |
| #include "DebugMap.h" |
| #include "MachOUtils.h" |
| |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/WithColor.h" |
| |
| #ifdef __APPLE__ |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <uuid/uuid.h> |
| #endif |
| |
| namespace llvm { |
| namespace dsymutil { |
| |
| StringRef SymbolMapTranslator::operator()(StringRef Input) { |
| if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) |
| return Input; |
| |
| bool MightNeedUnderscore = false; |
| StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); |
| if (Line[0] == '#') { |
| Line = Line.drop_front(); |
| MightNeedUnderscore = true; |
| } |
| |
| std::size_t LineNumber = std::numeric_limits<std::size_t>::max(); |
| Line.split('_').first.getAsInteger(10, LineNumber); |
| if (LineNumber >= UnobfuscatedStrings.size()) { |
| WithColor::warning() << "reference to a unexisting unobfuscated string " |
| << Input << ": symbol map mismatch?\n" |
| << Line << '\n'; |
| return Input; |
| } |
| |
| const std::string &Translation = UnobfuscatedStrings[LineNumber]; |
| if (!MightNeedUnderscore || !MangleNames) |
| return Translation; |
| |
| // Objective-C symbols for the MachO symbol table start with a \1. Please see |
| // `CGObjCCommonMac::GetNameForMethod` in clang. |
| if (Translation[0] == 1) |
| return StringRef(Translation).drop_front(); |
| |
| // We need permanent storage for the string we are about to create. Just |
| // append it to the vector containing translations. This should only happen |
| // during MachO symbol table translation, thus there should be no risk on |
| // exponential growth. |
| UnobfuscatedStrings.emplace_back("_" + Translation); |
| return UnobfuscatedStrings.back(); |
| } |
| |
| SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile, |
| const DebugMap &Map) const { |
| if (SymbolMap.empty()) |
| return {}; |
| |
| std::string SymbolMapPath = SymbolMap; |
| |
| #if __APPLE__ |
| // Look through the UUID Map. |
| if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { |
| uuid_string_t UUIDString; |
| uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); |
| |
| SmallString<256> PlistPath( |
| sys::path::parent_path(sys::path::parent_path(InputFile))); |
| sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); |
| |
| CFStringRef plistFile = CFStringCreateWithCString( |
| kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); |
| CFURLRef fileURL = CFURLCreateWithFileSystemPath( |
| kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); |
| CFReadStreamRef resourceData = |
| CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); |
| if (resourceData) { |
| CFReadStreamOpen(resourceData); |
| CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( |
| kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, |
| nullptr, nullptr); |
| |
| if (plist) { |
| if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { |
| CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( |
| plist, CFSTR("DBGOriginalUUID")); |
| |
| StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); |
| SmallString<256> BCSymbolMapPath(SymbolMapPath); |
| sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); |
| SymbolMapPath = BCSymbolMapPath.str(); |
| } |
| CFRelease(plist); |
| } |
| CFReadStreamClose(resourceData); |
| CFRelease(resourceData); |
| } |
| CFRelease(fileURL); |
| CFRelease(plistFile); |
| } |
| #endif |
| |
| if (sys::fs::is_directory(SymbolMapPath)) { |
| SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + |
| MachOUtils::getArchName(Map.getTriple().getArchName()) + |
| ".bcsymbolmap") |
| .str(); |
| } |
| |
| auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); |
| if (auto EC = ErrOrMemBuffer.getError()) { |
| WithColor::warning() << SymbolMapPath << ": " << EC.message() |
| << ": not unobfuscating.\n"; |
| return {}; |
| } |
| |
| std::vector<std::string> UnobfuscatedStrings; |
| auto &MemBuf = **ErrOrMemBuffer; |
| StringRef Data(MemBuf.getBufferStart(), |
| MemBuf.getBufferEnd() - MemBuf.getBufferStart()); |
| StringRef LHS; |
| std::tie(LHS, Data) = Data.split('\n'); |
| bool MangleNames = false; |
| |
| // Check version string first. |
| if (!LHS.startswith("BCSymbolMap Version:")) { |
| // Version string not present, warns but try to parse it. |
| WithColor::warning() << SymbolMapPath |
| << " is missing version string: assuming 1.0.\n"; |
| UnobfuscatedStrings.emplace_back(LHS); |
| } else if (LHS.equals("BCSymbolMap Version: 1.0")) { |
| MangleNames = true; |
| } else if (LHS.equals("BCSymbolMap Version: 2.0")) { |
| MangleNames = false; |
| } else { |
| StringRef VersionNum; |
| std::tie(LHS, VersionNum) = LHS.split(':'); |
| WithColor::warning() << SymbolMapPath |
| << " has unsupported symbol map version" << VersionNum |
| << ": not unobfuscating.\n"; |
| return {}; |
| } |
| |
| while (!Data.empty()) { |
| std::tie(LHS, Data) = Data.split('\n'); |
| UnobfuscatedStrings.emplace_back(LHS); |
| } |
| |
| return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); |
| } |
| |
| } // namespace dsymutil |
| } // namespace llvm |