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(args&: Line1, args&: LHS) = LHS.split(Separator: '\n'); |
27 | std::tie(args&: Line2, args&: RHS) = RHS.split(Separator: '\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(Buf: 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(Buf: 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(Buf: 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(Buf: 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(Buf: 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(Buf: Data); |
180 | std::string ErrorMessage = toString(E: 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(x: SymBar); |
230 | Stub.Symbols.push_back(x: SymFoo); |
231 | Stub.Symbols.push_back(x: SymNor); |
232 | Stub.Symbols.push_back(x: 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 | Result = OS.str(); |
241 | compareByLine(LHS: Result.c_str(), RHS: Expected); |
242 | } |
243 | |
244 | TEST(ElfYamlTextAPI, YAMLWritesNoTBESyms) { |
245 | const char Expected[] = "--- !ifs-v1\n" |
246 | "IfsVersion: 1.0\n" |
247 | "SoName: nosyms.so\n" |
248 | "Target: { ObjectFormat: ELF, Arch: x86_64, " |
249 | "Endianness: little, BitWidth: 64 }\n" |
250 | "NeededLibs:\n" |
251 | " - libc.so\n" |
252 | " - libfoo.so\n" |
253 | " - libbar.so\n" |
254 | "Symbols: []\n" |
255 | "...\n" ; |
256 | IFSStub Stub; |
257 | Stub.IfsVersion = VersionTuple(1, 0); |
258 | Stub.SoName = "nosyms.so" ; |
259 | Stub.Target.Arch = ELF::EM_X86_64; |
260 | Stub.Target.BitWidth = IFSBitWidthType::IFS64; |
261 | Stub.Target.Endianness = IFSEndiannessType::Little; |
262 | Stub.Target.ObjectFormat = "ELF" ; |
263 | Stub.NeededLibs.push_back(x: "libc.so" ); |
264 | Stub.NeededLibs.push_back(x: "libfoo.so" ); |
265 | Stub.NeededLibs.push_back(x: "libbar.so" ); |
266 | |
267 | std::string Result; |
268 | raw_string_ostream OS(Result); |
269 | ASSERT_THAT_ERROR(writeIFSToOutputStream(OS, Stub), Succeeded()); |
270 | Result = OS.str(); |
271 | compareByLine(LHS: Result.c_str(), RHS: Expected); |
272 | } |
273 | |