1 /**
2 * Copyright (c) 2021-2022 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 panda::compiler;
48
49 namespace panda::aoutdump {
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(ref_idx_++)).data);
64 }
65 }
66
67 private:
68 panda_file::ProtoDataAccessor &pda_;
69 std::ostream &out_;
70 uint32_t ref_idx_ {0};
71 };
72
73 class PandaFileHelper {
74 public:
PandaFileHelper(const char * file_name)75 explicit PandaFileHelper(const char *file_name) : file_(panda_file::OpenPandaFile(file_name)) {}
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 file_id = panda_file::File::EntityId(id);
86 panda_file::MethodDataAccessor mda(*file_, file_id);
87 panda_file::ProtoDataAccessor pda(*file_, panda_file::MethodDataAccessor::GetProtoId(*file_, file_id));
88 std::ostringstream ss;
89 TypePrinter type_printer(pda, ss);
90
91 type_printer.Dump(pda.GetReturnType());
92 ss << ' ';
93
94 auto class_name = ClassHelper::GetName(file_->GetStringData(mda.GetClassId()).data);
95 ss << class_name << "::" << file_->GetStringData(mda.GetNameId()).data;
96
97 ss << '(';
98 bool first_arg = true;
99 // inject class name as the first argument of non static method for consitency with panda::Method::GetFullName
100 if (!mda.IsStatic()) {
101 first_arg = false;
102 ss << class_name;
103 }
104 for (uint32_t arg_idx = 0; arg_idx < pda.GetNumArgs(); ++arg_idx) {
105 if (!first_arg) {
106 ss << ", ";
107 }
108 first_arg = false;
109 type_printer.Dump(pda.GetArgType(arg_idx));
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(panda::ArenaAllocator * allocator)129 explicit AotDump(panda::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[])
135 {
136 panda::PandArgParser pa_parser;
137 PandArg<std::string> input_file {"input_file", "", "Input file path"};
138 pa_parser.EnableTail();
139 pa_parser.PushBackTail(&input_file);
140 std::array<char, NAME_MAX> tmpfile_buf {"/tmp/fixed_aot_XXXXXX"};
141 // Remove temporary file at the function exit
142 auto finalizer = [](const char *file_name) {
143 if (file_name != nullptr) {
144 remove(file_name);
145 }
146 };
147 std::unique_ptr<const char, decltype(finalizer)> temp_file_remover(nullptr, finalizer);
148
149 panda::Span<const char *> sp(argv, argc);
150 panda::aoutdump::Options options(sp[0]);
151 options.AddOptions(&pa_parser);
152 if (!pa_parser.Parse(argc, argv)) {
153 std::cerr << "Parse options failed: " << pa_parser.GetErrorString() << std::endl;
154 return -1;
155 }
156 if (input_file.GetValue().empty()) {
157 std::cerr << "Please specify input file\n";
158 return -1;
159 }
160 Expected<std::unique_ptr<AotFile>, std::string> aot_res;
161 // Fix elf header for cross platform files. Cross opening is available only in X86_64 arch.
162 if (RUNTIME_ARCH == Arch::X86_64) {
163 if (!FixElfHeader(tmpfile_buf, input_file)) {
164 return -1;
165 }
166 temp_file_remover.reset(tmpfile_buf.data());
167 aot_res = AotFile::Open(tmpfile_buf.data(), 0, true);
168 } else {
169 aot_res = AotFile::Open(input_file.GetValue(), 0, true);
170 }
171 if (!aot_res) {
172 std::cerr << "Open AOT file failed: " << aot_res.Error() << std::endl;
173 return -1;
174 }
175 auto aot_file = std::move(aot_res.Value());
176 std::ostream *output_stream;
177 std::ofstream out_fstream;
178 if (options.WasSetOutputFile()) {
179 out_fstream.open(options.GetOutputFile());
180 output_stream = &out_fstream;
181 } else {
182 output_stream = &std::cerr;
183 }
184 auto &stream = *output_stream;
185 DumpHeader(stream, aot_file);
186 DumpFiles(stream, aot_file, options);
187 return 0;
188 }
189
FixElfHeader(std::array<char,NAME_MAX> & tmpfile_buf,PandArg<std::string> & input_file)190 bool FixElfHeader(std::array<char, NAME_MAX> &tmpfile_buf, PandArg<std::string> &input_file)
191 {
192 int fd = mkstemp(tmpfile_buf.data());
193 if (fd == -1) {
194 std::cerr << "Failed to open temporary file\n";
195 return false;
196 }
197 close(fd);
198 std::ofstream ostm(tmpfile_buf.data(), std::ios::binary);
199 std::ifstream istm(input_file.GetValue(), std::ios::binary);
200 if (!ostm.is_open() || !istm.is_open()) {
201 std::cerr << "Cannot open tmpfile or input file\n";
202 return false;
203 }
204 std::vector<char> buffer(std::istreambuf_iterator<char>(istm), {});
205 Elf64_Ehdr *header = reinterpret_cast<Elf64_Ehdr *>(buffer.data());
206 header->e_machine = EM_X86_64;
207 ostm.write(buffer.data(), buffer.size());
208 return true;
209 }
210
DumpHeader(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aot_file)211 void DumpHeader(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aot_file)
212 {
213 auto aot_header = aot_file->GetAotHeader();
214 stream << "header:" << std::endl;
215 stream << " magic: " << aot_header->magic.data() << std::endl;
216 stream << " version: " << aot_header->version.data() << std::endl;
217 stream << " filename: " << aot_file->GetFileName() << std::endl;
218 stream << " cmdline: " << aot_file->GetCommandLine() << std::endl;
219 stream << " checksum: " << aot_header->checksum << std::endl;
220 stream << " env checksum: " << aot_header->environment_checksum << std::endl;
221 stream << " arch: " << GetArchString(static_cast<Arch>(aot_header->arch)) << std::endl;
222 stream << " gc_type: " << mem::GCStringFromType(static_cast<mem::GCType>(aot_header->gc_type)) << std::endl;
223 stream << " files_count: " << aot_header->files_count << std::endl;
224 stream << " files_offset: " << aot_header->files_offset << std::endl;
225 stream << " classes_offset: " << aot_header->classes_offset << std::endl;
226 stream << " methods_offset: " << aot_header->methods_offset << std::endl;
227 stream << " bitmap_offset: " << aot_header->bitmap_offset << std::endl;
228 stream << " strtab_offset: " << aot_header->strtab_offset << std::endl;
229 }
230
DumpClassHashTable(std::ostream & stream,panda::compiler::AotPandaFile & aot_panda_file,PandaFileHelper & pfile)231 void DumpClassHashTable(std::ostream &stream, panda::compiler::AotPandaFile &aot_panda_file, PandaFileHelper &pfile)
232 {
233 stream << " class_hash_table:" << std::endl;
234 constexpr int ALIGN_SIZE = 32;
235 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "i" << std::left << std::setfill(' ')
236 << std::setw(ALIGN_SIZE) << "next_pos";
237 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "entity_id_offset" << std::left
238 << std::setfill(' ') << std::setw(ALIGN_SIZE) << "descriptor" << std::endl;
239 auto class_hash_table = aot_panda_file.GetClassHashTable();
240 auto hash_table_size = class_hash_table.size();
241 for (size_t i = 0; i < hash_table_size; i++) {
242 auto entity_pair = class_hash_table[i];
243 if (entity_pair.descriptor_hash != 0) {
244 auto descriptor = pfile.GetClassName(entity_pair.entity_id_offset);
245 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1);
246 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE) << entity_pair.next_pos;
247 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE)
248 << entity_pair.entity_id_offset;
249 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << descriptor << std::endl;
250 } else {
251 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1) << std::endl;
252 }
253 }
254 }
255
DumpFiles(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aot_file,panda::aoutdump::Options & options)256 void DumpFiles(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aot_file,
257 panda::aoutdump::Options &options)
258 {
259 stream << "files:" << std::endl;
260 for (decltype(auto) file_header : aot_file->FileHeaders()) {
261 AotPandaFile aot_panda_file(aot_file.get(), &file_header);
262 auto file_name = aot_file->GetString(file_header.file_name_str);
263 PandaFileHelper pfile(file_name);
264 stream << "- name: " << file_name << std::endl;
265 stream << " classes:\n";
266 for (decltype(auto) class_header : aot_panda_file.GetClassHeaders()) {
267 AotClass klass(aot_file.get(), &class_header);
268 stream << " - class_id: " << class_header.class_id << std::endl;
269 stream << " name: " << pfile.GetClassName(class_header.class_id) << std::endl;
270 auto methods_bitmap = klass.GetBitmap();
271 BitMemoryRegion rgn(methods_bitmap.data(), methods_bitmap.size());
272 stream << " methods_bitmap: " << rgn << std::endl;
273 stream << " methods:\n";
274 for (decltype(auto) method_header : klass.GetMethodHeaders()) {
275 auto method_name = pfile.GetMethodName(method_header.method_id);
276 if (options.WasSetMethodRegex() && !method_name.empty()) {
277 static std::regex rgx(options.GetMethodRegex());
278 if (!std::regex_match(method_name, rgx)) {
279 continue;
280 }
281 }
282 stream << " - id: " << std::dec << method_header.method_id << std::endl;
283 stream << " name: " << method_name << std::endl;
284 stream << " code_offset: 0x" << std::hex << method_header.code_offset << std::dec << std::endl;
285 stream << " code_size: " << method_header.code_size << std::endl;
286 auto code_info = aot_panda_file.GetMethodCodeInfo(&method_header);
287 if (options.GetShowCode() == "disasm") {
288 stream << " code: |\n";
289 PrintCode(" ", *aot_file, code_info, stream, pfile);
290 }
291 }
292 }
293 DumpClassHashTable(stream, aot_panda_file, pfile);
294 }
295 }
296
PrintCode(const char * prefix,const AotFile & aot_file,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const297 void PrintCode(const char *prefix, const AotFile &aot_file, const CodeInfo &code_info, std::ostream &stream,
298 const PandaFileHelper &pfile) const
299 {
300 Arch arch = static_cast<Arch>(aot_file.GetAotHeader()->arch);
301 switch (arch) {
302 #ifdef PANDA_COMPILER_TARGET_AARCH64
303 case Arch::AARCH64:
304 return PrintCodeArm64(prefix, code_info, stream, pfile);
305 #endif // PANDA_COMPILER_TARGET_AARCH64
306 #ifdef PANDA_COMPILER_TARGET_X86_64
307 case Arch::X86_64:
308 return PrintCodeX86_64(prefix, code_info, stream, pfile);
309 #endif // PANDA_COMPILER_TARGET_X86_64
310 default:
311 stream << prefix << "Unsupported target arch: " << GetArchString(arch) << std::endl;
312 break;
313 }
314 }
315
316 #ifdef PANDA_COMPILER_TARGET_AARCH64
PrintCodeArm64(const char * prefix,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const317 void PrintCodeArm64(const char *prefix, const CodeInfo &code_info, std::ostream &stream,
318 const PandaFileHelper &pfile) const
319 {
320 Span<const uint8_t> code = code_info.GetCodeSpan();
321
322 Decoder decoder(allocator_);
323 Disassembler disasm(allocator_);
324 decoder.AppendVisitor(&disasm);
325 auto start_instr = reinterpret_cast<const Instruction *>(code.data());
326 auto end_instr = reinterpret_cast<const Instruction *>(code.end());
327 uint32_t pc = 0;
328
329 for (auto instr = start_instr; instr < end_instr; instr += vixl::aarch64::kInstructionSize) {
330 auto stackmap = code_info.FindStackMapForNativePc(pc, Arch::AARCH64);
331 if (stackmap.IsValid()) {
332 PrintStackmap(stream, prefix, code_info, stackmap, Arch::AARCH64, pfile);
333 }
334 decoder.Decode(instr);
335 stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << instr - start_instr << ": "
336 << disasm.GetOutput() << std::endl;
337 pc += vixl::aarch64::kInstructionSize;
338 }
339 stream << std::dec;
340 }
341 #endif // PANDA_COMPILER_TARGET_AARCH64
342 #ifdef PANDA_COMPILER_TARGET_X86_64
PrintCodeX86_64(const char * prefix,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const343 void PrintCodeX86_64(const char *prefix, const CodeInfo &code_info, std::ostream &stream,
344 const PandaFileHelper &pfile) const
345 {
346 Span<const uint8_t> code = code_info.GetCodeSpan();
347 constexpr size_t LENGTH = ZYDIS_MAX_INSTRUCTION_LENGTH; // 15 bytes is max inst length in amd64
348 size_t code_size = code.size();
349
350 // Initialize decoder context
351 ZydisDecoder decoder;
352 if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) {
353 LOG(FATAL, AOT) << "ZydisDecoderInit failed";
354 }
355
356 // Initialize formatter
357 ZydisFormatter formatter;
358 if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) {
359 LOG(FATAL, AOT) << "ZydisFormatterInit failed";
360 }
361
362 for (size_t pos = 0; pos < code_size;) {
363 ZydisDecodedInstruction instruction;
364 constexpr auto BUF_SIZE = 256;
365 std::array<char, BUF_SIZE> buffer;
366 auto len = std::min(LENGTH, code_size - pos);
367 if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, &code[pos], len, &instruction))) {
368 LOG(FATAL, AOT) << "ZydisDecoderDecodeBuffer failed";
369 }
370 if (!ZYAN_SUCCESS(ZydisFormatterFormatInstruction(&formatter, &instruction, buffer.data(), buffer.size(),
371 uintptr_t(&code[pos])))) {
372 LOG(FATAL, AOT) << "ZydisFormatterFormatInstruction failed";
373 }
374 auto stackmap = code_info.FindStackMapForNativePc(pos, Arch::X86_64);
375 if (stackmap.IsValid()) {
376 PrintStackmap(stream, prefix, code_info, stackmap, Arch::X86_64, pfile);
377 }
378 stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << pos << ": " << buffer.data()
379 << std::endl;
380 pos += instruction.length;
381 }
382 }
383 #endif // PANDA_COMPILER_TARGET_X86_64
384
PrintStackmap(std::ostream & stream,const char * prefix,const CodeInfo & code_info,const StackMap & stackmap,Arch arch,const PandaFileHelper & pfile) const385 void PrintStackmap(std::ostream &stream, const char *prefix, const CodeInfo &code_info, const StackMap &stackmap,
386 Arch arch, const PandaFileHelper &pfile) const
387 {
388 stream << prefix << " ";
389 code_info.Dump(stream, stackmap, arch);
390 stream << std::endl;
391 if (stackmap.HasInlineInfoIndex()) {
392 for (auto ii : const_cast<CodeInfo &>(code_info).GetInlineInfos(stackmap)) {
393 stream << prefix << " ";
394 code_info.DumpInlineInfo(stream, stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
395 auto id =
396 const_cast<CodeInfo &>(code_info).GetMethod(stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
397 stream << ", method: " << pfile.GetMethodName(std::get<uint32_t>(id));
398 stream << std::endl;
399 }
400 }
401 }
402
403 private:
404 [[maybe_unused]] panda::ArenaAllocator *allocator_;
405 };
406
407 } // namespace panda::aoutdump
408
main(int argc,const char * argv[])409 int main(int argc, const char *argv[])
410 {
411 panda::mem::MemConfig::Initialize(panda::operator""_MB(64ULL), panda::operator""_MB(64ULL),
412 panda::operator""_MB(64ULL), panda::operator""_MB(32ULL));
413 panda::PoolManager::Initialize();
414 auto allocator = new panda::ArenaAllocator(panda::SpaceType::SPACE_TYPE_COMPILER);
415 panda::aoutdump::AotDump aotdump(allocator);
416
417 auto result = aotdump.Run(argc, argv);
418
419 delete allocator;
420 panda::PoolManager::Finalize();
421 panda::mem::MemConfig::Finalize();
422 return result;
423 }
424