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