1cb14a3feSDimitry Andric //===- Utils.cpp ----------------------------------------------------------===// 2cb14a3feSDimitry Andric // 3cb14a3feSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4cb14a3feSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5cb14a3feSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6cb14a3feSDimitry Andric // 7cb14a3feSDimitry Andric //===----------------------------------------------------------------------===// 8cb14a3feSDimitry Andric // 9cb14a3feSDimitry Andric // Implements utility functions for TextAPI Darwin operations. 10cb14a3feSDimitry Andric // 11cb14a3feSDimitry Andric //===----------------------------------------------------------------------===// 12cb14a3feSDimitry Andric 13cb14a3feSDimitry Andric #include "llvm/TextAPI/Utils.h" 14*0fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h" 15*0fca6ea1SDimitry Andric #include "llvm/TextAPI/TextAPIError.h" 16cb14a3feSDimitry Andric 17cb14a3feSDimitry Andric using namespace llvm; 18cb14a3feSDimitry Andric using namespace llvm::MachO; 19cb14a3feSDimitry Andric 20cb14a3feSDimitry Andric void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, 21cb14a3feSDimitry Andric const Twine &Extension) { 22cb14a3feSDimitry Andric StringRef P(Path.begin(), Path.size()); 23cb14a3feSDimitry Andric auto ParentPath = sys::path::parent_path(P); 24cb14a3feSDimitry Andric auto Filename = sys::path::filename(P); 25cb14a3feSDimitry Andric 26cb14a3feSDimitry Andric if (!ParentPath.ends_with(Filename.str() + ".framework")) { 27cb14a3feSDimitry Andric sys::path::replace_extension(Path, Extension); 28cb14a3feSDimitry Andric return; 29cb14a3feSDimitry Andric } 30cb14a3feSDimitry Andric // Framework dylibs do not have a file extension, in those cases the new 31cb14a3feSDimitry Andric // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: 32cb14a3feSDimitry Andric // "tbd", the result is "Foo.framework/Foo.tbd". 33cb14a3feSDimitry Andric SmallString<8> Storage; 34cb14a3feSDimitry Andric StringRef Ext = Extension.toStringRef(Storage); 35cb14a3feSDimitry Andric 36cb14a3feSDimitry Andric // Append '.' if needed. 37cb14a3feSDimitry Andric if (!Ext.empty() && Ext[0] != '.') 38cb14a3feSDimitry Andric Path.push_back('.'); 39cb14a3feSDimitry Andric 40cb14a3feSDimitry Andric // Append extension. 41cb14a3feSDimitry Andric Path.append(Ext.begin(), Ext.end()); 42cb14a3feSDimitry Andric } 43*0fca6ea1SDimitry Andric 44*0fca6ea1SDimitry Andric std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path, 45*0fca6ea1SDimitry Andric bool &Result) { 46*0fca6ea1SDimitry Andric Result = false; 47*0fca6ea1SDimitry Andric SmallString<PATH_MAX> Storage; 48*0fca6ea1SDimitry Andric auto P = Path.toNullTerminatedStringRef(Storage); 49*0fca6ea1SDimitry Andric sys::fs::file_status Stat1; 50*0fca6ea1SDimitry Andric auto EC = sys::fs::status(P.data(), Stat1); 51*0fca6ea1SDimitry Andric if (EC == std::errc::too_many_symbolic_link_levels) { 52*0fca6ea1SDimitry Andric Result = true; 53*0fca6ea1SDimitry Andric return {}; 54*0fca6ea1SDimitry Andric } 55*0fca6ea1SDimitry Andric 56*0fca6ea1SDimitry Andric if (EC) 57*0fca6ea1SDimitry Andric return EC; 58*0fca6ea1SDimitry Andric 59*0fca6ea1SDimitry Andric StringRef Parent = sys::path::parent_path(P); 60*0fca6ea1SDimitry Andric while (!Parent.empty()) { 61*0fca6ea1SDimitry Andric sys::fs::file_status Stat2; 62*0fca6ea1SDimitry Andric if (auto ec = sys::fs::status(Parent, Stat2)) 63*0fca6ea1SDimitry Andric return ec; 64*0fca6ea1SDimitry Andric 65*0fca6ea1SDimitry Andric if (sys::fs::equivalent(Stat1, Stat2)) { 66*0fca6ea1SDimitry Andric Result = true; 67*0fca6ea1SDimitry Andric return {}; 68*0fca6ea1SDimitry Andric } 69*0fca6ea1SDimitry Andric 70*0fca6ea1SDimitry Andric Parent = sys::path::parent_path(Parent); 71*0fca6ea1SDimitry Andric } 72*0fca6ea1SDimitry Andric return {}; 73*0fca6ea1SDimitry Andric } 74*0fca6ea1SDimitry Andric 75*0fca6ea1SDimitry Andric std::error_code 76*0fca6ea1SDimitry Andric llvm::MachO::make_relative(StringRef From, StringRef To, 77*0fca6ea1SDimitry Andric SmallVectorImpl<char> &RelativePath) { 78*0fca6ea1SDimitry Andric SmallString<PATH_MAX> Src = From; 79*0fca6ea1SDimitry Andric SmallString<PATH_MAX> Dst = To; 80*0fca6ea1SDimitry Andric if (auto EC = sys::fs::make_absolute(Src)) 81*0fca6ea1SDimitry Andric return EC; 82*0fca6ea1SDimitry Andric 83*0fca6ea1SDimitry Andric if (auto EC = sys::fs::make_absolute(Dst)) 84*0fca6ea1SDimitry Andric return EC; 85*0fca6ea1SDimitry Andric 86*0fca6ea1SDimitry Andric SmallString<PATH_MAX> Result; 87*0fca6ea1SDimitry Andric Src = sys::path::parent_path(From); 88*0fca6ea1SDimitry Andric auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst), 89*0fca6ea1SDimitry Andric IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst); 90*0fca6ea1SDimitry Andric // Ignore the common part. 91*0fca6ea1SDimitry Andric for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) { 92*0fca6ea1SDimitry Andric if (*IT1 != *IT2) 93*0fca6ea1SDimitry Andric break; 94*0fca6ea1SDimitry Andric } 95*0fca6ea1SDimitry Andric 96*0fca6ea1SDimitry Andric for (; IT1 != IE1; ++IT1) 97*0fca6ea1SDimitry Andric sys::path::append(Result, "../"); 98*0fca6ea1SDimitry Andric 99*0fca6ea1SDimitry Andric for (; IT2 != IE2; ++IT2) 100*0fca6ea1SDimitry Andric sys::path::append(Result, *IT2); 101*0fca6ea1SDimitry Andric 102*0fca6ea1SDimitry Andric if (Result.empty()) 103*0fca6ea1SDimitry Andric Result = "."; 104*0fca6ea1SDimitry Andric 105*0fca6ea1SDimitry Andric RelativePath.swap(Result); 106*0fca6ea1SDimitry Andric 107*0fca6ea1SDimitry Andric return {}; 108*0fca6ea1SDimitry Andric } 109*0fca6ea1SDimitry Andric 110*0fca6ea1SDimitry Andric bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) { 111*0fca6ea1SDimitry Andric // Remove the iOSSupport and DriverKit prefix to identify public locations. 112*0fca6ea1SDimitry Andric Path.consume_front(MACCATALYST_PREFIX_PATH); 113*0fca6ea1SDimitry Andric Path.consume_front(DRIVERKIT_PREFIX_PATH); 114*0fca6ea1SDimitry Andric // Also /Library/Apple prefix for ROSP. 115*0fca6ea1SDimitry Andric Path.consume_front("/Library/Apple"); 116*0fca6ea1SDimitry Andric 117*0fca6ea1SDimitry Andric if (Path.starts_with("/usr/local/lib")) 118*0fca6ea1SDimitry Andric return true; 119*0fca6ea1SDimitry Andric 120*0fca6ea1SDimitry Andric if (Path.starts_with("/System/Library/PrivateFrameworks")) 121*0fca6ea1SDimitry Andric return true; 122*0fca6ea1SDimitry Andric 123*0fca6ea1SDimitry Andric // Everything in /usr/lib/swift (including sub-directories) are considered 124*0fca6ea1SDimitry Andric // public. 125*0fca6ea1SDimitry Andric if (Path.consume_front("/usr/lib/swift/")) 126*0fca6ea1SDimitry Andric return false; 127*0fca6ea1SDimitry Andric 128*0fca6ea1SDimitry Andric // Only libraries directly in /usr/lib are public. All other libraries in 129*0fca6ea1SDimitry Andric // sub-directories are private. 130*0fca6ea1SDimitry Andric if (Path.consume_front("/usr/lib/")) 131*0fca6ea1SDimitry Andric return Path.contains('/'); 132*0fca6ea1SDimitry Andric 133*0fca6ea1SDimitry Andric // "/System/Library/Frameworks/" is a public location. 134*0fca6ea1SDimitry Andric if (Path.starts_with("/System/Library/Frameworks/")) { 135*0fca6ea1SDimitry Andric StringRef Name, Rest; 136*0fca6ea1SDimitry Andric std::tie(Name, Rest) = 137*0fca6ea1SDimitry Andric Path.drop_front(sizeof("/System/Library/Frameworks")).split('.'); 138*0fca6ea1SDimitry Andric 139*0fca6ea1SDimitry Andric // Allow symlinks to top-level frameworks. 140*0fca6ea1SDimitry Andric if (IsSymLink && Rest == "framework") 141*0fca6ea1SDimitry Andric return false; 142*0fca6ea1SDimitry Andric 143*0fca6ea1SDimitry Andric // Only top level framework are public. 144*0fca6ea1SDimitry Andric // /System/Library/Frameworks/Foo.framework/Foo ==> true 145*0fca6ea1SDimitry Andric // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true 146*0fca6ea1SDimitry Andric // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false 147*0fca6ea1SDimitry Andric // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar 148*0fca6ea1SDimitry Andric // ==> false 149*0fca6ea1SDimitry Andric // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo 150*0fca6ea1SDimitry Andric // ==> false 151*0fca6ea1SDimitry Andric return !(Rest.starts_with("framework/") && 152*0fca6ea1SDimitry Andric (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) || 153*0fca6ea1SDimitry Andric (IsSymLink && Rest.ends_with("Current")))); 154*0fca6ea1SDimitry Andric } 155*0fca6ea1SDimitry Andric return false; 156*0fca6ea1SDimitry Andric } 157*0fca6ea1SDimitry Andric 158*0fca6ea1SDimitry Andric static StringLiteral RegexMetachars = "()^$|+.[]\\{}"; 159*0fca6ea1SDimitry Andric 160*0fca6ea1SDimitry Andric llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) { 161*0fca6ea1SDimitry Andric SmallString<128> RegexString("^"); 162*0fca6ea1SDimitry Andric unsigned NumWildcards = 0; 163*0fca6ea1SDimitry Andric for (unsigned i = 0; i < Glob.size(); ++i) { 164*0fca6ea1SDimitry Andric char C = Glob[i]; 165*0fca6ea1SDimitry Andric switch (C) { 166*0fca6ea1SDimitry Andric case '?': 167*0fca6ea1SDimitry Andric RegexString += '.'; 168*0fca6ea1SDimitry Andric break; 169*0fca6ea1SDimitry Andric case '*': { 170*0fca6ea1SDimitry Andric const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr; 171*0fca6ea1SDimitry Andric NumWildcards = 1; 172*0fca6ea1SDimitry Andric ++i; 173*0fca6ea1SDimitry Andric while (i < Glob.size() && Glob[i] == '*') { 174*0fca6ea1SDimitry Andric ++NumWildcards; 175*0fca6ea1SDimitry Andric ++i; 176*0fca6ea1SDimitry Andric } 177*0fca6ea1SDimitry Andric const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr; 178*0fca6ea1SDimitry Andric 179*0fca6ea1SDimitry Andric if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') && 180*0fca6ea1SDimitry Andric (NextChar == nullptr || *NextChar == '/')) { 181*0fca6ea1SDimitry Andric RegexString += "(([^/]*(/|$))*)"; 182*0fca6ea1SDimitry Andric } else 183*0fca6ea1SDimitry Andric RegexString += "([^/]*)"; 184*0fca6ea1SDimitry Andric break; 185*0fca6ea1SDimitry Andric } 186*0fca6ea1SDimitry Andric default: 187*0fca6ea1SDimitry Andric if (RegexMetachars.contains(C)) 188*0fca6ea1SDimitry Andric RegexString.push_back('\\'); 189*0fca6ea1SDimitry Andric RegexString.push_back(C); 190*0fca6ea1SDimitry Andric } 191*0fca6ea1SDimitry Andric } 192*0fca6ea1SDimitry Andric RegexString.push_back('$'); 193*0fca6ea1SDimitry Andric if (NumWildcards == 0) 194*0fca6ea1SDimitry Andric return make_error<StringError>("not a glob", inconvertibleErrorCode()); 195*0fca6ea1SDimitry Andric 196*0fca6ea1SDimitry Andric llvm::Regex Rule = Regex(RegexString); 197*0fca6ea1SDimitry Andric std::string Error; 198*0fca6ea1SDimitry Andric if (!Rule.isValid(Error)) 199*0fca6ea1SDimitry Andric return make_error<StringError>(Error, inconvertibleErrorCode()); 200*0fca6ea1SDimitry Andric 201*0fca6ea1SDimitry Andric return std::move(Rule); 202*0fca6ea1SDimitry Andric } 203*0fca6ea1SDimitry Andric 204*0fca6ea1SDimitry Andric Expected<AliasMap> 205*0fca6ea1SDimitry Andric llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) { 206*0fca6ea1SDimitry Andric SmallVector<StringRef, 16> Lines; 207*0fca6ea1SDimitry Andric AliasMap Aliases; 208*0fca6ea1SDimitry Andric Buffer->getBuffer().split(Lines, "\n", /*MaxSplit=*/-1, 209*0fca6ea1SDimitry Andric /*KeepEmpty=*/false); 210*0fca6ea1SDimitry Andric for (const StringRef Line : Lines) { 211*0fca6ea1SDimitry Andric StringRef L = Line.trim(); 212*0fca6ea1SDimitry Andric if (L.empty()) 213*0fca6ea1SDimitry Andric continue; 214*0fca6ea1SDimitry Andric // Skip comments. 215*0fca6ea1SDimitry Andric if (L.starts_with("#")) 216*0fca6ea1SDimitry Andric continue; 217*0fca6ea1SDimitry Andric StringRef Symbol, Remain, Alias; 218*0fca6ea1SDimitry Andric // Base symbol is separated by whitespace. 219*0fca6ea1SDimitry Andric std::tie(Symbol, Remain) = getToken(L); 220*0fca6ea1SDimitry Andric // The Alias symbol ends before a comment or EOL. 221*0fca6ea1SDimitry Andric std::tie(Alias, Remain) = getToken(Remain, "#"); 222*0fca6ea1SDimitry Andric Alias = Alias.trim(); 223*0fca6ea1SDimitry Andric if (Alias.empty()) 224*0fca6ea1SDimitry Andric return make_error<TextAPIError>( 225*0fca6ea1SDimitry Andric TextAPIError(TextAPIErrorCode::InvalidInputFormat, 226*0fca6ea1SDimitry Andric ("missing alias for: " + Symbol).str())); 227*0fca6ea1SDimitry Andric SimpleSymbol AliasSym = parseSymbol(Alias); 228*0fca6ea1SDimitry Andric SimpleSymbol BaseSym = parseSymbol(Symbol); 229*0fca6ea1SDimitry Andric Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(), 230*0fca6ea1SDimitry Andric BaseSym.Kind}; 231*0fca6ea1SDimitry Andric } 232*0fca6ea1SDimitry Andric 233*0fca6ea1SDimitry Andric return Aliases; 234*0fca6ea1SDimitry Andric } 235*0fca6ea1SDimitry Andric 236*0fca6ea1SDimitry Andric PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths, 237*0fca6ea1SDimitry Andric PlatformType Platform) { 238*0fca6ea1SDimitry Andric PathSeq Result; 239*0fca6ea1SDimitry Andric for (const auto &[Path, CurrP] : Paths) { 240*0fca6ea1SDimitry Andric if (!CurrP.has_value() || CurrP.value() == Platform) 241*0fca6ea1SDimitry Andric Result.push_back(Path); 242*0fca6ea1SDimitry Andric } 243*0fca6ea1SDimitry Andric return Result; 244*0fca6ea1SDimitry Andric } 245