| 1 | //===-- ObjDumper.cpp - Base dumper class -----------------------*- C++ -*-===// |
| 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 ObjDumper. |
| 11 | /// |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "ObjDumper.h" |
| 15 | #include "llvm-readobj.h" |
| 16 | #include "llvm/Object/Archive.h" |
| 17 | #include "llvm/Object/Decompressor.h" |
| 18 | #include "llvm/Object/ObjectFile.h" |
| 19 | #include "llvm/Support/Error.h" |
| 20 | #include "llvm/Support/FormatVariadic.h" |
| 21 | #include "llvm/Support/ScopedPrinter.h" |
| 22 | #include "llvm/Support/SystemZ/zOSSupport.h" |
| 23 | #include "llvm/Support/raw_ostream.h" |
| 24 | #include <map> |
| 25 | |
| 26 | namespace llvm { |
| 27 | |
| 28 | static inline Error createError(const Twine &Msg) { |
| 29 | return createStringError(EC: object::object_error::parse_failed, S: Msg); |
| 30 | } |
| 31 | |
| 32 | ObjDumper::ObjDumper(ScopedPrinter &Writer, StringRef ObjName) : W(Writer) { |
| 33 | // Dumper reports all non-critical errors as warnings. |
| 34 | // It does not print the same warning more than once. |
| 35 | WarningHandler = [=](const Twine &Msg) { |
| 36 | if (Warnings.insert(x: Msg.str()).second) |
| 37 | reportWarning(Err: createError(Msg), Input: ObjName); |
| 38 | return Error::success(); |
| 39 | }; |
| 40 | } |
| 41 | |
| 42 | ObjDumper::~ObjDumper() {} |
| 43 | |
| 44 | void ObjDumper::reportUniqueWarning(Error Err) const { |
| 45 | reportUniqueWarning(Msg: toString(E: std::move(Err))); |
| 46 | } |
| 47 | |
| 48 | void ObjDumper::reportUniqueWarning(const Twine &Msg) const { |
| 49 | cantFail(Err: WarningHandler(Msg), |
| 50 | Msg: "WarningHandler should always return ErrorSuccess" ); |
| 51 | } |
| 52 | |
| 53 | static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) { |
| 54 | for (size_t i = 0; i < Len; i++) |
| 55 | W << (isPrint(C: Start[i]) ? static_cast<char>(Start[i]) : '.'); |
| 56 | } |
| 57 | |
| 58 | void ObjDumper::printAsStringList(StringRef StringContent, |
| 59 | size_t StringDataOffset) { |
| 60 | size_t StrSize = StringContent.size(); |
| 61 | if (StrSize == 0) |
| 62 | return; |
| 63 | if (StrSize < StringDataOffset) { |
| 64 | reportUniqueWarning(Msg: "offset (0x" + Twine::utohexstr(Val: StringDataOffset) + |
| 65 | ") is past the end of the contents (size 0x" + |
| 66 | Twine::utohexstr(Val: StrSize) + ")" ); |
| 67 | return; |
| 68 | } |
| 69 | |
| 70 | const uint8_t *StrContent = StringContent.bytes_begin(); |
| 71 | // Some formats contain additional metadata at the start which should not be |
| 72 | // interpreted as strings. Skip these bytes, but account for them in the |
| 73 | // string offsets. |
| 74 | const uint8_t *CurrentWord = StrContent + StringDataOffset; |
| 75 | const uint8_t *StrEnd = StringContent.bytes_end(); |
| 76 | |
| 77 | while (CurrentWord <= StrEnd) { |
| 78 | size_t WordSize = strnlen(string: reinterpret_cast<const char *>(CurrentWord), |
| 79 | maxlen: StrEnd - CurrentWord); |
| 80 | if (!WordSize) { |
| 81 | CurrentWord++; |
| 82 | continue; |
| 83 | } |
| 84 | W.startLine() << format(Fmt: "[%6tx] " , Vals: CurrentWord - StrContent); |
| 85 | printAsPrintable(W&: W.getOStream(), Start: CurrentWord, Len: WordSize); |
| 86 | W.getOStream() << '\n'; |
| 87 | CurrentWord += WordSize + 1; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj, |
| 92 | ArrayRef<std::string> InputFilenames, |
| 93 | const object::Archive *A) { |
| 94 | if (!FileStr.empty()) { |
| 95 | W.getOStream() << "\n" ; |
| 96 | W.printString(Label: "File" , Value: FileStr); |
| 97 | } |
| 98 | W.printString(Label: "Format" , Value: Obj.getFileFormatName()); |
| 99 | W.printString(Label: "Arch" , Value: Triple::getArchTypeName(Kind: Obj.getArch())); |
| 100 | W.printString(Label: "AddressSize" , |
| 101 | Value: std::string(formatv(Fmt: "{0}bit" , Vals: 8 * Obj.getBytesInAddress()))); |
| 102 | this->printLoadName(); |
| 103 | } |
| 104 | |
| 105 | static std::vector<object::SectionRef> |
| 106 | getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, |
| 107 | ArrayRef<std::string> Sections) { |
| 108 | std::vector<object::SectionRef> Ret; |
| 109 | std::map<std::string, bool, std::less<>> SecNames; |
| 110 | std::map<unsigned, bool> SecIndices; |
| 111 | unsigned SecIndex; |
| 112 | for (StringRef Section : Sections) { |
| 113 | if (!Section.getAsInteger(Radix: 0, Result&: SecIndex)) |
| 114 | SecIndices.emplace(args&: SecIndex, args: false); |
| 115 | else |
| 116 | SecNames.emplace(args: std::string(Section), args: false); |
| 117 | } |
| 118 | |
| 119 | SecIndex = Obj.isELF() ? 0 : 1; |
| 120 | for (object::SectionRef SecRef : Obj.sections()) { |
| 121 | StringRef SecName = unwrapOrError(Input: Obj.getFileName(), EO: SecRef.getName()); |
| 122 | auto NameIt = SecNames.find(x: SecName); |
| 123 | if (NameIt != SecNames.end()) |
| 124 | NameIt->second = true; |
| 125 | auto IndexIt = SecIndices.find(x: SecIndex); |
| 126 | if (IndexIt != SecIndices.end()) |
| 127 | IndexIt->second = true; |
| 128 | if (NameIt != SecNames.end() || IndexIt != SecIndices.end()) |
| 129 | Ret.push_back(x: SecRef); |
| 130 | SecIndex++; |
| 131 | } |
| 132 | |
| 133 | for (const std::pair<const std::string, bool> &S : SecNames) |
| 134 | if (!S.second) |
| 135 | reportWarning( |
| 136 | Err: createError(Msg: formatv(Fmt: "could not find section '{0}'" , Vals: S.first).str()), |
| 137 | Input: Obj.getFileName()); |
| 138 | |
| 139 | for (std::pair<unsigned, bool> S : SecIndices) |
| 140 | if (!S.second) |
| 141 | reportWarning( |
| 142 | Err: createError(Msg: formatv(Fmt: "could not find section {0}" , Vals&: S.first).str()), |
| 143 | Input: Obj.getFileName()); |
| 144 | |
| 145 | return Ret; |
| 146 | } |
| 147 | |
| 148 | static void maybeDecompress(const object::ObjectFile &Obj, |
| 149 | StringRef SectionName, StringRef &SectionContent, |
| 150 | SmallString<0> &Out) { |
| 151 | Expected<object::Decompressor> Decompressor = object::Decompressor::create( |
| 152 | Name: SectionName, Data: SectionContent, IsLE: Obj.isLittleEndian(), Is64Bit: Obj.is64Bit()); |
| 153 | if (!Decompressor) |
| 154 | reportWarning(Err: Decompressor.takeError(), Input: Obj.getFileName()); |
| 155 | else if (auto Err = Decompressor->resizeAndDecompress(Out)) |
| 156 | reportWarning(Err: std::move(Err), Input: Obj.getFileName()); |
| 157 | else |
| 158 | SectionContent = Out; |
| 159 | } |
| 160 | |
| 161 | void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj, |
| 162 | ArrayRef<std::string> Sections, |
| 163 | bool Decompress) { |
| 164 | SmallString<0> Out; |
| 165 | for (object::SectionRef Section : |
| 166 | getSectionRefsByNameOrIndex(Obj, Sections)) { |
| 167 | StringRef SectionName = unwrapOrError(Input: Obj.getFileName(), EO: Section.getName()); |
| 168 | W.getOStream() << '\n'; |
| 169 | W.startLine() << "String dump of section '" << SectionName << "':\n" ; |
| 170 | |
| 171 | StringRef SectionContent = |
| 172 | unwrapOrError(Input: Obj.getFileName(), EO: Section.getContents()); |
| 173 | if (Decompress && Section.isCompressed()) |
| 174 | maybeDecompress(Obj, SectionName, SectionContent, Out); |
| 175 | printAsStringList(StringContent: SectionContent); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | void ObjDumper::printSectionsAsHex(const object::ObjectFile &Obj, |
| 180 | ArrayRef<std::string> Sections, |
| 181 | bool Decompress) { |
| 182 | SmallString<0> Out; |
| 183 | for (object::SectionRef Section : |
| 184 | getSectionRefsByNameOrIndex(Obj, Sections)) { |
| 185 | StringRef SectionName = unwrapOrError(Input: Obj.getFileName(), EO: Section.getName()); |
| 186 | W.getOStream() << '\n'; |
| 187 | W.startLine() << "Hex dump of section '" << SectionName << "':\n" ; |
| 188 | |
| 189 | StringRef SectionContent = |
| 190 | unwrapOrError(Input: Obj.getFileName(), EO: Section.getContents()); |
| 191 | if (Decompress && Section.isCompressed()) |
| 192 | maybeDecompress(Obj, SectionName, SectionContent, Out); |
| 193 | const uint8_t *SecContent = SectionContent.bytes_begin(); |
| 194 | const uint8_t *SecEnd = SecContent + SectionContent.size(); |
| 195 | |
| 196 | for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) { |
| 197 | const uint8_t *TmpSecPtr = SecPtr; |
| 198 | uint8_t i; |
| 199 | uint8_t k; |
| 200 | |
| 201 | W.startLine() << format_hex(N: Section.getAddress() + (SecPtr - SecContent), |
| 202 | Width: 10); |
| 203 | W.getOStream() << ' '; |
| 204 | for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) { |
| 205 | for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) { |
| 206 | uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr)); |
| 207 | W.getOStream() << format_hex_no_prefix(N: Val, Width: 2); |
| 208 | } |
| 209 | W.getOStream() << ' '; |
| 210 | } |
| 211 | |
| 212 | // We need to print the correct amount of spaces to match the format. |
| 213 | // We are adding the (4 - i) last rows that are 8 characters each. |
| 214 | // Then, the (4 - i) spaces that are in between the rows. |
| 215 | // Least, if we cut in a middle of a row, we add the remaining characters, |
| 216 | // which is (8 - (k * 2)). |
| 217 | if (i < 4) |
| 218 | W.getOStream() << format(Fmt: "%*c" , Vals: (4 - i) * 8 + (4 - i), Vals: ' '); |
| 219 | if (k < 4) |
| 220 | W.getOStream() << format(Fmt: "%*c" , Vals: 8 - k * 2, Vals: ' '); |
| 221 | |
| 222 | TmpSecPtr = SecPtr; |
| 223 | for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i) |
| 224 | W.getOStream() << (isPrint(C: TmpSecPtr[i]) |
| 225 | ? static_cast<char>(TmpSecPtr[i]) |
| 226 | : '.'); |
| 227 | |
| 228 | W.getOStream() << '\n'; |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | } // namespace llvm |
| 234 | |