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 () override {} |
36 | }; |
37 | } // namespace |
38 | |
39 | std::unique_ptr<objdump::Dumper> |
40 | objdump::createXCOFFDumper(const object::XCOFFObjectFile &Obj) { |
41 | return std::make_unique<XCOFFDumper>(args: 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 | Args: "invalid symbol reference in relocation entry" , |
52 | Args: 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(MangledName: *SymNameOrErr) : SymNameOrErr->str(); |
60 | if (SymbolDescription) |
61 | SymName = getXCOFFSymbolDescription(SymbolInfo: createSymbolInfo(Obj, Symbol: *SymI), SymbolName: SymName); |
62 | |
63 | Result.append(in_start: SymName.begin(), in_end: 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(Ref: 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(Ref: 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(Ref: 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(first: SymbolName.begin(), last: SymbolName.end()); |
123 | |
124 | if (SymbolInfo.XCOFFSymInfo.StorageMappingClass && |
125 | !SymbolInfo.XCOFFSymInfo.IsLabel) { |
126 | const XCOFF::StorageMappingClass Smc = |
127 | *SymbolInfo.XCOFFSymInfo.StorageMappingClass; |
128 | Result.append(str: ("[" + XCOFF::getMappingClassString(SMC: 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: Bytes.slice(N: Index, M: 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(Ptr: Bytes.data() + Index, Size&: SizeCopy, Is64Bits: 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(X: Address) + "\n>>> " |
174 | << toString(E: 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(P: Bytes.slice(N: I, M: 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: Bytes.slice(N: Index, M: 4), Address: Address + Index, OS&: 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(Message: WarningMsgStr, File: Obj->getFileName()); |
197 | return; |
198 | } |
199 | |
200 | auto PrintBytes = [&](uint64_t N) { |
201 | printRawData(Bytes: Bytes.slice(N: Index, M: N), Address: 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(Fmt: "\t# Version = %i" , Vals: 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(NumSpaces: 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 | Message: "the length of the function name must be greater than zero if the " |
294 | "isFuncNamePresent bit is set in the traceback table" , |
295 | File: 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: Bytes.slice(N: Index, M: PrintLen), Address: 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(Fmt: "\t# AllocaRegister = %u" , Vals: *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(a: 4 - WordPos % 4, b: Size - WordPos); |
387 | return std::all_of(first: Bytes.begin() + WordPos, |
388 | last: Bytes.begin() + WordPos + LineLength, |
389 | pred: [](uint8_t Byte) { return Byte == 0; }); |
390 | }; |
391 | |
392 | bool AreWordsZero[] = {IsWordZero(Index), IsWordZero(alignTo(Value: Index, Align: 4) + 4), |
393 | IsWordZero(alignTo(Value: Index, Align: 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(a: 4 - Index % 4, b: Size - Index); |
400 | if (ShouldPrintLine) { |
401 | // Print the line. |
402 | printRawData(Bytes: Bytes.slice(N: Index, M: LineLength), Address: 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 | |