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, (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, YAMLReadsInvalidSymbols) { 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: not, Type: File, Undefined: true, Size: 111, " 68 "Weak: true, Warning: \'All fields populated!\' }\n" 69 "...\n"; 70 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 71 ASSERT_THAT_ERROR( 72 StubOrErr.takeError(), 73 FailedWithMessage("IFS symbol type for symbol 'not' is unsupported")); 74 } 75 76 TEST(ElfYamlTextAPI, YAMLReadsTBESymbols) { 77 const char Data[] = 78 "--- !ifs-v1\n" 79 "IfsVersion: 1.0\n" 80 "SoName: test.so\n" 81 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: little, " 82 "BitWidth: 64 }\n" 83 "Symbols:\n" 84 " - { Name: bar, Type: Object, Size: 42 }\n" 85 " - { Name: baz, Type: TLS, Size: 3 }\n" 86 " - { Name: foo, Type: Func, Warning: \"Deprecated!\" }\n" 87 " - { Name: nor, Type: NoType, Undefined: true }\n" 88 " - { Name: not, Type: NoType, Undefined: true, Size: 111, " 89 "Weak: true, Warning: \'All fields populated!\' }\n" 90 "...\n"; 91 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 92 ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded()); 93 std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get()); 94 EXPECT_NE(Stub.get(), nullptr); 95 EXPECT_TRUE(Stub->SoName); 96 EXPECT_STREQ(Stub->SoName->c_str(), "test.so"); 97 EXPECT_EQ(Stub->Symbols.size(), 5u); 98 99 auto Iterator = Stub->Symbols.begin(); 100 IFSSymbol const &SymBar = *Iterator++; 101 EXPECT_STREQ(SymBar.Name.c_str(), "bar"); 102 EXPECT_EQ(*SymBar.Size, 42u); 103 EXPECT_EQ(SymBar.Type, IFSSymbolType::Object); 104 EXPECT_FALSE(SymBar.Undefined); 105 EXPECT_FALSE(SymBar.Weak); 106 EXPECT_FALSE(SymBar.Warning); 107 108 IFSSymbol const &SymBaz = *Iterator++; 109 EXPECT_STREQ(SymBaz.Name.c_str(), "baz"); 110 EXPECT_EQ(*SymBaz.Size, 3u); 111 EXPECT_EQ(SymBaz.Type, IFSSymbolType::TLS); 112 EXPECT_FALSE(SymBaz.Undefined); 113 EXPECT_FALSE(SymBaz.Weak); 114 EXPECT_FALSE(SymBaz.Warning.has_value()); 115 116 IFSSymbol const &SymFoo = *Iterator++; 117 EXPECT_STREQ(SymFoo.Name.c_str(), "foo"); 118 EXPECT_FALSE(SymFoo.Size.has_value()); 119 EXPECT_EQ(SymFoo.Type, IFSSymbolType::Func); 120 EXPECT_FALSE(SymFoo.Undefined); 121 EXPECT_FALSE(SymFoo.Weak); 122 EXPECT_TRUE(SymFoo.Warning.has_value()); 123 EXPECT_STREQ(SymFoo.Warning->c_str(), "Deprecated!"); 124 125 IFSSymbol const &SymNor = *Iterator++; 126 EXPECT_STREQ(SymNor.Name.c_str(), "nor"); 127 EXPECT_FALSE(SymNor.Size.has_value()); 128 EXPECT_EQ(SymNor.Type, IFSSymbolType::NoType); 129 EXPECT_TRUE(SymNor.Undefined); 130 EXPECT_FALSE(SymNor.Weak); 131 EXPECT_FALSE(SymNor.Warning.has_value()); 132 133 IFSSymbol const &SymNot = *Iterator++; 134 EXPECT_STREQ(SymNot.Name.c_str(), "not"); 135 EXPECT_EQ(*SymNot.Size, 111u); 136 EXPECT_EQ(SymNot.Type, IFSSymbolType::NoType); 137 EXPECT_TRUE(SymNot.Undefined); 138 EXPECT_TRUE(SymNot.Weak); 139 EXPECT_TRUE(SymNot.Warning); 140 EXPECT_STREQ(SymNot.Warning->c_str(), "All fields populated!"); 141 } 142 143 TEST(ElfYamlTextAPI, YAMLReadsNoTBESyms) { 144 const char Data[] = "--- !ifs-v1\n" 145 "IfsVersion: 1.0\n" 146 "SoName: test.so\n" 147 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 148 "little, BitWidth: 64 }\n" 149 "Symbols: []\n" 150 "...\n"; 151 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 152 ASSERT_THAT_ERROR(StubOrErr.takeError(), Succeeded()); 153 std::unique_ptr<IFSStub> Stub = std::move(StubOrErr.get()); 154 EXPECT_NE(Stub.get(), nullptr); 155 EXPECT_EQ(0u, Stub->Symbols.size()); 156 } 157 158 TEST(ElfYamlTextAPI, YAMLUnreadableTBE) { 159 // Can't read: wrong format/version. 160 const char Data[] = "--- !tapi-tbz\n" 161 "IfsVersion: z.3\n" 162 "SoName: test.so\n" 163 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 164 "little, BitWidth: 64 }\n" 165 "Symbols:\n" 166 " foo: { Type: Func, Undefined: true }\n"; 167 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 168 ASSERT_THAT_ERROR(StubOrErr.takeError(), Failed()); 169 } 170 171 TEST(ElfYamlTextAPI, YAMLUnsupportedVersion) { 172 const char Data[] = "--- !ifs-v1\n" 173 "IfsVersion: 9.9.9\n" 174 "SoName: test.so\n" 175 "Target: { ObjectFormat: ELF, Arch: x86_64, Endianness: " 176 "little, BitWidth: 64 }\n" 177 "Symbols: []\n" 178 "...\n"; 179 Expected<std::unique_ptr<IFSStub>> StubOrErr = readIFSFromBuffer(Data); 180 std::string ErrorMessage = toString(StubOrErr.takeError()); 181 EXPECT_EQ("IFS version 9.9.9 is unsupported.", ErrorMessage); 182 } 183 184 TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) { 185 const char Expected[] = 186 "--- !ifs-v1\n" 187 "IfsVersion: 1.0\n" 188 "Target: { ObjectFormat: ELF, Arch: AArch64, Endianness: " 189 "little, BitWidth: 64 }\n" 190 "Symbols:\n" 191 " - { Name: bar, Type: Func, Weak: true }\n" 192 " - { Name: foo, Type: NoType, Size: 99, Warning: Does nothing }\n" 193 " - { Name: nor, Type: Func, Undefined: true }\n" 194 " - { Name: not, Type: Unknown, Size: 12345678901234 }\n" 195 "...\n"; 196 IFSStub Stub; 197 Stub.IfsVersion = VersionTuple(1, 0); 198 Stub.Target.Arch = ELF::EM_AARCH64; 199 Stub.Target.BitWidth = IFSBitWidthType::IFS64; 200 Stub.Target.Endianness = IFSEndiannessType::Little; 201 Stub.Target.ObjectFormat = "ELF"; 202 203 IFSSymbol SymBar("bar"); 204 SymBar.Size = 128u; 205 SymBar.Type = IFSSymbolType::Func; 206 SymBar.Undefined = false; 207 SymBar.Weak = true; 208 209 IFSSymbol SymFoo("foo"); 210 SymFoo.Size = 99u; 211 SymFoo.Type = IFSSymbolType::NoType; 212 SymFoo.Undefined = false; 213 SymFoo.Weak = false; 214 SymFoo.Warning = "Does nothing"; 215 216 IFSSymbol SymNor("nor"); 217 SymNor.Size = 1234u; 218 SymNor.Type = IFSSymbolType::Func; 219 SymNor.Undefined = true; 220 SymNor.Weak = false; 221 222 IFSSymbol SymNot("not"); 223 SymNot.Size = 12345678901234u; 224 SymNot.Type = IFSSymbolType::Unknown; 225 SymNot.Undefined = false; 226 SymNot.Weak = false; 227 228 // Symbol order is preserved instead of being sorted. 229 Stub.Symbols.push_back(SymBar); 230 Stub.Symbols.push_back(SymFoo); 231 Stub.Symbols.push_back(SymNor); 232 Stub.Symbols.push_back(SymNot); 233 234 // Ensure move constructor works as expected. 235 IFSStub Moved = std::move(Stub); 236 237 std::string Result; 238 raw_string_ostream OS(Result); 239 ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Moved), Succeeded()); 240 compareByLine(Result.c_str(), Expected); 241 } 242 243 TEST(ElfYamlTextAPI, YAMLWritesNoTBESyms) { 244 const char Expected[] = "--- !ifs-v1\n" 245 "IfsVersion: 1.0\n" 246 "SoName: nosyms.so\n" 247 "Target: { ObjectFormat: ELF, Arch: x86_64, " 248 "Endianness: little, BitWidth: 64 }\n" 249 "NeededLibs:\n" 250 " - libc.so\n" 251 " - libfoo.so\n" 252 " - libbar.so\n" 253 "Symbols: []\n" 254 "...\n"; 255 IFSStub Stub; 256 Stub.IfsVersion = VersionTuple(1, 0); 257 Stub.SoName = "nosyms.so"; 258 Stub.Target.Arch = ELF::EM_X86_64; 259 Stub.Target.BitWidth = IFSBitWidthType::IFS64; 260 Stub.Target.Endianness = IFSEndiannessType::Little; 261 Stub.Target.ObjectFormat = "ELF"; 262 Stub.NeededLibs.push_back("libc.so"); 263 Stub.NeededLibs.push_back("libfoo.so"); 264 Stub.NeededLibs.push_back("libbar.so"); 265 266 std::string Result; 267 raw_string_ostream OS(Result); 268 ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Stub), Succeeded()); 269 compareByLine(Result.c_str(), Expected); 270 } 271