| /* |
| * Copyright 2016 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "wabt/binary-reader-objdump.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cinttypes> |
| #include <cstdio> |
| #include <cstring> |
| #include <vector> |
| |
| #if HAVE_STRCASECMP |
| #include <strings.h> |
| #endif |
| |
| #include "wabt/binary-reader-nop.h" |
| #include "wabt/filenames.h" |
| #include "wabt/literal.h" |
| #include "wabt/string-util.h" |
| |
| namespace wabt { |
| |
| namespace { |
| |
| class BinaryReaderObjdumpBase : public BinaryReaderNop { |
| public: |
| BinaryReaderObjdumpBase(const uint8_t* data, |
| size_t size, |
| ObjdumpOptions* options, |
| ObjdumpState* state); |
| |
| bool OnError(const Error&) override; |
| |
| Result BeginModule(uint32_t version) override; |
| Result BeginSection(Index section_index, |
| BinarySection section_type, |
| Offset size) override; |
| |
| Result OnOpcode(Opcode Opcode) override; |
| Result OnRelocCount(Index count, Index section_index) override; |
| |
| protected: |
| std::string_view GetTypeName(Index index) const; |
| std::string_view GetFunctionName(Index index) const; |
| std::string_view GetGlobalName(Index index) const; |
| std::string_view GetLocalName(Index function_index, Index local_index) const; |
| std::string_view GetSectionName(Index index) const; |
| std::string_view GetTagName(Index index) const; |
| std::string_view GetSymbolName(Index index) const; |
| std::string_view GetSegmentName(Index index) const; |
| std::string_view GetTableName(Index index) const; |
| void PrintRelocation(const Reloc& reloc, Offset offset) const; |
| Offset GetPrintOffset(Offset offset) const; |
| Offset GetSectionStart(BinarySection section_code) const { |
| return section_starts_[static_cast<size_t>(section_code)]; |
| } |
| |
| ObjdumpOptions* options_; |
| ObjdumpState* objdump_state_; |
| const uint8_t* data_; |
| size_t size_; |
| bool print_details_ = false; |
| BinarySection reloc_section_ = BinarySection::Invalid; |
| Offset section_starts_[kBinarySectionCount]; |
| // Map of section index to section type |
| std::vector<BinarySection> section_types_; |
| bool section_found_ = false; |
| std::string module_name_; |
| Opcode current_opcode = Opcode::Unreachable; |
| |
| std::unique_ptr<FileStream> err_stream_; |
| }; |
| |
| BinaryReaderObjdumpBase::BinaryReaderObjdumpBase(const uint8_t* data, |
| size_t size, |
| ObjdumpOptions* options, |
| ObjdumpState* objdump_state) |
| : options_(options), |
| objdump_state_(objdump_state), |
| data_(data), |
| size_(size), |
| err_stream_(FileStream::CreateStderr()) { |
| ZeroMemory(section_starts_); |
| } |
| |
| Result BinaryReaderObjdumpBase::BeginSection(Index section_index, |
| BinarySection section_code, |
| Offset size) { |
| section_starts_[static_cast<size_t>(section_code)] = state->offset; |
| section_types_.push_back(section_code); |
| return Result::Ok; |
| } |
| |
| bool BinaryReaderObjdumpBase::OnError(const Error&) { |
| // Tell the BinaryReader that this error is "handled" for all passes other |
| // than the prepass. When the error is handled the default message will be |
| // suppressed. |
| return options_->mode != ObjdumpMode::Prepass; |
| } |
| |
| Result BinaryReaderObjdumpBase::BeginModule(uint32_t version) { |
| switch (options_->mode) { |
| case ObjdumpMode::Headers: |
| printf("\n"); |
| printf("Sections:\n\n"); |
| break; |
| case ObjdumpMode::Details: |
| printf("\n"); |
| printf("Section Details:\n\n"); |
| break; |
| case ObjdumpMode::Disassemble: |
| printf("\n"); |
| printf("Code Disassembly:\n\n"); |
| break; |
| case ObjdumpMode::Prepass: { |
| std::string_view basename = GetBasename(options_->filename); |
| if (basename == "-") { |
| basename = "<stdin>"; |
| } |
| printf("%s:\tfile format wasm %#x\n", std::string(basename).c_str(), |
| version); |
| break; |
| } |
| case ObjdumpMode::RawData: |
| break; |
| } |
| |
| return Result::Ok; |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetTypeName(Index index) const { |
| return objdump_state_->type_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const { |
| return objdump_state_->function_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const { |
| return objdump_state_->global_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetLocalName( |
| Index function_index, |
| Index local_index) const { |
| return objdump_state_->local_names.Get(function_index, local_index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const { |
| return objdump_state_->section_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetTagName(Index index) const { |
| return objdump_state_->tag_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const { |
| return objdump_state_->segment_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetTableName(Index index) const { |
| return objdump_state_->table_names.Get(index); |
| } |
| |
| std::string_view BinaryReaderObjdumpBase::GetSymbolName( |
| Index symbol_index) const { |
| if (symbol_index >= objdump_state_->symtab.size()) |
| return "<illegal_symbol_index>"; |
| ObjdumpSymbol& sym = objdump_state_->symtab[symbol_index]; |
| switch (sym.kind) { |
| case SymbolType::Function: |
| return GetFunctionName(sym.index); |
| case SymbolType::Data: |
| return sym.name; |
| case SymbolType::Global: |
| return GetGlobalName(sym.index); |
| case SymbolType::Section: |
| return GetSectionName(sym.index); |
| case SymbolType::Tag: |
| return GetTagName(sym.index); |
| case SymbolType::Table: |
| return GetTableName(sym.index); |
| } |
| WABT_UNREACHABLE; |
| } |
| |
| void BinaryReaderObjdumpBase::PrintRelocation(const Reloc& reloc, |
| Offset offset) const { |
| printf(" %06" PRIzx ": %-18s %" PRIindex, offset, |
| GetRelocTypeName(reloc.type), reloc.index); |
| if (reloc.addend) { |
| printf(" + %d", reloc.addend); |
| } |
| if (reloc.type != RelocType::TypeIndexLEB) { |
| printf(" <" PRIstringview ">", |
| WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(reloc.index))); |
| } |
| printf("\n"); |
| } |
| |
| Offset BinaryReaderObjdumpBase::GetPrintOffset(Offset offset) const { |
| return options_->section_offsets |
| ? offset - GetSectionStart(BinarySection::Code) |
| : offset; |
| } |
| |
| Result BinaryReaderObjdumpBase::OnOpcode(Opcode opcode) { |
| current_opcode = opcode; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpBase::OnRelocCount(Index count, Index section_index) { |
| if (section_index >= section_types_.size()) { |
| err_stream_->Writef("invalid relocation section index: %" PRIindex "\n", |
| section_index); |
| reloc_section_ = BinarySection::Invalid; |
| return Result::Error; |
| } |
| reloc_section_ = section_types_[section_index]; |
| return Result::Ok; |
| } |
| |
| class BinaryReaderObjdumpPrepass : public BinaryReaderObjdumpBase { |
| public: |
| using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; |
| |
| Result BeginSection(Index section_index, |
| BinarySection section_code, |
| Offset size) override { |
| BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); |
| if (section_code != BinarySection::Custom) { |
| objdump_state_->section_names.Set(section_index, |
| wabt::GetSectionName(section_code)); |
| } |
| return Result::Ok; |
| } |
| |
| Result BeginCustomSection(Index section_index, |
| Offset size, |
| std::string_view section_name) override { |
| objdump_state_->section_names.Set(section_index, section_name); |
| return Result::Ok; |
| } |
| |
| Result OnFunctionName(Index index, std::string_view name) override { |
| SetFunctionName(index, name); |
| return Result::Ok; |
| } |
| |
| Result OnFuncType(Index index, |
| Index param_count, |
| Type* param_types, |
| Index result_count, |
| Type* result_types) override { |
| objdump_state_->function_param_counts[index] = param_count; |
| return Result::Ok; |
| } |
| |
| Result OnNameEntry(NameSectionSubsection type, |
| Index index, |
| std::string_view name) override { |
| switch (type) { |
| // TODO(sbc): remove OnFunctionName in favor of just using |
| // OnNameEntry so that this works |
| /* |
| case NameSectionSubsection::Function: |
| SetFunctionName(index, name); |
| break; |
| */ |
| case NameSectionSubsection::Type: |
| SetTypeName(index, name); |
| break; |
| case NameSectionSubsection::Global: |
| SetGlobalName(index, name); |
| break; |
| case NameSectionSubsection::Table: |
| SetTableName(index, name); |
| break; |
| case NameSectionSubsection::DataSegment: |
| SetSegmentName(index, name); |
| break; |
| case NameSectionSubsection::Tag: |
| SetTagName(index, name); |
| break; |
| default: |
| break; |
| } |
| return Result::Ok; |
| } |
| |
| Result OnLocalName(Index function_index, |
| Index local_index, |
| std::string_view local_name) override { |
| SetLocalName(function_index, local_index, local_name); |
| return Result::Ok; |
| } |
| |
| Result OnSymbolCount(Index count) override { |
| objdump_state_->symtab.resize(count); |
| return Result::Ok; |
| } |
| |
| Result OnDataSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index segment, |
| uint32_t offset, |
| uint32_t size) override { |
| objdump_state_->symtab[index] = {SymbolType::Data, std::string(name), 0}; |
| return Result::Ok; |
| } |
| |
| Result OnFunctionSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index func_index) override { |
| if (!name.empty()) { |
| SetFunctionName(func_index, name); |
| } |
| objdump_state_->symtab[index] = {SymbolType::Function, std::string(name), |
| func_index}; |
| return Result::Ok; |
| } |
| |
| Result OnGlobalSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index global_index) override { |
| if (!name.empty()) { |
| SetGlobalName(global_index, name); |
| } |
| objdump_state_->symtab[index] = {SymbolType::Global, std::string(name), |
| global_index}; |
| return Result::Ok; |
| } |
| |
| Result OnSectionSymbol(Index index, |
| uint32_t flags, |
| Index section_index) override { |
| objdump_state_->symtab[index] = {SymbolType::Section, |
| std::string(GetSectionName(section_index)), |
| section_index}; |
| return Result::Ok; |
| } |
| |
| Result OnTagSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index tag_index) override { |
| if (!name.empty()) { |
| SetTagName(tag_index, name); |
| } |
| objdump_state_->symtab[index] = {SymbolType::Tag, std::string(name), |
| tag_index}; |
| return Result::Ok; |
| } |
| |
| Result OnTableSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index table_index) override { |
| if (!name.empty()) { |
| SetTableName(table_index, name); |
| } |
| objdump_state_->symtab[index] = {SymbolType::Table, std::string(name), |
| table_index}; |
| return Result::Ok; |
| } |
| |
| Result OnImportFunc(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index func_index, |
| Index sig_index) override { |
| SetFunctionName(func_index, module_name + "." + field_name); |
| return Result::Ok; |
| } |
| |
| Result OnImportTag(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index tag_index, |
| Index sig_index) override { |
| SetTagName(tag_index, module_name + "." + field_name); |
| return Result::Ok; |
| } |
| |
| Result OnImportGlobal(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index global_index, |
| Type type, |
| bool mutable_) override { |
| SetGlobalName(global_index, module_name + "." + field_name); |
| return Result::Ok; |
| } |
| |
| Result OnImportTable(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index table_index, |
| Type elem_type, |
| const Limits* elem_limits) override { |
| SetTableName(table_index, module_name + "." + field_name); |
| return Result::Ok; |
| } |
| |
| Result OnExport(Index index, |
| ExternalKind kind, |
| Index item_index, |
| std::string_view name) override { |
| if (kind == ExternalKind::Func) { |
| SetFunctionName(item_index, name); |
| } else if (kind == ExternalKind::Global) { |
| SetGlobalName(item_index, name); |
| } |
| return Result::Ok; |
| } |
| |
| Result OnReloc(RelocType type, |
| Offset offset, |
| Index index, |
| uint32_t addend) override; |
| |
| Result OnModuleName(std::string_view name) override { |
| if (options_->mode == ObjdumpMode::Prepass) { |
| printf("module name: <" PRIstringview ">\n", |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| return Result::Ok; |
| } |
| |
| Result OnSegmentInfo(Index index, |
| std::string_view name, |
| Address alignment_log2, |
| uint32_t flags) override { |
| SetSegmentName(index, name); |
| return Result::Ok; |
| } |
| |
| protected: |
| void SetTypeName(Index index, std::string_view name); |
| void SetFunctionName(Index index, std::string_view name); |
| void SetGlobalName(Index index, std::string_view name); |
| void SetLocalName(Index function_index, |
| Index local_index, |
| std::string_view name); |
| void SetTagName(Index index, std::string_view name); |
| void SetTableName(Index index, std::string_view name); |
| void SetSegmentName(Index index, std::string_view name); |
| }; |
| |
| void BinaryReaderObjdumpPrepass::SetTypeName(Index index, |
| std::string_view name) { |
| objdump_state_->type_names.Set(index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetFunctionName(Index index, |
| std::string_view name) { |
| objdump_state_->function_names.Set(index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetGlobalName(Index index, |
| std::string_view name) { |
| objdump_state_->global_names.Set(index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetLocalName(Index function_index, |
| Index local_index, |
| std::string_view name) { |
| objdump_state_->local_names.Set(function_index, local_index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetTagName(Index index, |
| std::string_view name) { |
| objdump_state_->tag_names.Set(index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetTableName(Index index, |
| std::string_view name) { |
| objdump_state_->table_names.Set(index, name); |
| } |
| |
| void BinaryReaderObjdumpPrepass::SetSegmentName(Index index, |
| std::string_view name) { |
| objdump_state_->segment_names.Set(index, name); |
| } |
| |
| Result BinaryReaderObjdumpPrepass::OnReloc(RelocType type, |
| Offset offset, |
| Index index, |
| uint32_t addend) { |
| BinaryReaderObjdumpBase::OnReloc(type, offset, index, addend); |
| if (reloc_section_ == BinarySection::Code) { |
| objdump_state_->code_relocations.emplace_back(type, offset, index, addend); |
| } else if (reloc_section_ == BinarySection::Data) { |
| objdump_state_->data_relocations.emplace_back(type, offset, index, addend); |
| } |
| return Result::Ok; |
| } |
| |
| class BinaryReaderObjdumpDisassemble : public BinaryReaderObjdumpBase { |
| public: |
| using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; |
| |
| std::string BlockSigToString(Type type) const; |
| |
| Result OnFunction(Index index, Index sig_index) override; |
| |
| Result BeginFunctionBody(Index index, Offset size) override; |
| Result EndFunctionBody(Index index) override; |
| |
| Result OnLocalDeclCount(Index count) override; |
| Result OnLocalDecl(Index decl_index, Index count, Type type) override; |
| |
| Result OnOpcode(Opcode Opcode) override; |
| Result OnOpcodeBare() override; |
| Result OnOpcodeIndex(Index value) override; |
| Result OnOpcodeIndexIndex(Index value, Index value2) override; |
| Result OnOpcodeUint32(uint32_t value) override; |
| Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override; |
| Result OnCallIndirectExpr(uint32_t sig_indix, uint32_t table_index) override; |
| Result OnOpcodeUint32Uint32Uint32(uint32_t value, |
| uint32_t value2, |
| uint32_t value3) override; |
| Result OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, |
| uint32_t value2, |
| uint32_t value3, |
| uint32_t value4) override; |
| Result OnOpcodeUint64(uint64_t value) override; |
| Result OnOpcodeF32(uint32_t value) override; |
| Result OnOpcodeF64(uint64_t value) override; |
| Result OnOpcodeV128(v128 value) override; |
| Result OnOpcodeBlockSig(Type sig_type) override; |
| Result OnOpcodeType(Type type) override; |
| |
| Result OnBrTableExpr(Index num_targets, |
| Index* target_depths, |
| Index default_target_depth) override; |
| Result OnDelegateExpr(Index) override; |
| Result OnEndExpr() override; |
| |
| private: |
| void LogOpcode(const char* fmt, ...); |
| |
| Offset current_opcode_offset = 0; |
| Offset last_opcode_end = 0; |
| int indent_level = 0; |
| Index next_reloc = 0; |
| Index current_function_index = 0; |
| Index local_index_ = 0; |
| bool in_function_body = false; |
| bool skip_next_opcode_ = false; |
| }; |
| |
| std::string BinaryReaderObjdumpDisassemble::BlockSigToString(Type type) const { |
| if (type.IsIndex()) { |
| return StringPrintf("type[%d]", type.GetIndex()); |
| } else if (type == Type::Void) { |
| return ""; |
| } else { |
| return type.GetName(); |
| } |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcode(Opcode opcode) { |
| BinaryReaderObjdumpBase::OnOpcode(opcode); |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| if (options_->debug) { |
| const char* opcode_name = opcode.GetName(); |
| err_stream_->Writef("on_opcode: %#" PRIzx ": %s\n", state->offset, |
| opcode_name); |
| } |
| |
| if (last_opcode_end) { |
| // Takes care of cases where opcode's bytes was a non-canonical leb128 |
| // encoding. In this case, opcode.GetLength() under-reports the length, |
| // since it canonicalizes the opcode. |
| if (state->offset < last_opcode_end + opcode.GetLength()) { |
| Opcode missing_opcode = Opcode::FromCode(data_[last_opcode_end]); |
| const char* opcode_name = missing_opcode.GetName(); |
| fprintf(stderr, |
| "error: %#" PRIzx " missing opcode callback at %#" PRIzx |
| " (%#02x=%s)\n", |
| state->offset, last_opcode_end + 1, data_[last_opcode_end], |
| opcode_name); |
| return Result::Error; |
| } |
| } |
| |
| current_opcode_offset = state->offset; |
| return Result::Ok; |
| } |
| |
| #define IMMEDIATE_OCTET_COUNT 9 |
| |
| Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| current_opcode_offset = state->offset; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index, |
| Index count, |
| Type type) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| Offset offset = current_opcode_offset; |
| size_t data_size = state->offset - offset; |
| |
| printf(" %06" PRIzx ":", GetPrintOffset(offset)); |
| for (size_t i = 0; i < data_size && i < IMMEDIATE_OCTET_COUNT; |
| i++, offset++) { |
| printf(" %02x", data_[offset]); |
| } |
| for (size_t i = data_size; i < IMMEDIATE_OCTET_COUNT; i++) { |
| printf(" "); |
| } |
| printf(" | local["); |
| if (count > 0) { |
| printf("%" PRIindex, local_index_); |
| |
| if (count != 1) { |
| printf("..%" PRIindex "", local_index_ + count - 1); |
| } |
| local_index_ += count; |
| } |
| printf("] type=%s\n", type.GetName().c_str()); |
| |
| last_opcode_end = current_opcode_offset + data_size; |
| current_opcode_offset = last_opcode_end; |
| |
| return Result::Ok; |
| } |
| |
| void BinaryReaderObjdumpDisassemble::LogOpcode(const char* fmt, ...) { |
| // BinaryReaderObjdumpDisassemble is only used to disassembly function bodies |
| // so this should never be called for instructions outside of function bodies |
| // (i.e. init expresions). |
| assert(in_function_body); |
| if (skip_next_opcode_) { |
| skip_next_opcode_ = false; |
| return; |
| } |
| const Offset immediate_len = state->offset - current_opcode_offset; |
| const Offset opcode_size = current_opcode.GetLength(); |
| const Offset total_size = opcode_size + immediate_len; |
| // current_opcode_offset has already read past this opcode; rewind it by the |
| // size of this opcode, which may be more than one byte. |
| Offset offset = current_opcode_offset - opcode_size; |
| const Offset offset_end = offset + total_size; |
| |
| bool first_line = true; |
| while (offset < offset_end) { |
| // Print bytes, but only display a maximum of IMMEDIATE_OCTET_COUNT on each |
| // line. |
| printf(" %06" PRIzx ":", GetPrintOffset(offset)); |
| size_t i; |
| for (i = 0; offset < offset_end && i < IMMEDIATE_OCTET_COUNT; |
| ++i, ++offset) { |
| printf(" %02x", data_[offset]); |
| } |
| // Fill the rest of the remaining space with spaces. |
| for (; i < IMMEDIATE_OCTET_COUNT; ++i) { |
| printf(" "); |
| } |
| printf(" | "); |
| |
| if (first_line) { |
| first_line = false; |
| |
| // Print disassembly. |
| int indent_level = this->indent_level; |
| switch (current_opcode) { |
| case Opcode::Else: |
| case Opcode::Catch: |
| case Opcode::CatchAll: |
| indent_level--; |
| break; |
| default: |
| break; |
| } |
| for (int j = 0; j < indent_level; j++) { |
| printf(" "); |
| } |
| |
| const char* opcode_name = current_opcode.GetName(); |
| printf("%s", opcode_name); |
| if (fmt) { |
| printf(" "); |
| va_list args; |
| va_start(args, fmt); |
| vprintf(fmt, args); |
| va_end(args); |
| } |
| } |
| |
| printf("\n"); |
| } |
| |
| last_opcode_end = state->offset; |
| |
| // Print relocation after then full (potentially multi-line) instruction. |
| if (options_->relocs && |
| next_reloc < objdump_state_->code_relocations.size()) { |
| const Reloc& reloc = objdump_state_->code_relocations[next_reloc]; |
| Offset code_start = GetSectionStart(BinarySection::Code); |
| Offset abs_offset = code_start + reloc.offset; |
| if (last_opcode_end > abs_offset) { |
| PrintRelocation(reloc, abs_offset); |
| next_reloc++; |
| } |
| } |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeBare() { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| LogOpcode(0, nullptr); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| std::string_view name; |
| if (current_opcode == Opcode::Call && |
| !(name = GetFunctionName(value)).empty()) { |
| LogOpcode("%d <" PRIstringview ">", value, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else if (current_opcode == Opcode::Throw && |
| !(name = GetTagName(value)).empty()) { |
| LogOpcode("%d <" PRIstringview ">", value, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else if ((current_opcode == Opcode::GlobalGet || |
| current_opcode == Opcode::GlobalSet) && |
| !(name = GetGlobalName(value)).empty()) { |
| LogOpcode("%d <" PRIstringview ">", value, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else if ((current_opcode == Opcode::LocalGet || |
| current_opcode == Opcode::LocalSet) && |
| !(name = GetLocalName(current_function_index, value)).empty()) { |
| LogOpcode("%d <" PRIstringview ">", value, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else { |
| LogOpcode("%d", value); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value, |
| Index value2) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| LogOpcode("%" PRIindex " %" PRIindex, value, value2); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| std::string_view name; |
| if (current_opcode == Opcode::DataDrop && |
| !(name = GetSegmentName(value)).empty()) { |
| LogOpcode("%d <" PRIstringview ">", value, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else { |
| LogOpcode("%u", value); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value, |
| uint32_t value2) { |
| if (!in_function_body) |
| return Result::Ok; |
| std::string_view name; |
| if (current_opcode == Opcode::MemoryInit && |
| !(name = GetSegmentName(value)).empty()) { |
| LogOpcode("%u %u <" PRIstringview ">", value, value2, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } else { |
| LogOpcode("%u %u", value, value2); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnCallIndirectExpr( |
| uint32_t sig_index, |
| uint32_t table_index) { |
| std::string_view table_name = GetTableName(table_index); |
| std::string_view type_name = GetTypeName(sig_index); |
| if (!type_name.empty() && !table_name.empty()) { |
| LogOpcode("%u <" PRIstringview "> (type %u <" PRIstringview ">)", |
| table_index, WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index, |
| WABT_PRINTF_STRING_VIEW_ARG(type_name)); |
| } else if (!table_name.empty()) { |
| LogOpcode("%u <" PRIstringview "> (type %u)", table_index, |
| WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index); |
| } else if (!type_name.empty()) { |
| LogOpcode("%u (type %u <" PRIstringview ">)", table_index, sig_index, |
| WABT_PRINTF_STRING_VIEW_ARG(type_name)); |
| } else { |
| LogOpcode("%u (type %u)", table_index, sig_index); |
| } |
| skip_next_opcode_ = true; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32( |
| uint32_t value, |
| uint32_t value2, |
| uint32_t value3) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| LogOpcode("%u %u %u", value, value2, value3); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32Uint32( |
| uint32_t value, |
| uint32_t value2, |
| uint32_t value3, |
| uint32_t value4) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| LogOpcode("%u %u %u %u", value, value2, value3, value4); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| LogOpcode("%" PRIu64, value); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| char buffer[WABT_MAX_FLOAT_HEX]; |
| WriteFloatHex(buffer, sizeof(buffer), value); |
| LogOpcode(buffer); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| char buffer[WABT_MAX_DOUBLE_HEX]; |
| WriteDoubleHex(buffer, sizeof(buffer), value); |
| LogOpcode(buffer); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| // v128 is always dumped as i32x4: |
| LogOpcode("0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0), value.u32(1), |
| value.u32(2), value.u32(3)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeType(Type type) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| if (current_opcode == Opcode::SelectT) { |
| LogOpcode(type.GetName().c_str()); |
| } else { |
| LogOpcode(type.GetRefKindName()); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnBrTableExpr( |
| Index num_targets, |
| Index* target_depths, |
| Index default_target_depth) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| |
| std::string buffer = std::string(); |
| for (Index i = 0; i < num_targets; i++) { |
| buffer.append(std::to_string(target_depths[i])).append(" "); |
| } |
| buffer.append(std::to_string(default_target_depth)); |
| |
| LogOpcode("%s", buffer.c_str()); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| // Because `delegate` ends the block we need to dedent here, and |
| // we don't need to dedent it in LogOpcode. |
| if (indent_level > 0) { |
| indent_level--; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnEndExpr() { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| if (indent_level > 0) { |
| indent_level--; |
| } |
| LogOpcode(0, nullptr); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnFunction(Index index, |
| Index sig_index) { |
| objdump_state_->function_types[index] = sig_index; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::BeginFunctionBody(Index index, |
| Offset size) { |
| printf("%06" PRIzx " func[%" PRIindex "]", GetPrintOffset(state->offset), |
| index); |
| auto name = GetFunctionName(index); |
| if (!name.empty()) { |
| printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| printf(":\n"); |
| |
| last_opcode_end = 0; |
| in_function_body = true; |
| current_function_index = index; |
| auto type_index = objdump_state_->function_types[index]; |
| local_index_ = objdump_state_->function_param_counts[type_index]; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::EndFunctionBody(Index index) { |
| assert(in_function_body); |
| in_function_body = false; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) { |
| if (!in_function_body) { |
| return Result::Ok; |
| } |
| if (sig_type != Type::Void) { |
| LogOpcode("%s", BlockSigToString(sig_type).c_str()); |
| } else { |
| LogOpcode(nullptr); |
| } |
| indent_level++; |
| return Result::Ok; |
| } |
| |
| enum class InitExprType { |
| Invalid, |
| I32, |
| F32, |
| I64, |
| F64, |
| V128, |
| Global, |
| FuncRef, |
| // TODO: There isn't a nullref anymore, this just represents ref.null of some |
| // type T. |
| NullRef, |
| }; |
| |
| struct InitInst { |
| Opcode opcode; |
| union { |
| Index index; |
| uint32_t i32; |
| uint32_t f32; |
| uint64_t i64; |
| uint64_t f64; |
| v128 v128_v; |
| Type type; |
| } imm; |
| }; |
| |
| struct InitExpr { |
| InitExprType type; |
| std::vector<InitInst> insts; |
| }; |
| |
| class BinaryReaderObjdump : public BinaryReaderObjdumpBase { |
| public: |
| BinaryReaderObjdump(const uint8_t* data, |
| size_t size, |
| ObjdumpOptions* options, |
| ObjdumpState* state); |
| |
| Result EndModule() override; |
| Result BeginSection(Index section_index, |
| BinarySection section_type, |
| Offset size) override; |
| Result BeginCustomSection(Index section_index, |
| Offset size, |
| std::string_view section_name) override; |
| |
| Result OnTypeCount(Index count) override; |
| Result OnFuncType(Index index, |
| Index param_count, |
| Type* param_types, |
| Index result_count, |
| Type* result_types) override; |
| Result OnStructType(Index index, Index field_count, TypeMut* fields) override; |
| Result OnArrayType(Index index, TypeMut field) override; |
| |
| Result OnImportCount(Index count) override; |
| Result OnImportFunc(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index func_index, |
| Index sig_index) override; |
| Result OnImportTable(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index table_index, |
| Type elem_type, |
| const Limits* elem_limits) override; |
| Result OnImportMemory(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index memory_index, |
| const Limits* page_limits) override; |
| Result OnImportGlobal(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index global_index, |
| Type type, |
| bool mutable_) override; |
| Result OnImportTag(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index tag_index, |
| Index sig_index) override; |
| |
| Result OnFunctionCount(Index count) override; |
| Result OnFunction(Index index, Index sig_index) override; |
| |
| Result OnTableCount(Index count) override; |
| Result OnTable(Index index, |
| Type elem_type, |
| const Limits* elem_limits) override; |
| |
| Result OnMemoryCount(Index count) override; |
| Result OnMemory(Index index, const Limits* limits) override; |
| |
| Result OnGlobalCount(Index count) override; |
| Result BeginGlobal(Index index, Type type, bool mutable_) override; |
| |
| Result OnExportCount(Index count) override; |
| Result OnExport(Index index, |
| ExternalKind kind, |
| Index item_index, |
| std::string_view name) override; |
| |
| Result OnStartFunction(Index func_index) override; |
| Result OnDataCount(Index count) override; |
| |
| Result OnFunctionBodyCount(Index count) override; |
| Result BeginFunctionBody(Index index, Offset size) override; |
| |
| Result OnElemSegmentCount(Index count) override; |
| Result BeginElemSegment(Index index, |
| Index table_index, |
| uint8_t flags) override; |
| Result OnElemSegmentElemType(Index index, Type elem_type) override; |
| Result OnElemSegmentElemExprCount(Index index, Index count) override; |
| |
| void BeginInitExpr() { current_init_expr_.insts.clear(); } |
| |
| Result BeginElemSegmentInitExpr(Index index) override { |
| reading_elem_init_expr_ = true; |
| BeginInitExpr(); |
| return Result::Ok; |
| } |
| |
| Result EndElemSegmentInitExpr(Index index) override { return EndInitExpr(); } |
| |
| Result BeginDataSegmentInitExpr(Index index) override { |
| reading_data_init_expr_ = true; |
| BeginInitExpr(); |
| return Result::Ok; |
| } |
| |
| Result EndDataSegmentInitExpr(Index index) override { return EndInitExpr(); } |
| |
| Result BeginGlobalInitExpr(Index index) override { |
| reading_global_init_expr_ = true; |
| BeginInitExpr(); |
| return Result::Ok; |
| } |
| |
| Result EndGlobalInitExpr(Index index) override { return EndInitExpr(); } |
| |
| Result BeginElemExpr(Index elem_index, Index expr_index) override { |
| reading_elem_expr_ = true; |
| elem_index_ = expr_index; |
| BeginInitExpr(); |
| return Result::Ok; |
| } |
| |
| Result EndElemExpr(Index elem_index, Index expr_index) override { |
| assert(expr_index == elem_index_); |
| return EndInitExpr(); |
| } |
| |
| Result OnDataSegmentCount(Index count) override; |
| Result BeginDataSegment(Index index, |
| Index memory_index, |
| uint8_t flags) override; |
| Result OnDataSegmentData(Index index, |
| const void* data, |
| Address size) override; |
| |
| Result OnModuleName(std::string_view name) override; |
| Result OnFunctionName(Index function_index, |
| std::string_view function_name) override; |
| Result OnLocalName(Index function_index, |
| Index local_index, |
| std::string_view local_name) override; |
| Result OnNameEntry(NameSectionSubsection type, |
| Index index, |
| std::string_view name) override; |
| |
| Result OnDylinkInfo(uint32_t mem_size, |
| uint32_t mem_align_log2, |
| uint32_t table_size, |
| uint32_t table_align_log2) override; |
| Result OnDylinkNeededCount(Index count) override; |
| Result OnDylinkNeeded(std::string_view so_name) override; |
| Result OnDylinkImportCount(Index count) override; |
| Result OnDylinkExportCount(Index count) override; |
| Result OnDylinkImport(std::string_view module, |
| std::string_view name, |
| uint32_t flags) override; |
| Result OnDylinkExport(std::string_view name, uint32_t flags) override; |
| |
| Result OnRelocCount(Index count, Index section_index) override; |
| Result OnReloc(RelocType type, |
| Offset offset, |
| Index index, |
| uint32_t addend) override; |
| |
| Result OnFeature(uint8_t prefix, std::string_view name) override; |
| |
| Result OnSymbolCount(Index count) override; |
| Result OnDataSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index segment, |
| uint32_t offset, |
| uint32_t size) override; |
| Result OnFunctionSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index func_index) override; |
| Result OnGlobalSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index global_index) override; |
| Result OnSectionSymbol(Index index, |
| uint32_t flags, |
| Index section_index) override; |
| Result OnTagSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index tag_index) override; |
| Result OnTableSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index table_index) override; |
| Result OnSegmentInfoCount(Index count) override; |
| Result OnSegmentInfo(Index index, |
| std::string_view name, |
| Address alignment_log2, |
| uint32_t flags) override; |
| Result OnInitFunctionCount(Index count) override; |
| Result OnInitFunction(uint32_t priority, Index symbol_index) override; |
| Result OnComdatCount(Index count) override; |
| Result OnComdatBegin(std::string_view name, |
| uint32_t flags, |
| Index count) override; |
| Result OnComdatEntry(ComdatType kind, Index index) override; |
| |
| Result OnTagCount(Index count) override; |
| Result OnTagType(Index index, Index sig_index) override; |
| |
| Result OnOpcode(Opcode Opcode) override; |
| Result OnI32ConstExpr(uint32_t value) override; |
| Result OnI64ConstExpr(uint64_t value) override; |
| Result OnF32ConstExpr(uint32_t value) override; |
| Result OnF64ConstExpr(uint64_t value) override; |
| Result OnRefFuncExpr(Index func_index) override; |
| Result OnRefNullExpr(Type type) override; |
| Result OnGlobalGetExpr(Index global_index) override; |
| Result OnCodeMetadataCount(Index function_index, Index count) override; |
| Result OnCodeMetadata(Offset code_offset, |
| const void* data, |
| Address size) override; |
| |
| private: |
| Result EndInitExpr(); |
| bool ShouldPrintDetails(); |
| void PrintDetails(const char* fmt, ...); |
| Result PrintSymbolFlags(uint32_t flags); |
| Result PrintSegmentFlags(uint32_t flags); |
| void PrintInitExpr(const InitExpr& expr, |
| bool as_unsigned = false, |
| bool with_prefix = true); |
| Result OnCount(Index count); |
| |
| std::unique_ptr<FileStream> out_stream_; |
| Index elem_index_ = 0; |
| Index table_index_ = 0; |
| Index next_data_reloc_ = 0; |
| bool reading_elem_init_expr_ = false; |
| bool reading_data_init_expr_ = false; |
| bool reading_global_init_expr_ = false; |
| bool reading_elem_expr_ = false; |
| InitExpr current_init_expr_{}; |
| uint8_t data_flags_ = 0; |
| uint8_t elem_flags_ = 0; |
| Index data_mem_index_ = 0; |
| uint64_t data_offset_ = 0; |
| uint64_t elem_offset_ = 0; |
| |
| bool ReadingInitExpr() { |
| return reading_elem_init_expr_ || reading_data_init_expr_ || |
| reading_global_init_expr_ || reading_elem_expr_; |
| } |
| }; |
| |
| BinaryReaderObjdump::BinaryReaderObjdump(const uint8_t* data, |
| size_t size, |
| ObjdumpOptions* options, |
| ObjdumpState* objdump_state) |
| : BinaryReaderObjdumpBase(data, size, options, objdump_state), |
| out_stream_(FileStream::CreateStdout()) {} |
| |
| Result BinaryReaderObjdump::BeginCustomSection(Index section_index, |
| Offset size, |
| std::string_view section_name) { |
| PrintDetails(" - name: \"" PRIstringview "\"\n", |
| WABT_PRINTF_STRING_VIEW_ARG(section_name)); |
| if (options_->mode == ObjdumpMode::Headers) { |
| printf("\"" PRIstringview "\"\n", |
| WABT_PRINTF_STRING_VIEW_ARG(section_name)); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::BeginSection(Index section_index, |
| BinarySection section_code, |
| Offset size) { |
| BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); |
| |
| // |section_name| and |match_name| are identical for known sections. For |
| // custom sections, |section_name| is "Custom", but |match_name| is the name |
| // of the custom section. |
| const char* section_name = wabt::GetSectionName(section_code); |
| std::string match_name(GetSectionName(section_index)); |
| |
| bool section_match = !options_->section_name || |
| !strcasecmp(options_->section_name, match_name.c_str()); |
| if (section_match) { |
| section_found_ = true; |
| } |
| |
| switch (options_->mode) { |
| case ObjdumpMode::Headers: |
| printf("%9s start=%#010" PRIzx " end=%#010" PRIzx " (size=%#010" PRIoffset |
| ") ", |
| section_name, state->offset, state->offset + size, size); |
| break; |
| case ObjdumpMode::Details: |
| if (section_match) { |
| printf("%s", section_name); |
| // All known section types except the Start and DataCount sections have |
| // a count in which case this line gets completed in OnCount(). |
| if (section_code == BinarySection::Start || |
| section_code == BinarySection::DataCount || |
| section_code == BinarySection::Custom) { |
| printf(":\n"); |
| } |
| print_details_ = true; |
| } else { |
| print_details_ = false; |
| } |
| break; |
| case ObjdumpMode::RawData: |
| if (section_match) { |
| printf("\nContents of section %s:\n", section_name); |
| out_stream_->WriteMemoryDump(data_ + state->offset, size, state->offset, |
| PrintChars::Yes); |
| } |
| break; |
| case ObjdumpMode::Prepass: |
| case ObjdumpMode::Disassemble: |
| break; |
| } |
| return Result::Ok; |
| } |
| |
| bool BinaryReaderObjdump::ShouldPrintDetails() { |
| if (options_->mode != ObjdumpMode::Details) { |
| return false; |
| } |
| return print_details_; |
| } |
| |
| void WABT_PRINTF_FORMAT(2, 3) BinaryReaderObjdump::PrintDetails(const char* fmt, |
| ...) { |
| if (!ShouldPrintDetails()) { |
| return; |
| } |
| va_list args; |
| va_start(args, fmt); |
| vprintf(fmt, args); |
| va_end(args); |
| } |
| |
| Result BinaryReaderObjdump::OnCount(Index count) { |
| if (options_->mode == ObjdumpMode::Headers) { |
| printf("count: %" PRIindex "\n", count); |
| } else if (options_->mode == ObjdumpMode::Details && print_details_) { |
| printf("[%" PRIindex "]:\n", count); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::EndModule() { |
| if (options_->section_name && !section_found_) { |
| err_stream_->Writef("Section not found: %s\n", options_->section_name); |
| return Result::Error; |
| } |
| |
| if (options_->relocs && ShouldPrintDetails()) { |
| if (next_data_reloc_ != objdump_state_->data_relocations.size()) { |
| err_stream_->Writef("Data reloctions outside of segments!:\n"); |
| for (size_t i = next_data_reloc_; |
| i < objdump_state_->data_relocations.size(); i++) { |
| const Reloc& reloc = objdump_state_->data_relocations[i]; |
| PrintRelocation(reloc, reloc.offset); |
| } |
| |
| return Result::Error; |
| } |
| } |
| |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnTypeCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnFuncType(Index index, |
| Index param_count, |
| Type* param_types, |
| Index result_count, |
| Type* result_types) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - type[%" PRIindex "] (", index); |
| for (Index i = 0; i < param_count; i++) { |
| if (i != 0) { |
| printf(", "); |
| } |
| printf("%s", param_types[i].GetName().c_str()); |
| } |
| printf(") -> "); |
| switch (result_count) { |
| case 0: |
| printf("nil"); |
| break; |
| case 1: |
| printf("%s", result_types[0].GetName().c_str()); |
| break; |
| default: |
| printf("("); |
| for (Index i = 0; i < result_count; i++) { |
| if (i != 0) { |
| printf(", "); |
| } |
| printf("%s", result_types[i].GetName().c_str()); |
| } |
| printf(")"); |
| break; |
| } |
| printf("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnStructType(Index index, |
| Index field_count, |
| TypeMut* fields) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - type[%" PRIindex "] (struct", index); |
| for (Index i = 0; i < field_count; i++) { |
| if (fields[i].mutable_) { |
| printf(" (mut"); |
| } |
| printf(" %s", fields[i].type.GetName().c_str()); |
| if (fields[i].mutable_) { |
| printf(")"); |
| } |
| } |
| printf(")\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnArrayType(Index index, TypeMut field) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - type[%" PRIindex "] (array", index); |
| if (field.mutable_) { |
| printf(" (mut"); |
| } |
| printf(" %s", field.type.GetName().c_str()); |
| if (field.mutable_) { |
| printf(")"); |
| } |
| printf(")\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnFunctionCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnFunction(Index index, Index sig_index) { |
| PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, index, sig_index); |
| auto name = GetFunctionName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| objdump_state_->function_types[index] = sig_index; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnFunctionBodyCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::BeginFunctionBody(Index index, Offset size) { |
| PrintDetails(" - func[%" PRIindex "] size=%" PRIzd, index, size); |
| auto name = GetFunctionName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnStartFunction(Index func_index) { |
| if (options_->mode == ObjdumpMode::Headers) { |
| printf("start: %" PRIindex "\n", func_index); |
| } else { |
| PrintDetails(" - start function: %" PRIindex, func_index); |
| auto name = GetFunctionName(func_index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDataCount(Index count) { |
| if (options_->mode == ObjdumpMode::Headers) { |
| printf("count: %" PRIindex "\n", count); |
| } else { |
| PrintDetails(" - data count: %" PRIindex "\n", count); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnImportCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnImportFunc(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index func_index, |
| Index sig_index) { |
| PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, func_index, sig_index); |
| auto name = GetFunctionName(func_index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails(" <- " PRIstringview "." PRIstringview "\n", |
| WABT_PRINTF_STRING_VIEW_ARG(module_name), |
| WABT_PRINTF_STRING_VIEW_ARG(field_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnImportTable(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index table_index, |
| Type elem_type, |
| const Limits* elem_limits) { |
| PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, table_index, |
| elem_type.GetName().c_str(), elem_limits->initial); |
| if (elem_limits->has_max) { |
| PrintDetails(" max=%" PRId64, elem_limits->max); |
| } |
| PrintDetails(" <- " PRIstringview "." PRIstringview "\n", |
| WABT_PRINTF_STRING_VIEW_ARG(module_name), |
| WABT_PRINTF_STRING_VIEW_ARG(field_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnImportMemory(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index memory_index, |
| const Limits* page_limits) { |
| PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, memory_index, |
| page_limits->initial); |
| if (page_limits->has_max) { |
| PrintDetails(" max=%" PRId64, page_limits->max); |
| } |
| if (page_limits->is_shared) { |
| PrintDetails(" shared"); |
| } |
| if (page_limits->is_64) { |
| PrintDetails(" i64"); |
| } |
| PrintDetails(" <- " PRIstringview "." PRIstringview "\n", |
| WABT_PRINTF_STRING_VIEW_ARG(module_name), |
| WABT_PRINTF_STRING_VIEW_ARG(field_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnImportGlobal(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index global_index, |
| Type type, |
| bool mutable_) { |
| PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index, |
| type.GetName().c_str(), mutable_); |
| PrintDetails(" <- " PRIstringview "." PRIstringview "\n", |
| WABT_PRINTF_STRING_VIEW_ARG(module_name), |
| WABT_PRINTF_STRING_VIEW_ARG(field_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnImportTag(Index import_index, |
| std::string_view module_name, |
| std::string_view field_name, |
| Index tag_index, |
| Index sig_index) { |
| PrintDetails(" - tag[%" PRIindex "] sig=%" PRIindex, tag_index, sig_index); |
| auto name = GetTagName(tag_index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails(" <- " PRIstringview "." PRIstringview "\n", |
| WABT_PRINTF_STRING_VIEW_ARG(module_name), |
| WABT_PRINTF_STRING_VIEW_ARG(field_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnMemoryCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnMemory(Index index, const Limits* page_limits) { |
| PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, index, |
| page_limits->initial); |
| if (page_limits->has_max) { |
| PrintDetails(" max=%" PRId64, page_limits->max); |
| } |
| if (page_limits->is_shared) { |
| PrintDetails(" shared"); |
| } |
| if (page_limits->is_64) { |
| PrintDetails(" i64"); |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnTableCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnTable(Index index, |
| Type elem_type, |
| const Limits* elem_limits) { |
| PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index, |
| elem_type.GetName().c_str(), elem_limits->initial); |
| if (elem_limits->has_max) { |
| PrintDetails(" max=%" PRId64, elem_limits->max); |
| } |
| auto name = GetTableName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnExportCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnExport(Index index, |
| ExternalKind kind, |
| Index item_index, |
| std::string_view name) { |
| PrintDetails(" - %s[%" PRIindex "]", GetKindName(kind), item_index); |
| if (kind == ExternalKind::Func) { |
| auto name = GetFunctionName(item_index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| } |
| |
| PrintDetails(" -> \"" PRIstringview "\"\n", |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnElemSegmentCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::BeginElemSegment(Index index, |
| Index table_index, |
| uint8_t flags) { |
| table_index_ = table_index; |
| elem_index_ = 0; |
| elem_flags_ = flags; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnElemSegmentElemType(Index index, Type elem_type) { |
| // TODO: Add support for this. |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnElemSegmentElemExprCount(Index index, |
| Index count) { |
| PrintDetails(" - segment[%" PRIindex "] flags=%d table=%" PRIindex |
| " count=%" PRIindex, |
| index, elem_flags_, table_index_, count); |
| if (elem_flags_ & SegPassive) { |
| PrintDetails("\n"); |
| } else { |
| PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnGlobalCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::BeginGlobal(Index index, Type type, bool mutable_) { |
| PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index, |
| type.GetName().c_str(), mutable_); |
| std::string_view name = GetGlobalName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| return Result::Ok; |
| } |
| |
| void BinaryReaderObjdump::PrintInitExpr(const InitExpr& expr, |
| bool as_unsigned, |
| bool with_prefix) { |
| if (with_prefix) { |
| PrintDetails(" - init "); |
| } |
| |
| if (expr.insts.empty()) { |
| PrintDetails("<EMPTY>\n"); |
| return; |
| } |
| |
| // We have two different way to print init expressions. One for |
| // extended expressions involving more than one instruction, and |
| // a short form for the more traditional single instruction form. |
| if (expr.insts.size() > 1) { |
| PrintDetails("("); |
| bool first = true; |
| for (auto& inst : expr.insts) { |
| if (!first) { |
| PrintDetails(", "); |
| } |
| first = false; |
| PrintDetails("%s", inst.opcode.GetName()); |
| switch (inst.opcode) { |
| case Opcode::I32Const: |
| PrintDetails(" %d", inst.imm.i32); |
| break; |
| case Opcode::I64Const: |
| PrintDetails(" %" PRId64, inst.imm.i64); |
| break; |
| case Opcode::F32Const: { |
| char buffer[WABT_MAX_FLOAT_HEX]; |
| WriteFloatHex(buffer, sizeof(buffer), inst.imm.f32); |
| PrintDetails(" %s\n", buffer); |
| break; |
| } |
| case Opcode::F64Const: { |
| char buffer[WABT_MAX_DOUBLE_HEX]; |
| WriteDoubleHex(buffer, sizeof(buffer), inst.imm.f64); |
| PrintDetails(" %s\n", buffer); |
| break; |
| } |
| case Opcode::GlobalGet: { |
| PrintDetails(" %" PRIindex, inst.imm.index); |
| std::string_view name = GetGlobalName(inst.imm.index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| PrintDetails(")\n"); |
| return; |
| } |
| |
| switch (expr.type) { |
| case InitExprType::I32: |
| if (as_unsigned) { |
| PrintDetails("i32=%u\n", expr.insts[0].imm.i32); |
| } else { |
| PrintDetails("i32=%d\n", expr.insts[0].imm.i32); |
| } |
| break; |
| case InitExprType::I64: |
| if (as_unsigned) { |
| PrintDetails("i64=%" PRIu64 "\n", expr.insts[0].imm.i64); |
| } else { |
| PrintDetails("i64=%" PRId64 "\n", expr.insts[0].imm.i64); |
| } |
| break; |
| case InitExprType::F64: { |
| char buffer[WABT_MAX_DOUBLE_HEX]; |
| WriteDoubleHex(buffer, sizeof(buffer), expr.insts[0].imm.f64); |
| PrintDetails("f64=%s\n", buffer); |
| break; |
| } |
| case InitExprType::F32: { |
| char buffer[WABT_MAX_FLOAT_HEX]; |
| WriteFloatHex(buffer, sizeof(buffer), expr.insts[0].imm.f32); |
| PrintDetails("f32=%s\n", buffer); |
| break; |
| } |
| case InitExprType::V128: { |
| PrintDetails( |
| "v128=0x%08x 0x%08x 0x%08x 0x%08x \n", |
| expr.insts[0].imm.v128_v.u32(0), expr.insts[0].imm.v128_v.u32(1), |
| expr.insts[0].imm.v128_v.u32(2), expr.insts[0].imm.v128_v.u32(3)); |
| break; |
| } |
| case InitExprType::Global: { |
| PrintDetails("global=%" PRIindex, expr.insts[0].imm.index); |
| std::string_view name = GetGlobalName(expr.insts[0].imm.index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| break; |
| } |
| case InitExprType::FuncRef: { |
| PrintDetails("ref.func:%" PRIindex, expr.insts[0].imm.index); |
| std::string_view name = GetFunctionName(expr.insts[0].imm.index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| break; |
| } |
| case InitExprType::NullRef: |
| PrintDetails("ref.null %s\n", expr.insts[0].imm.type.GetName().c_str()); |
| break; |
| case InitExprType::Invalid: |
| PrintDetails("<INVALID>\n"); |
| break; |
| } |
| } |
| |
| static void InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset) { |
| if (expr.insts.size() == 1) { |
| switch (expr.type) { |
| case InitExprType::I32: |
| *out_offset = expr.insts[0].imm.i32; |
| break; |
| case InitExprType::I64: |
| *out_offset = expr.insts[0].imm.i64; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| Result BinaryReaderObjdump::EndInitExpr() { |
| if (reading_data_init_expr_) { |
| reading_data_init_expr_ = false; |
| InitExprToConstOffset(current_init_expr_, &data_offset_); |
| } else if (reading_elem_init_expr_) { |
| reading_elem_init_expr_ = false; |
| InitExprToConstOffset(current_init_expr_, &elem_offset_); |
| } else if (reading_global_init_expr_) { |
| reading_global_init_expr_ = false; |
| PrintInitExpr(current_init_expr_); |
| } else if (reading_elem_expr_) { |
| reading_elem_expr_ = false; |
| PrintDetails(" - elem[%" PRIu64 "] = ", elem_offset_ + elem_index_); |
| PrintInitExpr(current_init_expr_, /*as_unsigned=*/false, |
| /*with_prefix=*/false); |
| } else { |
| WABT_UNREACHABLE; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnI32ConstExpr(uint32_t value) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::I32; |
| current_init_expr_.insts.back().imm.i32 = value; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnI64ConstExpr(uint64_t value) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::I64; |
| current_init_expr_.insts.back().imm.i64 = value; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnF32ConstExpr(uint32_t value) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::F32; |
| current_init_expr_.insts.back().imm.f32 = value; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnF64ConstExpr(uint64_t value) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::F64; |
| current_init_expr_.insts.back().imm.f64 = value; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnRefFuncExpr(Index func_index) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::FuncRef; |
| current_init_expr_.insts.back().imm.index = func_index; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnRefNullExpr(Type type) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::NullRef; |
| current_init_expr_.insts.back().imm.type = type; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnOpcode(Opcode opcode) { |
| BinaryReaderObjdumpBase::OnOpcode(opcode); |
| if (ReadingInitExpr() && opcode != Opcode::End) { |
| InitInst i; |
| i.opcode = current_opcode; |
| current_init_expr_.insts.push_back(i); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnGlobalGetExpr(Index global_index) { |
| if (ReadingInitExpr()) { |
| current_init_expr_.type = InitExprType::Global; |
| current_init_expr_.insts.back().imm.index = global_index; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnModuleName(std::string_view name) { |
| PrintDetails(" - module <" PRIstringview ">\n", |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnFunctionName(Index index, std::string_view name) { |
| PrintDetails(" - func[%" PRIindex "] <" PRIstringview ">\n", index, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnNameEntry(NameSectionSubsection type, |
| Index index, |
| std::string_view name) { |
| PrintDetails(" - %s[%" PRIindex "] <" PRIstringview ">\n", |
| GetNameSectionSubsectionName(type), index, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnLocalName(Index func_index, |
| Index local_index, |
| std::string_view name) { |
| if (!name.empty()) { |
| PrintDetails(" - func[%" PRIindex "] local[%" PRIindex "] <" PRIstringview |
| ">\n", |
| func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDataSegmentCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::BeginDataSegment(Index index, |
| Index memory_index, |
| uint8_t flags) { |
| data_mem_index_ = memory_index; |
| data_flags_ = flags; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDataSegmentData(Index index, |
| const void* src_data, |
| Address size) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| |
| PrintDetails(" - segment[%" PRIindex "]", index); |
| auto name = GetSegmentName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| if (data_flags_ & SegPassive) { |
| PrintDetails(" passive"); |
| } else { |
| PrintDetails(" memory=%" PRIindex, data_mem_index_); |
| } |
| PrintDetails(" size=%" PRIaddress, size); |
| if (data_flags_ & SegPassive) { |
| PrintDetails("\n"); |
| } else { |
| PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); |
| } |
| |
| out_stream_->WriteMemoryDump(src_data, size, data_offset_, PrintChars::Yes, |
| " - "); |
| |
| // Print relocations from this segment. |
| if (!options_->relocs) { |
| return Result::Ok; |
| } |
| |
| Offset data_start = GetSectionStart(BinarySection::Data); |
| Offset segment_start = state->offset - size; |
| Offset segment_offset = segment_start - data_start; |
| while (next_data_reloc_ < objdump_state_->data_relocations.size()) { |
| const Reloc& reloc = objdump_state_->data_relocations[next_data_reloc_]; |
| Offset abs_offset = data_start + reloc.offset; |
| if (abs_offset > state->offset) { |
| break; |
| } |
| PrintRelocation(reloc, reloc.offset - segment_offset + data_offset_); |
| next_data_reloc_++; |
| } |
| |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkInfo(uint32_t mem_size, |
| uint32_t mem_align_log2, |
| uint32_t table_size, |
| uint32_t table_align_log2) { |
| PrintDetails(" - mem_size : %u\n", mem_size); |
| PrintDetails(" - mem_p2align : %u\n", mem_align_log2); |
| PrintDetails(" - table_size : %u\n", table_size); |
| PrintDetails(" - table_p2align: %u\n", table_align_log2); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkNeededCount(Index count) { |
| if (count) { |
| PrintDetails(" - needed_dynlibs[%u]:\n", count); |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkImportCount(Index count) { |
| PrintDetails(" - imports[%u]:\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkExportCount(Index count) { |
| PrintDetails(" - exports[%u]:\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkExport(std::string_view name, |
| uint32_t flags) { |
| PrintDetails(" - " PRIstringview, WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkImport(std::string_view module, |
| std::string_view name, |
| uint32_t flags) { |
| PrintDetails(" - " PRIstringview "." PRIstringview, |
| WABT_PRINTF_STRING_VIEW_ARG(module), |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnDylinkNeeded(std::string_view so_name) { |
| PrintDetails(" - " PRIstringview "\n", WABT_PRINTF_STRING_VIEW_ARG(so_name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnRelocCount(Index count, Index section_index) { |
| BinaryReaderObjdumpBase::OnRelocCount(count, section_index); |
| PrintDetails(" - relocations for section: %d (" PRIstringview ") [%d]\n", |
| section_index, |
| WABT_PRINTF_STRING_VIEW_ARG(GetSectionName(section_index)), |
| count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnReloc(RelocType type, |
| Offset offset, |
| Index index, |
| uint32_t addend) { |
| Offset total_offset = GetSectionStart(reloc_section_) + offset; |
| PrintDetails(" - %-18s offset=%#08" PRIoffset "(file=%#08" PRIoffset ") ", |
| GetRelocTypeName(type), offset, total_offset); |
| if (type == RelocType::TypeIndexLEB) { |
| PrintDetails("type=%" PRIindex, index); |
| } else { |
| PrintDetails("symbol=%" PRIindex " <" PRIstringview ">", index, |
| WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(index))); |
| } |
| |
| if (addend) { |
| int32_t signed_addend = static_cast<int32_t>(addend); |
| if (signed_addend < 0) { |
| PrintDetails("-"); |
| signed_addend = -signed_addend; |
| } else { |
| PrintDetails("+"); |
| } |
| PrintDetails("%#x", signed_addend); |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnFeature(uint8_t prefix, std::string_view name) { |
| PrintDetails(" - [%c] " PRIstringview "\n", prefix, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnSymbolCount(Index count) { |
| PrintDetails(" - symbol table [count=%d]\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::PrintSymbolFlags(uint32_t flags) { |
| if (flags > WABT_SYMBOL_FLAG_MAX) { |
| err_stream_->Writef("Unknown symbols flags: %x\n", flags); |
| return Result::Error; |
| } |
| |
| const char* binding_name = nullptr; |
| SymbolBinding binding = |
| static_cast<SymbolBinding>(flags & WABT_SYMBOL_MASK_BINDING); |
| switch (binding) { |
| case SymbolBinding::Global: |
| binding_name = "global"; |
| break; |
| case SymbolBinding::Local: |
| binding_name = "local"; |
| break; |
| case SymbolBinding::Weak: |
| binding_name = "weak"; |
| break; |
| } |
| flags &= ~WABT_SYMBOL_MASK_BINDING; |
| |
| const char* vis_name = nullptr; |
| SymbolVisibility vis = |
| static_cast<SymbolVisibility>(flags & WABT_SYMBOL_MASK_VISIBILITY); |
| switch (vis) { |
| case SymbolVisibility::Hidden: |
| vis_name = "hidden"; |
| break; |
| case SymbolVisibility::Default: |
| vis_name = "default"; |
| break; |
| } |
| flags &= ~WABT_SYMBOL_MASK_VISIBILITY; |
| |
| PrintDetails(" ["); |
| if (flags & WABT_SYMBOL_FLAG_UNDEFINED) { |
| PrintDetails(" undefined"); |
| flags &= ~WABT_SYMBOL_FLAG_UNDEFINED; |
| } |
| if (flags & WABT_SYMBOL_FLAG_EXPORTED) { |
| PrintDetails(" exported"); |
| flags &= ~WABT_SYMBOL_FLAG_EXPORTED; |
| } |
| if (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) { |
| PrintDetails(" explicit_name"); |
| flags &= ~WABT_SYMBOL_FLAG_EXPLICIT_NAME; |
| } |
| if (flags & WABT_SYMBOL_FLAG_NO_STRIP) { |
| PrintDetails(" no_strip"); |
| flags &= ~WABT_SYMBOL_FLAG_NO_STRIP; |
| } |
| if (flags & WABT_SYMBOL_FLAG_TLS) { |
| PrintDetails(" tls"); |
| flags &= ~WABT_SYMBOL_FLAG_TLS; |
| } |
| if (flags & WABT_SYMBOL_FLAG_ABS) { |
| PrintDetails(" abs"); |
| flags &= ~WABT_SYMBOL_FLAG_ABS; |
| } |
| if (flags != 0) { |
| PrintDetails(" unknown_flags=%#x", flags); |
| } |
| PrintDetails(" binding=%s vis=%s ]\n", binding_name, vis_name); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::PrintSegmentFlags(uint32_t flags) { |
| if (flags > WABT_SYMBOL_FLAG_MAX) { |
| err_stream_->Writef("Unknown symbols flags: %x\n", flags); |
| return Result::Error; |
| } |
| PrintDetails(" ["); |
| if (flags & WABT_SEGMENT_FLAG_STRINGS) { |
| PrintDetails(" STRINGS"); |
| flags &= ~WABT_SEGMENT_FLAG_STRINGS; |
| } |
| if (flags & WABT_SEGMENT_FLAG_TLS) { |
| PrintDetails(" TLS"); |
| flags &= ~WABT_SEGMENT_FLAG_TLS; |
| } |
| if (flags & WASM_SEGMENT_FLAG_RETAIN) { |
| PrintDetails(" RETAIN"); |
| flags &= ~WASM_SEGMENT_FLAG_RETAIN; |
| } |
| if (flags != 0) { |
| PrintDetails(" unknown_flags=%#x", flags); |
| } |
| PrintDetails(" ]\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnDataSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index segment, |
| uint32_t offset, |
| uint32_t size) { |
| PrintDetails(" - %d: D <" PRIstringview ">", index, |
| WABT_PRINTF_STRING_VIEW_ARG(name)); |
| if (!(flags & WABT_SYMBOL_FLAG_UNDEFINED)) { |
| if (flags & WABT_SYMBOL_FLAG_ABS) { |
| PrintDetails(" address=%d size=%d", offset, size); |
| } else { |
| PrintDetails(" segment=%" PRIindex " offset=%d size=%d", segment, offset, |
| size); |
| } |
| } |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnFunctionSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index func_index) { |
| if (name.empty()) { |
| name = GetFunctionName(func_index); |
| } |
| PrintDetails(" - %d: F <" PRIstringview "> func=%" PRIindex, index, |
| WABT_PRINTF_STRING_VIEW_ARG(name), func_index); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnGlobalSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index global_index) { |
| if (name.empty()) { |
| name = GetGlobalName(global_index); |
| } |
| PrintDetails(" - %d: G <" PRIstringview "> global=%" PRIindex, index, |
| WABT_PRINTF_STRING_VIEW_ARG(name), global_index); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnSectionSymbol(Index index, |
| uint32_t flags, |
| Index section_index) { |
| auto sym_name = GetSectionName(section_index); |
| assert(!sym_name.empty()); |
| PrintDetails(" - %d: S <" PRIstringview "> section=%" PRIindex, index, |
| WABT_PRINTF_STRING_VIEW_ARG(sym_name), section_index); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnTagSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index tag_index) { |
| if (name.empty()) { |
| name = GetTagName(tag_index); |
| } |
| PrintDetails(" - %d: E <" PRIstringview "> tag=%" PRIindex, index, |
| WABT_PRINTF_STRING_VIEW_ARG(name), tag_index); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnTableSymbol(Index index, |
| uint32_t flags, |
| std::string_view name, |
| Index table_index) { |
| if (name.empty()) { |
| name = GetTableName(table_index); |
| } |
| PrintDetails(" - %d: T <" PRIstringview "> table=%" PRIindex, index, |
| WABT_PRINTF_STRING_VIEW_ARG(name), table_index); |
| return PrintSymbolFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnSegmentInfoCount(Index count) { |
| PrintDetails(" - segment info [count=%d]\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnSegmentInfo(Index index, |
| std::string_view name, |
| Address alignment_log2, |
| uint32_t flags) { |
| PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress, index, |
| WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2); |
| return PrintSegmentFlags(flags); |
| } |
| |
| Result BinaryReaderObjdump::OnInitFunctionCount(Index count) { |
| PrintDetails(" - init functions [count=%d]\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnInitFunction(uint32_t priority, |
| Index symbol_index) { |
| PrintDetails(" - %d: priority=%d", symbol_index, priority); |
| auto name = GetSymbolName(symbol_index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnComdatCount(Index count) { |
| PrintDetails(" - comdat groups [count=%d]\n", count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnComdatBegin(std::string_view name, |
| uint32_t flags, |
| Index count) { |
| PrintDetails(" - " PRIstringview ": [count=%d]\n", |
| WABT_PRINTF_STRING_VIEW_ARG(name), count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnComdatEntry(ComdatType kind, Index index) { |
| switch (kind) { |
| case ComdatType::Data: { |
| PrintDetails(" - segment[%" PRIindex "]", index); |
| auto name = GetSegmentName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| break; |
| } |
| case ComdatType::Function: { |
| PrintDetails(" - func[%" PRIindex "]", index); |
| auto name = GetFunctionName(index); |
| if (!name.empty()) { |
| PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| break; |
| } |
| } |
| PrintDetails("\n"); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnTagCount(Index count) { |
| return OnCount(count); |
| } |
| |
| Result BinaryReaderObjdump::OnTagType(Index index, Index sig_index) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - tag[%" PRIindex "] sig=%" PRIindex "\n", index, sig_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderObjdump::OnCodeMetadataCount(Index function_index, |
| Index count) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - func[%" PRIindex "]", function_index); |
| auto name = GetFunctionName(function_index); |
| if (!name.empty()) { |
| printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); |
| } |
| printf(":\n"); |
| return Result::Ok; |
| } |
| Result BinaryReaderObjdump::OnCodeMetadata(Offset code_offset, |
| const void* data, |
| Address size) { |
| if (!ShouldPrintDetails()) { |
| return Result::Ok; |
| } |
| printf(" - meta[%" PRIzx "]:\n", code_offset); |
| |
| out_stream_->WriteMemoryDump(data, size, 0, PrintChars::Yes, " - "); |
| return Result::Ok; |
| } |
| |
| } // end anonymous namespace |
| |
| std::string_view ObjdumpNames::Get(Index index) const { |
| auto iter = names.find(index); |
| if (iter == names.end()) |
| return std::string_view(); |
| return iter->second; |
| } |
| |
| void ObjdumpNames::Set(Index index, std::string_view name) { |
| names[index] = std::string(name); |
| } |
| |
| std::string_view ObjdumpLocalNames::Get(Index function_index, |
| Index local_index) const { |
| auto iter = names.find(std::pair<Index, Index>(function_index, local_index)); |
| if (iter == names.end()) |
| return std::string_view(); |
| return iter->second; |
| } |
| |
| void ObjdumpLocalNames::Set(Index function_index, |
| Index local_index, |
| std::string_view name) { |
| names[std::pair<Index, Index>(function_index, local_index)] = |
| std::string(name); |
| } |
| |
| Result ReadBinaryObjdump(const uint8_t* data, |
| size_t size, |
| ObjdumpOptions* options, |
| ObjdumpState* state) { |
| Features features; |
| features.EnableAll(); |
| const bool kReadDebugNames = true; |
| const bool kStopOnFirstError = false; |
| const bool kFailOnCustomSectionError = false; |
| ReadBinaryOptions read_options(features, options->log_stream, kReadDebugNames, |
| kStopOnFirstError, kFailOnCustomSectionError); |
| |
| switch (options->mode) { |
| case ObjdumpMode::Prepass: { |
| read_options.skip_function_bodies = true; |
| BinaryReaderObjdumpPrepass reader(data, size, options, state); |
| return ReadBinary(data, size, &reader, read_options); |
| } |
| case ObjdumpMode::Disassemble: { |
| BinaryReaderObjdumpDisassemble reader(data, size, options, state); |
| return ReadBinary(data, size, &reader, read_options); |
| } |
| default: { |
| read_options.skip_function_bodies = true; |
| BinaryReaderObjdump reader(data, size, options, state); |
| return ReadBinary(data, size, &reader, read_options); |
| } |
| } |
| } |
| |
| } // namespace wabt |