1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "aot_manager.h"
17 #include "aotdump_options.h"
18 #include "class_data_accessor.h"
19 #include "file.h"
20 #include "file-inl.h"
21 #include "mem/arena_allocator.h"
22 #include "mem/gc/gc_types.h"
23 #include "mem/pool_manager.h"
24 #include "method_data_accessor.h"
25 #include "method_data_accessor-inl.h"
26 #include "proto_data_accessor.h"
27 #include "proto_data_accessor-inl.h"
28 #include "runtime/include/class_helper.h"
29 #include "utils/arch.h"
30 #include "utils/bit_memory_region-inl.h"
31
32 #ifdef PANDA_COMPILER_TARGET_AARCH64
33 #include "aarch64/disasm-aarch64.h"
34 using vixl::aarch64::Decoder;
35 using vixl::aarch64::Disassembler;
36 using vixl::aarch64::Instruction;
37 #endif // PANDA_COMPILER_TARGET_AARCH64
38 #ifdef PANDA_COMPILER_TARGET_X86_64
39 #include "Zydis/Zydis.h"
40 #endif // PANDA_COMPILER_TARGET_X86_64
41
42 #include <fstream>
43 #include <iomanip>
44 #include <elf.h>
45 #include <regex>
46
47 using namespace ark::compiler; // NOLINT(*-build-using-namespace)
48
49 namespace ark::aotdump {
50
51 class TypePrinter {
52 public:
TypePrinter(panda_file::ProtoDataAccessor & pda,std::ostream & out)53 explicit TypePrinter(panda_file::ProtoDataAccessor &pda, std::ostream &out) : pda_(pda), out_(out) {}
54 NO_COPY_SEMANTIC(TypePrinter);
55 NO_MOVE_SEMANTIC(TypePrinter);
56 ~TypePrinter() = default;
57
Dump(panda_file::Type type)58 void Dump(panda_file::Type type)
59 {
60 if (!type.IsReference()) {
61 out_ << type;
62 } else {
63 out_ << ClassHelper::GetName(pda_.GetPandaFile().GetStringData(pda_.GetReferenceType(refIdx_++)).data);
64 }
65 }
66
67 private:
68 panda_file::ProtoDataAccessor &pda_;
69 std::ostream &out_;
70 uint32_t refIdx_ {0};
71 };
72
73 class PandaFileHelper {
74 public:
PandaFileHelper(const char * fileName)75 explicit PandaFileHelper(const char *fileName) : file_(panda_file::OpenPandaFile(fileName)) {}
76 NO_COPY_SEMANTIC(PandaFileHelper);
77 NO_MOVE_SEMANTIC(PandaFileHelper);
78 ~PandaFileHelper() = default;
79
GetMethodName(uint32_t id) const80 std::string GetMethodName(uint32_t id) const
81 {
82 if (!file_) {
83 return "-";
84 }
85 auto fileId = panda_file::File::EntityId(id);
86 panda_file::MethodDataAccessor mda(*file_, fileId);
87 panda_file::ProtoDataAccessor pda(*file_, panda_file::MethodDataAccessor::GetProtoId(*file_, fileId));
88 std::ostringstream ss;
89 TypePrinter typePrinter(pda, ss);
90
91 typePrinter.Dump(pda.GetReturnType());
92 ss << ' ';
93
94 auto className = ClassHelper::GetName(file_->GetStringData(mda.GetClassId()).data);
95 ss << className << "::" << file_->GetStringData(mda.GetNameId()).data;
96
97 ss << '(';
98 bool firstArg = true;
99 // inject class name as the first argument of non static method for consitency with ark::Method::GetFullName
100 if (!mda.IsStatic()) {
101 firstArg = false;
102 ss << className;
103 }
104 for (uint32_t argIdx = 0; argIdx < pda.GetNumArgs(); ++argIdx) {
105 if (!firstArg) {
106 ss << ", ";
107 }
108 firstArg = false;
109 typePrinter.Dump(pda.GetArgType(argIdx));
110 }
111 ss << ')';
112 return ss.str();
113 }
114
GetClassName(uint32_t id) const115 std::string GetClassName(uint32_t id) const
116 {
117 if (!file_) {
118 return "-";
119 }
120 return ClassHelper::GetName(file_->GetStringData(panda_file::File::EntityId(id)).data);
121 }
122
123 private:
124 std::unique_ptr<const panda_file::File> file_;
125 };
126
127 class AotDump {
128 public:
AotDump(ark::ArenaAllocator * allocator)129 explicit AotDump(ark::ArenaAllocator *allocator) : allocator_(allocator) {}
130 NO_COPY_SEMANTIC(AotDump);
131 NO_MOVE_SEMANTIC(AotDump);
132 ~AotDump() = default;
133
Run(int argc,const char * argv[])134 int Run(int argc, const char *argv[]) // NOLINT(modernize-avoid-c-arrays)
135 {
136 ark::PandArgParser paParser;
137 PandArg<std::string> inputFile {"input_file", "", "Input file path"};
138 paParser.EnableTail();
139 paParser.PushBackTail(&inputFile);
140 std::array<char, NAME_MAX> tmpfileBuf {"/tmp/fixed_aot_XXXXXX"};
141 // Remove temporary file at the function exit
142 auto finalizer = [](const char *fileName) {
143 if (fileName != nullptr) {
144 remove(fileName);
145 }
146 };
147 std::unique_ptr<const char, decltype(finalizer)> tempFileRemover(nullptr, finalizer);
148
149 ark::Span<const char *> sp(argv, argc);
150 ark::aotdump::Options options(sp[0]);
151 options.AddOptions(&paParser);
152 options_ = &options;
153 if (!paParser.Parse(argc, argv)) {
154 std::cerr << "Parse options failed: " << paParser.GetErrorString() << std::endl;
155 return -1;
156 }
157 if (inputFile.GetValue().empty()) {
158 std::cerr << "Please specify input file\n";
159 return -1;
160 }
161 Expected<std::unique_ptr<AotFile>, std::string> aotRes;
162 // Fix elf header for cross platform files. Cross opening is available only in X86_64 arch.
163 if (RUNTIME_ARCH == Arch::X86_64) {
164 if (!FixElfHeader(tmpfileBuf, inputFile)) {
165 return -1;
166 }
167 tempFileRemover.reset(tmpfileBuf.data());
168 aotRes = AotFile::Open(tmpfileBuf.data(), 0, true);
169 } else {
170 aotRes = AotFile::Open(inputFile.GetValue(), 0, true);
171 }
172 if (!aotRes) {
173 std::cerr << "Open AOT file failed: " << aotRes.Error() << std::endl;
174 return -1;
175 }
176 aotFile_ = std::move(aotRes.Value());
177 DumpAll();
178 return 0;
179 }
180
DumpAll()181 void DumpAll()
182 {
183 std::ofstream outFstream;
184 if (options_->WasSetOutputFile()) {
185 outFstream.open(options_->GetOutputFile());
186 stream_ = &outFstream;
187 } else {
188 stream_ = &std::cerr;
189 }
190 DumpHeader();
191 DumpFiles();
192 stream_->flush();
193 aotFile_.reset();
194 }
195
FixElfHeader(std::array<char,NAME_MAX> & tmpfileBuf,PandArg<std::string> & inputFile)196 bool FixElfHeader(std::array<char, NAME_MAX> &tmpfileBuf, PandArg<std::string> &inputFile)
197 {
198 int fd = mkstemp(tmpfileBuf.data());
199 if (fd == -1) {
200 std::cerr << "Failed to open temporary file\n";
201 return false;
202 }
203 close(fd);
204 std::ofstream ostm(tmpfileBuf.data(), std::ios::binary);
205 std::ifstream istm(inputFile.GetValue(), std::ios::binary);
206 if (!ostm.is_open() || !istm.is_open()) {
207 std::cerr << "Cannot open tmpfile or input file\n";
208 return false;
209 }
210 std::vector<char> buffer(std::istreambuf_iterator<char>(istm), {});
211 auto *header = reinterpret_cast<Elf64_Ehdr *>(buffer.data());
212 header->e_machine = EM_X86_64;
213 ostm.write(buffer.data(), buffer.size());
214 return true;
215 }
216
DumpHeader()217 void DumpHeader()
218 {
219 auto aotHeader = aotFile_->GetAotHeader();
220 (*stream_) << "header:" << std::endl;
221 (*stream_) << " magic: " << aotHeader->magic.data() << std::endl;
222 (*stream_) << " version: " << aotHeader->version.data() << std::endl;
223 (*stream_) << " filename: " << aotFile_->GetFileName() << std::endl;
224 (*stream_) << " cmdline: " << aotFile_->GetCommandLine() << std::endl;
225 (*stream_) << " checksum: " << aotHeader->checksum << std::endl;
226 (*stream_) << " env checksum: " << aotHeader->environmentChecksum << std::endl;
227 (*stream_) << " arch: " << GetArchString(static_cast<Arch>(aotHeader->arch)) << std::endl;
228 (*stream_) << " gc_type: " << mem::GCStringFromType(static_cast<mem::GCType>(aotHeader->gcType)) << std::endl;
229 (*stream_) << " files_count: " << aotHeader->filesCount << std::endl;
230 (*stream_) << " files_offset: " << aotHeader->filesOffset << std::endl;
231 (*stream_) << " classes_offset: " << aotHeader->classesOffset << std::endl;
232 (*stream_) << " methods_offset: " << aotHeader->methodsOffset << std::endl;
233 (*stream_) << " bitmap_offset: " << aotHeader->bitmapOffset << std::endl;
234 (*stream_) << " strtab_offset: " << aotHeader->strtabOffset << std::endl;
235 }
236
DumpClassHashTable(ark::compiler::AotPandaFile & aotPandaFile,PandaFileHelper & pfile)237 void DumpClassHashTable(ark::compiler::AotPandaFile &aotPandaFile, PandaFileHelper &pfile)
238 {
239 (*stream_) << " class_hash_table:" << std::endl;
240 constexpr int ALIGN_SIZE = 32;
241 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "i" << std::left << std::setfill(' ')
242 << std::setw(ALIGN_SIZE) << "next_pos";
243 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "entity_id_offset" << std::left
244 << std::setfill(' ') << std::setw(ALIGN_SIZE) << "descriptor" << std::endl;
245 auto classHashTable = aotPandaFile.GetClassHashTable();
246 auto hashTableSize = classHashTable.size();
247 for (size_t i = 0; i < hashTableSize; i++) {
248 auto entityPair = classHashTable[i];
249 if (entityPair.descriptorHash != 0) {
250 auto descriptor = pfile.GetClassName(entityPair.entityIdOffset);
251 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1);
252 (*stream_) << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE) << entityPair.nextPos;
253 (*stream_) << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE)
254 << entityPair.entityIdOffset;
255 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << descriptor << std::endl;
256 } else {
257 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1) << std::endl;
258 }
259 }
260 }
261
DumpFiles()262 void DumpFiles()
263 {
264 (*stream_) << "files:" << std::endl;
265 for (decltype(auto) fileHeader : aotFile_->FileHeaders()) {
266 AotPandaFile aotPandaFile(aotFile_.get(), &fileHeader);
267 auto fileName = aotFile_->GetString(fileHeader.fileNameStr);
268 PandaFileHelper pfile(fileName);
269 (*stream_) << "- name: " << fileName << std::endl;
270 (*stream_) << " classes:\n";
271 for (decltype(auto) classHeader : aotPandaFile.GetClassHeaders()) {
272 AotClass klass(aotFile_.get(), &classHeader);
273 (*stream_) << " - class_id: " << classHeader.classId << std::endl;
274 (*stream_) << " name: " << pfile.GetClassName(classHeader.classId) << std::endl;
275 auto methodsBitmap = klass.GetBitmap();
276 BitMemoryRegion rgn(methodsBitmap.data(), methodsBitmap.size());
277 (*stream_) << " methods_bitmap: " << rgn << std::endl;
278 (*stream_) << " methods:\n";
279 for (decltype(auto) methodHeader : klass.GetMethodHeaders()) {
280 DumpMethodHeader(pfile, aotPandaFile, methodHeader);
281 }
282 }
283 DumpClassHashTable(aotPandaFile, pfile);
284 }
285 }
286
DumpMethodHeader(const PandaFileHelper & pfile,const AotPandaFile & aotPandaFile,const MethodHeader & methodHeader)287 void DumpMethodHeader(const PandaFileHelper &pfile, const AotPandaFile &aotPandaFile,
288 const MethodHeader &methodHeader)
289 {
290 auto methodName = pfile.GetMethodName(methodHeader.methodId);
291 if (options_->WasSetMethodRegex() && !methodName.empty()) {
292 static std::regex rgx(options_->GetMethodRegex());
293 if (!std::regex_match(methodName, rgx)) {
294 return;
295 }
296 }
297 (*stream_) << " - id: " << std::dec << methodHeader.methodId << std::endl;
298 (*stream_) << " name: " << methodName << std::endl;
299 (*stream_) << " code_offset: 0x" << std::hex << methodHeader.codeOffset << std::dec << std::endl;
300 (*stream_) << " code_size: " << methodHeader.codeSize << std::endl;
301 auto codeInfo = aotPandaFile.GetMethodCodeInfo(&methodHeader);
302 if (options_->GetShowCode() == "disasm") {
303 (*stream_) << " code: |\n";
304 PrintCode(" ", codeInfo, pfile);
305 }
306 }
307
PrintCode(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const308 void PrintCode(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
309 {
310 Arch arch = static_cast<Arch>(aotFile_->GetAotHeader()->arch);
311 switch (arch) {
312 #ifdef PANDA_COMPILER_TARGET_AARCH64
313 case Arch::AARCH64:
314 return PrintCodeArm64(prefix, codeInfo, pfile);
315 #endif // PANDA_COMPILER_TARGET_AARCH64
316 #ifdef PANDA_COMPILER_TARGET_X86_64
317 case Arch::X86_64:
318 return PrintCodeX8664(prefix, codeInfo, pfile);
319 #endif // PANDA_COMPILER_TARGET_X86_64
320 default:
321 (*stream_) << prefix << "Unsupported target arch: " << GetArchString(arch) << std::endl;
322 break;
323 }
324 }
325
326 #ifdef PANDA_COMPILER_TARGET_AARCH64
PrintCodeArm64(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const327 void PrintCodeArm64(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
328 {
329 Span<const uint8_t> code = codeInfo.GetCodeSpan();
330
331 Decoder decoder(allocator_);
332 Disassembler disasm(allocator_);
333 decoder.AppendVisitor(&disasm);
334 auto startInstr = reinterpret_cast<const Instruction *>(code.data());
335 auto endInstr = reinterpret_cast<const Instruction *>(code.end());
336 uint32_t pc = 0;
337
338 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
339 for (auto instr = startInstr; instr < endInstr; instr += vixl::aarch64::kInstructionSize) {
340 auto stackmap = codeInfo.FindStackMapForNativePc(pc, Arch::AARCH64);
341 if (stackmap.IsValid()) {
342 PrintStackmap(prefix, codeInfo, stackmap, Arch::AARCH64, pfile);
343 }
344 decoder.Decode(instr);
345 (*stream_) << prefix << std::hex << std::setw(8U) << std::setfill('0') << instr - startInstr << ": "
346 << disasm.GetOutput() << std::endl;
347 pc += vixl::aarch64::kInstructionSize;
348 }
349 (*stream_) << std::dec;
350 }
351 #endif // PANDA_COMPILER_TARGET_AARCH64
352 #ifdef PANDA_COMPILER_TARGET_X86_64
PrintCodeX8664(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const353 void PrintCodeX8664(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
354 {
355 Span<const uint8_t> code = codeInfo.GetCodeSpan();
356 constexpr size_t LENGTH = ZYDIS_MAX_INSTRUCTION_LENGTH; // 15 bytes is max inst length in amd64
357 size_t codeSize = code.size();
358
359 // Initialize decoder context
360 ZydisDecoder decoder;
361 if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) {
362 LOG(FATAL, AOT) << "ZydisDecoderInit failed";
363 }
364
365 // Initialize formatter
366 ZydisFormatter formatter;
367 if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) {
368 LOG(FATAL, AOT) << "ZydisFormatterInit failed";
369 }
370
371 for (size_t pos = 0; pos < codeSize;) {
372 ZydisDecodedInstruction instruction;
373 constexpr auto BUF_SIZE = 256;
374 std::array<char, BUF_SIZE> buffer; // NOLINT(cppcoreguidelines-pro-type-member-init)
375 auto len = std::min(LENGTH, codeSize - pos);
376 if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, &code[pos], len, &instruction))) {
377 LOG(FATAL, AOT) << "ZydisDecoderDecodeBuffer failed";
378 }
379 if (!ZYAN_SUCCESS(ZydisFormatterFormatInstruction(&formatter, &instruction, buffer.data(), buffer.size(),
380 uintptr_t(&code[pos])))) {
381 LOG(FATAL, AOT) << "ZydisFormatterFormatInstruction failed";
382 }
383 auto stackmap = codeInfo.FindStackMapForNativePc(pos, Arch::X86_64);
384 if (stackmap.IsValid()) {
385 PrintStackmap(prefix, codeInfo, stackmap, Arch::X86_64, pfile);
386 }
387 (*stream_) << prefix << std::hex << std::setw(8U) << std::setfill('0') << pos << ": " << buffer.data()
388 << std::endl;
389 pos += instruction.length;
390 }
391 }
392 #endif // PANDA_COMPILER_TARGET_X86_64
393
PrintStackmap(const char * prefix,const CodeInfo & codeInfo,const StackMap & stackmap,Arch arch,const PandaFileHelper & pfile) const394 void PrintStackmap(const char *prefix, const CodeInfo &codeInfo, const StackMap &stackmap, Arch arch,
395 const PandaFileHelper &pfile) const
396 {
397 (*stream_) << prefix << " ";
398 codeInfo.Dump(*stream_, stackmap, arch);
399 (*stream_) << std::endl;
400 if (stackmap.HasInlineInfoIndex()) {
401 for (auto ii : const_cast<CodeInfo &>(codeInfo).GetInlineInfos(stackmap)) {
402 (*stream_) << prefix << " ";
403 codeInfo.DumpInlineInfo(*stream_, stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
404 auto id =
405 const_cast<CodeInfo &>(codeInfo).GetMethod(stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
406 (*stream_) << ", method: " << pfile.GetMethodName(std::get<uint32_t>(id));
407 (*stream_) << std::endl;
408 }
409 }
410 }
411
412 private:
413 [[maybe_unused]] ark::ArenaAllocator *allocator_;
414 std::ostream *stream_ {};
415 ark::aotdump::Options *options_ {};
416 std::unique_ptr<AotFile> aotFile_ {};
417 };
418
419 } // namespace ark::aotdump
420
main(int argc,const char * argv[])421 int main(int argc, const char *argv[])
422 {
423 // NOLINTBEGIN(readability-magic-numbers)
424 ark::mem::MemConfig::Initialize(ark::operator""_MB(64ULL), ark::operator""_MB(64ULL), ark::operator""_MB(64ULL),
425 ark::operator""_MB(32ULL), 0, 0);
426 // NOLINTEND(readability-magic-numbers)
427 ark::PoolManager::Initialize();
428 auto allocator = new ark::ArenaAllocator(ark::SpaceType::SPACE_TYPE_COMPILER);
429 ark::aotdump::AotDump aotdump(allocator);
430
431 auto result = aotdump.Run(argc, argv);
432
433 delete allocator;
434 ark::PoolManager::Finalize();
435 ark::mem::MemConfig::Finalize();
436 return result;
437 }
438