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.startLine(), Start: CurrentWord, Len: WordSize); |
86 | W.startLine() << '\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 | W.startLine() << "\n" ; |
95 | W.printString(Label: "File" , Value: FileStr); |
96 | W.printString(Label: "Format" , Value: Obj.getFileFormatName()); |
97 | W.printString(Label: "Arch" , Value: Triple::getArchTypeName(Kind: Obj.getArch())); |
98 | W.printString(Label: "AddressSize" , |
99 | Value: std::string(formatv(Fmt: "{0}bit" , Vals: 8 * Obj.getBytesInAddress()))); |
100 | this->printLoadName(); |
101 | } |
102 | |
103 | static std::vector<object::SectionRef> |
104 | getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, |
105 | ArrayRef<std::string> Sections) { |
106 | std::vector<object::SectionRef> Ret; |
107 | std::map<std::string, bool> SecNames; |
108 | std::map<unsigned, bool> SecIndices; |
109 | unsigned SecIndex; |
110 | for (StringRef Section : Sections) { |
111 | if (!Section.getAsInteger(Radix: 0, Result&: SecIndex)) |
112 | SecIndices.emplace(args&: SecIndex, args: false); |
113 | else |
114 | SecNames.emplace(args: std::string(Section), args: false); |
115 | } |
116 | |
117 | SecIndex = Obj.isELF() ? 0 : 1; |
118 | for (object::SectionRef SecRef : Obj.sections()) { |
119 | StringRef SecName = unwrapOrError(Input: Obj.getFileName(), EO: SecRef.getName()); |
120 | auto NameIt = SecNames.find(x: std::string(SecName)); |
121 | if (NameIt != SecNames.end()) |
122 | NameIt->second = true; |
123 | auto IndexIt = SecIndices.find(x: SecIndex); |
124 | if (IndexIt != SecIndices.end()) |
125 | IndexIt->second = true; |
126 | if (NameIt != SecNames.end() || IndexIt != SecIndices.end()) |
127 | Ret.push_back(x: SecRef); |
128 | SecIndex++; |
129 | } |
130 | |
131 | for (const std::pair<const std::string, bool> &S : SecNames) |
132 | if (!S.second) |
133 | reportWarning( |
134 | Err: createError(Msg: formatv(Fmt: "could not find section '{0}'" , Vals: S.first).str()), |
135 | Input: Obj.getFileName()); |
136 | |
137 | for (std::pair<unsigned, bool> S : SecIndices) |
138 | if (!S.second) |
139 | reportWarning( |
140 | Err: createError(Msg: formatv(Fmt: "could not find section {0}" , Vals&: S.first).str()), |
141 | Input: Obj.getFileName()); |
142 | |
143 | return Ret; |
144 | } |
145 | |
146 | static void maybeDecompress(const object::ObjectFile &Obj, |
147 | StringRef SectionName, StringRef &SectionContent, |
148 | SmallString<0> &Out) { |
149 | Expected<object::Decompressor> Decompressor = object::Decompressor::create( |
150 | Name: SectionName, Data: SectionContent, IsLE: Obj.isLittleEndian(), Is64Bit: Obj.is64Bit()); |
151 | if (!Decompressor) |
152 | reportWarning(Err: Decompressor.takeError(), Input: Obj.getFileName()); |
153 | else if (auto Err = Decompressor->resizeAndDecompress(Out)) |
154 | reportWarning(Err: std::move(Err), Input: Obj.getFileName()); |
155 | else |
156 | SectionContent = Out; |
157 | } |
158 | |
159 | void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj, |
160 | ArrayRef<std::string> Sections, |
161 | bool Decompress) { |
162 | SmallString<0> Out; |
163 | for (object::SectionRef Section : |
164 | getSectionRefsByNameOrIndex(Obj, Sections)) { |
165 | StringRef SectionName = unwrapOrError(Input: Obj.getFileName(), EO: Section.getName()); |
166 | W.startLine() << "\nString dump of section '" << SectionName << "':\n" ; |
167 | |
168 | StringRef SectionContent = |
169 | unwrapOrError(Input: Obj.getFileName(), EO: Section.getContents()); |
170 | if (Decompress && Section.isCompressed()) |
171 | maybeDecompress(Obj, SectionName, SectionContent, Out); |
172 | printAsStringList(StringContent: SectionContent); |
173 | } |
174 | } |
175 | |
176 | void ObjDumper::printSectionsAsHex(const object::ObjectFile &Obj, |
177 | ArrayRef<std::string> Sections, |
178 | bool Decompress) { |
179 | SmallString<0> Out; |
180 | for (object::SectionRef Section : |
181 | getSectionRefsByNameOrIndex(Obj, Sections)) { |
182 | StringRef SectionName = unwrapOrError(Input: Obj.getFileName(), EO: Section.getName()); |
183 | W.startLine() << "\nHex dump of section '" << SectionName << "':\n" ; |
184 | |
185 | StringRef SectionContent = |
186 | unwrapOrError(Input: Obj.getFileName(), EO: Section.getContents()); |
187 | if (Decompress && Section.isCompressed()) |
188 | maybeDecompress(Obj, SectionName, SectionContent, Out); |
189 | const uint8_t *SecContent = SectionContent.bytes_begin(); |
190 | const uint8_t *SecEnd = SecContent + SectionContent.size(); |
191 | |
192 | for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) { |
193 | const uint8_t *TmpSecPtr = SecPtr; |
194 | uint8_t i; |
195 | uint8_t k; |
196 | |
197 | W.startLine() << format_hex(N: Section.getAddress() + (SecPtr - SecContent), |
198 | Width: 10); |
199 | W.startLine() << ' '; |
200 | for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) { |
201 | for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) { |
202 | uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr)); |
203 | W.startLine() << format_hex_no_prefix(N: Val, Width: 2); |
204 | } |
205 | W.startLine() << ' '; |
206 | } |
207 | |
208 | // We need to print the correct amount of spaces to match the format. |
209 | // We are adding the (4 - i) last rows that are 8 characters each. |
210 | // Then, the (4 - i) spaces that are in between the rows. |
211 | // Least, if we cut in a middle of a row, we add the remaining characters, |
212 | // which is (8 - (k * 2)). |
213 | if (i < 4) |
214 | W.startLine() << format(Fmt: "%*c" , Vals: (4 - i) * 8 + (4 - i), Vals: ' '); |
215 | if (k < 4) |
216 | W.startLine() << format(Fmt: "%*c" , Vals: 8 - k * 2, Vals: ' '); |
217 | |
218 | TmpSecPtr = SecPtr; |
219 | for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i) |
220 | W.startLine() << (isPrint(C: TmpSecPtr[i]) |
221 | ? static_cast<char>(TmpSecPtr[i]) |
222 | : '.'); |
223 | |
224 | W.startLine() << '\n'; |
225 | } |
226 | } |
227 | } |
228 | |
229 | } // namespace llvm |
230 | |