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 
operator ()(StringRef Input)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   // `MangleContext::mangleObjCMethodName` 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 
Load(StringRef InputFile,const DebugMap & Map) const62 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