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