void WriteDebugName(Stream* stream, std::string_view name, constchar* 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;
constchar* name;
Index section_index;
std::vector<Reloc> relocations;
};
class Symbol { public: struct Function { staticconst SymbolType type = SymbolType::Function;
Index index;
}; struct Data { staticconst SymbolType type = SymbolType::Data;
Index index;
Offset offset;
Address size;
}; struct Global { staticconst SymbolType type = SymbolType::Global;
Index index;
}; struct Section { staticconst SymbolType type = SymbolType::Section;
Index section;
}; struct Tag { staticconst SymbolType type = SymbolType::Tag;
Index index;
}; struct Table { staticconst 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_;
};
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 <typename T>
Result AddSymbol(std::vector<Index>* 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;
}
Index SymbolIndex(const std::vector<Index>& 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;
}
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)}));
}
// 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;
}
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);
}
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::WriteTableNumberWithReloc(Index value, constchar* 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);
} elseif (var.is_index()) { return var.index();
} else { return kInvalidIndex;
}
}
// TODO(binji): Rename this, it is used for more than loads/stores now. template <typename T> void BinaryWriter::WriteLoadStoreExpr(const Func* func, const Expr* expr, constchar* desc) { auto* typed_expr = cast<T>(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");
}
WriteU64Leb128(stream_, typed_expr->offset, desc);
}
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::MemoryAddressLocRelI32: case RelocType::FunctionOffsetI32: case RelocType::FunctionOffsetI64: case RelocType::SectionOffsetI32: case RelocType::MemoryAddressTLSSLEB: case RelocType::MemoryAddressTLSSLEB64:
WriteU32Leb128(stream_, reloc.addend, "reloc addend"); break; case RelocType::FuncIndexLEB: case RelocType::FuncIndexI32: 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::TableIndexRelSLEB64: case RelocType::TableNumberLEB: break; default:
fprintf(stderr, "warning: unsupported relocation type: %s\n",
GetRelocTypeName(reloc.type));
}
}
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<RefFuncExpr>(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();
}
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;
}
}
}
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();
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();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.