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 | |