/* * 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 #include #include #include #include #include #if HAVE_STRCASECMP #include #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(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 section_types_; bool section_found_ = false; std::string module_name_; Opcode current_opcode = Opcode::Unreachable; std::unique_ptr 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(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 = ""; } 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 ""; 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 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[%" 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("%" PRId64, 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::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; local_index_ = objdump_state_->function_param_counts[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 { 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 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; Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override; Result OnElemSegmentElemExpr_RefFunc(Index segment_index, Index func_index) 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 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 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); Result OnCount(Index count); std::unique_ptr 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; 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_; } }; 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"); 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::OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) { PrintDetails(" - elem[%" PRIu64 "] = ref.null %s\n", elem_offset_ + elem_index_, type.GetName().c_str()); elem_index_++; return Result::Ok; } Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index, Index func_index) { PrintDetails(" - elem[%" PRIu64 "] = func[%" PRIindex "]", elem_offset_ + elem_index_, func_index); auto name = GetFunctionName(func_index); if (!name.empty()) { PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); } PrintDetails("\n"); elem_index_++; 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) { assert(expr.insts.size() > 0); // 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(" - init ("); 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(" - init i32=%u\n", expr.insts[0].imm.i32); } else { PrintDetails(" - init i32=%d\n", expr.insts[0].imm.i32); } break; case InitExprType::I64: if (as_unsigned) { PrintDetails(" - init i64=%" PRIu64 "\n", expr.insts[0].imm.i64); } else { PrintDetails(" - init 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(" - init f64=%s\n", buffer); break; } case InitExprType::F32: { char buffer[WABT_MAX_FLOAT_HEX]; WriteFloatHex(buffer, sizeof(buffer), expr.insts[0].imm.f32); PrintDetails(" - init f32=%s\n", buffer); break; } case InitExprType::V128: { PrintDetails( " - init 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(" - init 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(" - init 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(" - init null\n"); break; 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 { 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::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(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(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(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 != 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 != 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)) 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(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(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