[Object] Add tapi files to object

Summary:
The intention for this is to allow reading and printing symbols out from
llvm-nm. Tapi file, and Tapi universal follow a similiar format to
their respective MachO Object format.

The tests are dependent on llvm-nm processing tbd files which is why its in D66160

Reviewers: ributzka, steven_wu, lhames

Reviewed By: ributzka, lhames

Subscribers: mgorny, hiraditya, dexonsmith, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D66159

llvm-svn: 369600
diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp
index 1ceda54..944d2bc 100644
--- a/llvm/lib/Object/Binary.cpp
+++ b/llvm/lib/Object/Binary.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Object/Minidump.h"
 #include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/TapiUniversal.h"
 #include "llvm/Object/WindowsResource.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -87,8 +88,7 @@
   case file_magic::minidump:
     return MinidumpFile::create(Buffer);
   case file_magic::tapi_file:
-    // Placeholder until TAPI is supported for lib/Object
-    return errorCodeToError(object_error::invalid_file_type);
+    return TapiUniversal::create(Buffer);
   }
   llvm_unreachable("Unexpected Binary File Type");
 }
diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt
index e3ed640..59f21e9 100644
--- a/llvm/lib/Object/CMakeLists.txt
+++ b/llvm/lib/Object/CMakeLists.txt
@@ -21,6 +21,8 @@
   RelocationResolver.cpp
   SymbolicFile.cpp
   SymbolSize.cpp
+  TapiFile.cpp
+  TapiUniversal.cpp
   WasmObjectFile.cpp
   WindowsMachineFlag.cpp
   WindowsResource.cpp
diff --git a/llvm/lib/Object/LLVMBuild.txt b/llvm/lib/Object/LLVMBuild.txt
index 605f1fb..dd30496 100644
--- a/llvm/lib/Object/LLVMBuild.txt
+++ b/llvm/lib/Object/LLVMBuild.txt
@@ -18,4 +18,4 @@
 type = Library
 name = Object
 parent = Libraries
