1 //===- llvm/unittests/TextAPI/YAMLTest.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 "llvm/ADT/StringRef.h" 10 #include "llvm/BinaryFormat/ELF.h" 11 #include "llvm/InterfaceStub/IFSHandler.h" 12 #include "llvm/InterfaceStub/IFSStub.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Testing/Support/Error.h" 15 #include "gtest/gtest.h" 16 #include <string> 17 18 using namespace llvm; 19 using namespace llvm::ELF; 20 using namespace llvm::ifs; 21 22 void compareByLine(StringRef LHS, StringRef RHS) { 23 StringRef Line1; 24 StringRef Line2; 25 while (LHS.size() > 0 && RHS.size() > 0) { 26 std::tie(Line1, LHS) = LHS.split('\n'); 27 std::tie(Line2, RHS) = RHS.split('\n'); 28 // Comparing StringRef objects works, but has messy output when not equal. 29 // Using STREQ on StringRef.data() doesn't work since these substrings are 30 // not null terminated. 31 // This is inefficient, but forces null terminated strings that can be 32 // cleanly compared. 33 EXPECT_STREQ(Line1.str().data(), Line2.str().data()); 34 } 35 } 36 37 TEST(ElfYamlTextAPI, YAMLReadableTBE) { 38 const char Data[] = "--- !ifs-v1\n" 39 "IfsVersion: 1.0\n" 40 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 41 "little, BitWidth: 64 }\n" 42 "NeededLibs: [libc.so, libfoo.so, libbar.so]\n" 43 "Symbols:\n" 44 " - { Name: foo, Type: Func, Undefined: true }\n" 45 "...\n"; 46 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 47 ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded()); 48 std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get()); 49 EXPECT_NE(Stub.get(), nullptr); 50 EXPECT_FALSE(Stub->SoName.has_value()); 51 EXPECT_TRUE(Stub->Target.Arch.has_value()); 52 EXPECT_EQ(Stub->Target.Arch.value(), (uint16_t)llvm::ELF::EM_X86_64); 53 EXPECT_EQ(Stub->NeededLibs.size(), 3u); 54 EXPECT_STREQ(Stub->NeededLibs[0].c_str(), "libc.so"); 55 EXPECT_STREQ(Stub->NeededLibs[1].c_str(), "libfoo.so"); 56 EXPECT_STREQ(Stub->NeededLibs[2].c_str(), "libbar.so"); 57 } 58 59 TEST(ElfYamlTextAPI, YAMLReadsTBESymbols) { 60 const char Data[] = 61 "--- !ifs-v1\n" 62 "IfsVersion: 1.0\n" 63 "SoName: test.so\n" 64 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: little, " 65 "BitWidth: 64 }\n" 66 "Symbols:\n" 67 " - { Name: bar, Type: Object, Size: 42 }\n" 68 " - { Name: baz, Type: TLS, Size: 3 }\n" 69 " - { Name: foo, Type: Func, Warning: \"Deprecated!\" }\n" 70 " - { Name: nor, Type: NoType, Undefined: true }\n" 71 " - { Name: not, Type: File, Undefined: true, Size: 111, " 72 "Weak: true, Warning: \'All fields populated!\' }\n" 73 "...\n"; 74 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 75 ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded()); 76 std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get()); 77 EXPECT_NE(Stub.get(), nullptr); 78 EXPECT_TRUE(Stub->SoName); 79 EXPECT_STREQ(Stub->SoName->c_str(), "test.so"); 80 EXPECT_EQ(Stub->Symbols.size(), 5u); 81 82 auto Iterator = Stub->Symbols.begin(); 83 IFSSymbol const &SymBar = *Iterator++; 84 EXPECT_STREQ(SymBar.Name.c_str(), "bar"); 85 EXPECT_EQ(*SymBar.Size, 42u); 86 EXPECT_EQ(SymBar.Type, IFSSymbolType::Object); 87 EXPECT_FALSE(SymBar.Undefined); 88 EXPECT_FALSE(SymBar.Weak); 89 EXPECT_FALSE(SymBar.Warning); 90 91 IFSSymbol const &SymBaz = *Iterator++; 92 EXPECT_STREQ(SymBaz.Name.c_str(), "baz"); 93 EXPECT_EQ(*SymBaz.Size, 3u); 94 EXPECT_EQ(SymBaz.Type, IFSSymbolType::TLS); 95 EXPECT_FALSE(SymBaz.Undefined); 96 EXPECT_FALSE(SymBaz.Weak); 97 EXPECT_FALSE(SymBaz.Warning.has_value()); 98 99 IFSSymbol const &SymFoo = *Iterator++; 100 EXPECT_STREQ(SymFoo.Name.c_str(), "foo"); 101 EXPECT_FALSE(SymFoo.Size.has_value()); 102 EXPECT_EQ(SymFoo.Type, IFSSymbolType::Func); 103 EXPECT_FALSE(SymFoo.Undefined); 104 EXPECT_FALSE(SymFoo.Weak); 105 EXPECT_TRUE(SymFoo.Warning.has_value()); 106 EXPECT_STREQ(SymFoo.Warning->c_str(), "Deprecated!"); 107 108 IFSSymbol const &SymNor = *Iterator++; 109 EXPECT_STREQ(SymNor.Name.c_str(), "nor"); 110 EXPECT_FALSE(SymNor.Size.has_value()); 111 EXPECT_EQ(SymNor.Type, IFSSymbolType::NoType); 112 EXPECT_TRUE(SymNor.Undefined); 113 EXPECT_FALSE(SymNor.Weak); 114 EXPECT_FALSE(SymNor.Warning.has_value()); 115 116 IFSSymbol const &SymNot = *Iterator++; 117 EXPECT_STREQ(SymNot.Name.c_str(), "not"); 118 EXPECT_EQ(*SymNot.Size, 111u); 119 EXPECT_EQ(SymNot.Type, IFSSymbolType::Unknown); 120 EXPECT_TRUE(SymNot.Undefined); 121 EXPECT_TRUE(SymNot.Weak); 122 EXPECT_TRUE(SymNot.Warning); 123 EXPECT_STREQ(SymNot.Warning->c_str(), "All fields populated!"); 124 } 125 126 TEST(ElfYamlTextAPI, YAMLReadsNoTBESyms) { 127 const char Data[] = "--- !ifs-v1\n" 128 "IfsVersion: 1.0\n" 129 "SoName: test.so\n" 130 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 131 "little, BitWidth: 64 }\n" 132 "Symbols: []\n" 133 "...\n"; 134 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 135 ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded()); 136 std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get()); 137 EXPECT_NE(Stub.get(), nullptr); 138 EXPECT_EQ(0u, Stub->Symbols.size()); 139 } 140 141 TEST(ElfYamlTextAPI, YAMLUnreadableTBE) { 142 // Can't read: wrong format/version. 143 const char Data[] = "--- !tapi-tbz\n" 144 "IfsVersion: z.3\n" 145 "SoName: test.so\n" 146 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 147 "little, BitWidth: 64 }\n" 148 "Symbols:\n" 149 " foo: { Type: Func, Undefined: true }\n"; 150 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 151 ASSERT_THAT_ERROR(StubOrErr.takeError(), Failed()); 152 } 153 154 TEST(ElfYamlTextAPI, YAMLUnsupportedVersion) { 155 const char Data[] = "--- !ifs-v1\n" 156 "IfsVersion: 9.9.9\n" 157 "SoName: test.so\n" 158 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 159 "little, BitWidth: 64 }\n" 160 "Symbols: []\n" 161 "...\n"; 162 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 163 std::string ErrorMessage = toString(StubOrErr.takeError()); 164 EXPECT_EQ("IFS version 9.9.9 is unsupported.", ErrorMessage); 165 } 166 167 TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) { 168 const char Expected[] = 169 "--- !ifs-v1\n" 170 "IfsVersion: 1.0\n" 171 "Target: { ObjectFormat: ELF, Arch: AArch64, Endianness: " 172 "little, BitWidth: 64 }\n" 173 "Symbols:\n" 174 " - { Name: bar, Type: Func, Weak: true }\n" 175 " - { Name: foo, Type: NoType, Size: 99, Warning: Does nothing }\n" 176 " - { Name: nor, Type: Func, Undefined: true }\n" 177 " - { Name: not, Type: Unknown, Size: 12345678901234 }\n" 178 "...\n"; 179 IFSStub Stub; 180 Stub.IfsVersion = VersionTuple(1, 0); 181 Stub.Target.Arch = ELF::EM_AARCH64; 182 Stub.Target.BitWidth = IFSBitWidthType::IFS64; 183 Stub.Target.Endianness = IFSEndiannessType::Little; 184 Stub.Target.ObjectFormat = "ELF"; 185 186 IFSSymbol SymBar("bar"); 187 SymBar.Size = 128u; 188 SymBar.Type = IFSSymbolType::Func; 189 SymBar.Undefined = false; 190 SymBar.Weak = true; 191 192 IFSSymbol SymFoo("foo"); 193 SymFoo.Size = 99u; 194 SymFoo.Type = IFSSymbolType::NoType; 195 SymFoo.Undefined = false; 196 SymFoo.Weak = false; 197 SymFoo.Warning = "Does nothing"; 198 199 IFSSymbol SymNor("nor"); 200 SymNor.Size = 1234u; 201 SymNor.Type = IFSSymbolType::Func; 202 SymNor.Undefined = true; 203 SymNor.Weak = false; 204 205 IFSSymbol SymNot("not"); 206 SymNot.Size = 12345678901234u; 207 SymNot.Type = IFSSymbolType::Unknown; 208 SymNot.Undefined = false; 209 SymNot.Weak = false; 210 211 // Symbol order is preserved instead of being sorted. 212 Stub.Symbols.push_back(SymBar); 213 Stub.Symbols.push_back(SymFoo); 214 Stub.Symbols.push_back(SymNor); 215 Stub.Symbols.push_back(SymNot); 216 217 // Ensure move constructor works as expected. 218 IFSStub Moved = std::move(Stub); 219 220 std::string Result; 221 raw_string_ostream OS(Result); 222 ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Moved), Succeeded()); 223 Result = OS.str(); 224 compareByLine(Result.c_str(), Expected); 225 } 226 227 TEST(ElfYamlTextAPI, YAMLWritesNoTBESyms) { 228 const char Expected[] = "--- !ifs-v1\n" 229 "IfsVersion: 1.0\n" 230 "SoName: nosyms.so\n" 231 "Target: { ObjectFormat: ELF, Arch: x86_64, " 232 "Endianness: little, BitWidth: 64 }\n" 233 "NeededLibs:\n" 234 " - libc.so\n" 235 " - libfoo.so\n" 236 " - libbar.so\n" 237 "Symbols: []\n" 238 "...\n"; 239 IFSStub Stub; 240 Stub.IfsVersion = VersionTuple(1, 0); 241 Stub.SoName = "nosyms.so"; 242 Stub.Target.Arch = ELF::EM_X86_64; 243 Stub.Target.BitWidth = IFSBitWidthType::IFS64; 244 Stub.Target.Endianness = IFSEndiannessType::Little; 245 Stub.Target.ObjectFormat = "ELF"; 246 Stub.NeededLibs.push_back("libc.so"); 247 Stub.NeededLibs.push_back("libfoo.so"); 248 Stub.NeededLibs.push_back("libbar.so"); 249 250 std::string Result; 251 raw_string_ostream OS(Result); 252 ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Stub), Succeeded()); 253 Result = OS.str(); 254 compareByLine(Result.c_str(), Expected); 255 } 256