xref: /llvm-project/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp (revision 52b48a70d3752f9db36ddcfd26d0451c009b19fc)
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