1 //===- IFSHandler.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/InterfaceStub/IFSHandler.h" 10 #include "llvm/ADT/STLExtras.h" 11 #include "llvm/ADT/StringRef.h" 12 #include "llvm/ADT/StringSwitch.h" 13 #include "llvm/ADT/Triple.h" 14 #include "llvm/BinaryFormat/ELF.h" 15 #include "llvm/InterfaceStub/IFSStub.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/GlobPattern.h" 18 #include "llvm/Support/LineIterator.h" 19 #include "llvm/Support/YAMLTraits.h" 20 #include <functional> 21 22 using namespace llvm; 23 using namespace llvm::ifs; 24 25 LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol) 26 27 namespace llvm { 28 namespace yaml { 29 30 /// YAML traits for ELFSymbolType. 31 template <> struct ScalarEnumerationTraits<IFSSymbolType> { 32 static void enumeration(IO &IO, IFSSymbolType &SymbolType) { 33 IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType); 34 IO.enumCase(SymbolType, "Func", IFSSymbolType::Func); 35 IO.enumCase(SymbolType, "Object", IFSSymbolType::Object); 36 IO.enumCase(SymbolType, "TLS", IFSSymbolType::TLS); 37 IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown); 38 // Treat other symbol types as noise, and map to Unknown. 39 if (!IO.outputting() && IO.matchEnumFallback()) 40 SymbolType = IFSSymbolType::Unknown; 41 } 42 }; 43 44 template <> struct ScalarTraits<IFSEndiannessType> { 45 static void output(const IFSEndiannessType &Value, void *, 46 llvm::raw_ostream &Out) { 47 switch (Value) { 48 case IFSEndiannessType::Big: 49 Out << "big"; 50 break; 51 case IFSEndiannessType::Little: 52 Out << "little"; 53 break; 54 default: 55 llvm_unreachable("Unsupported endianness"); 56 } 57 } 58 59 static StringRef input(StringRef Scalar, void *, IFSEndiannessType &Value) { 60 Value = StringSwitch<IFSEndiannessType>(Scalar) 61 .Case("big", IFSEndiannessType::Big) 62 .Case("little", IFSEndiannessType::Little) 63 .Default(IFSEndiannessType::Unknown); 64 if (Value == IFSEndiannessType::Unknown) { 65 return "Unsupported endianness"; 66 } 67 return StringRef(); 68 } 69 70 static QuotingType mustQuote(StringRef) { return QuotingType::None; } 71 }; 72 73 template <> struct ScalarTraits<IFSBitWidthType> { 74 static void output(const IFSBitWidthType &Value, void *, 75 llvm::raw_ostream &Out) { 76 switch (Value) { 77 case IFSBitWidthType::IFS32: 78 Out << "32"; 79 break; 80 case IFSBitWidthType::IFS64: 81 Out << "64"; 82 break; 83 default: 84 llvm_unreachable("Unsupported bit width"); 85 } 86 } 87 88 static StringRef input(StringRef Scalar, void *, IFSBitWidthType &Value) { 89 Value = StringSwitch<IFSBitWidthType>(Scalar) 90 .Case("32", IFSBitWidthType::IFS32) 91 .Case("64", IFSBitWidthType::IFS64) 92 .Default(IFSBitWidthType::Unknown); 93 if (Value == IFSBitWidthType::Unknown) { 94 return "Unsupported bit width"; 95 } 96 return StringRef(); 97 } 98 99 static QuotingType mustQuote(StringRef) { return QuotingType::None; } 100 }; 101 102 template <> struct MappingTraits<IFSTarget> { 103 static void mapping(IO &IO, IFSTarget &Target) { 104 IO.mapOptional("ObjectFormat", Target.ObjectFormat); 105 IO.mapOptional("Arch", Target.ArchString); 106 IO.mapOptional("Endianness", Target.Endianness); 107 IO.mapOptional("BitWidth", Target.BitWidth); 108 } 109 110 // Compacts symbol information into a single line. 111 static const bool flow = true; // NOLINT(readability-identifier-naming) 112 }; 113 114 /// YAML traits for ELFSymbol. 115 template <> struct MappingTraits<IFSSymbol> { 116 static void mapping(IO &IO, IFSSymbol &Symbol) { 117 IO.mapRequired("Name", Symbol.Name); 118 IO.mapRequired("Type", Symbol.Type); 119 // The need for symbol size depends on the symbol type. 120 if (Symbol.Type == IFSSymbolType::NoType) { 121 // Size is None, so we are reading it in, or it is non 0 so we 122 // should emit it. 123 if (!Symbol.Size || *Symbol.Size) 124 IO.mapOptional("Size", Symbol.Size); 125 } else if (Symbol.Type != IFSSymbolType::Func) { 126 IO.mapOptional("Size", Symbol.Size); 127 } 128 IO.mapOptional("Undefined", Symbol.Undefined, false); 129 IO.mapOptional("Weak", Symbol.Weak, false); 130 IO.mapOptional("Warning", Symbol.Warning); 131 } 132 133 // Compacts symbol information into a single line. 134 static const bool flow = true; // NOLINT(readability-identifier-naming) 135 }; 136 137 /// YAML traits for ELFStub objects. 138 template <> struct MappingTraits<IFSStub> { 139 static void mapping(IO &IO, IFSStub &Stub) { 140 if (!IO.mapTag("!ifs-v1", true)) 141 IO.setError("Not a .tbe YAML file."); 142 IO.mapRequired("IfsVersion", Stub.IfsVersion); 143 IO.mapOptional("SoName", Stub.SoName); 144 IO.mapOptional("Target", Stub.Target); 145 IO.mapOptional("NeededLibs", Stub.NeededLibs); 146 IO.mapRequired("Symbols", Stub.Symbols); 147 } 148 }; 149 150 /// YAML traits for ELFStubTriple objects. 151 template <> struct MappingTraits<IFSStubTriple> { 152 static void mapping(IO &IO, IFSStubTriple &Stub) { 153 if (!IO.mapTag("!ifs-v1", true)) 154 IO.setError("Not a .tbe YAML file."); 155 IO.mapRequired("IfsVersion", Stub.IfsVersion); 156 IO.mapOptional("SoName", Stub.SoName); 157 IO.mapOptional("Target", Stub.Target.Triple); 158 IO.mapOptional("NeededLibs", Stub.NeededLibs); 159 IO.mapRequired("Symbols", Stub.Symbols); 160 } 161 }; 162 } // end namespace yaml 163 } // end namespace llvm 164 165 /// Attempt to determine if a Text stub uses target triple. 166 bool usesTriple(StringRef Buf) { 167 for (line_iterator I(MemoryBufferRef(Buf, "ELFStub")); !I.is_at_eof(); ++I) { 168 StringRef Line = (*I).trim(); 169 if (Line.startswith("Target:")) { 170 if (Line == "Target:" || Line.contains("{")) { 171 return false; 172 } 173 } 174 } 175 return true; 176 } 177 178 Expected<std::unique_ptr<IFSStub>> ifs::readIFSFromBuffer(StringRef Buf) { 179 yaml::Input YamlIn(Buf); 180 std::unique_ptr<IFSStubTriple> Stub(new IFSStubTriple()); 181 if (usesTriple(Buf)) { 182 YamlIn >> *Stub; 183 } else { 184 YamlIn >> *static_cast<IFSStub *>(Stub.get()); 185 } 186 if (std::error_code Err = YamlIn.error()) { 187 return createStringError(Err, "YAML failed reading as IFS"); 188 } 189 190 if (Stub->IfsVersion > IFSVersionCurrent) 191 return make_error<StringError>( 192 "IFS version " + Stub->IfsVersion.getAsString() + " is unsupported.", 193 std::make_error_code(std::errc::invalid_argument)); 194 if (Stub->Target.ArchString) { 195 Stub->Target.Arch = 196 ELF::convertArchNameToEMachine(*Stub->Target.ArchString); 197 } 198 return std::move(Stub); 199 } 200 201 Error ifs::writeIFSToOutputStream(raw_ostream &OS, const IFSStub &Stub) { 202 yaml::Output YamlOut(OS, nullptr, /*WrapColumn =*/0); 203 std::unique_ptr<IFSStubTriple> CopyStub(new IFSStubTriple(Stub)); 204 if (Stub.Target.Arch) { 205 CopyStub->Target.ArchString = 206 std::string(ELF::convertEMachineToArchName(Stub.Target.Arch.value())); 207 } 208 IFSTarget Target = Stub.Target; 209 210 if (CopyStub->Target.Triple || 211 (!CopyStub->Target.ArchString && !CopyStub->Target.Endianness && 212 !CopyStub->Target.BitWidth)) 213 YamlOut << *CopyStub; 214 else 215 YamlOut << *static_cast<IFSStub *>(CopyStub.get()); 216 return Error::success(); 217 } 218 219 Error ifs::overrideIFSTarget(IFSStub &Stub, Optional<IFSArch> OverrideArch, 220 Optional<IFSEndiannessType> OverrideEndianness, 221 Optional<IFSBitWidthType> OverrideBitWidth, 222 Optional<std::string> OverrideTriple) { 223 std::error_code OverrideEC(1, std::generic_category()); 224 if (OverrideArch) { 225 if (Stub.Target.Arch && Stub.Target.Arch.value() != OverrideArch.value()) { 226 return make_error<StringError>( 227 "Supplied Arch conflicts with the text stub", OverrideEC); 228 } 229 Stub.Target.Arch = OverrideArch.value(); 230 } 231 if (OverrideEndianness) { 232 if (Stub.Target.Endianness && 233 Stub.Target.Endianness.value() != OverrideEndianness.value()) { 234 return make_error<StringError>( 235 "Supplied Endianness conflicts with the text stub", OverrideEC); 236 } 237 Stub.Target.Endianness = OverrideEndianness.value(); 238 } 239 if (OverrideBitWidth) { 240 if (Stub.Target.BitWidth && 241 Stub.Target.BitWidth.value() != OverrideBitWidth.value()) { 242 return make_error<StringError>( 243 "Supplied BitWidth conflicts with the text stub", OverrideEC); 244 } 245 Stub.Target.BitWidth = OverrideBitWidth.value(); 246 } 247 if (OverrideTriple) { 248 if (Stub.Target.Triple && 249 Stub.Target.Triple.value() != OverrideTriple.value()) { 250 return make_error<StringError>( 251 "Supplied Triple conflicts with the text stub", OverrideEC); 252 } 253 Stub.Target.Triple = OverrideTriple.value(); 254 } 255 return Error::success(); 256 } 257 258 Error ifs::validateIFSTarget(IFSStub &Stub, bool ParseTriple) { 259 std::error_code ValidationEC(1, std::generic_category()); 260 if (Stub.Target.Triple) { 261 if (Stub.Target.Arch || Stub.Target.BitWidth || Stub.Target.Endianness || 262 Stub.Target.ObjectFormat) { 263 return make_error<StringError>( 264 "Target triple cannot be used simultaneously with ELF target format", 265 ValidationEC); 266 } 267 if (ParseTriple) { 268 IFSTarget TargetFromTriple = parseTriple(*Stub.Target.Triple); 269 Stub.Target.Arch = TargetFromTriple.Arch; 270 Stub.Target.BitWidth = TargetFromTriple.BitWidth; 271 Stub.Target.Endianness = TargetFromTriple.Endianness; 272 } 273 return Error::success(); 274 } 275 if (!Stub.Target.Arch || !Stub.Target.BitWidth || !Stub.Target.Endianness) { 276 // TODO: unify the error message. 277 if (!Stub.Target.Arch) { 278 return make_error<StringError>("Arch is not defined in the text stub", 279 ValidationEC); 280 } 281 if (!Stub.Target.BitWidth) { 282 return make_error<StringError>("BitWidth is not defined in the text stub", 283 ValidationEC); 284 } 285 if (!Stub.Target.Endianness) { 286 return make_error<StringError>( 287 "Endianness is not defined in the text stub", ValidationEC); 288 } 289 } 290 return Error::success(); 291 } 292 293 IFSTarget ifs::parseTriple(StringRef TripleStr) { 294 Triple IFSTriple(TripleStr); 295 IFSTarget RetTarget; 296 // TODO: Implement a Triple Arch enum to e_machine map. 297 switch (IFSTriple.getArch()) { 298 case Triple::ArchType::aarch64: 299 RetTarget.Arch = (IFSArch)ELF::EM_AARCH64; 300 break; 301 case Triple::ArchType::x86_64: 302 RetTarget.Arch = (IFSArch)ELF::EM_X86_64; 303 break; 304 default: 305 RetTarget.Arch = (IFSArch)ELF::EM_NONE; 306 } 307 RetTarget.Endianness = IFSTriple.isLittleEndian() ? IFSEndiannessType::Little 308 : IFSEndiannessType::Big; 309 RetTarget.BitWidth = 310 IFSTriple.isArch64Bit() ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32; 311 return RetTarget; 312 } 313 314 void ifs::stripIFSTarget(IFSStub &Stub, bool StripTriple, bool StripArch, 315 bool StripEndianness, bool StripBitWidth) { 316 if (StripTriple || StripArch) { 317 Stub.Target.Arch.reset(); 318 Stub.Target.ArchString.reset(); 319 } 320 if (StripTriple || StripEndianness) { 321 Stub.Target.Endianness.reset(); 322 } 323 if (StripTriple || StripBitWidth) { 324 Stub.Target.BitWidth.reset(); 325 } 326 if (StripTriple) { 327 Stub.Target.Triple.reset(); 328 } 329 if (!Stub.Target.Arch && !Stub.Target.BitWidth && !Stub.Target.Endianness) { 330 Stub.Target.ObjectFormat.reset(); 331 } 332 } 333 334 Error ifs::filterIFSSyms(IFSStub &Stub, bool StripUndefined, 335 const std::vector<std::string> &Exclude) { 336 std::function<bool(const IFSSymbol &)> Filter = [](const IFSSymbol &) { 337 return false; 338 }; 339 340 if (StripUndefined) { 341 Filter = [Filter](const IFSSymbol &Sym) { 342 return Sym.Undefined || Filter(Sym); 343 }; 344 } 345 346 for (StringRef Glob : Exclude) { 347 Expected<llvm::GlobPattern> PatternOrErr = llvm::GlobPattern::create(Glob); 348 if (!PatternOrErr) 349 return PatternOrErr.takeError(); 350 Filter = [Pattern = *PatternOrErr, Filter](const IFSSymbol &Sym) { 351 return Pattern.match(Sym.Name) || Filter(Sym); 352 }; 353 } 354 355 llvm::erase_if(Stub.Symbols, Filter); 356 357 return Error::success(); 358 } 359