[TextAPI] Add support for TBDv5 Files to nm & tapi-diff

This includes handling of new attributes for symbols & rpath.
In the event that an older format file is compared to tbd_v5, ignore these new attributes.

Reviewed By: ributzka

Differential Revision: https://reviews.llvm.org/D144529
This commit is contained in:
Cyndy Ishida 2023-02-22 17:13:14 -08:00
parent 471c0e000a
commit bc85cf1687
13 changed files with 259 additions and 49 deletions

View File

@ -15,6 +15,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBufferRef.h"
@ -34,7 +35,7 @@ namespace object {
class TapiFile : public SymbolicFile {
public:
TapiFile(MemoryBufferRef Source, const MachO::InterfaceFile &interface,
TapiFile(MemoryBufferRef Source, const MachO::InterfaceFile &Interface,
MachO::Architecture Arch);
~TapiFile() override;
@ -48,6 +49,8 @@ public:
basic_symbol_iterator symbol_end() const override;
Expected<SymbolRef::Type> getSymbolType(DataRefImpl DRI) const;
static bool classof(const Binary *v) { return v->isTapiFile(); }
bool is64Bit() const override { return MachO::is64Bit(Arch); }
@ -57,9 +60,11 @@ private:
StringRef Prefix;
StringRef Name;
uint32_t Flags;
SymbolRef::Type Type;
constexpr Symbol(StringRef Prefix, StringRef Name, uint32_t Flags)
: Prefix(Prefix), Name(Name), Flags(Flags) {}
constexpr Symbol(StringRef Prefix, StringRef Name, uint32_t Flags,
SymbolRef::Type Type)
: Prefix(Prefix), Name(Name), Flags(Flags), Type(Type) {}
};
std::vector<Symbol> Symbols;

View File

@ -123,10 +123,7 @@ public:
void dump() const { dump(llvm::errs()); }
#endif
bool operator==(const Symbol &O) const {
return std::tie(Name, Kind, Targets, Flags) ==
std::tie(O.Name, O.Kind, O.Targets, O.Flags);
}
bool operator==(const Symbol &O) const;
bool operator!=(const Symbol &O) const { return !(*this == O); }

View File

@ -228,11 +228,14 @@ file_magic llvm::identify_magic(StringRef Magic) {
return file_magic::coff_object;
break;
case 0x2d: // YAML '-'
case 0x2d: // YAML '-' MachO TBD.
if (startswith(Magic, "--- !tapi") || startswith(Magic, "---\narchs:"))
return file_magic::tapi_file;
break;
case 0x7b: // JSON '{' MachO TBD.
return file_magic::tapi_file;
break;
case 'D': // DirectX container file - DXBC
if (startswith(Magic, "DXBC"))
return file_magic::dxcontainer_object;

View File

@ -37,35 +37,46 @@ static uint32_t getFlags(const Symbol *Sym) {
return Flags;
}
TapiFile::TapiFile(MemoryBufferRef Source, const InterfaceFile &interface,
static SymbolRef::Type getType(const Symbol *Sym) {
SymbolRef::Type Type = SymbolRef::ST_Unknown;
if (Sym->isData())
Type = SymbolRef::ST_Data;
else if (Sym->isText())
Type = SymbolRef::ST_Function;
return Type;
}
TapiFile::TapiFile(MemoryBufferRef Source, const InterfaceFile &Interface,
Architecture Arch)
: SymbolicFile(ID_TapiFile, Source), Arch(Arch) {
for (const auto *Symbol : interface.symbols()) {
for (const auto *Symbol : Interface.symbols()) {
if (!Symbol->getArchitectures().has(Arch))
continue;
switch (Symbol->getKind()) {
case SymbolKind::GlobalSymbol:
Symbols.emplace_back(StringRef(), Symbol->getName(), getFlags(Symbol));
Symbols.emplace_back(StringRef(), Symbol->getName(), getFlags(Symbol),
::getType(Symbol));
break;
case SymbolKind::ObjectiveCClass:
if (interface.getPlatforms().count(PLATFORM_MACOS) && Arch == AK_i386) {
if (Interface.getPlatforms().count(PLATFORM_MACOS) && Arch == AK_i386) {
Symbols.emplace_back(ObjC1ClassNamePrefix, Symbol->getName(),
getFlags(Symbol));
getFlags(Symbol), ::getType(Symbol));
} else {
Symbols.emplace_back(ObjC2ClassNamePrefix, Symbol->getName(),
getFlags(Symbol));
getFlags(Symbol), ::getType(Symbol));
Symbols.emplace_back(ObjC2MetaClassNamePrefix, Symbol->getName(),
getFlags(Symbol));
getFlags(Symbol), ::getType(Symbol));
}
break;
case SymbolKind::ObjectiveCClassEHType:
Symbols.emplace_back(ObjC2EHTypePrefix, Symbol->getName(),
getFlags(Symbol));
getFlags(Symbol), ::getType(Symbol));
break;
case SymbolKind::ObjectiveCInstanceVariable:
Symbols.emplace_back(ObjC2IVarPrefix, Symbol->getName(),
getFlags(Symbol));
Symbols.emplace_back(ObjC2IVarPrefix, Symbol->getName(), getFlags(Symbol),
::getType(Symbol));
break;
}
}
@ -82,6 +93,11 @@ Error TapiFile::printSymbolName(raw_ostream &OS, DataRefImpl DRI) const {
return Error::success();
}
Expected<SymbolRef::Type> TapiFile::getSymbolType(DataRefImpl DRI) const {
assert(DRI.d.a < Symbols.size() && "Attempt to access symbol out of bounds");
return Symbols[DRI.d.a].Type;
}
Expected<uint32_t> TapiFile::getSymbolFlags(DataRefImpl DRI) const {
assert(DRI.d.a < Symbols.size() && "Attempt to access symbol out of bounds");
return Symbols[DRI.d.a].Flags;

View File

@ -141,6 +141,10 @@ void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
Documents.insert(Pos, Document);
}
static bool isYAMLTextStub(const FileType &Kind) {
return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
}
bool InterfaceFile::operator==(const InterfaceFile &O) const {
if (Targets != O.Targets)
return false;
@ -165,6 +169,13 @@ bool InterfaceFile::operator==(const InterfaceFile &O) const {
return false;
if (Symbols != O.Symbols)
return false;
// Don't compare run search paths for older filetypes that cannot express
// them.
if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
if (RPaths != O.RPaths)
return false;
}
if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
O.Documents.end(),
[](const std::shared_ptr<InterfaceFile> LHS,

View File

@ -54,5 +54,24 @@ Symbol::targets(ArchitectureSet Architectures) const {
return make_filter_range(Targets, FN);
}
bool Symbol::operator==(const Symbol &O) const {
// Older Tapi files do not express all these symbol flags. In those
// cases, ignore those differences.
auto RemoveFlag = [](const Symbol &Sym, SymbolFlags &Flag) {
if (Sym.isData())
Flag &= ~SymbolFlags::Data;
if (Sym.isText())
Flag &= ~SymbolFlags::Text;
};
SymbolFlags LHSFlags = Flags;
SymbolFlags RHSFlags = O.Flags;
if ((!O.isData() && !O.isText()) || (!isData() && !isText())) {
RemoveFlag(*this, LHSFlags);
RemoveFlag(O, RHSFlags);
}
return std::tie(Name, Kind, Targets, LHSFlags) ==
std::tie(O.Name, O.Kind, O.Targets, RHSFlags);
}
} // end namespace MachO.
} // end namespace llvm.

View File

@ -0,0 +1,73 @@
{
"main_library": {
"current_versions": [
{
"version": "1.2.3"
}
],
"exported_symbols": [
{
"data": {
"global": [
"_publicGlobalVariable",
"_extraGlobalAPI1",
"_privateGlobalVariable"
],
"objc_class": [
"SubClass",
"Basic6",
"Basic1",
"Base",
"Basic3",
"FooClass",
"Basic4",
"ExternalManagedObject"
],
"objc_eh_type": [
"SubClass",
"Base"
],
"objc_ivar": [
"Basic4.ivar2",
"Basic4.ivar1",
"Basic4_2.ivar2"
],
"weak": [
"_weakPrivateGlobalVariable",
"_weakPublicGlobalVariable"
]
}
},
{
"text": {
"global": [
"_publicGlobalFunc"
]
}
}
],
"flags": [
{
"attributes": [
"not_app_extension_safe"
]
}
],
"install_names": [
{
"name": "/System/Library/Frameworks/Simple.framework/Versions/A/Simple"
}
],
"target_info": [
{
"min_deployment": "10.10",
"target": "x86_64-macos"
},
{
"min_deployment": "10.10",
"target": "arm64-macos"
}
]
},
"tapi_tbd_version": 5
}

View File

@ -13,6 +13,9 @@ RUN: | FileCheck %s -check-prefix V4
RUN: llvm-nm %p/Inputs/tapi-v4-watchos.tbd \
RUN: | FileCheck %s -check-prefix V4-WATCH
RUN: llvm-nm %p/Inputs/tapi-v5.tbd \
RUN: | FileCheck %s -check-prefix V5
V1: /u/l/libfoo.dylib (for architecture armv7):
V1-NEXT: 00000000 S _sym
V1: /u/l/libfoo.dylib (for architecture armv7s):
@ -64,3 +67,61 @@ V4-WATCH: /u/l/libFoo.dylib (for architecture armv7k)
V4-WATCH-NEXT: 00000000 S _sym1
V4-WATCH: /u/l/libFoo.dylib (for architecture arm64_32)
V4-WATCH-NEXT: 00000000 S _sym1
V5: /System/Library/Frameworks/Simple.framework/Versions/A/Simple (for architecture x86_64):
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Base
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic1
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic3
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic4
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic6
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_ExternalManagedObject
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_FooClass
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_SubClass
V5-NEXT: 0000000000000000 D _OBJC_EHTYPE_$_Base
V5-NEXT: 0000000000000000 D _OBJC_EHTYPE_$_SubClass
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4.ivar1
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4.ivar2
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4_2.ivar2
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Base
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic1
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic3
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic4
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic6
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_ExternalManagedObject
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_FooClass
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_SubClass
V5-NEXT: 0000000000000000 D _extraGlobalAPI1
V5-NEXT: 0000000000000000 D _privateGlobalVariable
V5-NEXT: 0000000000000000 T _publicGlobalFunc
V5-NEXT: 0000000000000000 D _publicGlobalVariable
V5-NEXT: 0000000000000000 W _weakPrivateGlobalVariable
V5-NEXT: 0000000000000000 W _weakPublicGlobalVariable
V5: /System/Library/Frameworks/Simple.framework/Versions/A/Simple (for architecture arm64):
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Base
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic1
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic3
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic4
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_Basic6
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_ExternalManagedObject
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_FooClass
V5-NEXT: 0000000000000000 D _OBJC_CLASS_$_SubClass
V5-NEXT: 0000000000000000 D _OBJC_EHTYPE_$_Base
V5-NEXT: 0000000000000000 D _OBJC_EHTYPE_$_SubClass
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4.ivar1
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4.ivar2
V5-NEXT: 0000000000000000 D _OBJC_IVAR_$_Basic4_2.ivar2
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Base
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic1
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic3
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic4
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_Basic6
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_ExternalManagedObject
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_FooClass
V5-NEXT: 0000000000000000 D _OBJC_METACLASS_$_SubClass
V5-NEXT: 0000000000000000 D _extraGlobalAPI1
V5-NEXT: 0000000000000000 D _privateGlobalVariable
V5-NEXT: 0000000000000000 T _publicGlobalFunc
V5-NEXT: 0000000000000000 D _publicGlobalVariable
V5-NEXT: 0000000000000000 W _weakPrivateGlobalVariable
V5-NEXT: 0000000000000000 W _weakPublicGlobalVariable

View File

@ -0,0 +1,21 @@
; RUN: rm -rf %t
; RUN: split-file %s %t
; RUN: llvm-tapi-diff %t/Simple_v5.tbd %t/Simple_v5.tbd 2>&1 | FileCheck %s --allow-empty
; RUN: llvm-tapi-diff %t/Simple_v5.tbd %t/Simple_v4.tbd 2>&1 | FileCheck %s --allow-empty
; CHECK-NOT: error:
; CHECK-NOT: warning:
//--- Simple_v4.tbd
--- !tapi-tbd
tbd-version: 4
targets: [ x86_64-macos, arm64-macos ]
flags: [ not_app_extension_safe ]
install-name: '@rpath/libFake.dylib'
exports:
- targets: [ x86_64-macos, arm64-macos ]
symbols: [ _foo ]
...
//--- Simple_v5.tbd
{"main_library":{"exported_symbols":[{"text":{"global":["_foo"]}}],"flags":[{"attributes":["not_app_extension_safe"]}],"install_names":[{"name":"@rpath/libFake.dylib"}],"target_info":[{"min_deployment":"13","target":"x86_64-macos"},{"min_deployment":"13","target":"arm64-macos"}]},"tapi_tbd_version":5}

View File

@ -1029,7 +1029,15 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
}
static char getSymbolNMTypeChar(TapiFile &Obj, basic_symbol_iterator I) {
return 's';
auto Type = cantFail(Obj.getSymbolType(I->getRawDataRefImpl()));
switch (Type) {
case SymbolRef::ST_Data:
return 'd';
case SymbolRef::ST_Function:
return 't';
default:
return 's';
}
}
static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) {

View File

@ -74,41 +74,42 @@ StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) {
llvm_unreachable("Unknown llvm::MachO::SymbolKind enum");
}
std::string SymScalar::stringifySymbolFlag(MachO::SymbolFlags Flag) {
switch (Flag) {
case MachO::SymbolFlags::None:
return "";
case MachO::SymbolFlags::ThreadLocalValue:
return "Thread-Local";
case MachO::SymbolFlags::WeakDefined:
return "Weak-Defined";
case MachO::SymbolFlags::WeakReferenced:
return "Weak-Referenced";
case MachO::SymbolFlags::Undefined:
return "Undefined";
case MachO::SymbolFlags::Rexported:
return "Reexported";
default:
return "";
}
llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum");
std::string SymScalar::getFlagString(const MachO::Symbol *Sym) {
if (Sym->getFlags() == SymbolFlags::None)
return {};
SmallString<64> Flags(" - ");
if (Sym->isThreadLocalValue())
Flags.append("Thread-Local ");
if (Sym->isWeakDefined())
Flags.append("Weak-Defined ");
if (Sym->isWeakReferenced())
Flags.append("Weak-Referenced ");
if (Sym->isUndefined())
Flags.append("Undefined ");
if (Sym->isReexported())
Flags.append("Reexported ");
if (Sym->isData())
Flags.append("Data ");
if (Sym->isText())
Flags.append("Text ");
return std::string(Flags);
}
void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) {
if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) {
if (Targ.Arch == MachO::AK_i386 && Targ.Platform == MachO::PLATFORM_MACOS) {
OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
<< ObjC1ClassNamePrefix << Val->getName()
<< getFlagString(Val->getFlags()) << "\n";
<< ObjC1ClassNamePrefix << Val->getName() << getFlagString(Val)
<< "\n";
return;
}
OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
<< ObjC2ClassNamePrefix << Val->getName()
<< getFlagString(Val->getFlags()) << "\n";
<< ObjC2ClassNamePrefix << Val->getName() << getFlagString(Val) << "\n";
}
OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
<< getSymbolNamePrefix(Val->getKind()) << Val->getName()
<< getFlagString(Val->getFlags()) << "\n";
<< getFlagString(Val) << "\n";
}
bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS,

View File

@ -83,11 +83,7 @@ public:
SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym)
: Order(Order), Val(Sym){};
std::string getFlagString(MachO::SymbolFlags Flags) {
return Flags != MachO::SymbolFlags::None
? " - " + stringifySymbolFlag(Flags)
: stringifySymbolFlag(Flags);
}
std::string getFlagString(const MachO::Symbol *Sym);
void print(raw_ostream &OS, std::string Indent, MachO::Target Targ);
@ -99,7 +95,6 @@ private:
InterfaceInputOrder Order;
const MachO::Symbol *Val;
StringLiteral getSymbolNamePrefix(MachO::SymbolKind Kind);
std::string stringifySymbolFlag(MachO::SymbolFlags Flag);
};
class DiffStrVec : public AttributeDiff {