1c6f29dbbSCyndy Ishida //===- Utils.cpp ----------------------------------------------------------===// 2c6f29dbbSCyndy Ishida // 3c6f29dbbSCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c6f29dbbSCyndy Ishida // See https://llvm.org/LICENSE.txt for license information. 5c6f29dbbSCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c6f29dbbSCyndy Ishida // 7c6f29dbbSCyndy Ishida //===----------------------------------------------------------------------===// 8c6f29dbbSCyndy Ishida // 9c6f29dbbSCyndy Ishida // Implements utility functions for TextAPI Darwin operations. 10c6f29dbbSCyndy Ishida // 11c6f29dbbSCyndy Ishida //===----------------------------------------------------------------------===// 12c6f29dbbSCyndy Ishida 13c6f29dbbSCyndy Ishida #include "llvm/TextAPI/Utils.h" 144c18681aSCyndy Ishida #include "llvm/ADT/StringExtras.h" 154c18681aSCyndy Ishida #include "llvm/TextAPI/TextAPIError.h" 16c6f29dbbSCyndy Ishida 17c6f29dbbSCyndy Ishida using namespace llvm; 18c6f29dbbSCyndy Ishida using namespace llvm::MachO; 19c6f29dbbSCyndy Ishida 20c6f29dbbSCyndy Ishida void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, 21c6f29dbbSCyndy Ishida const Twine &Extension) { 22c6f29dbbSCyndy Ishida StringRef P(Path.begin(), Path.size()); 23c6f29dbbSCyndy Ishida auto ParentPath = sys::path::parent_path(P); 24c6f29dbbSCyndy Ishida auto Filename = sys::path::filename(P); 25c6f29dbbSCyndy Ishida 26c6f29dbbSCyndy Ishida if (!ParentPath.ends_with(Filename.str() + ".framework")) { 27c6f29dbbSCyndy Ishida sys::path::replace_extension(Path, Extension); 28c6f29dbbSCyndy Ishida return; 29c6f29dbbSCyndy Ishida } 30c6f29dbbSCyndy Ishida // Framework dylibs do not have a file extension, in those cases the new 31c6f29dbbSCyndy Ishida // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: 32c6f29dbbSCyndy Ishida // "tbd", the result is "Foo.framework/Foo.tbd". 33c6f29dbbSCyndy Ishida SmallString<8> Storage; 34c6f29dbbSCyndy Ishida StringRef Ext = Extension.toStringRef(Storage); 35c6f29dbbSCyndy Ishida 36c6f29dbbSCyndy Ishida // Append '.' if needed. 37c6f29dbbSCyndy Ishida if (!Ext.empty() && Ext[0] != '.') 38c6f29dbbSCyndy Ishida Path.push_back('.'); 39c6f29dbbSCyndy Ishida 40c6f29dbbSCyndy Ishida // Append extension. 41c6f29dbbSCyndy Ishida Path.append(Ext.begin(), Ext.end()); 42c6f29dbbSCyndy Ishida } 437189219eSCyndy Ishida 447189219eSCyndy Ishida std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path, 457189219eSCyndy Ishida bool &Result) { 467189219eSCyndy Ishida Result = false; 477189219eSCyndy Ishida SmallString<PATH_MAX> Storage; 487189219eSCyndy Ishida auto P = Path.toNullTerminatedStringRef(Storage); 497189219eSCyndy Ishida sys::fs::file_status Stat1; 507189219eSCyndy Ishida auto EC = sys::fs::status(P.data(), Stat1); 517189219eSCyndy Ishida if (EC == std::errc::too_many_symbolic_link_levels) { 527189219eSCyndy Ishida Result = true; 537189219eSCyndy Ishida return {}; 547189219eSCyndy Ishida } 557189219eSCyndy Ishida 567189219eSCyndy Ishida if (EC) 577189219eSCyndy Ishida return EC; 587189219eSCyndy Ishida 597189219eSCyndy Ishida StringRef Parent = sys::path::parent_path(P); 607189219eSCyndy Ishida while (!Parent.empty()) { 617189219eSCyndy Ishida sys::fs::file_status Stat2; 627189219eSCyndy Ishida if (auto ec = sys::fs::status(Parent, Stat2)) 637189219eSCyndy Ishida return ec; 647189219eSCyndy Ishida 657189219eSCyndy Ishida if (sys::fs::equivalent(Stat1, Stat2)) { 667189219eSCyndy Ishida Result = true; 677189219eSCyndy Ishida return {}; 687189219eSCyndy Ishida } 697189219eSCyndy Ishida 707189219eSCyndy Ishida Parent = sys::path::parent_path(Parent); 717189219eSCyndy Ishida } 727189219eSCyndy Ishida return {}; 737189219eSCyndy Ishida } 747189219eSCyndy Ishida 757189219eSCyndy Ishida std::error_code 767189219eSCyndy Ishida llvm::MachO::make_relative(StringRef From, StringRef To, 777189219eSCyndy Ishida SmallVectorImpl<char> &RelativePath) { 787189219eSCyndy Ishida SmallString<PATH_MAX> Src = From; 797189219eSCyndy Ishida SmallString<PATH_MAX> Dst = To; 807189219eSCyndy Ishida if (auto EC = sys::fs::make_absolute(Src)) 817189219eSCyndy Ishida return EC; 827189219eSCyndy Ishida 837189219eSCyndy Ishida if (auto EC = sys::fs::make_absolute(Dst)) 847189219eSCyndy Ishida return EC; 857189219eSCyndy Ishida 867189219eSCyndy Ishida SmallString<PATH_MAX> Result; 877189219eSCyndy Ishida Src = sys::path::parent_path(From); 887189219eSCyndy Ishida auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst), 897189219eSCyndy Ishida IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst); 907189219eSCyndy Ishida // Ignore the common part. 917189219eSCyndy Ishida for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) { 927189219eSCyndy Ishida if (*IT1 != *IT2) 937189219eSCyndy Ishida break; 947189219eSCyndy Ishida } 957189219eSCyndy Ishida 967189219eSCyndy Ishida for (; IT1 != IE1; ++IT1) 977189219eSCyndy Ishida sys::path::append(Result, "../"); 987189219eSCyndy Ishida 997189219eSCyndy Ishida for (; IT2 != IE2; ++IT2) 1007189219eSCyndy Ishida sys::path::append(Result, *IT2); 1017189219eSCyndy Ishida 1027189219eSCyndy Ishida if (Result.empty()) 1037189219eSCyndy Ishida Result = "."; 1047189219eSCyndy Ishida 1057189219eSCyndy Ishida RelativePath.swap(Result); 1067189219eSCyndy Ishida 1077189219eSCyndy Ishida return {}; 1087189219eSCyndy Ishida } 1097189219eSCyndy Ishida 1107189219eSCyndy Ishida bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) { 1117189219eSCyndy Ishida // Remove the iOSSupport and DriverKit prefix to identify public locations. 1127189219eSCyndy Ishida Path.consume_front(MACCATALYST_PREFIX_PATH); 1137189219eSCyndy Ishida Path.consume_front(DRIVERKIT_PREFIX_PATH); 1147189219eSCyndy Ishida // Also /Library/Apple prefix for ROSP. 1157189219eSCyndy Ishida Path.consume_front("/Library/Apple"); 1167189219eSCyndy Ishida 1177189219eSCyndy Ishida if (Path.starts_with("/usr/local/lib")) 1187189219eSCyndy Ishida return true; 1197189219eSCyndy Ishida 1207189219eSCyndy Ishida if (Path.starts_with("/System/Library/PrivateFrameworks")) 1217189219eSCyndy Ishida return true; 1227189219eSCyndy Ishida 123*2d48489cSCyndy Ishida if (Path.starts_with("/System/Library/SubFrameworks")) 124*2d48489cSCyndy Ishida return true; 125*2d48489cSCyndy Ishida 1267189219eSCyndy Ishida // Everything in /usr/lib/swift (including sub-directories) are considered 1277189219eSCyndy Ishida // public. 1287189219eSCyndy Ishida if (Path.consume_front("/usr/lib/swift/")) 1297189219eSCyndy Ishida return false; 1307189219eSCyndy Ishida 1317189219eSCyndy Ishida // Only libraries directly in /usr/lib are public. All other libraries in 1327189219eSCyndy Ishida // sub-directories are private. 1337189219eSCyndy Ishida if (Path.consume_front("/usr/lib/")) 1347189219eSCyndy Ishida return Path.contains('/'); 1357189219eSCyndy Ishida 1367189219eSCyndy Ishida // "/System/Library/Frameworks/" is a public location. 1377189219eSCyndy Ishida if (Path.starts_with("/System/Library/Frameworks/")) { 1387189219eSCyndy Ishida StringRef Name, Rest; 1397189219eSCyndy Ishida std::tie(Name, Rest) = 1407189219eSCyndy Ishida Path.drop_front(sizeof("/System/Library/Frameworks")).split('.'); 1417189219eSCyndy Ishida 1427189219eSCyndy Ishida // Allow symlinks to top-level frameworks. 1437189219eSCyndy Ishida if (IsSymLink && Rest == "framework") 1447189219eSCyndy Ishida return false; 1457189219eSCyndy Ishida 1467189219eSCyndy Ishida // Only top level framework are public. 1477189219eSCyndy Ishida // /System/Library/Frameworks/Foo.framework/Foo ==> true 1487189219eSCyndy Ishida // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true 1497189219eSCyndy Ishida // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false 1507189219eSCyndy Ishida // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar 1517189219eSCyndy Ishida // ==> false 1527189219eSCyndy Ishida // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo 1537189219eSCyndy Ishida // ==> false 1547189219eSCyndy Ishida return !(Rest.starts_with("framework/") && 1557189219eSCyndy Ishida (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) || 1567189219eSCyndy Ishida (IsSymLink && Rest.ends_with("Current")))); 1577189219eSCyndy Ishida } 1587189219eSCyndy Ishida return false; 1597189219eSCyndy Ishida } 160487720fcSCyndy Ishida 161487720fcSCyndy Ishida static StringLiteral RegexMetachars = "()^$|+.[]\\{}"; 162487720fcSCyndy Ishida 163487720fcSCyndy Ishida llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) { 164487720fcSCyndy Ishida SmallString<128> RegexString("^"); 165487720fcSCyndy Ishida unsigned NumWildcards = 0; 166487720fcSCyndy Ishida for (unsigned i = 0; i < Glob.size(); ++i) { 167487720fcSCyndy Ishida char C = Glob[i]; 168487720fcSCyndy Ishida switch (C) { 169487720fcSCyndy Ishida case '?': 170487720fcSCyndy Ishida RegexString += '.'; 171487720fcSCyndy Ishida break; 172487720fcSCyndy Ishida case '*': { 173487720fcSCyndy Ishida const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr; 174487720fcSCyndy Ishida NumWildcards = 1; 175487720fcSCyndy Ishida ++i; 176487720fcSCyndy Ishida while (i < Glob.size() && Glob[i] == '*') { 177487720fcSCyndy Ishida ++NumWildcards; 178487720fcSCyndy Ishida ++i; 179487720fcSCyndy Ishida } 180487720fcSCyndy Ishida const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr; 181487720fcSCyndy Ishida 182487720fcSCyndy Ishida if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') && 183487720fcSCyndy Ishida (NextChar == nullptr || *NextChar == '/')) { 184487720fcSCyndy Ishida RegexString += "(([^/]*(/|$))*)"; 185487720fcSCyndy Ishida } else 186487720fcSCyndy Ishida RegexString += "([^/]*)"; 187487720fcSCyndy Ishida break; 188487720fcSCyndy Ishida } 189487720fcSCyndy Ishida default: 19089d09373SKazu Hirata if (RegexMetachars.contains(C)) 191487720fcSCyndy Ishida RegexString.push_back('\\'); 192487720fcSCyndy Ishida RegexString.push_back(C); 193487720fcSCyndy Ishida } 194487720fcSCyndy Ishida } 195487720fcSCyndy Ishida RegexString.push_back('$'); 196487720fcSCyndy Ishida if (NumWildcards == 0) 197487720fcSCyndy Ishida return make_error<StringError>("not a glob", inconvertibleErrorCode()); 198487720fcSCyndy Ishida 199487720fcSCyndy Ishida llvm::Regex Rule = Regex(RegexString); 200487720fcSCyndy Ishida std::string Error; 201487720fcSCyndy Ishida if (!Rule.isValid(Error)) 202487720fcSCyndy Ishida return make_error<StringError>(Error, inconvertibleErrorCode()); 203487720fcSCyndy Ishida 204487720fcSCyndy Ishida return std::move(Rule); 205487720fcSCyndy Ishida } 2064c18681aSCyndy Ishida 2074c18681aSCyndy Ishida Expected<AliasMap> 2084c18681aSCyndy Ishida llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) { 2094c18681aSCyndy Ishida SmallVector<StringRef, 16> Lines; 2104c18681aSCyndy Ishida AliasMap Aliases; 2114c18681aSCyndy Ishida Buffer->getBuffer().split(Lines, "\n", /*MaxSplit=*/-1, 2124c18681aSCyndy Ishida /*KeepEmpty=*/false); 2134c18681aSCyndy Ishida for (const StringRef Line : Lines) { 2144c18681aSCyndy Ishida StringRef L = Line.trim(); 2154c18681aSCyndy Ishida if (L.empty()) 2164c18681aSCyndy Ishida continue; 2174c18681aSCyndy Ishida // Skip comments. 2184c18681aSCyndy Ishida if (L.starts_with("#")) 2194c18681aSCyndy Ishida continue; 2204c18681aSCyndy Ishida StringRef Symbol, Remain, Alias; 221d4a01549SJay Foad // Base symbol is separated by whitespace. 2224c18681aSCyndy Ishida std::tie(Symbol, Remain) = getToken(L); 2234c18681aSCyndy Ishida // The Alias symbol ends before a comment or EOL. 2244c18681aSCyndy Ishida std::tie(Alias, Remain) = getToken(Remain, "#"); 2254c18681aSCyndy Ishida Alias = Alias.trim(); 2264c18681aSCyndy Ishida if (Alias.empty()) 2274c18681aSCyndy Ishida return make_error<TextAPIError>( 2284c18681aSCyndy Ishida TextAPIError(TextAPIErrorCode::InvalidInputFormat, 2294c18681aSCyndy Ishida ("missing alias for: " + Symbol).str())); 2304c18681aSCyndy Ishida SimpleSymbol AliasSym = parseSymbol(Alias); 2314c18681aSCyndy Ishida SimpleSymbol BaseSym = parseSymbol(Symbol); 2324c18681aSCyndy Ishida Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(), 2334c18681aSCyndy Ishida BaseSym.Kind}; 2344c18681aSCyndy Ishida } 2354c18681aSCyndy Ishida 2364c18681aSCyndy Ishida return Aliases; 2374c18681aSCyndy Ishida } 238062f6fe3SCyndy Ishida 239062f6fe3SCyndy Ishida PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths, 240062f6fe3SCyndy Ishida PlatformType Platform) { 241062f6fe3SCyndy Ishida PathSeq Result; 242062f6fe3SCyndy Ishida for (const auto &[Path, CurrP] : Paths) { 243062f6fe3SCyndy Ishida if (!CurrP.has_value() || CurrP.value() == Platform) 244062f6fe3SCyndy Ishida Result.push_back(Path); 245062f6fe3SCyndy Ishida } 246062f6fe3SCyndy Ishida return Result; 247062f6fe3SCyndy Ishida } 248