1 //===- IFSHandler.cpp -----------------------------------------------------===//
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
7 //===-----------------------------------------------------------------------===/
9 #include "llvm/InterfaceStub/IFSHandler.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/ADT/StringSwitch.h"
13 #include "llvm/BinaryFormat/ELF.h"
14 #include "llvm/InterfaceStub/IFSStub.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/GlobPattern.h"
17 #include "llvm/Support/LineIterator.h"
18 #include "llvm/Support/YAMLTraits.h"
19 #include "llvm/TargetParser/Triple.h"
24 using namespace llvm::ifs
;
26 LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol
)
31 /// YAML traits for ELFSymbolType.
32 template <> struct ScalarEnumerationTraits
<IFSSymbolType
> {
33 static void enumeration(IO
&IO
, IFSSymbolType
&SymbolType
) {
34 IO
.enumCase(SymbolType
, "NoType", IFSSymbolType::NoType
);
35 IO
.enumCase(SymbolType
, "Func", IFSSymbolType::Func
);
36 IO
.enumCase(SymbolType
, "Object", IFSSymbolType::Object
);
37 IO
.enumCase(SymbolType
, "TLS", IFSSymbolType::TLS
);
38 IO
.enumCase(SymbolType
, "Unknown", IFSSymbolType::Unknown
);
39 // Treat other symbol types as noise, and map to Unknown.
40 if (!IO
.outputting() && IO
.matchEnumFallback())
41 SymbolType
= IFSSymbolType::Unknown
;
45 template <> struct ScalarTraits
<IFSEndiannessType
> {
46 static void output(const IFSEndiannessType
&Value
, void *,
47 llvm::raw_ostream
&Out
) {
49 case IFSEndiannessType::Big
:
52 case IFSEndiannessType::Little
:
56 llvm_unreachable("Unsupported endianness");
60 static StringRef
input(StringRef Scalar
, void *, IFSEndiannessType
&Value
) {
61 Value
= StringSwitch
<IFSEndiannessType
>(Scalar
)
62 .Case("big", IFSEndiannessType::Big
)
63 .Case("little", IFSEndiannessType::Little
)
64 .Default(IFSEndiannessType::Unknown
);
65 if (Value
== IFSEndiannessType::Unknown
) {
66 return "Unsupported endianness";
71 static QuotingType
mustQuote(StringRef
) { return QuotingType::None
; }
74 template <> struct ScalarTraits
<IFSBitWidthType
> {
75 static void output(const IFSBitWidthType
&Value
, void *,
76 llvm::raw_ostream
&Out
) {
78 case IFSBitWidthType::IFS32
:
81 case IFSBitWidthType::IFS64
:
85 llvm_unreachable("Unsupported bit width");
89 static StringRef
input(StringRef Scalar
, void *, IFSBitWidthType
&Value
) {
90 Value
= StringSwitch
<IFSBitWidthType
>(Scalar
)
91 .Case("32", IFSBitWidthType::IFS32
)
92 .Case("64", IFSBitWidthType::IFS64
)
93 .Default(IFSBitWidthType::Unknown
);
94 if (Value
== IFSBitWidthType::Unknown
) {
95 return "Unsupported bit width";
100 static QuotingType
mustQuote(StringRef
) { return QuotingType::None
; }
103 template <> struct MappingTraits
<IFSTarget
> {
104 static void mapping(IO
&IO
, IFSTarget
&Target
) {
105 IO
.mapOptional("ObjectFormat", Target
.ObjectFormat
);
106 IO
.mapOptional("Arch", Target
.ArchString
);
107 IO
.mapOptional("Endianness", Target
.Endianness
);
108 IO
.mapOptional("BitWidth", Target
.BitWidth
);
111 // Compacts symbol information into a single line.
112 static const bool flow
= true; // NOLINT(readability-identifier-naming)
115 /// YAML traits for ELFSymbol.
116 template <> struct MappingTraits
<IFSSymbol
> {
117 static void mapping(IO
&IO
, IFSSymbol
&Symbol
) {
118 IO
.mapRequired("Name", Symbol
.Name
);
119 IO
.mapRequired("Type", Symbol
.Type
);
120 // The need for symbol size depends on the symbol type.
121 if (Symbol
.Type
== IFSSymbolType::NoType
) {
122 // Size is None, so we are reading it in, or it is non 0 so we
124 if (!Symbol
.Size
|| *Symbol
.Size
)
125 IO
.mapOptional("Size", Symbol
.Size
);
126 } else if (Symbol
.Type
!= IFSSymbolType::Func
) {
127 IO
.mapOptional("Size", Symbol
.Size
);
129 IO
.mapOptional("Undefined", Symbol
.Undefined
, false);
130 IO
.mapOptional("Weak", Symbol
.Weak
, false);
131 IO
.mapOptional("Warning", Symbol
.Warning
);
134 // Compacts symbol information into a single line.
135 static const bool flow
= true; // NOLINT(readability-identifier-naming)
138 /// YAML traits for ELFStub objects.
139 template <> struct MappingTraits
<IFSStub
> {
140 static void mapping(IO
&IO
, IFSStub
&Stub
) {
141 if (!IO
.mapTag("!ifs-v1", true))
142 IO
.setError("Not a .tbe YAML file.");
143 IO
.mapRequired("IfsVersion", Stub
.IfsVersion
);
144 IO
.mapOptional("SoName", Stub
.SoName
);
145 IO
.mapOptional("Target", Stub
.Target
);
146 IO
.mapOptional("NeededLibs", Stub
.NeededLibs
);
147 IO
.mapRequired("Symbols", Stub
.Symbols
);
151 /// YAML traits for ELFStubTriple objects.
152 template <> struct MappingTraits
<IFSStubTriple
> {
153 static void mapping(IO
&IO
, IFSStubTriple
&Stub
) {
154 if (!IO
.mapTag("!ifs-v1", true))
155 IO
.setError("Not a .tbe YAML file.");
156 IO
.mapRequired("IfsVersion", Stub
.IfsVersion
);
157 IO
.mapOptional("SoName", Stub
.SoName
);
158 IO
.mapOptional("Target", Stub
.Target
.Triple
);
159 IO
.mapOptional("NeededLibs", Stub
.NeededLibs
);
160 IO
.mapRequired("Symbols", Stub
.Symbols
);
163 } // end namespace yaml
164 } // end namespace llvm
166 /// Attempt to determine if a Text stub uses target triple.
167 bool usesTriple(StringRef Buf
) {
168 for (line_iterator
I(MemoryBufferRef(Buf
, "ELFStub")); !I
.is_at_eof(); ++I
) {
169 StringRef Line
= (*I
).trim();
170 if (Line
.starts_with("Target:")) {
171 if (Line
== "Target:" || Line
.contains("{")) {
179 Expected
<std::unique_ptr
<IFSStub
>> ifs::readIFSFromBuffer(StringRef Buf
) {
180 yaml::Input
YamlIn(Buf
);
181 std::unique_ptr
<IFSStubTriple
> Stub(new IFSStubTriple());
182 if (usesTriple(Buf
)) {
185 YamlIn
>> *static_cast<IFSStub
*>(Stub
.get());
187 if (std::error_code Err
= YamlIn
.error()) {
188 return createStringError(Err
, "YAML failed reading as IFS");
191 if (Stub
->IfsVersion
> IFSVersionCurrent
)
192 return make_error
<StringError
>(
193 "IFS version " + Stub
->IfsVersion
.getAsString() + " is unsupported.",
194 std::make_error_code(std::errc::invalid_argument
));
195 if (Stub
->Target
.ArchString
) {
197 ELF::convertArchNameToEMachine(*Stub
->Target
.ArchString
);
198 if (eMachine
== ELF::EM_NONE
)
199 return createStringError(
200 std::make_error_code(std::errc::invalid_argument
),
201 "IFS arch '" + *Stub
->Target
.ArchString
+ "' is unsupported");
202 Stub
->Target
.Arch
= eMachine
;
204 for (const auto &Item
: Stub
->Symbols
) {
205 if (Item
.Type
== IFSSymbolType::Unknown
)
206 return createStringError(
207 std::make_error_code(std::errc::invalid_argument
),
208 "IFS symbol type for symbol '" + Item
.Name
+ "' is unsupported");
210 return std::move(Stub
);
213 Error
ifs::writeIFSToOutputStream(raw_ostream
&OS
, const IFSStub
&Stub
) {
214 yaml::Output
YamlOut(OS
, nullptr, /*WrapColumn =*/0);
215 std::unique_ptr
<IFSStubTriple
> CopyStub(new IFSStubTriple(Stub
));
216 if (Stub
.Target
.Arch
) {
217 CopyStub
->Target
.ArchString
=
218 std::string(ELF::convertEMachineToArchName(*Stub
.Target
.Arch
));
220 IFSTarget Target
= Stub
.Target
;
222 if (CopyStub
->Target
.Triple
||
223 (!CopyStub
->Target
.ArchString
&& !CopyStub
->Target
.Endianness
&&
224 !CopyStub
->Target
.BitWidth
))
225 YamlOut
<< *CopyStub
;
227 YamlOut
<< *static_cast<IFSStub
*>(CopyStub
.get());
228 return Error::success();
231 Error
ifs::overrideIFSTarget(
232 IFSStub
&Stub
, std::optional
<IFSArch
> OverrideArch
,
233 std::optional
<IFSEndiannessType
> OverrideEndianness
,
234 std::optional
<IFSBitWidthType
> OverrideBitWidth
,
235 std::optional
<std::string
> OverrideTriple
) {
236 std::error_code
OverrideEC(1, std::generic_category());
238 if (Stub
.Target
.Arch
&& *Stub
.Target
.Arch
!= *OverrideArch
) {
239 return make_error
<StringError
>(
240 "Supplied Arch conflicts with the text stub", OverrideEC
);
242 Stub
.Target
.Arch
= *OverrideArch
;
244 if (OverrideEndianness
) {
245 if (Stub
.Target
.Endianness
&&
246 *Stub
.Target
.Endianness
!= *OverrideEndianness
) {
247 return make_error
<StringError
>(
248 "Supplied Endianness conflicts with the text stub", OverrideEC
);
250 Stub
.Target
.Endianness
= *OverrideEndianness
;
252 if (OverrideBitWidth
) {
253 if (Stub
.Target
.BitWidth
&& *Stub
.Target
.BitWidth
!= *OverrideBitWidth
) {
254 return make_error
<StringError
>(
255 "Supplied BitWidth conflicts with the text stub", OverrideEC
);
257 Stub
.Target
.BitWidth
= *OverrideBitWidth
;
259 if (OverrideTriple
) {
260 if (Stub
.Target
.Triple
&& *Stub
.Target
.Triple
!= *OverrideTriple
) {
261 return make_error
<StringError
>(
262 "Supplied Triple conflicts with the text stub", OverrideEC
);
264 Stub
.Target
.Triple
= *OverrideTriple
;
266 return Error::success();
269 Error
ifs::validateIFSTarget(IFSStub
&Stub
, bool ParseTriple
) {
270 std::error_code
ValidationEC(1, std::generic_category());
271 if (Stub
.Target
.Triple
) {
272 if (Stub
.Target
.Arch
|| Stub
.Target
.BitWidth
|| Stub
.Target
.Endianness
||
273 Stub
.Target
.ObjectFormat
) {
274 return make_error
<StringError
>(
275 "Target triple cannot be used simultaneously with ELF target format",
279 IFSTarget TargetFromTriple
= parseTriple(*Stub
.Target
.Triple
);
280 Stub
.Target
.Arch
= TargetFromTriple
.Arch
;
281 Stub
.Target
.BitWidth
= TargetFromTriple
.BitWidth
;
282 Stub
.Target
.Endianness
= TargetFromTriple
.Endianness
;
284 return Error::success();
286 if (!Stub
.Target
.Arch
|| !Stub
.Target
.BitWidth
|| !Stub
.Target
.Endianness
) {
287 // TODO: unify the error message.
288 if (!Stub
.Target
.Arch
) {
289 return make_error
<StringError
>("Arch is not defined in the text stub",
292 if (!Stub
.Target
.BitWidth
) {
293 return make_error
<StringError
>("BitWidth is not defined in the text stub",
296 if (!Stub
.Target
.Endianness
) {
297 return make_error
<StringError
>(
298 "Endianness is not defined in the text stub", ValidationEC
);
301 return Error::success();
304 IFSTarget
ifs::parseTriple(StringRef TripleStr
) {
305 Triple
IFSTriple(TripleStr
);
307 // TODO: Implement a Triple Arch enum to e_machine map.
308 switch (IFSTriple
.getArch()) {
309 case Triple::ArchType::aarch64
:
310 RetTarget
.Arch
= (IFSArch
)ELF::EM_AARCH64
;
312 case Triple::ArchType::x86_64
:
313 RetTarget
.Arch
= (IFSArch
)ELF::EM_X86_64
;
315 case Triple::ArchType::riscv64
:
316 RetTarget
.Arch
= (IFSArch
)ELF::EM_RISCV
;
319 RetTarget
.Arch
= (IFSArch
)ELF::EM_NONE
;
321 RetTarget
.Endianness
= IFSTriple
.isLittleEndian() ? IFSEndiannessType::Little
322 : IFSEndiannessType::Big
;
324 IFSTriple
.isArch64Bit() ? IFSBitWidthType::IFS64
: IFSBitWidthType::IFS32
;
328 void ifs::stripIFSTarget(IFSStub
&Stub
, bool StripTriple
, bool StripArch
,
329 bool StripEndianness
, bool StripBitWidth
) {
330 if (StripTriple
|| StripArch
) {
331 Stub
.Target
.Arch
.reset();
332 Stub
.Target
.ArchString
.reset();
334 if (StripTriple
|| StripEndianness
) {
335 Stub
.Target
.Endianness
.reset();
337 if (StripTriple
|| StripBitWidth
) {
338 Stub
.Target
.BitWidth
.reset();
341 Stub
.Target
.Triple
.reset();
343 if (!Stub
.Target
.Arch
&& !Stub
.Target
.BitWidth
&& !Stub
.Target
.Endianness
) {
344 Stub
.Target
.ObjectFormat
.reset();
348 Error
ifs::filterIFSSyms(IFSStub
&Stub
, bool StripUndefined
,
349 const std::vector
<std::string
> &Exclude
) {
350 std::function
<bool(const IFSSymbol
&)> Filter
= [](const IFSSymbol
&) {
354 if (StripUndefined
) {
355 Filter
= [Filter
](const IFSSymbol
&Sym
) {
356 return Sym
.Undefined
|| Filter(Sym
);
360 for (StringRef Glob
: Exclude
) {
361 Expected
<llvm::GlobPattern
> PatternOrErr
= llvm::GlobPattern::create(Glob
);
363 return PatternOrErr
.takeError();
364 Filter
= [Pattern
= *PatternOrErr
, Filter
](const IFSSymbol
&Sym
) {
365 return Pattern
.match(Sym
.Name
) || Filter(Sym
);
369 llvm::erase_if(Stub
.Symbols
, Filter
);
371 return Error::success();