1 //===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===// 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 /// \file 10 /// This file implements the XCOFF-specific dumper for llvm-objdump. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "XCOFFDump.h" 15 16 #include "llvm-objdump.h" 17 #include "llvm/ADT/StringExtras.h" 18 #include "llvm/Demangle/Demangle.h" 19 #include "llvm/MC/MCInstPrinter.h" 20 #include "llvm/MC/MCSubtargetInfo.h" 21 #include "llvm/Support/Casting.h" 22 #include "llvm/Support/Endian.h" 23 #include "llvm/Support/FormattedStream.h" 24 #include <algorithm> 25 26 using namespace llvm; 27 using namespace llvm::object; 28 using namespace llvm::XCOFF; 29 using namespace llvm::support; 30 31 namespace { 32 class XCOFFDumper : public objdump::Dumper { 33 public: 34 XCOFFDumper(const object::XCOFFObjectFile &O) : Dumper(O) {} 35 void printPrivateHeaders() override {} 36 }; 37 } // namespace 38 39 std::unique_ptr<objdump::Dumper> 40 objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) { 41 return std::make_unique<XCOFFDumper>(Obj); 42 } 43 44 Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile &Obj, 45 const RelocationRef &Rel, 46 bool SymbolDescription, 47 SmallVectorImpl<char> &Result) { 48 symbol_iterator SymI = Rel.getSymbol(); 49 if (SymI == Obj.symbol_end()) 50 return make_error<GenericBinaryError>( 51 "invalid symbol reference in relocation entry", 52 object_error::parse_failed); 53 54 Expected<StringRef> SymNameOrErr = SymI->getName(); 55 if (!SymNameOrErr) 56 return SymNameOrErr.takeError(); 57 58 std::string SymName = 59 Demangle ? demangle(*SymNameOrErr) : SymNameOrErr->str(); 60 if (SymbolDescription) 61 SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName); 62 63 Result.append(SymName.begin(), SymName.end()); 64 return Error::success(); 65 } 66 67 std::optional<XCOFF::StorageMappingClass> 68 objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile &Obj, 69 const SymbolRef &Sym) { 70 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); 71 72 if (!SymRef.isCsectSymbol()) 73 return std::nullopt; 74 75 auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); 76 if (!CsectAuxEntOrErr) 77 return std::nullopt; 78 79 return CsectAuxEntOrErr.get().getStorageMappingClass(); 80 } 81 82 std::optional<object::SymbolRef> 83 objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile &Obj, 84 const SymbolRef &Sym) { 85 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); 86 if (!SymRef.isCsectSymbol()) 87 return std::nullopt; 88 89 Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); 90 if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) 91 return std::nullopt; 92 uint32_t Idx = 93 static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength()); 94 DataRefImpl DRI; 95 DRI.p = Obj.getSymbolByIndex(Idx); 96 return SymbolRef(DRI, &Obj); 97 } 98 99 bool objdump::isLabel(const XCOFFObjectFile &Obj, const SymbolRef &Sym) { 100 const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); 101 if (!SymRef.isCsectSymbol()) 102 return false; 103 104 auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); 105 if (!CsectAuxEntOrErr) 106 return false; 107 108 return CsectAuxEntOrErr.get().isLabel(); 109 } 110 111 std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, 112 StringRef SymbolName) { 113 assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo."); 114 115 std::string Result; 116 // Dummy symbols have no symbol index. 117 if (SymbolInfo.XCOFFSymInfo.Index) 118 Result = 119 ("(idx: " + Twine(*SymbolInfo.XCOFFSymInfo.Index) + ") " + SymbolName) 120 .str(); 121 else 122 Result.append(SymbolName.begin(), SymbolName.end()); 123 124 if (SymbolInfo.XCOFFSymInfo.StorageMappingClass && 125 !SymbolInfo.XCOFFSymInfo.IsLabel) { 126 const XCOFF::StorageMappingClass Smc = 127 *SymbolInfo.XCOFFSymInfo.StorageMappingClass; 128 Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str()); 129 } 130 131 return Result; 132 } 133 134 #define PRINTBOOL(Prefix, Obj, Field) \ 135 OS << Prefix << " " << ((Obj.Field()) ? "+" : "-") << #Field 136 137 #define PRINTGET(Prefix, Obj, Field) \ 138 OS << Prefix << " " << #Field << " = " \ 139 << static_cast<unsigned>(Obj.get##Field()) 140 141 #define PRINTOPTIONAL(Field) \ 142 if (TbTable.get##Field()) { \ 143 OS << '\n'; \ 144 printRawData(Bytes.slice(Index, 4), Address + Index, OS, STI); \ 145 Index += 4; \ 146 OS << "\t# " << #Field << " = " << *TbTable.get##Field(); \ 147 } 148 149 void objdump::dumpTracebackTable(ArrayRef<uint8_t> Bytes, uint64_t Address, 150 formatted_raw_ostream &OS, uint64_t End, 151 const MCSubtargetInfo &STI, 152 const XCOFFObjectFile *Obj) { 153 uint64_t Index = 0; 154 unsigned TabStop = getInstStartColumn(STI) - 1; 155 // Print traceback table boundary. 156 printRawData(Bytes.slice(Index, 4), Address, OS, STI); 157 OS << "\t# Traceback table start\n"; 158 Index += 4; 159 160 uint64_t Size = End - Address; 161 bool Is64Bit = Obj->is64Bit(); 162 163 // XCOFFTracebackTable::create modifies the size parameter, so ensure Size 164 // isn't changed. 165 uint64_t SizeCopy = End - Address; 166 Expected<XCOFFTracebackTable> TTOrErr = 167 XCOFFTracebackTable::create(Bytes.data() + Index, SizeCopy, Is64Bit); 168 169 if (!TTOrErr) { 170 std::string WarningMsgStr; 171 raw_string_ostream WarningStream(WarningMsgStr); 172 WarningStream << "failure parsing traceback table with address: 0x" 173 << utohexstr(Address) + "\n>>> " 174 << toString(TTOrErr.takeError()) 175 << "\n>>> Raw traceback table data is:\n"; 176 177 uint64_t LastNonZero = Index; 178 for (uint64_t I = Index; I < Size; I += 4) 179 if (support::endian::read32be(Bytes.slice(I, 4).data()) != 0) 180 LastNonZero = I + 4 > Size ? Size : I + 4; 181 182 if (Size - LastNonZero <= 4) 183 LastNonZero = Size; 184 185 formatted_raw_ostream FOS(WarningStream); 186 while (Index < LastNonZero) { 187 printRawData(Bytes.slice(Index, 4), Address + Index, FOS, STI); 188 Index += 4; 189 WarningStream << '\n'; 190 } 191 192 // Print all remaining zeroes as ... 193 if (Size - LastNonZero >= 8) 194 WarningStream << "\t\t...\n"; 195 196 reportWarning(WarningMsgStr, Obj->getFileName()); 197 return; 198 } 199 200 auto PrintBytes = [&](uint64_t N) { 201 printRawData(Bytes.slice(Index, N), Address + Index, OS, STI); 202 Index += N; 203 }; 204 205 XCOFFTracebackTable TbTable = *TTOrErr; 206 // Print the first of the 8 bytes of mandatory fields. 207 PrintBytes(1); 208 OS << format("\t# Version = %i", TbTable.getVersion()) << '\n'; 209 210 // Print the second of the 8 bytes of mandatory fields. 211 PrintBytes(1); 212 TracebackTable::LanguageID LangId = 213 static_cast<TracebackTable::LanguageID>(TbTable.getLanguageID()); 214 OS << "\t# Language = " << getNameForTracebackTableLanguageId(LangId) << '\n'; 215 216 auto Split = [&]() { 217 OS << '\n'; 218 OS.indent(TabStop); 219 }; 220 221 // Print the third of the 8 bytes of mandatory fields. 222 PrintBytes(1); 223 PRINTBOOL("\t#", TbTable, isGlobalLinkage); 224 PRINTBOOL(",", TbTable, isOutOfLineEpilogOrPrologue); 225 Split(); 226 PRINTBOOL("\t ", TbTable, hasTraceBackTableOffset); 227 PRINTBOOL(",", TbTable, isInternalProcedure); 228 Split(); 229 PRINTBOOL("\t ", TbTable, hasControlledStorage); 230 PRINTBOOL(",", TbTable, isTOCless); 231 Split(); 232 PRINTBOOL("\t ", TbTable, isFloatingPointPresent); 233 Split(); 234 PRINTBOOL("\t ", TbTable, isFloatingPointOperationLogOrAbortEnabled); 235 OS << '\n'; 236 237 // Print the 4th of the 8 bytes of mandatory fields. 238 PrintBytes(1); 239 PRINTBOOL("\t#", TbTable, isInterruptHandler); 240 PRINTBOOL(",", TbTable, isFuncNamePresent); 241 PRINTBOOL(",", TbTable, isAllocaUsed); 242 Split(); 243 PRINTGET("\t ", TbTable, OnConditionDirective); 244 PRINTBOOL(",", TbTable, isCRSaved); 245 PRINTBOOL(",", TbTable, isLRSaved); 246 OS << '\n'; 247 248 // Print the 5th of the 8 bytes of mandatory fields. 249 PrintBytes(1); 250 PRINTBOOL("\t#", TbTable, isBackChainStored); 251 PRINTBOOL(",", TbTable, isFixup); 252 PRINTGET(",", TbTable, NumOfFPRsSaved); 253 OS << '\n'; 254 255 // Print the 6th of the 8 bytes of mandatory fields. 256 PrintBytes(1); 257 PRINTBOOL("\t#", TbTable, hasExtensionTable); 258 PRINTBOOL(",", TbTable, hasVectorInfo); 259 PRINTGET(",", TbTable, NumOfGPRsSaved); 260 OS << '\n'; 261 262 // Print the 7th of the 8 bytes of mandatory fields. 263 PrintBytes(1); 264 PRINTGET("\t#", TbTable, NumberOfFixedParms); 265 OS << '\n'; 266 267 // Print the 8th of the 8 bytes of mandatory fields. 268 PrintBytes(1); 269 PRINTGET("\t#", TbTable, NumberOfFPParms); 270 PRINTBOOL(",", TbTable, hasParmsOnStack); 271 272 PRINTOPTIONAL(ParmsType); 273 PRINTOPTIONAL(TraceBackTableOffset); 274 PRINTOPTIONAL(HandlerMask); 275 PRINTOPTIONAL(NumOfCtlAnchors); 276 277 if (TbTable.getControlledStorageInfoDisp()) { 278 SmallVector<uint32_t, 8> Disp = *TbTable.getControlledStorageInfoDisp(); 279 for (unsigned I = 0; I < Disp.size(); ++I) { 280 OS << '\n'; 281 PrintBytes(4); 282 OS << "\t" << (I ? " " : "#") << " ControlledStorageInfoDisp[" << I 283 << "] = " << Disp[I]; 284 } 285 } 286 287 // If there is a name, print the function name and function name length. 288 if (TbTable.isFuncNamePresent()) { 289 uint16_t FunctionNameLen = TbTable.getFunctionName()->size(); 290 if (FunctionNameLen == 0) { 291 OS << '\n'; 292 reportWarning( 293 "the length of the function name must be greater than zero if the " 294 "isFuncNamePresent bit is set in the traceback table", 295 Obj->getFileName()); 296 return; 297 } 298 299 OS << '\n'; 300 PrintBytes(2); 301 OS << "\t# FunctionNameLen = " << FunctionNameLen; 302 303 uint16_t RemainingBytes = FunctionNameLen; 304 bool HasPrinted = false; 305 while (RemainingBytes > 0) { 306 OS << '\n'; 307 uint16_t PrintLen = RemainingBytes >= 4 ? 4 : RemainingBytes; 308 printRawData(Bytes.slice(Index, PrintLen), Address + Index, OS, STI); 309 Index += PrintLen; 310 RemainingBytes -= PrintLen; 311 312 if (!HasPrinted) { 313 OS << "\t# FunctionName = " << *TbTable.getFunctionName(); 314 HasPrinted = true; 315 } 316 } 317 } 318 319 if (TbTable.isAllocaUsed()) { 320 OS << '\n'; 321 PrintBytes(1); 322 OS << format("\t# AllocaRegister = %u", *TbTable.getAllocaRegister()); 323 } 324 325 if (TbTable.getVectorExt()) { 326 OS << '\n'; 327 TBVectorExt VecExt = *TbTable.getVectorExt(); 328 // Print first byte of VectorExt. 329 PrintBytes(1); 330 PRINTGET("\t#", VecExt, NumberOfVRSaved); 331 PRINTBOOL(",", VecExt, isVRSavedOnStack); 332 PRINTBOOL(",", VecExt, hasVarArgs); 333 OS << '\n'; 334 335 // Print the second byte of VectorExt. 336 PrintBytes(1); 337 PRINTGET("\t#", VecExt, NumberOfVectorParms); 338 PRINTBOOL(",", VecExt, hasVMXInstruction); 339 OS << '\n'; 340 341 PrintBytes(4); 342 OS << "\t# VectorParmsInfoString = " << VecExt.getVectorParmsInfo(); 343 344 // There are two bytes of padding after vector info. 345 OS << '\n'; 346 PrintBytes(2); 347 OS << "\t# Padding"; 348 } 349 350 if (TbTable.getExtensionTable()) { 351 OS << '\n'; 352 PrintBytes(1); 353 ExtendedTBTableFlag Flag = 354 static_cast<ExtendedTBTableFlag>(*TbTable.getExtensionTable()); 355 OS << "\t# ExtensionTable = " << getExtendedTBTableFlagString(Flag); 356 } 357 358 if (TbTable.getEhInfoDisp()) { 359 // There are 4 bytes alignment before eh info displacement. 360 if (Index % 4) { 361 OS << '\n'; 362 PrintBytes(4 - Index % 4); 363 OS << "\t# Alignment padding for eh info displacement"; 364 } 365 OS << '\n'; 366 // The size of the displacement (address) is 4 bytes in 32-bit object files, 367 // and 8 bytes in 64-bit object files. 368 PrintBytes(4); 369 OS << "\t# EH info displacement"; 370 if (Is64Bit) { 371 OS << '\n'; 372 PrintBytes(4); 373 } 374 } 375 376 OS << '\n'; 377 if (End == Address + Index) 378 return; 379 380 Size = End - Address; 381 382 const char *LineSuffix = "\t# Padding\n"; 383 auto IsWordZero = [&](uint64_t WordPos) { 384 if (WordPos >= Size) 385 return false; 386 uint64_t LineLength = std::min(4 - WordPos % 4, Size - WordPos); 387 return std::all_of(Bytes.begin() + WordPos, 388 Bytes.begin() + WordPos + LineLength, 389 [](uint8_t Byte) { return Byte == 0; }); 390 }; 391 392 bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Index, 4) + 4), 393 IsWordZero(alignTo(Index, 4) + 8)}; 394 bool ShouldPrintLine = true; 395 while (true) { 396 // Determine the length of the line (4, except for the first line, which 397 // will be just enough to align to the word boundary, and the last line, 398 // which will be the remainder of the data). 399 uint64_t LineLength = std::min(4 - Index % 4, Size - Index); 400 if (ShouldPrintLine) { 401 // Print the line. 402 printRawData(Bytes.slice(Index, LineLength), Address + Index, OS, STI); 403 OS << LineSuffix; 404 LineSuffix = "\n"; 405 } 406 407 Index += LineLength; 408 if (Index == Size) 409 return; 410 411 // For 3 or more consecutive lines of zeros, skip all but the first one, and 412 // replace them with "...". 413 if (AreWordsZero[0] && AreWordsZero[1] && AreWordsZero[2]) { 414 if (ShouldPrintLine) 415 OS << std::string(8, ' ') << "...\n"; 416 ShouldPrintLine = false; 417 } else if (!AreWordsZero[1]) { 418 // We have reached the end of a skipped block of zeros. 419 ShouldPrintLine = true; 420 } 421 AreWordsZero[0] = AreWordsZero[1]; 422 AreWordsZero[1] = AreWordsZero[2]; 423 AreWordsZero[2] = IsWordZero(Index + 8); 424 } 425 } 426 #undef PRINTBOOL 427 #undef PRINTGET 428 #undef PRINTOPTIONAL 429