/* * 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-writer.h" #include #include #include #include #include #include #include #include #include "wabt/config.h" #include "wabt/binary.h" #include "wabt/cast.h" #include "wabt/expr-visitor.h" #include "wabt/ir.h" #include "wabt/leb128.h" #include "wabt/stream.h" #define PRINT_HEADER_NO_INDEX -1 #define MAX_U32_LEB128_BYTES 5 namespace wabt { void WriteStr(Stream* stream, std::string_view s, const char* desc, PrintChars print_chars) { WriteU32Leb128(stream, s.length(), "string length"); stream->WriteData(s.data(), s.length(), desc, print_chars); } void WriteOpcode(Stream* stream, Opcode opcode) { if (opcode.HasPrefix()) { stream->WriteU8(opcode.GetPrefix(), "prefix"); WriteU32Leb128(stream, opcode.GetCode(), opcode.GetName()); } else { stream->WriteU8(opcode.GetCode(), opcode.GetName()); } } void WriteType(Stream* stream, Type type, const char* desc) { WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str()); if (type.IsReferenceWithIndex()) { WriteS32Leb128(stream, type.GetReferenceIndex(), desc ? desc : type.GetName().c_str()); } } void WriteLimits(Stream* stream, const Limits* limits) { uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0; flags |= limits->is_shared ? WABT_BINARY_LIMITS_IS_SHARED_FLAG : 0; flags |= limits->is_64 ? WABT_BINARY_LIMITS_IS_64_FLAG : 0; WriteU32Leb128(stream, flags, "limits: flags"); if (limits->is_64) { WriteU64Leb128(stream, limits->initial, "limits: initial"); if (limits->has_max) { WriteU64Leb128(stream, limits->max, "limits: max"); } } else { WriteU32Leb128(stream, limits->initial, "limits: initial"); if (limits->has_max) { WriteU32Leb128(stream, limits->max, "limits: max"); } } } void WriteDebugName(Stream* stream, std::string_view name, const char* desc) { std::string_view stripped_name = name; if (!stripped_name.empty()) { // Strip leading $ from name assert(stripped_name.front() == '$'); stripped_name.remove_prefix(1); } WriteStr(stream, stripped_name, desc, PrintChars::Yes); } namespace { /* TODO(binji): better leb size guess. Some sections we know will only be 1 byte, but others we can be fairly certain will be larger. */ constexpr size_t LEB_SECTION_SIZE_GUESS = 1; #define ALLOC_FAILURE \ fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__) struct RelocSection { RelocSection(const char* name, Index index) : name(name), section_index(index) {} const char* name; Index section_index; std::vector relocations; }; class Symbol { public: struct Function { static const SymbolType type = SymbolType::Function; Index index; }; struct Data { static const SymbolType type = SymbolType::Data; Index index; Offset offset; Address size; }; struct Global { static const SymbolType type = SymbolType::Global; Index index; }; struct Section { static const SymbolType type = SymbolType::Section; Index section; }; struct Tag { static const SymbolType type = SymbolType::Tag; Index index; }; struct Table { static const SymbolType type = SymbolType::Table; Index index; }; private: SymbolType type_; std::string_view name_; uint8_t flags_; union { Function function_; Data data_; Global global_; Section section_; Tag tag_; Table table_; }; public: Symbol(const std::string_view& name, uint8_t flags, const Function& f) : type_(Function::type), name_(name), flags_(flags), function_(f) {} Symbol(const std::string_view& name, uint8_t flags, const Data& d) : type_(Data::type), name_(name), flags_(flags), data_(d) {} Symbol(const std::string_view& name, uint8_t flags, const Global& g) : type_(Global::type), name_(name), flags_(flags), global_(g) {} Symbol(const std::string_view& name, uint8_t flags, const Section& s) : type_(Section::type), name_(name), flags_(flags), section_(s) {} Symbol(const std::string_view& name, uint8_t flags, const Tag& e) : type_(Tag::type), name_(name), flags_(flags), tag_(e) {} Symbol(const std::string_view& name, uint8_t flags, const Table& t) : type_(Table::type), name_(name), flags_(flags), table_(t) {} SymbolType type() const { return type_; } const std::string_view& name() const { return name_; } uint8_t flags() const { return flags_; } SymbolVisibility visibility() const { return static_cast(flags() & WABT_SYMBOL_MASK_VISIBILITY); } SymbolBinding binding() const { return static_cast(flags() & WABT_SYMBOL_MASK_BINDING); } bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; } bool defined() const { return !undefined(); } bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; } bool explicit_name() const { return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME; } bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; } bool IsFunction() const { return type() == Function::type; } bool IsData() const { return type() == Data::type; } bool IsGlobal() const { return type() == Global::type; } bool IsSection() const { return type() == Section::type; } bool IsTag() const { return type() == Tag::type; } bool IsTable() const { return type() == Table::type; } const Function& AsFunction() const { assert(IsFunction()); return function_; } const Data& AsData() const { assert(IsData()); return data_; } const Global& AsGlobal() const { assert(IsGlobal()); return global_; } const Section& AsSection() const { assert(IsSection()); return section_; } const Tag& AsTag() const { assert(IsTag()); return tag_; } const Table& AsTable() const { assert(IsTable()); return table_; } }; class SymbolTable { WABT_DISALLOW_COPY_AND_ASSIGN(SymbolTable); std::vector symbols_; std::vector functions_; std::vector tables_; std::vector globals_; std::set seen_names_; Result EnsureUnique(const std::string_view& name) { if (seen_names_.count(name)) { fprintf(stderr, "error: duplicate symbol when writing relocatable " "binary: %s\n", &name[0]); return Result::Error; } seen_names_.insert(name); return Result::Ok; }; template Result AddSymbol(std::vector* map, std::string_view name, bool imported, bool exported, T&& sym) { uint8_t flags = 0; if (imported) { flags |= WABT_SYMBOL_FLAG_UNDEFINED; // Wabt currently has no way for a user to explicitly specify the name of // an import, so never set the EXPLICIT_NAME flag, and ignore any display // name fabricated by wabt. name = std::string_view(); } else { if (name.empty()) { // Definitions without a name are local. flags |= uint8_t(SymbolBinding::Local); flags |= uint8_t(SymbolVisibility::Hidden); } else { // Otherwise, strip the dollar off the name; a definition $foo is // available for linking as "foo". assert(name[0] == '$'); name.remove_prefix(1); } if (exported) { CHECK_RESULT(EnsureUnique(name)); flags |= uint8_t(SymbolVisibility::Hidden); flags |= WABT_SYMBOL_FLAG_NO_STRIP; } } if (exported) { flags |= WABT_SYMBOL_FLAG_EXPORTED; } map->push_back(symbols_.size()); symbols_.emplace_back(name, flags, sym); return Result::Ok; }; Index SymbolIndex(const std::vector& table, Index index) const { // For well-formed modules, an index into (e.g.) functions_ will always be // within bounds; the out-of-bounds case here is just to allow --relocatable // to write known-invalid modules. return index < table.size() ? table[index] : kInvalidIndex; } public: SymbolTable() {} Result Populate(const Module* module) { std::set exported_funcs; std::set exported_globals; std::set exported_tags; std::set exported_tables; for (const Export* export_ : module->exports) { switch (export_->kind) { case ExternalKind::Func: exported_funcs.insert(module->GetFuncIndex(export_->var)); break; case ExternalKind::Table: exported_tables.insert(module->GetTableIndex(export_->var)); break; case ExternalKind::Memory: break; case ExternalKind::Global: exported_globals.insert(module->GetGlobalIndex(export_->var)); break; case ExternalKind::Tag: exported_tags.insert(module->GetTagIndex(export_->var)); break; } } // We currently only create symbol table entries for function, table, and // global symbols. for (size_t i = 0; i < module->funcs.size(); ++i) { const Func* func = module->funcs[i]; bool imported = i < module->num_func_imports; bool exported = exported_funcs.count(i); CHECK_RESULT(AddSymbol(&functions_, func->name, imported, exported, Symbol::Function{Index(i)})); } for (size_t i = 0; i < module->tables.size(); ++i) { const Table* table = module->tables[i]; bool imported = i < module->num_table_imports; bool exported = exported_tables.count(i); CHECK_RESULT(AddSymbol(&tables_, table->name, imported, exported, Symbol::Table{Index(i)})); } for (size_t i = 0; i < module->globals.size(); ++i) { const Global* global = module->globals[i]; bool imported = i < module->num_global_imports; bool exported = exported_globals.count(i); CHECK_RESULT(AddSymbol(&globals_, global->name, imported, exported, Symbol::Global{Index(i)})); } return Result::Ok; } const std::vector& symbols() const { return symbols_; } Index FunctionSymbolIndex(Index index) const { return SymbolIndex(functions_, index); } Index TableSymbolIndex(Index index) const { return SymbolIndex(tables_, index); } Index GlobalSymbolIndex(Index index) const { return SymbolIndex(globals_, index); } }; struct CodeMetadata { Offset offset; std::vector data; CodeMetadata(Offset offset, std::vector data) : offset(offset), data(std::move(data)) {} }; struct FuncCodeMetadata { Index func_idx; std::vector entries; FuncCodeMetadata(Index func_idx) : func_idx(func_idx) {} }; struct CodeMetadataSection { std::vector entries; }; using CodeMetadataSections = std::unordered_map; class BinaryWriter { WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter); public: BinaryWriter(Stream*, const WriteBinaryOptions& options, const Module* module); Result WriteModule(); private: void WriteHeader(const char* name, int index); Offset WriteU32Leb128Space(Offset leb_size_guess, const char* desc); Offset WriteFixupU32Leb128Size(Offset offset, Offset leb_size_guess, const char* desc); void BeginKnownSection(BinarySection section_code); void BeginCustomSection(const char* name); void WriteSectionHeader(const char* desc, BinarySection section_code); void EndSection(); void BeginSubsection(const char* name); void EndSubsection(); Index GetLabelVarDepth(const Var* var); Index GetTagVarDepth(const Var* var); Index GetLocalIndex(const Func* func, const Var& var); Index GetSymbolIndex(RelocType reloc_type, Index index); void AddReloc(RelocType reloc_type, Index index); void WriteBlockDecl(const BlockDeclaration& decl); void WriteU32Leb128WithReloc(Index index, const char* desc, RelocType reloc_type); void WriteS32Leb128WithReloc(int32_t value, const char* desc, RelocType reloc_type); void WriteTableNumberWithReloc(Index table_number, const char* desc); template void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc); template void WriteSimdLoadStoreLaneExpr(const Func* func, const Expr* expr, const char* desc); void WriteExpr(const Func* func, const Expr* expr); void WriteExprList(const Func* func, const ExprList& exprs); void WriteInitExpr(const ExprList& expr); void WriteFuncLocals(const Func* func, const LocalTypes& local_types); void WriteFunc(const Func* func); void WriteTable(const Table* table); void WriteMemory(const Memory* memory); void WriteGlobalHeader(const Global* global); void WriteTagType(const Tag* tag); void WriteRelocSection(const RelocSection* reloc_section); void WriteLinkingSection(); template void WriteNames(const std::vector& elems, NameSectionSubsection type); void WriteCodeMetadataSections(); Stream* stream_; const WriteBinaryOptions& options_; const Module* module_; SymbolTable symtab_; std::vector reloc_sections_; RelocSection* current_reloc_section_ = nullptr; Index section_count_ = 0; size_t last_section_offset_ = 0; size_t last_section_leb_size_guess_ = 0; BinarySection last_section_type_ = BinarySection::Invalid; size_t last_section_payload_offset_ = 0; size_t last_subsection_offset_ = 0; size_t last_subsection_leb_size_guess_ = 0; size_t last_subsection_payload_offset_ = 0; // Information about the data count section, so it can be removed if it is // not needed, and relocs relative to the code section patched up. size_t code_start_ = 0; size_t data_count_start_ = 0; size_t data_count_end_ = 0; bool has_data_segment_instruction_ = false; CodeMetadataSections code_metadata_sections_; Offset cur_func_start_offset_; Index cur_func_index_; }; static uint8_t log2_u32(uint32_t x) { uint8_t result = 0; while (x > 1) { x >>= 1; result++; } return result; } BinaryWriter::BinaryWriter(Stream* stream, const WriteBinaryOptions& options, const Module* module) : stream_(stream), options_(options), module_(module) {} void BinaryWriter::WriteHeader(const char* name, int index) { if (stream_->has_log_stream()) { if (index == PRINT_HEADER_NO_INDEX) { stream_->log_stream().Writef("; %s\n", name); } else { stream_->log_stream().Writef("; %s %d\n", name, index); } } } /* returns offset of leb128 */ Offset BinaryWriter::WriteU32Leb128Space(Offset leb_size_guess, const char* desc) { assert(leb_size_guess <= MAX_U32_LEB128_BYTES); uint8_t data[MAX_U32_LEB128_BYTES] = {0}; Offset result = stream_->offset(); Offset bytes_to_write = options_.canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES; stream_->WriteData(data, bytes_to_write, desc); return result; } Offset BinaryWriter::WriteFixupU32Leb128Size(Offset offset, Offset leb_size_guess, const char* desc) { if (options_.canonicalize_lebs) { Offset size = stream_->offset() - offset - leb_size_guess; Offset leb_size = U32Leb128Length(size); Offset delta = leb_size - leb_size_guess; if (delta != 0) { Offset src_offset = offset + leb_size_guess; Offset dst_offset = offset + leb_size; stream_->MoveData(dst_offset, src_offset, size); } WriteU32Leb128At(stream_, offset, size, desc); stream_->AddOffset(delta); return delta; } else { Offset size = stream_->offset() - offset - MAX_U32_LEB128_BYTES; WriteFixedU32Leb128At(stream_, offset, size, desc); return 0; } } void BinaryWriter::WriteBlockDecl(const BlockDeclaration& decl) { if (decl.sig.GetNumParams() == 0 && decl.sig.GetNumResults() <= 1) { if (decl.sig.GetNumResults() == 0) { WriteType(stream_, Type::Void); } else if (decl.sig.GetNumResults() == 1) { WriteType(stream_, decl.sig.GetResultType(0)); } return; } Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var) : module_->GetFuncTypeIndex(decl.sig); assert(index != kInvalidIndex); WriteS32Leb128WithReloc(index, "block type function index", RelocType::TypeIndexLEB); } void BinaryWriter::WriteSectionHeader(const char* desc, BinarySection section_code) { assert(last_section_leb_size_guess_ == 0); WriteHeader(desc, PRINT_HEADER_NO_INDEX); stream_->WriteU8Enum(section_code, "section code"); last_section_type_ = section_code; last_section_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; last_section_offset_ = WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "section size (guess)"); last_section_payload_offset_ = stream_->offset(); } void BinaryWriter::BeginKnownSection(BinarySection section_code) { char desc[100]; wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)", GetSectionName(section_code), static_cast(section_code)); WriteSectionHeader(desc, section_code); } void BinaryWriter::BeginCustomSection(const char* name) { char desc[100]; wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name); WriteSectionHeader(desc, BinarySection::Custom); WriteStr(stream_, name, "custom section name", PrintChars::Yes); } void BinaryWriter::EndSection() { assert(last_section_leb_size_guess_ != 0); WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_, "FIXUP section size"); last_section_leb_size_guess_ = 0; section_count_++; } void BinaryWriter::BeginSubsection(const char* name) { assert(last_subsection_leb_size_guess_ == 0); last_subsection_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; last_subsection_offset_ = WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "subsection size (guess)"); last_subsection_payload_offset_ = stream_->offset(); } void BinaryWriter::EndSubsection() { assert(last_subsection_leb_size_guess_ != 0); WriteFixupU32Leb128Size(last_subsection_offset_, last_subsection_leb_size_guess_, "FIXUP subsection size"); last_subsection_leb_size_guess_ = 0; } Index BinaryWriter::GetLabelVarDepth(const Var* var) { return var->index(); } Index BinaryWriter::GetTagVarDepth(const Var* var) { return var->index(); } Index BinaryWriter::GetSymbolIndex(RelocType reloc_type, Index index) { switch (reloc_type) { case RelocType::FuncIndexLEB: return symtab_.FunctionSymbolIndex(index); case RelocType::TableNumberLEB: return symtab_.TableSymbolIndex(index); case RelocType::GlobalIndexLEB: return symtab_.GlobalSymbolIndex(index); case RelocType::TypeIndexLEB: // Type indexes don't create entries in the symbol table; instead their // index is used directly. return index; default: fprintf(stderr, "warning: unsupported relocation type: %s\n", GetRelocTypeName(reloc_type)); return kInvalidIndex; } } void BinaryWriter::AddReloc(RelocType reloc_type, Index index) { // Add a new reloc section if needed if (!current_reloc_section_ || current_reloc_section_->section_index != section_count_) { reloc_sections_.emplace_back(GetSectionName(last_section_type_), section_count_); current_reloc_section_ = &reloc_sections_.back(); } // Add a new relocation to the curent reloc section size_t offset = stream_->offset() - last_section_payload_offset_; Index symbol_index = GetSymbolIndex(reloc_type, index); if (symbol_index == kInvalidIndex) { // The file is invalid, for example a reference to function 42 where only 10 // functions are defined. The user must have already passed --no-check, so // no extra warning here is needed. return; } current_reloc_section_->relocations.emplace_back(reloc_type, offset, symbol_index); } void BinaryWriter::WriteU32Leb128WithReloc(Index index, const char* desc, RelocType reloc_type) { if (options_.relocatable) { AddReloc(reloc_type, index); WriteFixedU32Leb128(stream_, index, desc); } else { WriteU32Leb128(stream_, index, desc); } } void BinaryWriter::WriteS32Leb128WithReloc(int32_t value, const char* desc, RelocType reloc_type) { if (options_.relocatable) { AddReloc(reloc_type, value); WriteFixedS32Leb128(stream_, value, desc); } else { WriteS32Leb128(stream_, value, desc); } } void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) { // Unless reference types are enabled, all references to tables refer to table // 0, so no relocs need be emitted when making relocatable binaries. if (options_.relocatable && options_.features.reference_types_enabled()) { AddReloc(RelocType::TableNumberLEB, value); WriteFixedS32Leb128(stream_, value, desc); } else { WriteS32Leb128(stream_, value, desc); } } Index BinaryWriter::GetLocalIndex(const Func* func, const Var& var) { // func can be nullptr when using local.get/local.set/local.tee in an // init_expr. if (func) { return func->GetLocalIndex(var); } else if (var.is_index()) { return var.index(); } else { return kInvalidIndex; } } // TODO(binji): Rename this, it is used for more than loads/stores now. template void BinaryWriter::WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc) { auto* typed_expr = cast(expr); WriteOpcode(stream_, typed_expr->opcode); Address align = typed_expr->opcode.GetAlignment(typed_expr->align); Index memidx = module_->GetMemoryIndex(typed_expr->memidx); if (memidx != 0) { stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment"); WriteU32Leb128(stream_, memidx, "memidx"); } else { stream_->WriteU8(log2_u32(align), "alignment"); } WriteU32Leb128(stream_, typed_expr->offset, desc); } template void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func, const Expr* expr, const char* desc) { WriteLoadStoreExpr(func, expr, desc); auto* typed_expr = cast(expr); stream_->WriteU8(static_cast(typed_expr->val), "Simd Lane literal"); } void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) { switch (expr->type()) { case ExprType::AtomicLoad: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::AtomicRmw: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::AtomicRmwCmpxchg: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::AtomicStore: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::AtomicWait: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::AtomicFence: { auto* fence_expr = cast(expr); WriteOpcode(stream_, Opcode::AtomicFence); WriteU32Leb128(stream_, fence_expr->consistency_model, "consistency model"); break; } case ExprType::AtomicNotify: WriteLoadStoreExpr(func, expr, "memory offset"); break; case ExprType::Binary: WriteOpcode(stream_, cast(expr)->opcode); break; case ExprType::Block: WriteOpcode(stream_, Opcode::Block); WriteBlockDecl(cast(expr)->block.decl); WriteExprList(func, cast(expr)->block.exprs); WriteOpcode(stream_, Opcode::End); break; case ExprType::Br: WriteOpcode(stream_, Opcode::Br); WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), "break depth"); break; case ExprType::BrIf: WriteOpcode(stream_, Opcode::BrIf); WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), "break depth"); break; case ExprType::BrTable: { auto* br_table_expr = cast(expr); WriteOpcode(stream_, Opcode::BrTable); WriteU32Leb128(stream_, br_table_expr->targets.size(), "num targets"); Index depth; for (const Var& var : br_table_expr->targets) { depth = GetLabelVarDepth(&var); WriteU32Leb128(stream_, depth, "break depth"); } depth = GetLabelVarDepth(&br_table_expr->default_target); WriteU32Leb128(stream_, depth, "break depth for default"); break; } case ExprType::Call: { Index index = module_->GetFuncIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::Call); WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); break; } case ExprType::ReturnCall: { Index index = module_->GetFuncIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::ReturnCall); WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); break; } case ExprType::CallIndirect: { Index sig_index = module_->GetFuncTypeIndex(cast(expr)->decl); Index table_index = module_->GetTableIndex(cast(expr)->table); WriteOpcode(stream_, Opcode::CallIndirect); WriteU32Leb128WithReloc(sig_index, "signature index", RelocType::TypeIndexLEB); WriteTableNumberWithReloc(table_index, "table index"); break; } case ExprType::CallRef: { WriteOpcode(stream_, Opcode::CallRef); break; } case ExprType::ReturnCallIndirect: { Index sig_index = module_->GetFuncTypeIndex(cast(expr)->decl); Index table_index = module_->GetTableIndex(cast(expr)->table); WriteOpcode(stream_, Opcode::ReturnCallIndirect); WriteU32Leb128WithReloc(sig_index, "signature index", RelocType::TypeIndexLEB); WriteTableNumberWithReloc(table_index, "table index"); break; } case ExprType::Compare: WriteOpcode(stream_, cast(expr)->opcode); break; case ExprType::Const: { const Const& const_ = cast(expr)->const_; switch (const_.type()) { case Type::I32: { WriteOpcode(stream_, Opcode::I32Const); WriteS32Leb128(stream_, const_.u32(), "i32 literal"); break; } case Type::I64: WriteOpcode(stream_, Opcode::I64Const); WriteS64Leb128(stream_, const_.u64(), "i64 literal"); break; case Type::F32: WriteOpcode(stream_, Opcode::F32Const); stream_->WriteU32(const_.f32_bits(), "f32 literal"); break; case Type::F64: WriteOpcode(stream_, Opcode::F64Const); stream_->WriteU64(const_.f64_bits(), "f64 literal"); break; case Type::V128: WriteOpcode(stream_, Opcode::V128Const); stream_->WriteU128(const_.vec128(), "v128 literal"); break; default: assert(0); } break; } case ExprType::Convert: WriteOpcode(stream_, cast(expr)->opcode); break; case ExprType::Drop: WriteOpcode(stream_, Opcode::Drop); break; case ExprType::GlobalGet: { Index index = module_->GetGlobalIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::GlobalGet); WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); break; } case ExprType::GlobalSet: { Index index = module_->GetGlobalIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::GlobalSet); WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); break; } case ExprType::If: { auto* if_expr = cast(expr); WriteOpcode(stream_, Opcode::If); WriteBlockDecl(if_expr->true_.decl); WriteExprList(func, if_expr->true_.exprs); if (!if_expr->false_.empty()) { WriteOpcode(stream_, Opcode::Else); WriteExprList(func, if_expr->false_); } WriteOpcode(stream_, Opcode::End); break; } case ExprType::Load: WriteLoadStoreExpr(func, expr, "load offset"); break; case ExprType::LocalGet: { Index index = GetLocalIndex(func, cast(expr)->var); WriteOpcode(stream_, Opcode::LocalGet); WriteU32Leb128(stream_, index, "local index"); break; } case ExprType::LocalSet: { Index index = GetLocalIndex(func, cast(expr)->var); WriteOpcode(stream_, Opcode::LocalSet); WriteU32Leb128(stream_, index, "local index"); break; } case ExprType::LocalTee: { Index index = GetLocalIndex(func, cast(expr)->var); WriteOpcode(stream_, Opcode::LocalTee); WriteU32Leb128(stream_, index, "local index"); break; } case ExprType::Loop: WriteOpcode(stream_, Opcode::Loop); WriteBlockDecl(cast(expr)->block.decl); WriteExprList(func, cast(expr)->block.exprs); WriteOpcode(stream_, Opcode::End); break; case ExprType::MemoryCopy: { Index srcmemidx = module_->GetMemoryIndex(cast(expr)->srcmemidx); Index destmemidx = module_->GetMemoryIndex(cast(expr)->destmemidx); WriteOpcode(stream_, Opcode::MemoryCopy); WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx"); WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx"); break; } case ExprType::DataDrop: { Index index = module_->GetDataSegmentIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::DataDrop); WriteU32Leb128(stream_, index, "data.drop segment"); has_data_segment_instruction_ = true; break; } case ExprType::MemoryFill: { Index memidx = module_->GetMemoryIndex(cast(expr)->memidx); WriteOpcode(stream_, Opcode::MemoryFill); WriteU32Leb128(stream_, memidx, "memory.fill memidx"); break; } case ExprType::MemoryGrow: { Index memidx = module_->GetMemoryIndex(cast(expr)->memidx); WriteOpcode(stream_, Opcode::MemoryGrow); WriteU32Leb128(stream_, memidx, "memory.grow memidx"); break; } case ExprType::MemoryInit: { Index index = module_->GetDataSegmentIndex(cast(expr)->var); Index memidx = module_->GetMemoryIndex(cast(expr)->memidx); WriteOpcode(stream_, Opcode::MemoryInit); WriteU32Leb128(stream_, index, "memory.init segment"); WriteU32Leb128(stream_, memidx, "memory.init memidx"); has_data_segment_instruction_ = true; break; } case ExprType::MemorySize: { Index memidx = module_->GetMemoryIndex(cast(expr)->memidx); WriteOpcode(stream_, Opcode::MemorySize); WriteU32Leb128(stream_, memidx, "memory.size memidx"); break; } case ExprType::TableCopy: { auto* copy_expr = cast(expr); Index dst = module_->GetTableIndex(copy_expr->dst_table); Index src = module_->GetTableIndex(copy_expr->src_table); WriteOpcode(stream_, Opcode::TableCopy); WriteTableNumberWithReloc(dst, "table.copy dst_table"); WriteTableNumberWithReloc(src, "table.copy src_table"); break; } case ExprType::ElemDrop: { Index index = module_->GetElemSegmentIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::ElemDrop); WriteU32Leb128(stream_, index, "elem.drop segment"); break; } case ExprType::TableInit: { auto* init_expr = cast(expr); Index table_index = module_->GetTableIndex(init_expr->table_index); Index segment_index = module_->GetElemSegmentIndex(init_expr->segment_index); WriteOpcode(stream_, Opcode::TableInit); WriteU32Leb128(stream_, segment_index, "table.init segment"); WriteTableNumberWithReloc(table_index, "table.init table"); break; } case ExprType::TableGet: { Index index = module_->GetTableIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::TableGet); WriteTableNumberWithReloc(index, "table.get table index"); break; } case ExprType::TableSet: { Index index = module_->GetTableIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::TableSet); WriteTableNumberWithReloc(index, "table.set table index"); break; } case ExprType::TableGrow: { Index index = module_->GetTableIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::TableGrow); WriteTableNumberWithReloc(index, "table.grow table index"); break; } case ExprType::TableSize: { Index index = module_->GetTableIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::TableSize); WriteTableNumberWithReloc(index, "table.size table index"); break; } case ExprType::TableFill: { Index index = module_->GetTableIndex(cast(expr)->var); WriteOpcode(stream_, Opcode::TableFill); WriteTableNumberWithReloc(index, "table.fill table index"); break; } case ExprType::RefFunc: { WriteOpcode(stream_, Opcode::RefFunc); Index index = module_->GetFuncIndex(cast(expr)->var); WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); break; } case ExprType::RefNull: { WriteOpcode(stream_, Opcode::RefNull); WriteType(stream_, cast(expr)->type, "ref.null type"); break; } case ExprType::RefIsNull: WriteOpcode(stream_, Opcode::RefIsNull); break; case ExprType::Nop: WriteOpcode(stream_, Opcode::Nop); break; case ExprType::Rethrow: WriteOpcode(stream_, Opcode::Rethrow); WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), "rethrow depth"); break; case ExprType::Return: WriteOpcode(stream_, Opcode::Return); break; case ExprType::Select: { auto* select_expr = cast(expr); if (select_expr->result_type.empty()) { WriteOpcode(stream_, Opcode::Select); } else { WriteOpcode(stream_, Opcode::SelectT); WriteU32Leb128(stream_, select_expr->result_type.size(), "num result types"); for (Type t : select_expr->result_type) { WriteType(stream_, t, "result type"); } } break; } case ExprType::Store: WriteLoadStoreExpr(func, expr, "store offset"); break; case ExprType::Throw: WriteOpcode(stream_, Opcode::Throw); WriteU32Leb128(stream_, GetTagVarDepth(&cast(expr)->var), "throw tag"); break; case ExprType::Try: { auto* try_expr = cast(expr); WriteOpcode(stream_, Opcode::Try); WriteBlockDecl(try_expr->block.decl); WriteExprList(func, try_expr->block.exprs); switch (try_expr->kind) { case TryKind::Catch: for (const Catch& catch_ : try_expr->catches) { if (catch_.IsCatchAll()) { WriteOpcode(stream_, Opcode::CatchAll); } else { WriteOpcode(stream_, Opcode::Catch); WriteU32Leb128(stream_, GetTagVarDepth(&catch_.var), "catch tag"); } WriteExprList(func, catch_.exprs); } WriteOpcode(stream_, Opcode::End); break; case TryKind::Delegate: WriteOpcode(stream_, Opcode::Delegate); WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target), "delegate depth"); break; case TryKind::Plain: WriteOpcode(stream_, Opcode::End); break; } break; } case ExprType::Unary: WriteOpcode(stream_, cast(expr)->opcode); break; case ExprType::Ternary: WriteOpcode(stream_, cast(expr)->opcode); break; case ExprType::SimdLaneOp: { const Opcode opcode = cast(expr)->opcode; WriteOpcode(stream_, opcode); stream_->WriteU8(static_cast(cast(expr)->val), "Simd Lane literal"); break; } case ExprType::SimdLoadLane: { WriteSimdLoadStoreLaneExpr(func, expr, "load offset"); break; } case ExprType::SimdStoreLane: { WriteSimdLoadStoreLaneExpr(func, expr, "store offset"); break; } case ExprType::SimdShuffleOp: { const Opcode opcode = cast(expr)->opcode; WriteOpcode(stream_, opcode); stream_->WriteU128(cast(expr)->val, "Simd Lane[16] literal"); break; } case ExprType::LoadSplat: WriteLoadStoreExpr(func, expr, "load offset"); break; case ExprType::LoadZero: WriteLoadStoreExpr(func, expr, "load offset"); break; case ExprType::Unreachable: WriteOpcode(stream_, Opcode::Unreachable); break; case ExprType::CodeMetadata: { auto* meta_expr = cast(expr); auto& s = code_metadata_sections_[meta_expr->name]; if (s.entries.empty() || s.entries.back().func_idx != cur_func_index_) { s.entries.emplace_back(cur_func_index_); } auto& a = s.entries.back(); Offset code_offset = stream_->offset() - cur_func_start_offset_; a.entries.emplace_back(code_offset, meta_expr->data); break; } } } void BinaryWriter::WriteExprList(const Func* func, const ExprList& exprs) { for (const Expr& expr : exprs) { WriteExpr(func, &expr); } } void BinaryWriter::WriteInitExpr(const ExprList& expr) { WriteExprList(nullptr, expr); WriteOpcode(stream_, Opcode::End); } void BinaryWriter::WriteFuncLocals(const Func* func, const LocalTypes& local_types) { if (local_types.size() == 0) { WriteU32Leb128(stream_, 0, "local decl count"); return; } Index local_decl_count = local_types.decls().size(); WriteU32Leb128(stream_, local_decl_count, "local decl count"); for (auto decl : local_types.decls()) { WriteU32Leb128(stream_, decl.second, "local type count"); WriteType(stream_, decl.first); } } void BinaryWriter::WriteFunc(const Func* func) { WriteFuncLocals(func, func->local_types); WriteExprList(func, func->exprs); WriteOpcode(stream_, Opcode::End); } void BinaryWriter::WriteTable(const Table* table) { WriteType(stream_, table->elem_type); WriteLimits(stream_, &table->elem_limits); } void BinaryWriter::WriteMemory(const Memory* memory) { WriteLimits(stream_, &memory->page_limits); } void BinaryWriter::WriteGlobalHeader(const Global* global) { WriteType(stream_, global->type); stream_->WriteU8(global->mutable_, "global mutability"); } void BinaryWriter::WriteTagType(const Tag* tag) { stream_->WriteU8(0, "tag attribute"); WriteU32Leb128(stream_, module_->GetFuncTypeIndex(tag->decl), "tag signature index"); } void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) { char section_name[128]; wabt_snprintf(section_name, sizeof(section_name), "%s.%s", WABT_BINARY_SECTION_RELOC, reloc_section->name); BeginCustomSection(section_name); WriteU32Leb128(stream_, reloc_section->section_index, "reloc section index"); const std::vector& relocs = reloc_section->relocations; WriteU32Leb128(stream_, relocs.size(), "num relocs"); for (const Reloc& reloc : relocs) { WriteU32Leb128(stream_, reloc.type, "reloc type"); WriteU32Leb128(stream_, reloc.offset, "reloc offset"); WriteU32Leb128(stream_, reloc.index, "reloc index"); switch (reloc.type) { case RelocType::MemoryAddressLEB: case RelocType::MemoryAddressLEB64: case RelocType::MemoryAddressSLEB: case RelocType::MemoryAddressSLEB64: case RelocType::MemoryAddressRelSLEB: case RelocType::MemoryAddressRelSLEB64: case RelocType::MemoryAddressI32: case RelocType::MemoryAddressI64: case RelocType::FunctionOffsetI32: case RelocType::SectionOffsetI32: case RelocType::MemoryAddressTLSSLEB: case RelocType::MemoryAddressTLSI32: WriteU32Leb128(stream_, reloc.addend, "reloc addend"); break; case RelocType::FuncIndexLEB: case RelocType::TableIndexSLEB: case RelocType::TableIndexSLEB64: case RelocType::TableIndexI32: case RelocType::TableIndexI64: case RelocType::TypeIndexLEB: case RelocType::GlobalIndexLEB: case RelocType::TagIndexLEB: case RelocType::TableIndexRelSLEB: case RelocType::TableNumberLEB: break; default: fprintf(stderr, "warning: unsupported relocation type: %s\n", GetRelocTypeName(reloc.type)); } } EndSection(); } void BinaryWriter::WriteLinkingSection() { BeginCustomSection(WABT_BINARY_SECTION_LINKING); WriteU32Leb128(stream_, 2, "metadata version"); const std::vector& symbols = symtab_.symbols(); if (symbols.size()) { stream_->WriteU8Enum(LinkingEntryType::SymbolTable, "symbol table"); BeginSubsection("symbol table"); WriteU32Leb128(stream_, symbols.size(), "num symbols"); for (const Symbol& sym : symbols) { stream_->WriteU8Enum(sym.type(), "symbol type"); WriteU32Leb128(stream_, sym.flags(), "symbol flags"); switch (sym.type()) { case SymbolType::Function: WriteU32Leb128(stream_, sym.AsFunction().index, "function index"); if (sym.defined() || sym.explicit_name()) { WriteStr(stream_, sym.name(), "function name", PrintChars::Yes); } break; case SymbolType::Data: WriteStr(stream_, sym.name(), "data name", PrintChars::Yes); if (sym.defined()) { WriteU32Leb128(stream_, sym.AsData().index, "data index"); WriteU32Leb128(stream_, sym.AsData().offset, "data offset"); WriteU32Leb128(stream_, sym.AsData().size, "data size"); } break; case SymbolType::Global: WriteU32Leb128(stream_, sym.AsGlobal().index, "global index"); if (sym.defined() || sym.explicit_name()) { WriteStr(stream_, sym.name(), "global name", PrintChars::Yes); } break; case SymbolType::Section: WriteU32Leb128(stream_, sym.AsSection().section, "section index"); break; case SymbolType::Tag: WriteU32Leb128(stream_, sym.AsTag().index, "tag index"); if (sym.defined() || sym.explicit_name()) { WriteStr(stream_, sym.name(), "tag name", PrintChars::Yes); } break; case SymbolType::Table: WriteU32Leb128(stream_, sym.AsTable().index, "table index"); if (sym.defined() || sym.explicit_name()) { WriteStr(stream_, sym.name(), "table name", PrintChars::Yes); } break; } } EndSubsection(); } EndSection(); } template void BinaryWriter::WriteNames(const std::vector& elems, NameSectionSubsection type) { size_t num_named_elems = 0; for (const T* elem : elems) { if (!elem->name.empty()) { num_named_elems++; } } if (!num_named_elems) { return; } WriteU32Leb128(stream_, type, "name subsection type"); BeginSubsection("name subsection"); char desc[100]; WriteU32Leb128(stream_, num_named_elems, "num names"); for (size_t i = 0; i < elems.size(); ++i) { const T* elem = elems[i]; if (elem->name.empty()) { continue; } WriteU32Leb128(stream_, i, "elem index"); wabt_snprintf(desc, sizeof(desc), "elem name %" PRIzd, i); WriteDebugName(stream_, elem->name, desc); } EndSubsection(); } Result BinaryWriter::WriteModule() { stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC"); stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION"); if (options_.relocatable) { CHECK_RESULT(symtab_.Populate(module_)); } if (module_->types.size()) { BeginKnownSection(BinarySection::Type); WriteU32Leb128(stream_, module_->types.size(), "num types"); for (size_t i = 0; i < module_->types.size(); ++i) { const TypeEntry* type = module_->types[i]; switch (type->kind()) { case TypeEntryKind::Func: { const FuncType* func_type = cast(type); const FuncSignature* sig = &func_type->sig; WriteHeader("func type", i); WriteType(stream_, Type::Func); Index num_params = sig->param_types.size(); Index num_results = sig->result_types.size(); WriteU32Leb128(stream_, num_params, "num params"); for (size_t j = 0; j < num_params; ++j) { WriteType(stream_, sig->param_types[j]); } WriteU32Leb128(stream_, num_results, "num results"); for (size_t j = 0; j < num_results; ++j) { WriteType(stream_, sig->result_types[j]); } break; } case TypeEntryKind::Struct: { const StructType* struct_type = cast(type); WriteHeader("struct type", i); WriteType(stream_, Type::Struct); Index num_fields = struct_type->fields.size(); WriteU32Leb128(stream_, num_fields, "num fields"); for (size_t j = 0; j < num_fields; ++j) { const Field& field = struct_type->fields[j]; WriteType(stream_, field.type); stream_->WriteU8(field.mutable_, "field mutability"); } break; } case TypeEntryKind::Array: { const ArrayType* array_type = cast(type); WriteHeader("array type", i); WriteType(stream_, Type::Array); WriteType(stream_, array_type->field.type); stream_->WriteU8(array_type->field.mutable_, "field mutability"); break; } } } EndSection(); } if (module_->imports.size()) { BeginKnownSection(BinarySection::Import); WriteU32Leb128(stream_, module_->imports.size(), "num imports"); for (size_t i = 0; i < module_->imports.size(); ++i) { const Import* import = module_->imports[i]; WriteHeader("import header", i); WriteStr(stream_, import->module_name, "import module name", PrintChars::Yes); WriteStr(stream_, import->field_name, "import field name", PrintChars::Yes); stream_->WriteU8Enum(import->kind(), "import kind"); switch (import->kind()) { case ExternalKind::Func: WriteU32Leb128( stream_, module_->GetFuncTypeIndex(cast(import)->func.decl), "import signature index"); break; case ExternalKind::Table: WriteTable(&cast(import)->table); break; case ExternalKind::Memory: WriteMemory(&cast(import)->memory); break; case ExternalKind::Global: WriteGlobalHeader(&cast(import)->global); break; case ExternalKind::Tag: WriteTagType(&cast(import)->tag); break; } } EndSection(); } assert(module_->funcs.size() >= module_->num_func_imports); Index num_funcs = module_->funcs.size() - module_->num_func_imports; if (num_funcs) { BeginKnownSection(BinarySection::Function); WriteU32Leb128(stream_, num_funcs, "num functions"); for (size_t i = 0; i < num_funcs; ++i) { const Func* func = module_->funcs[i + module_->num_func_imports]; char desc[100]; wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index", i); WriteU32Leb128(stream_, module_->GetFuncTypeIndex(func->decl), desc); } EndSection(); } assert(module_->tables.size() >= module_->num_table_imports); Index num_tables = module_->tables.size() - module_->num_table_imports; if (num_tables) { BeginKnownSection(BinarySection::Table); WriteU32Leb128(stream_, num_tables, "num tables"); for (size_t i = 0; i < num_tables; ++i) { const Table* table = module_->tables[i + module_->num_table_imports]; WriteHeader("table", i); WriteTable(table); } EndSection(); } assert(module_->memories.size() >= module_->num_memory_imports); Index num_memories = module_->memories.size() - module_->num_memory_imports; if (num_memories) { BeginKnownSection(BinarySection::Memory); WriteU32Leb128(stream_, num_memories, "num memories"); for (size_t i = 0; i < num_memories; ++i) { const Memory* memory = module_->memories[i + module_->num_memory_imports]; WriteHeader("memory", i); WriteMemory(memory); } EndSection(); } assert(module_->tags.size() >= module_->num_tag_imports); Index num_tags = module_->tags.size() - module_->num_tag_imports; if (num_tags) { BeginKnownSection(BinarySection::Tag); WriteU32Leb128(stream_, num_tags, "tag count"); for (size_t i = 0; i < num_tags; ++i) { WriteHeader("tag", i); const Tag* tag = module_->tags[i + module_->num_tag_imports]; WriteTagType(tag); } EndSection(); } assert(module_->globals.size() >= module_->num_global_imports); Index num_globals = module_->globals.size() - module_->num_global_imports; if (num_globals) { BeginKnownSection(BinarySection::Global); WriteU32Leb128(stream_, num_globals, "num globals"); for (size_t i = 0; i < num_globals; ++i) { const Global* global = module_->globals[i + module_->num_global_imports]; WriteGlobalHeader(global); WriteInitExpr(global->init_expr); } EndSection(); } if (module_->exports.size()) { BeginKnownSection(BinarySection::Export); WriteU32Leb128(stream_, module_->exports.size(), "num exports"); for (const Export* export_ : module_->exports) { WriteStr(stream_, export_->name, "export name", PrintChars::Yes); stream_->WriteU8Enum(export_->kind, "export kind"); switch (export_->kind) { case ExternalKind::Func: { Index index = module_->GetFuncIndex(export_->var); WriteU32Leb128(stream_, index, "export func index"); break; } case ExternalKind::Table: { Index index = module_->GetTableIndex(export_->var); WriteU32Leb128(stream_, index, "export table index"); break; } case ExternalKind::Memory: { Index index = module_->GetMemoryIndex(export_->var); WriteU32Leb128(stream_, index, "export memory index"); break; } case ExternalKind::Global: { Index index = module_->GetGlobalIndex(export_->var); WriteU32Leb128(stream_, index, "export global index"); break; } case ExternalKind::Tag: { Index index = module_->GetTagIndex(export_->var); WriteU32Leb128(stream_, index, "export tag index"); break; } } } EndSection(); } if (module_->starts.size()) { Index start_func_index = module_->GetFuncIndex(*module_->starts[0]); if (start_func_index != kInvalidIndex) { BeginKnownSection(BinarySection::Start); WriteU32Leb128(stream_, start_func_index, "start func index"); EndSection(); } } if (module_->elem_segments.size()) { BeginKnownSection(BinarySection::Elem); WriteU32Leb128(stream_, module_->elem_segments.size(), "num elem segments"); for (size_t i = 0; i < module_->elem_segments.size(); ++i) { ElemSegment* segment = module_->elem_segments[i]; WriteHeader("elem segment header", i); // 1. flags uint8_t flags = segment->GetFlags(module_); stream_->WriteU8(flags, "segment flags"); // 2. optional target table if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) { WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var), "table index"); } // 3. optional target location within the table (active segments only) if (!(flags & SegPassive)) { WriteInitExpr(segment->offset); } // 4. type of item in the following list (omitted for "legacy" segments) if (flags & (SegPassive | SegExplicitIndex)) { if (flags & SegUseElemExprs) { WriteType(stream_, segment->elem_type, "elem expr list type"); } else { stream_->WriteU8Enum(ExternalKind::Func, "elem list type"); } } // 5. actual list of elements (with extern indexes or elem expr's) // preceeded by length WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems"); if (flags & SegUseElemExprs) { for (const ExprList& elem_expr : segment->elem_exprs) { WriteInitExpr(elem_expr); } } else { for (const ExprList& elem_expr : segment->elem_exprs) { assert(elem_expr.size() == 1); const Expr* expr = &elem_expr.front(); assert(expr->type() == ExprType::RefFunc); WriteU32Leb128(stream_, module_->GetFuncIndex(cast(expr)->var), "elem function index"); } } } EndSection(); } if (options_.features.bulk_memory_enabled() && module_->data_segments.size()) { // Keep track of the data count section offset so it can be removed if // it isn't needed. data_count_start_ = stream_->offset(); BeginKnownSection(BinarySection::DataCount); WriteU32Leb128(stream_, module_->data_segments.size(), "data count"); EndSection(); data_count_end_ = stream_->offset(); } if (num_funcs) { code_start_ = stream_->offset(); BeginKnownSection(BinarySection::Code); WriteU32Leb128(stream_, num_funcs, "num functions"); for (size_t i = 0; i < num_funcs; ++i) { cur_func_index_ = i + module_->num_func_imports; WriteHeader("function body", i); const Func* func = module_->funcs[cur_func_index_]; /* TODO(binji): better guess of the size of the function body section */ const Offset leb_size_guess = 1; Offset body_size_offset = WriteU32Leb128Space(leb_size_guess, "func body size (guess)"); cur_func_start_offset_ = stream_->offset(); WriteFunc(func); auto func_start_offset = body_size_offset - last_section_payload_offset_; auto func_end_offset = stream_->offset() - last_section_payload_offset_; auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess, "FIXUP func body size"); if (current_reloc_section_ && delta != 0) { for (Reloc& reloc : current_reloc_section_->relocations) { if (reloc.offset >= func_start_offset && reloc.offset <= func_end_offset) { reloc.offset += delta; } } } } EndSection(); } // Remove the DataCount section if there are no instructions that require it. if (options_.features.bulk_memory_enabled() && module_->data_segments.size() && !has_data_segment_instruction_) { Offset size = stream_->offset() - data_count_end_; if (size) { // If the DataCount section was followed by anything, assert that it's // only the Code section. This limits the amount of fixing-up that we // need to do. assert(data_count_end_ == code_start_); assert(last_section_type_ == BinarySection::Code); stream_->MoveData(data_count_start_, data_count_end_, size); code_start_ = data_count_start_; } stream_->Truncate(data_count_start_ + size); --section_count_; // We just effectively decremented the code section's index; adjust anything // that might have captured it. for (RelocSection& section : reloc_sections_) { if (section.section_index == section_count_) { assert(last_section_type_ == BinarySection::Code); --section.section_index; } } } WriteCodeMetadataSections(); if (module_->data_segments.size()) { BeginKnownSection(BinarySection::Data); WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments"); for (size_t i = 0; i < module_->data_segments.size(); ++i) { const DataSegment* segment = module_->data_segments[i]; WriteHeader("data segment header", i); uint8_t flags = segment->GetFlags(module_); stream_->WriteU8(flags, "segment flags"); if (!(flags & SegPassive)) { if (options_.features.multi_memory_enabled() && (flags & SegExplicitIndex)) { WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var), "memidx"); } else { assert(module_->GetMemoryIndex(segment->memory_var) == 0); } WriteInitExpr(segment->offset); } WriteU32Leb128(stream_, segment->data.size(), "data segment size"); WriteHeader("data segment data", i); stream_->WriteData(segment->data, "data segment data"); } EndSection(); } if (options_.write_debug_names) { std::vector index_to_name; char desc[100]; BeginCustomSection(WABT_BINARY_SECTION_NAME); if (!module_->name.empty()) { WriteU32Leb128(stream_, NameSectionSubsection::Module, "module name type"); BeginSubsection("module name subsection"); WriteDebugName(stream_, module_->name, "module name"); EndSubsection(); } WriteNames(module_->funcs, NameSectionSubsection::Function); WriteU32Leb128(stream_, 2, "local name type"); BeginSubsection("local name subsection"); WriteU32Leb128(stream_, module_->funcs.size(), "num functions"); for (size_t i = 0; i < module_->funcs.size(); ++i) { const Func* func = module_->funcs[i]; Index num_params_and_locals = func->GetNumParamsAndLocals(); MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings, &index_to_name); Index num_named = 0; for (auto s : index_to_name) { if (!s.empty()) { num_named++; } } WriteU32Leb128(stream_, i, "function index"); WriteU32Leb128(stream_, num_named, "num locals"); for (size_t j = 0; j < num_params_and_locals; ++j) { const std::string& name = index_to_name[j]; if (!name.empty()) { wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j); WriteU32Leb128(stream_, j, "local index"); WriteDebugName(stream_, name, desc); } } } EndSubsection(); WriteNames(module_->types, NameSectionSubsection::Type); WriteNames(module_->tables, NameSectionSubsection::Table); WriteNames(module_->memories, NameSectionSubsection::Memory); WriteNames(module_->globals, NameSectionSubsection::Global); WriteNames(module_->elem_segments, NameSectionSubsection::ElemSegment); WriteNames(module_->data_segments, NameSectionSubsection::DataSegment); WriteNames(module_->tags, NameSectionSubsection::Tag); EndSection(); } if (options_.relocatable) { WriteLinkingSection(); for (RelocSection& section : reloc_sections_) { WriteRelocSection(§ion); } } return stream_->result(); } void BinaryWriter::WriteCodeMetadataSections() { if (code_metadata_sections_.empty()) return; section_count_ -= 1; // We have to increment the code section's index; adjust anything // that might have captured it. for (RelocSection& section : reloc_sections_) { if (section.section_index == section_count_) { assert(last_section_type_ == BinarySection::Code); section.section_index += code_metadata_sections_.size(); } } MemoryStream tmp_stream; Stream* main_stream = stream_; stream_ = &tmp_stream; for (auto& s : code_metadata_sections_) { std::string name = "metadata.code."; name.append(s.first); auto& section = s.second; BeginCustomSection(name.c_str()); WriteU32Leb128(stream_, section.entries.size(), "function count"); for (auto& f : section.entries) { WriteU32Leb128WithReloc(f.func_idx, "function index", RelocType::FuncIndexLEB); WriteU32Leb128(stream_, f.entries.size(), "instances count"); for (auto& a : f.entries) { WriteU32Leb128(stream_, a.offset, "code offset"); WriteU32Leb128(stream_, a.data.size(), "data length"); stream_->WriteData(a.data.data(), a.data.size(), "data", PrintChars::Yes); } } EndSection(); } stream_ = main_stream; auto buf = tmp_stream.ReleaseOutputBuffer(); stream_->MoveData(code_start_ + buf->data.size(), code_start_, stream_->offset() - code_start_); stream_->WriteDataAt(code_start_, buf->data.data(), buf->data.size()); stream_->AddOffset(buf->data.size()); code_start_ += buf->data.size(); section_count_ += 1; last_section_type_ = BinarySection::Code; } } // end anonymous namespace Result WriteBinaryModule(Stream* stream, const Module* module, const WriteBinaryOptions& options) { BinaryWriter binary_writer(stream, options, module); return binary_writer.WriteModule(); } } // namespace wabt