-required_libraries = BitReader Core MC BinaryFormat MCParser Support
+required_libraries = BitReader Core MC BinaryFormat MCParser Support TextAPI
diff --git a/llvm/lib/Object/TapiFile.cpp b/llvm/lib/Object/TapiFile.cpp
new file mode 100644
index 0000000..be7d762
--- /dev/null
+++ b/llvm/lib/Object/TapiFile.cpp
@@ -0,0 +1,104 @@
+//===- TapiFile.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Text-based Dynamcic Library Stub format.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/TapiFile.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace llvm;
+using namespace MachO;
+using namespace object;
+
+static constexpr StringLiteral ObjC1ClassNamePrefix = ".objc_class_name_";
+static constexpr StringLiteral ObjC2ClassNamePrefix = "_OBJC_CLASS_$_";
+static constexpr StringLiteral ObjC2MetaClassNamePrefix = "_OBJC_METACLASS_$_";
+static constexpr StringLiteral ObjC2EHTypePrefix = "_OBJC_EHTYPE_$_";
+static constexpr StringLiteral ObjC2IVarPrefix = "_OBJC_IVAR_$_";
+
+static uint32_t getFlags(const Symbol *Sym) {
+  uint32_t Flags = BasicSymbolRef::SF_Global;
+  if (Sym->isUndefined())
+    Flags |= BasicSymbolRef::SF_Undefined;
+  else
+    Flags |= BasicSymbolRef::SF_Exported;
+
+  if (Sym->isWeakDefined() || Sym->isWeakReferenced())
+    Flags |= BasicSymbolRef::SF_Weak;
+
+  return Flags;
+}
+
+TapiFile::TapiFile(MemoryBufferRef Source, const InterfaceFile &interface,
+                   Architecture Arch)
+    : SymbolicFile(ID_TapiFile, Source) {
+  for (const auto *Symbol : interface.symbols()) {
+    if (!Symbol->getArchitectures().has(Arch))
+      continue;
+
+    auto PlatformKind = interface.getPlatform();
+    switch (Symbol->getKind()) {
+    case SymbolKind::GlobalSymbol:
+      Symbols.emplace_back(StringRef(), Symbol->getName(), getFlags(Symbol));
+      break;
+    case SymbolKind::ObjectiveCClass:
+      if (PlatformKind == PlatformKind::macOS && Arch == AK_i386) {
+        Symbols.emplace_back(ObjC1ClassNamePrefix, Symbol->getName(),
+                             getFlags(Symbol));
+      } else {
+        Symbols.emplace_back(ObjC2ClassNamePrefix, Symbol->getName(),
+                             getFlags(Symbol));
+        Symbols.emplace_back(ObjC2MetaClassNamePrefix, Symbol->getName(),
+                             getFlags(Symbol));
+      }
+      break;
+    case SymbolKind::ObjectiveCClassEHType:
+      Symbols.emplace_back(ObjC2EHTypePrefix, Symbol->getName(),
+                           getFlags(Symbol));
+      break;
+    case SymbolKind::ObjectiveCInstanceVariable:
+      Symbols.emplace_back(ObjC2IVarPrefix, Symbol->getName(),
+                           getFlags(Symbol));
+      break;
+    }
+  }
+}
+
+TapiFile::~TapiFile() = default;
+
+void TapiFile::moveSymbolNext(DataRefImpl &DRI) const {
+  const auto *Sym = reinterpret_cast<const Symbol *>(DRI.p);
+  DRI.p = reinterpret_cast<uintptr_t>(++Sym);
+}
+
+Error TapiFile::printSymbolName(raw_ostream &OS, DataRefImpl DRI) const {
+  const auto *Sym = reinterpret_cast<const Symbol *>(DRI.p);
+  OS << Sym->Prefix << Sym->Name;
+  return Error::success();
+}
+
+uint32_t TapiFile::getSymbolFlags(DataRefImpl DRI) const {
+  const auto *Sym = reinterpret_cast<const Symbol *>(DRI.p);
+  return Sym->Flags;
+}
+
+basic_symbol_iterator TapiFile::symbol_begin() const {
+  DataRefImpl DRI;
+  DRI.p = reinterpret_cast<uintptr_t>(&*Symbols.begin());
+  return BasicSymbolRef{DRI, this};
+}
+
+basic_symbol_iterator TapiFile::symbol_end() const {
+  DataRefImpl DRI;
+  DRI.p = reinterpret_cast<uintptr_t>(&*Symbols.end());
+  return BasicSymbolRef{DRI, this};
+}
diff --git a/llvm/lib/Object/TapiUniversal.cpp b/llvm/lib/Object/TapiUniversal.cpp
new file mode 100644
index 0000000..b3273e3
--- /dev/null
+++ b/llvm/lib/Object/TapiUniversal.cpp
@@ -0,0 +1,54 @@
+//===- TapiUniversal.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Text-based Dynamic Library Stub format.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/TapiUniversal.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
+
+using namespace llvm;
+using namespace MachO;
+using namespace object;
+
+TapiUniversal::TapiUniversal(MemoryBufferRef Source, Error &Err)
+    : Binary(ID_TapiUniversal, Source) {
+  auto Result = TextAPIReader::get(Source);
+  ErrorAsOutParameter ErrAsOuParam(&Err);
+  if (!Result) {
+    Err = Result.takeError();
+    return;
+  }
+  ParsedFile = std::move(Result.get());
+
+  auto Archs = ParsedFile->getArchitectures();
+  for (auto Arch : Archs)
+    Architectures.emplace_back(Arch);
+}
+
+TapiUniversal::~TapiUniversal() = default;
+
+Expected<std::unique_ptr<TapiFile>>
+TapiUniversal::ObjectForArch::getAsObjectFile() const {
+  return std::unique_ptr<TapiFile>(new TapiFile(Parent->getMemoryBufferRef(),
+                                                *Parent->ParsedFile.get(),
+                                                Parent->Architectures[Index]));
+}
+
+Expected<std::unique_ptr<TapiUniversal>>
+TapiUniversal::create(MemoryBufferRef Source) {
+  Error Err = Error::success();
+  std::unique_ptr<TapiUniversal> Ret(new TapiUniversal(Source, Err));
+  if (Err)
+    return std::move(Err);
+  return std::move(Ret);
+}