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/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" 20 #include <functional> 21 #include <optional> 22 23 using namespace llvm; 24 using namespace llvm::ifs; 25 26 LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol) 27 28 namespace llvm { 29 namespace yaml { 30 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; 42 } 43 }; 44 45 template <> struct ScalarTraits<IFSEndiannessType> { 46 static void output(const IFSEndiannessType &Value, void *, 47 llvm::raw_ostream &Out) { 48 switch (Value) { 49 case IFSEndiannessType::Big: 50 Out << "big"; 51 break; 52 case IFSEndiannessType::Little: 53 Out << "little"; 54 break; 55 default: 56 llvm_unreachable("Unsupported endianness"); 57 } 58 } 59 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"; 67 } 68 return StringRef(); 69 } 70 71 static QuotingType mustQuote(StringRef) { return QuotingType::None; } 72 }; 73 74 template <> struct ScalarTraits<IFSBitWidthType> { 75 static void output(const IFSBitWidthType &Value, void *, 76 llvm::raw_ostream &Out) { 77 switch (Value) { 78 case IFSBitWidthType::IFS32: 79 Out << "32"; 80 break; 81 case IFSBitWidthType::IFS64: 82 Out << "64"; 83 break; 84 default: 85 llvm_unreachable("Unsupported bit width"); 86 } 87 } 88 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"; 96 } 97 return StringRef(); 98 } 99 100 static QuotingType mustQuote(StringRef) { return QuotingType::None; } 101 }; 102 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); 109 } 110 111 // Compacts symbol information into a single line. 112 static const bool flow = true; // NOLINT(readability-identifier-naming) 113 }; 114 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 123 // should emit it. 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); 128 } 129 IO.mapOptional("Undefined", Symbol.Undefined, false); 130 IO.mapOptional("Weak", Symbol.Weak, false); 131 IO.mapOptional("Warning", Symbol.Warning); 132 } 133 134 // Compacts symbol information into a single line. 135 static const bool flow = true; // NOLINT(readability-identifier-naming) 136 }; 137 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); 148 } 149 }; 150 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); 161 } 162 }; 163 } // end namespace yaml 164 } // end namespace llvm 165 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("{")) { 172 return false; 173 } 174 } 175 } 176 return true; 177 } 178 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)) { 183 YamlIn >> *Stub; 184 } else { 185 YamlIn >> *static_cast<IFSStub *>(Stub.get()); 186 } 187 if (std::error_code Err = YamlIn.error()) { 188 return createStringError(Err, "YAML failed reading as IFS"); 189 } 190 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) { 196 uint16_t eMachine = 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; 203 } 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"); 209 } 210 return std::move(Stub); 211 } 212 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)); 219 } 220 IFSTarget Target = Stub.Target; 221 222 if (CopyStub->Target.Triple || 223 (!CopyStub->Target.ArchString && !CopyStub->Target.Endianness && 224 !CopyStub->Target.BitWidth)) 225 YamlOut << *CopyStub; 226 else 227 YamlOut << *static_cast<IFSStub *>(CopyStub.get()); 228 return Error::success(); 229 } 230 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()); 237 if (OverrideArch) { 238 if (Stub.Target.Arch && *Stub.Target.Arch != *OverrideArch) { 239 return make_error<StringError>( 240 "Supplied Arch conflicts with the text stub", OverrideEC); 241 } 242 Stub.Target.Arch = *OverrideArch; 243 } 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); 249 } 250 Stub.Target.Endianness = *OverrideEndianness; 251 } 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); 256 } 257 Stub.Target.BitWidth = *OverrideBitWidth; 258 } 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); 263 } 264 Stub.Target.Triple = *OverrideTriple; 265 } 266 return Error::success(); 267 } 268 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", 276 ValidationEC); 277 } 278 if (ParseTriple) { 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; 283 } 284 return Error::success(); 285 } 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", 290 ValidationEC); 291 } 292 if (!Stub.Target.BitWidth) { 293 return make_error<StringError>("BitWidth is not defined in the text stub", 294 ValidationEC); 295 } 296 if (!Stub.Target.Endianness) { 297 return make_error<StringError>( 298 "Endianness is not defined in the text stub", ValidationEC); 299 } 300 } 301 return Error::success(); 302 } 303 304 IFSTarget ifs::parseTriple(StringRef TripleStr) { 305 Triple IFSTriple(TripleStr); 306 IFSTarget RetTarget; 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; 311 break; 312 case Triple::ArchType::x86_64: 313 RetTarget.Arch = (IFSArch)ELF::EM_X86_64; 314 break; 315 case Triple::ArchType::riscv64: 316 RetTarget.Arch = (IFSArch)ELF::EM_RISCV; 317 break; 318 default: 319 RetTarget.Arch = (IFSArch)ELF::EM_NONE; 320 } 321 RetTarget.Endianness = IFSTriple.isLittleEndian() ? IFSEndiannessType::Little 322 : IFSEndiannessType::Big; 323 RetTarget.BitWidth = 324 IFSTriple.isArch64Bit() ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32; 325 return RetTarget; 326 } 327 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(); 333 } 334 if (StripTriple || StripEndianness) { 335 Stub.Target.Endianness.reset(); 336 } 337 if (StripTriple || StripBitWidth) { 338 Stub.Target.BitWidth.reset(); 339 } 340 if (StripTriple) { 341 Stub.Target.Triple.reset(); 342 } 343 if (!Stub.Target.Arch && !Stub.Target.BitWidth && !Stub.Target.Endianness) { 344 Stub.Target.ObjectFormat.reset(); 345 } 346 } 347 348 Error ifs::filterIFSSyms(IFSStub &Stub, bool StripUndefined, 349 const std::vector<std::string> &Exclude) { 350 std::function<bool(const IFSSymbol &)> Filter = [](const IFSSymbol &) { 351 return false; 352 }; 353 354 if (StripUndefined) { 355 Filter = [Filter](const IFSSymbol &Sym) { 356 return Sym.Undefined || Filter(Sym); 357 }; 358 } 359 360 for (StringRef Glob : Exclude) { 361 Expected<llvm::GlobPattern> PatternOrErr = llvm::GlobPattern::create(Glob); 362 if (!PatternOrErr) 363 return PatternOrErr.takeError(); 364 Filter = [Pattern = *PatternOrErr, Filter](const IFSSymbol &Sym) { 365 return Pattern.match(Sym.Name) || Filter(Sym); 366 }; 367 } 368 369 llvm::erase_if(Stub.Symbols, Filter); 370 371 return Error::success(); 372 } 373