llvm.org GIT mirror llvm / bab257c
[llvm-tapi] initial commit, supports ELF text stubs http://lists.llvm.org/pipermail/llvm-dev/2018-September/126472.html TextAPI is a library and accompanying tool that allows conversion between binary shared object stubs and textual counterparts. The motivations and uses cases for this are explained thoroughly in the llvm-dev proposal [1]. This initial commit proposes a potential structure for the TAPI library, also including support for reading/writing text-based ELF stubs (.tbe) in addition to preliminary support for reading binary ELF files. The goal for this patch is to ensure the project architecture appropriately welcomes integration of Mach-O stubbing from Apple's TAPI [2]. Added: - TextAPI library - .tbe read support - .tbe write (to raw_ostream) support [1] http://lists.llvm.org/pipermail/llvm-dev/2018-September/126472.html [2] https://github.com/ributzka/tapi Differential Revision: https://reviews.llvm.org/D53051 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@348170 91177308-0d34-0410-b5e6-96231b3b80d8 Armando Montanez 11 months ago
11 changed file(s) with 575 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 //===- ELFStub.h ------------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===-----------------------------------------------------------------------===/
8 ///
9 /// \file
10 /// This file defines an internal representation of an ELF stub.
11 ///
12 //===-----------------------------------------------------------------------===/
13
14 #ifndef LLVM_TEXTAPI_ELF_ELFSTUB_H
15 #define LLVM_TEXTAPI_ELF_ELFSTUB_H
16
17 #include "llvm/BinaryFormat/ELF.h"
18 #include "llvm/Support/VersionTuple.h"
19 #include
20 #include
21
22 namespace llvm {
23 namespace elfabi {
24
25 typedef uint16_t ELFArch;
26
27 enum class ELFSymbolType {
28 NoType = ELF::STT_NOTYPE,
29 Object = ELF::STT_OBJECT,
30 Func = ELF::STT_FUNC,
31 TLS = ELF::STT_TLS,
32
33 // Type information is 4 bits, so 16 is safely out of range.
34 Unknown = 16,
35 };
36
37 struct ELFSymbol {
38 ELFSymbol(std::string SymbolName) : Name(SymbolName) {}
39 std::string Name;
40 uint64_t Size;
41 ELFSymbolType Type;
42 bool Undefined;
43 bool Weak;
44 Optional Warning;
45 bool operator<(const ELFSymbol &RHS) const {
46 return Name < RHS.Name;
47 }
48 };
49
50 // A cumulative representation of ELF stubs.
51 // Both textual and binary stubs will read into and write from this object.
52 class ELFStub {
53 // TODO: Add support for symbol versioning.
54 public:
55 VersionTuple TbeVersion;
56 std::string SoName;
57 ELFArch Arch;
58 std::vector NeededLibs;
59 std::set Symbols;
60
61 ELFStub() {}
62 ELFStub(const ELFStub &Stub);
63 ELFStub(ELFStub &&Stub);
64 };
65 } // end namespace elfabi
66 } // end namespace llvm
67
68 #endif // LLVM_TEXTAPI_ELF_ELFSTUB_H
0 //===- TBEHandler.h ---------------------------------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===-----------------------------------------------------------------------===/
8 ///
9 /// \file
10 /// This file declares an interface for reading and writing .tbe (text-based
11 /// ELF) files.
12 ///
13 //===-----------------------------------------------------------------------===/
14
15 #ifndef LLVM_TEXTAPI_ELF_TBEHANDLER_H
16 #define LLVM_TEXTAPI_ELF_TBEHANDLER_H
17
18 #include "llvm/Support/VersionTuple.h"
19 #include
20
21 namespace llvm {
22
23 class raw_ostream;
24 class Error;
25 class StringRef;
26 class VersionTuple;
27
28 namespace elfabi {
29
30 class ELFStub;
31
32 const VersionTuple TBEVersionCurrent(1, 0);
33
34 class TBEHandler {
35 public:
36 /// Attempts to read an ELF interface file from a StringRef buffer.
37 std::unique_ptr readFile(StringRef Buf);
38
39 /// Attempts to write an ELF interface file to a raw_ostream.
40 Error writeFile(raw_ostream &OS, const ELFStub &Stub);
41 };
42 } // end namespace elfabi
43 } // end namespace llvm
44
45 #endif // LLVM_TEXTAPI_ELF_TBEHANDLER_H
2222 add_subdirectory(LineEditor)
2323 add_subdirectory(ProfileData)
2424 add_subdirectory(Passes)
25 add_subdirectory(TextAPI)
2526 add_subdirectory(ToolDrivers)
2627 add_subdirectory(XRay)
2728 add_subdirectory(Testing)
3939 ProfileData
4040 Support
4141 TableGen
42 TextAPI
4243 Target
4344 Testing
4445 ToolDrivers
0 add_llvm_library(LLVMTextAPI
1 ELF/ELFStub.cpp
2 ELF/TBEHandler.cpp
3
4 ADDITIONAL_HEADER_DIRS
5 "${LLVM_MAIN_INCLUDE_DIR}/llvm/TextAPI"
6 )
0 //===- ELFStub.cpp --------------------------------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===-----------------------------------------------------------------------===/
8
9 #include "llvm/TextAPI/ELF/ELFStub.h"
10
11 using namespace llvm;
12 using namespace llvm::elfabi;
13
14 ELFStub::ELFStub(ELFStub const &Stub) {
15 TbeVersion = Stub.TbeVersion;
16 Arch = Stub.Arch;
17 SoName = Stub.SoName;
18 NeededLibs = Stub.NeededLibs;
19 Symbols = Stub.Symbols;
20 }
21
22 ELFStub::ELFStub(ELFStub &&Stub) {
23 TbeVersion = std::move(Stub.TbeVersion);
24 Arch = std::move(Stub.Arch);
25 SoName = std::move(Stub.SoName);
26 NeededLibs = std::move(Stub.NeededLibs);
27 Symbols = std::move(Stub.Symbols);
28 }
0 //===- TBEHandler.cpp -----------------------------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===-----------------------------------------------------------------------===/
8
9 #include "llvm/TextAPI/ELF/TBEHandler.h"
10 #include "llvm/ADT/StringSwitch.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/YAMLTraits.h"
14 #include "llvm/TextAPI/ELF/ELFStub.h"
15
16 using namespace llvm;
17 using namespace llvm::elfabi;
18
19 LLVM_YAML_STRONG_TYPEDEF(ELFArch, ELFArchMapper);
20
21 namespace llvm {
22 namespace yaml {
23
24 /// YAML traits for ELFSymbolType.
25 template <> struct ScalarEnumerationTraits {
26 static void enumeration(IO &IO, ELFSymbolType &SymbolType) {
27 IO.enumCase(SymbolType, "NoType", ELFSymbolType::NoType);
28 IO.enumCase(SymbolType, "Func", ELFSymbolType::Func);
29 IO.enumCase(SymbolType, "Object", ELFSymbolType::Object);
30 IO.enumCase(SymbolType, "TLS", ELFSymbolType::TLS);
31 IO.enumCase(SymbolType, "Unknown", ELFSymbolType::Unknown);
32 // Treat other symbol types as noise, and map to Unknown.
33 if (!IO.outputting() && IO.matchEnumFallback())
34 SymbolType = ELFSymbolType::Unknown;
35 }
36 };
37
38 /// YAML traits for ELFArch.
39 template <> struct ScalarTraits {
40 static void output(const ELFArchMapper &Value, void *,
41 llvm::raw_ostream &Out) {
42 // Map from integer to architecture string.
43 switch (Value) {
44 case (ELFArch)ELF::EM_X86_64:
45 Out << "x86_64";
46 break;
47 case (ELFArch)ELF::EM_AARCH64:
48 Out << "AArch64";
49 break;
50 case (ELFArch)ELF::EM_NONE:
51 default:
52 Out << "Unknown";
53 }
54 }
55
56 static StringRef input(StringRef Scalar, void *, ELFArchMapper &Value) {
57 // Map from architecture string to integer.
58 Value = StringSwitch(Scalar)
59 .Case("x86_64", ELF::EM_X86_64)
60 .Case("AArch64", ELF::EM_AARCH64)
61 .Case("Unknown", ELF::EM_NONE)
62 .Default(ELF::EM_NONE);
63
64 // Returning empty StringRef indicates successful parse.
65 return StringRef();
66 }
67
68 // Don't place quotation marks around architecture value.
69 static QuotingType mustQuote(StringRef) { return QuotingType::None; }
70 };
71
72 /// YAML traits for TbeVersion.
73 template <> struct ScalarTraits {
74 static void output(const VersionTuple &Value, void *,
75 llvm::raw_ostream &Out) {
76 Out << Value.getAsString();
77 }
78
79 static StringRef input(StringRef Scalar, void *, VersionTuple &Value) {
80 if (Value.tryParse(Scalar))
81 return StringRef("Can't parse version: invalid version format.");
82
83 if (Value > TBEVersionCurrent)
84 return StringRef("Unsupported TBE version.");
85
86 // Returning empty StringRef indicates successful parse.
87 return StringRef();
88 }
89
90 // Don't place quotation marks around version value.
91 static QuotingType mustQuote(StringRef) { return QuotingType::None; }
92 };
93
94 /// YAML traits for ELFSymbol.
95 template <> struct MappingTraits {
96 static void mapping(IO &IO, ELFSymbol &Symbol) {
97 IO.mapRequired("Type", Symbol.Type);
98 // The need for symbol size depends on the symbol type.
99 if (Symbol.Type == ELFSymbolType::NoType) {
100 IO.mapOptional("Size", Symbol.Size, (uint64_t)0);
101 } else if (Symbol.Type == ELFSymbolType::Func) {
102 Symbol.Size = 0;
103 } else {
104 IO.mapRequired("Size", Symbol.Size);
105 }
106 IO.mapOptional("Undefined", Symbol.Undefined, false);
107 IO.mapOptional("Warning", Symbol.Warning);
108 }
109
110 // Compacts symbol information into a single line.
111 static const bool flow = true;
112 };
113
114 /// YAML traits for set of ELFSymbols.
115 template <> struct CustomMappingTraits> {
116 static void inputOne(IO &IO, StringRef Key, std::set &Set) {
117 ELFSymbol Sym(Key.str());
118 IO.mapRequired(Key.str().c_str(), Sym);
119 Set.insert(Sym);
120 }
121
122 static void output(IO &IO, std::set &Set) {
123 for (auto &Sym : Set)
124 IO.mapRequired(Sym.Name.c_str(), const_cast(Sym));
125 }
126 };
127
128 /// YAML traits for generic string vectors (i.e. list of needed libraries).
129 template <> struct SequenceTraits> {
130 static size_t size(IO &IO, std::vector &List) {
131 return List.size();
132 }
133
134 static std::string &element(IO &IO, std::vector &List,
135 size_t Index) {
136 if (Index >= List.size())
137 List.resize(Index + 1);
138 return List[Index];
139 }
140
141 // Compacts list of needed libraries into a single line.
142 static const bool flow = true;
143 };
144
145 /// YAML traits for ELFStub objects.
146 template <> struct MappingTraits {
147 static void mapping(IO &IO, ELFStub &Stub) {
148 if (!IO.mapTag("!tapi-tbe", true))
149 IO.setError("Not a .tbe YAML file.");
150 IO.mapRequired("TbeVersion", Stub.TbeVersion);
151 IO.mapRequired("SoName", Stub.SoName);
152 IO.mapRequired("Arch", (ELFArchMapper &)Stub.Arch);
153 IO.mapOptional("NeededLibs", Stub.NeededLibs);
154 IO.mapRequired("Symbols", Stub.Symbols);
155 }
156 };
157
158 } // end namespace yaml
159 } // end namespace llvm
160
161 std::unique_ptr TBEHandler::readFile(StringRef Buf) {
162 yaml::Input YamlIn(Buf);
163 std::unique_ptr Stub(new ELFStub());
164 YamlIn >> *Stub;
165 if (YamlIn.error())
166 return nullptr;
167 return Stub;
168 }
169
170 Error TBEHandler::writeFile(raw_ostream &OS, const ELFStub &Stub) {
171 yaml::Output YamlOut(OS, NULL, /*WrapColumn =*/0);
172
173 YamlOut << const_cast(Stub);
174 return Error::success();
175 }
0 ;===- ./lib/TextAPI/LLVMBuild.txt ------------------------------*- Conf -*--===;
1 ;
2 ; The LLVM Compiler Infrastructure
3 ;
4 ; This file is distributed under the University of Illinois Open Source
5 ; License. See LICENSE.TXT for details.
6 ;
7 ;===------------------------------------------------------------------------===;
8 ;
9 ; This is an LLVMBuild description file for the components in this subdirectory.
10 ;
11 ; For more information on the LLVMBuild system, please see:
12 ;
13 ; http://llvm.org/docs/LLVMBuild.html
14 ;
15 ;===------------------------------------------------------------------------===;
16
17 [component_0]
18 type = Library
19 name = TextAPI
20 parent = Libraries
21 required_libraries = Support BinaryFormat
2929 add_subdirectory(Passes)
3030 add_subdirectory(ProfileData)
3131 add_subdirectory(Support)
32 add_subdirectory(TextAPI)
3233 add_subdirectory(Target)
3334 add_subdirectory(Transforms)
3435 add_subdirectory(XRay)
0 set(LLVM_LINK_COMPONENTS
1 TextAPI
2 )
3
4 add_llvm_unittest(TapiTests
5 ELFYAMLTest.cpp
6 )
0 //===- llvm/unittests/TextAPI/YAMLTest.cpp --------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===-----------------------------------------------------------------------===/
8
9 #include "llvm/ADT/StringRef.h"
10 #include "llvm/TextAPI/ELF/ELFStub.h"
11 #include "llvm/TextAPI/ELF/TBEHandler.h"
12 #include "llvm/Support/Error.h"
13 #include "gtest/gtest.h"
14 #include
15
16 using namespace llvm;
17 using namespace llvm::ELF;
18 using namespace llvm::elfabi;
19
20 std::unique_ptr readFromBuffer(const char Data[]) {
21 TBEHandler Handler;
22
23 StringRef Buf(Data);
24
25 std::unique_ptr Stub = Handler.readFile(Buf);
26 EXPECT_NE(Stub.get(), nullptr);
27 return Stub;
28 }
29
30 void compareByLine(StringRef LHS, StringRef RHS) {
31 StringRef Line1;
32 StringRef Line2;
33 while (LHS.size() > 0 && RHS.size() > 0) {
34 std::tie(Line1, LHS) = LHS.split('\n');
35 std::tie(Line2, RHS) = RHS.split('\n');
36 // Comparing StringRef objects works, but has messy output when not equal.
37 // Using STREQ on StringRef.data() doesn't work since these substrings are
38 // not null terminated.
39 // This is inefficient, but forces null terminated strings that can be
40 // cleanly compared.
41 EXPECT_STREQ(Line1.str().data(), Line2.str().data());
42 }
43 }
44
45 TEST(ElfYamlTextAPI, YAMLReadableTBE) {
46 const char Data[] = "--- !tapi-tbe\n"
47 "TbeVersion: 1.0\n"
48 "SoName: test.so\n"
49 "Arch: x86_64\n"
50 "NeededLibs: [libc.so, libfoo.so, libbar.so]\n"
51 "Symbols:\n"
52 " foo: { Type: Func, Undefined: true }\n"
53 "...\n";
54 StringRef Buf = StringRef(Data);
55 TBEHandler Handler;
56 std::unique_ptr Stub = Handler.readFile(Buf);
57 EXPECT_NE(Stub.get(), nullptr);
58 EXPECT_EQ(Stub->Arch, (uint16_t)llvm::ELF::EM_X86_64);
59 EXPECT_STREQ(Stub->SoName.c_str(), "test.so");
60 EXPECT_EQ(Stub->NeededLibs.size(), 3u);
61 EXPECT_STREQ(Stub->NeededLibs[0].c_str(), "libc.so");
62 EXPECT_STREQ(Stub->NeededLibs[1].c_str(), "libfoo.so");
63 EXPECT_STREQ(Stub->NeededLibs[2].c_str(), "libbar.so");
64 }
65
66 TEST(ElfYamlTextAPI, YAMLReadsTBESymbols) {
67 const char Data[] = "--- !tapi-tbe\n"
68 "TbeVersion: 1.0\n"
69 "SoName: test.so\n"
70 "Arch: x86_64\n"
71 "Symbols:\n"
72 " bar: { Type: Object, Size: 42 }\n"
73 " baz: { Type: TLS, Size: 3 }\n"
74 " foo: { Type: Func, Warning: \"Deprecated!\" }\n"
75 " nor: { Type: NoType, Undefined: true }\n"
76 " not: { Type: File, Undefined: true, Size: 111, "
77 "Warning: \'All fields populated!\' }\n"
78 "...\n";
79 std::unique_ptr Stub = readFromBuffer(Data);
80 EXPECT_EQ(Stub->Symbols.size(), 5u);
81
82 auto Iterator = Stub->Symbols.begin();
83 ELFSymbol const &SymBar = *Iterator++;
84 EXPECT_STREQ(SymBar.Name.c_str(), "bar");
85 EXPECT_EQ(SymBar.Size, 42u);
86 EXPECT_EQ(SymBar.Type, ELFSymbolType::Object);
87 EXPECT_FALSE(SymBar.Undefined);
88 EXPECT_FALSE(SymBar.Warning.hasValue());
89
90 ELFSymbol const &SymBaz = *Iterator++;
91 EXPECT_STREQ(SymBaz.Name.c_str(), "baz");
92 EXPECT_EQ(SymBaz.Size, 3u);
93 EXPECT_EQ(SymBaz.Type, ELFSymbolType::TLS);
94 EXPECT_FALSE(SymBaz.Undefined);
95 EXPECT_FALSE(SymBaz.Warning.hasValue());
96
97 ELFSymbol const &SymFoo = *Iterator++;
98 EXPECT_STREQ(SymFoo.Name.c_str(), "foo");
99 EXPECT_EQ(SymFoo.Size, 0u);
100 EXPECT_EQ(SymFoo.Type, ELFSymbolType::Func);
101 EXPECT_FALSE(SymFoo.Undefined);
102 EXPECT_TRUE(SymFoo.Warning.hasValue());
103 EXPECT_STREQ(SymFoo.Warning->c_str(), "Deprecated!");
104
105 ELFSymbol const &SymNor = *Iterator++;
106 EXPECT_STREQ(SymNor.Name.c_str(), "nor");
107 EXPECT_EQ(SymNor.Size, 0u);
108 EXPECT_EQ(SymNor.Type, ELFSymbolType::NoType);
109 EXPECT_TRUE(SymNor.Undefined);
110 EXPECT_FALSE(SymNor.Warning.hasValue());
111
112 ELFSymbol const &SymNot = *Iterator++;
113 EXPECT_STREQ(SymNot.Name.c_str(), "not");
114 EXPECT_EQ(SymNot.Size, 111u);
115 EXPECT_EQ(SymNot.Type, ELFSymbolType::Unknown);
116 EXPECT_TRUE(SymNot.Undefined);
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 std::unique_ptr Stub = readFromBuffer(Data);
129 EXPECT_EQ(0u, Stub->Symbols.size());
130 }
131
132 TEST(ElfYamlTextAPI, YAMLUnreadableTBE) {
133 TBEHandler Handler;
134 // Can't read: wrong format/version.
135 const char Data[] = "--- !tapi-tbz\n"
136 "TbeVersion: z.3\n"
137 "SoName: test.so\n"
138 "Arch: x86_64\n"
139 "Symbols:\n"
140 " foo: { Type: Func, Undefined: true }\n";
141 StringRef Buf = StringRef(Data);
142 std::unique_ptr Stub = Handler.readFile(Buf);
143 EXPECT_EQ(Stub.get(), nullptr);
144 }
145
146 TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) {
147 const char Expected[] =
148 "--- !tapi-tbe\n"
149 "TbeVersion: 1.0\n"
150 "SoName: test.so\n"
151 "Arch: AArch64\n"
152 "Symbols: \n"
153 " foo: { Type: NoType, Size: 99, Warning: Does nothing }\n"
154 " nor: { Type: Func, Undefined: true }\n"
155 " not: { Type: Unknown, Size: 12345678901234 }\n"
156 "...\n";
157 ELFStub Stub;
158 Stub.TbeVersion = VersionTuple(1, 0);
159 Stub.SoName = "test.so";
160 Stub.Arch = ELF::EM_AARCH64;
161
162 ELFSymbol SymFoo("foo");
163 SymFoo.Size = 99u;
164 SymFoo.Type = ELFSymbolType::NoType;
165 SymFoo.Undefined = false;
166 SymFoo.Warning = "Does nothing";
167
168 ELFSymbol SymNor("nor");
169 SymNor.Type = ELFSymbolType::Func;
170 SymNor.Undefined = true;
171
172 ELFSymbol SymNot("not");
173 SymNot.Size = 12345678901234u;
174 SymNot.Type = ELFSymbolType::Unknown;
175 SymNot.Undefined = false;
176
177 // Deliberately not in order to check that result is sorted.
178 Stub.Symbols.insert(SymNot);
179 Stub.Symbols.insert(SymFoo);
180 Stub.Symbols.insert(SymNor);
181
182 // Ensure move constructor works as expected.
183 ELFStub Moved = std::move(Stub);
184
185 std::string Result;
186 raw_string_ostream OS(Result);
187 TBEHandler Handler;
188 EXPECT_FALSE(Handler.writeFile(OS, Moved));
189 Result = OS.str();
190 compareByLine(Result.c_str(), Expected);
191 }
192
193 TEST(ElfYamlTextAPI, YAMLWritesNoTBESyms) {
194 const char Expected[] = "--- !tapi-tbe\n"
195 "TbeVersion: 1.0\n"
196 "SoName: nosyms.so\n"
197 "Arch: x86_64\n"
198 "NeededLibs: [ libc.so, libfoo.so, libbar.so ]\n"
199 "Symbols: {}\n"
200 "...\n";
201 ELFStub Stub;
202 Stub.TbeVersion = VersionTuple(1, 0);
203 Stub.SoName = "nosyms.so";
204 Stub.Arch = ELF::EM_X86_64;
205 Stub.NeededLibs.push_back("libc.so");
206 Stub.NeededLibs.push_back("libfoo.so");
207 Stub.NeededLibs.push_back("libbar.so");
208
209 std::string Result;
210 raw_string_ostream OS(Result);
211 TBEHandler Handler;
212 EXPECT_FALSE(Handler.writeFile(OS, Stub));
213 Result = OS.str();
214 compareByLine(Result.c_str(), Expected);
215 }