1 //===- llvm-ifs.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 "ErrorCollector.h" 10 #include "llvm/ADT/StringRef.h" 11 #include "llvm/ADT/StringSwitch.h" 12 #include "llvm/ADT/Triple.h" 13 #include "llvm/InterfaceStub/ELFObjHandler.h" 14 #include "llvm/InterfaceStub/IFSHandler.h" 15 #include "llvm/InterfaceStub/IFSStub.h" 16 #include "llvm/ObjectYAML/yaml2obj.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/Debug.h" 19 #include "llvm/Support/Errc.h" 20 #include "llvm/Support/Error.h" 21 #include "llvm/Support/FileOutputBuffer.h" 22 #include "llvm/Support/MemoryBuffer.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/VersionTuple.h" 25 #include "llvm/Support/WithColor.h" 26 #include "llvm/Support/YAMLTraits.h" 27 #include "llvm/Support/raw_ostream.h" 28 #include "llvm/TextAPI/InterfaceFile.h" 29 #include "llvm/TextAPI/TextAPIReader.h" 30 #include "llvm/TextAPI/TextAPIWriter.h" 31 #include <set> 32 #include <string> 33 #include <vector> 34 35 using namespace llvm; 36 using namespace llvm::yaml; 37 using namespace llvm::MachO; 38 using namespace llvm::ifs; 39 40 #define DEBUG_TYPE "llvm-ifs" 41 42 namespace { 43 const VersionTuple IfsVersionCurrent(3, 0); 44 45 enum class FileFormat { IFS, ELF, TBD }; 46 } // end anonymous namespace 47 48 cl::OptionCategory IfsCategory("Ifs Options"); 49 50 // TODO: Use OptTable for option parsing in the future. 51 // Command line flags: 52 cl::list<std::string> InputFilePaths(cl::Positional, cl::desc("input"), 53 cl::ZeroOrMore, cl::cat(IfsCategory)); 54 cl::opt<FileFormat> InputFormat( 55 "input-format", cl::desc("Specify the input file format"), 56 cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"), 57 clEnumValN(FileFormat::ELF, "ELF", "ELF object file")), 58 cl::cat(IfsCategory)); 59 cl::opt<FileFormat> OutputFormat( 60 "output-format", cl::desc("Specify the output file format"), 61 cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"), 62 clEnumValN(FileFormat::ELF, "ELF", "ELF stub file"), 63 clEnumValN(FileFormat::TBD, "TBD", "Apple TBD text stub file")), 64 cl::Required, cl::cat(IfsCategory)); 65 cl::opt<std::string> OptArch("arch", 66 cl::desc("Specify the architecture, e.g. x86_64"), 67 cl::cat(IfsCategory)); 68 cl::opt<IFSBitWidthType> 69 OptBitWidth("bitwidth", cl::desc("Specify the bit width"), 70 cl::values(clEnumValN(IFSBitWidthType::IFS32, "32", "32 bits"), 71 clEnumValN(IFSBitWidthType::IFS64, "64", "64 bits")), 72 cl::cat(IfsCategory)); 73 cl::opt<IFSEndiannessType> OptEndianness( 74 "endianness", cl::desc("Specify the endianness"), 75 cl::values(clEnumValN(IFSEndiannessType::Little, "little", "Little Endian"), 76 clEnumValN(IFSEndiannessType::Big, "big", "Big Endian")), 77 cl::cat(IfsCategory)); 78 cl::opt<std::string> OptTargetTriple( 79 "target", cl::desc("Specify the target triple, e.g. x86_64-linux-gnu"), 80 cl::cat(IfsCategory)); 81 cl::opt<std::string> OptTargetTripleHint( 82 "hint-ifs-target", 83 cl::desc("When --output-format is 'IFS', this flag will hint the expected " 84 "target triple for IFS output"), 85 cl::cat(IfsCategory)); 86 cl::opt<bool> StripIFSArch( 87 "strip-ifs-arch", 88 cl::desc("Strip target architecture information away from IFS output"), 89 cl::cat(IfsCategory)); 90 cl::opt<bool> StripIFSBitWidth( 91 "strip-ifs-bitwidth", 92 cl::desc("Strip target bit width information away from IFS output"), 93 cl::cat(IfsCategory)); 94 cl::opt<bool> StripIFSEndiannessWidth( 95 "strip-ifs-endianness", 96 cl::desc("Strip target endianness information away from IFS output"), 97 cl::cat(IfsCategory)); 98 cl::opt<bool> StripIFSTarget( 99 "strip-ifs-target", 100 cl::desc("Strip all target information away from IFS output"), 101 cl::cat(IfsCategory)); 102 cl::opt<std::string> 103 SoName("soname", 104 cl::desc("Manually set the DT_SONAME entry of any emitted files"), 105 cl::value_desc("name"), cl::cat(IfsCategory)); 106 cl::opt<std::string> OutputFilePath("output", cl::desc("Output file"), 107 cl::cat(IfsCategory)); 108 cl::alias OutputFilePathA("o", cl::desc("Alias for --output"), 109 cl::aliasopt(OutputFilePath), cl::cat(IfsCategory)); 110 cl::opt<bool> WriteIfChanged( 111 "write-if-changed", 112 cl::desc("Write the output file only if it is new or has changed."), 113 cl::cat(IfsCategory)); 114 115 static std::string getTypeName(IFSSymbolType Type) { 116 switch (Type) { 117 case IFSSymbolType::NoType: 118 return "NoType"; 119 case IFSSymbolType::Func: 120 return "Func"; 121 case IFSSymbolType::Object: 122 return "Object"; 123 case IFSSymbolType::TLS: 124 return "TLS"; 125 case IFSSymbolType::Unknown: 126 return "Unknown"; 127 } 128 llvm_unreachable("Unexpected ifs symbol type."); 129 } 130 131 static Expected<std::unique_ptr<IFSStub>> readInputFile(StringRef FilePath) { 132 // Read in file. 133 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = 134 MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true); 135 if (!BufOrError) 136 return createStringError(BufOrError.getError(), "Could not open `%s`", 137 FilePath.data()); 138 139 std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError); 140 ErrorCollector EC(/*UseFatalErrors=*/false); 141 142 // First try to read as a binary (fails fast if not binary). 143 if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::ELF) { 144 Expected<std::unique_ptr<IFSStub>> StubFromELF = 145 readELFFile(FileReadBuffer->getMemBufferRef()); 146 if (StubFromELF) { 147 (*StubFromELF)->IfsVersion = IfsVersionCurrent; 148 return std::move(*StubFromELF); 149 } 150 EC.addError(StubFromELF.takeError(), "BinaryRead"); 151 } 152 153 // Fall back to reading as a ifs. 154 if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::IFS) { 155 Expected<std::unique_ptr<IFSStub>> StubFromIFS = 156 readIFSFromBuffer(FileReadBuffer->getBuffer()); 157 if (StubFromIFS) { 158 if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent) 159 EC.addError( 160 createStringError(errc::not_supported, 161 "IFS version " + 162 (*StubFromIFS)->IfsVersion.getAsString() + 163 " is unsupported."), 164 "ReadInputFile"); 165 else 166 return std::move(*StubFromIFS); 167 } else { 168 EC.addError(StubFromIFS.takeError(), "YamlParse"); 169 } 170 } 171 172 // If both readers fail, build a new error that includes all information. 173 EC.addError(createStringError(errc::not_supported, 174 "No file readers succeeded reading `%s` " 175 "(unsupported/malformed file?)", 176 FilePath.data()), 177 "ReadInputFile"); 178 EC.escalateToFatal(); 179 return EC.makeError(); 180 } 181 182 static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols, 183 const StringRef Format, raw_ostream &Out) { 184 185 auto PlatformKindOrError = 186 [](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformKind> { 187 if (T.isMacOSX()) 188 return llvm::MachO::PlatformKind::macOS; 189 if (T.isTvOS()) 190 return llvm::MachO::PlatformKind::tvOS; 191 if (T.isWatchOS()) 192 return llvm::MachO::PlatformKind::watchOS; 193 // Note: put isiOS last because tvOS and watchOS are also iOS according 194 // to the Triple. 195 if (T.isiOS()) 196 return llvm::MachO::PlatformKind::iOS; 197 198 return createStringError(errc::not_supported, "Invalid Platform.\n"); 199 }(T); 200 201 if (!PlatformKindOrError) 202 return -1; 203 204 PlatformKind Plat = PlatformKindOrError.get(); 205 TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)}); 206 207 InterfaceFile File; 208 File.setFileType(FileType::TBD_V3); // Only supporting v3 for now. 209 File.addTargets(Targets); 210 211 for (const auto &Symbol : Symbols) { 212 auto Name = Symbol.Name; 213 auto Kind = SymbolKind::GlobalSymbol; 214 switch (Symbol.Type) { 215 default: 216 case IFSSymbolType::NoType: 217 Kind = SymbolKind::GlobalSymbol; 218 break; 219 case IFSSymbolType::Object: 220 Kind = SymbolKind::GlobalSymbol; 221 break; 222 case IFSSymbolType::Func: 223 Kind = SymbolKind::GlobalSymbol; 224 break; 225 } 226 if (Symbol.Weak) 227 File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined); 228 else 229 File.addSymbol(Kind, Name, Targets); 230 } 231 232 SmallString<4096> Buffer; 233 raw_svector_ostream OS(Buffer); 234 if (Error Result = TextAPIWriter::writeToStream(OS, File)) 235 return -1; 236 Out << OS.str(); 237 return 0; 238 } 239 240 static void fatalError(Error Err) { 241 WithColor::defaultErrorHandler(std::move(Err)); 242 exit(1); 243 } 244 245 /// writeIFS() writes a Text-Based ELF stub to a file using the latest version 246 /// of the YAML parser. 247 static Error writeIFS(StringRef FilePath, IFSStub &Stub) { 248 // Write IFS to memory first. 249 std::string IFSStr; 250 raw_string_ostream OutStr(IFSStr); 251 Error YAMLErr = writeIFSToOutputStream(OutStr, Stub); 252 if (YAMLErr) 253 return YAMLErr; 254 OutStr.flush(); 255 256 if (WriteIfChanged) { 257 if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = 258 MemoryBuffer::getFile(FilePath)) { 259 // Compare IFS output with the existing IFS file. If unchanged, avoid changing the file. 260 if ((*BufOrError)->getBuffer() == IFSStr) 261 return Error::success(); 262 } 263 } 264 // Open IFS file for writing. 265 std::error_code SysErr; 266 raw_fd_ostream Out(FilePath, SysErr); 267 if (SysErr) 268 return createStringError(SysErr, "Couldn't open `%s` for writing", 269 FilePath.data()); 270 Out << IFSStr; 271 return Error::success(); 272 } 273 274 int main(int argc, char *argv[]) { 275 // Parse arguments. 276 cl::HideUnrelatedOptions({&IfsCategory, &getColorCategory()}); 277 cl::ParseCommandLineOptions(argc, argv); 278 279 if (InputFilePaths.empty()) 280 InputFilePaths.push_back("-"); 281 282 // If input files are more than one, they can only be IFS files. 283 if (InputFilePaths.size() > 1) 284 InputFormat.setValue(FileFormat::IFS); 285 286 // Attempt to merge input. 287 IFSStub Stub; 288 std::map<std::string, IFSSymbol> SymbolMap; 289 std::string PreviousInputFilePath; 290 for (const std::string &InputFilePath : InputFilePaths) { 291 Expected<std::unique_ptr<IFSStub>> StubOrErr = readInputFile(InputFilePath); 292 if (!StubOrErr) 293 fatalError(StubOrErr.takeError()); 294 295 std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get()); 296 if (PreviousInputFilePath.empty()) { 297 Stub.IfsVersion = TargetStub->IfsVersion; 298 Stub.Target = TargetStub->Target; 299 Stub.SoName = TargetStub->SoName; 300 Stub.NeededLibs = TargetStub->NeededLibs; 301 } else { 302 if (Stub.IfsVersion != TargetStub->IfsVersion) { 303 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) { 304 WithColor::error() 305 << "Interface Stub: IfsVersion Mismatch." 306 << "\nFilenames: " << PreviousInputFilePath << " " 307 << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion 308 << " " << TargetStub->IfsVersion << "\n"; 309 return -1; 310 } 311 if (TargetStub->IfsVersion > Stub.IfsVersion) 312 Stub.IfsVersion = TargetStub->IfsVersion; 313 } 314 if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) { 315 WithColor::error() << "Interface Stub: Target Mismatch." 316 << "\nFilenames: " << PreviousInputFilePath << " " 317 << InputFilePath; 318 // << "\nTriple Values: " << Stub.Triple << " " 319 // << TargetStub->Triple << "\n"; 320 return -1; 321 } 322 if (Stub.SoName != TargetStub->SoName) { 323 WithColor::error() << "Interface Stub: SoName Mismatch." 324 << "\nFilenames: " << PreviousInputFilePath << " " 325 << InputFilePath 326 << "\nSoName Values: " << Stub.SoName << " " 327 << TargetStub->SoName << "\n"; 328 return -1; 329 } 330 if (Stub.NeededLibs != TargetStub->NeededLibs) { 331 WithColor::error() << "Interface Stub: NeededLibs Mismatch." 332 << "\nFilenames: " << PreviousInputFilePath << " " 333 << InputFilePath << "\n"; 334 return -1; 335 } 336 } 337 338 for (auto Symbol : TargetStub->Symbols) { 339 auto SI = SymbolMap.find(Symbol.Name); 340 if (SI == SymbolMap.end()) { 341 SymbolMap.insert( 342 std::pair<std::string, IFSSymbol>(Symbol.Name, Symbol)); 343 continue; 344 } 345 346 assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match."); 347 348 // Check conflicts: 349 if (Symbol.Type != SI->second.Type) { 350 WithColor::error() << "Interface Stub: Type Mismatch for " 351 << Symbol.Name << ".\nFilename: " << InputFilePath 352 << "\nType Values: " << getTypeName(SI->second.Type) 353 << " " << getTypeName(Symbol.Type) << "\n"; 354 355 return -1; 356 } 357 if (Symbol.Size != SI->second.Size) { 358 WithColor::error() << "Interface Stub: Size Mismatch for " 359 << Symbol.Name << ".\nFilename: " << InputFilePath 360 << "\nSize Values: " << SI->second.Size << " " 361 << Symbol.Size << "\n"; 362 363 return -1; 364 } 365 if (Symbol.Weak != SI->second.Weak) { 366 Symbol.Weak = false; 367 continue; 368 } 369 // TODO: Not checking Warning. Will be dropped. 370 } 371 372 PreviousInputFilePath = InputFilePath; 373 } 374 375 if (Stub.IfsVersion != IfsVersionCurrent) 376 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) { 377 WithColor::error() << "Interface Stub: Bad IfsVersion: " 378 << Stub.IfsVersion << ", llvm-ifs supported version: " 379 << IfsVersionCurrent << ".\n"; 380 return -1; 381 } 382 383 for (auto &Entry : SymbolMap) 384 Stub.Symbols.push_back(Entry.second); 385 386 // Change SoName before emitting stubs. 387 if (SoName.getNumOccurrences() == 1) 388 Stub.SoName = SoName; 389 Optional<IFSArch> OverrideArch; 390 Optional<IFSEndiannessType> OverrideEndianness; 391 Optional<IFSBitWidthType> OverrideBitWidth; 392 Optional<std::string> OverrideTriple; 393 if (OptArch.getNumOccurrences() == 1) 394 OverrideArch = ELF::convertArchNameToEMachine(OptArch.getValue()); 395 if (OptEndianness.getNumOccurrences() == 1) 396 OverrideEndianness = OptEndianness.getValue(); 397 if (OptBitWidth.getNumOccurrences() == 1) 398 OverrideBitWidth = OptBitWidth.getValue(); 399 if (OptTargetTriple.getNumOccurrences() == 1) 400 OverrideTriple = OptTargetTriple.getValue(); 401 Error OverrideError = overrideIFSTarget( 402 Stub, OverrideArch, OverrideEndianness, OverrideBitWidth, OverrideTriple); 403 if (OverrideError) 404 fatalError(std::move(OverrideError)); 405 406 switch (OutputFormat.getValue()) { 407 case FileFormat::TBD: { 408 std::error_code SysErr; 409 raw_fd_ostream Out(OutputFilePath, SysErr); 410 if (SysErr) { 411 WithColor::error() << "Couldn't open " << OutputFilePath 412 << " for writing.\n"; 413 return -1; 414 } 415 if (!Stub.Target.Triple) { 416 WithColor::error() 417 << "Triple should be defined when output format is TBD"; 418 return -1; 419 } 420 return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()), 421 Stub.Symbols, "TBD", Out); 422 } 423 case FileFormat::IFS: { 424 Stub.IfsVersion = IfsVersionCurrent; 425 if (InputFormat.getValue() == FileFormat::ELF && 426 OptTargetTripleHint.getNumOccurrences() == 1) { 427 std::error_code HintEC(1, std::generic_category()); 428 IFSTarget HintTarget = parseTriple(OptTargetTripleHint); 429 if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue()) 430 fatalError(make_error<StringError>( 431 "Triple hint does not match the actual architecture", HintEC)); 432 if (Stub.Target.Endianness.getValue() != 433 HintTarget.Endianness.getValue()) 434 fatalError(make_error<StringError>( 435 "Triple hint does not match the actual endianness", HintEC)); 436 if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue()) 437 fatalError(make_error<StringError>( 438 "Triple hint does not match the actual bit width", HintEC)); 439 440 stripIFSTarget(Stub, true, false, false, false); 441 Stub.Target.Triple = OptTargetTripleHint.getValue(); 442 } else { 443 stripIFSTarget(Stub, StripIFSTarget, StripIFSArch, 444 StripIFSEndiannessWidth, StripIFSBitWidth); 445 } 446 Error IFSWriteError = writeIFS(OutputFilePath.getValue(), Stub); 447 if (IFSWriteError) 448 fatalError(std::move(IFSWriteError)); 449 break; 450 } 451 case FileFormat::ELF: { 452 Error TargetError = validateIFSTarget(Stub, true); 453 if (TargetError) 454 fatalError(std::move(TargetError)); 455 Error BinaryWriteError = 456 writeBinaryStub(OutputFilePath, Stub, WriteIfChanged); 457 if (BinaryWriteError) 458 fatalError(std::move(BinaryWriteError)); 459 break; 460 } 461 } 462 return 0; 463 } 464