diff options
author | Derek Schuff <dschuff@chromium.org> | 2016-09-21 14:35:41 -0700 |
---|---|---|
committer | Derek Schuff <dschuff@chromium.org> | 2016-09-22 09:17:36 -0700 |
commit | 44978317c4068cea4778e264d03efb5537c009ea (patch) | |
tree | 45ac77901167e066adcdbcb6b7c628579bace8f7 | |
parent | 8be82627c6a8cbded0dab67ad1f31906a54ba78c (diff) | |
download | binaryen-44978317c4068cea4778e264d03efb5537c009ea.tar.gz binaryen-44978317c4068cea4778e264d03efb5537c009ea.tar.bz2 binaryen-44978317c4068cea4778e264d03efb5537c009ea.zip |
Update binary format toward 0xc (#704)
Updates section headers and formats for type, import, function, table,
memory, and export sections, as well as "names" section, which is now a
user section.
-rw-r--r-- | src/wasm-binary.h | 225 | ||||
-rw-r--r-- | src/wasm.cpp | 19 |
2 files changed, 141 insertions, 103 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h index bbfde5b21..32f51da96 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -237,20 +237,29 @@ enum Meta { Version = 0x0c }; -namespace Section { - extern const char* Memory; - extern const char* Signatures; - extern const char* ImportTable; - extern const char* FunctionSignatures; - extern const char* Functions; - extern const char* ExportTable; - extern const char* Globals; - extern const char* DataSegments; - extern const char* FunctionTable; - extern const char* Names; - extern const char* Start; +enum Section { + User = 0, + Type = 1, + Import = 2, + Function = 3, + Table = 4, + Memory = 5, + Global = 6, + Export = 7, + Start = 8, + Element = 9, + Code = 10, + Data = 11 }; +enum ElementType { + AnyFunc = 0x20 +}; + +namespace UserSections { +extern const char* Names; +} + enum ASTNodes { CurrentMemory = 0x3b, GrowMemory = 0x39, @@ -478,7 +487,7 @@ public: void write() { writeHeader(); - writeSignatures(); + writeTypes(); writeImports(); writeFunctionSignatures(); writeFunctionTable(); @@ -506,14 +515,22 @@ public: return ret; } - int32_t startSection(const char* name) { - // emit 5 bytes of 0, which we'll fill with LEB later - writeInlineString(name); - return writeU32LEBPlaceholder(); + void writeResizableLimits(Address initial, Address maximum) { + uint32_t flags = maximum ? 1 : 0; + o << U32LEB(flags); + o << U32LEB(initial); + if (flags) { + o << U32LEB(maximum); + } + } + + int32_t startSection(BinaryConsts::Section code) { + o << U32LEB(code); + return writeU32LEBPlaceholder(); // section size to be filled in later } void finishSection(int32_t start) { - int32_t size = o.size() - start - 5; // section size does not include the 5 bytes of the size field itself + int32_t size = o.size() - start - 6; // section size does not include the 6 bytes of the code and size field o.writeAt(start, U32LEB(size)); } @@ -529,15 +546,16 @@ public: if (wasm->memory.max == 0) return; if (debug) std::cerr << "== writeMemory" << std::endl; auto start = startSection(BinaryConsts::Section::Memory); - o << U32LEB(wasm->memory.initial) - << U32LEB(wasm->memory.max); + o << U32LEB(1); // Define 1 memory + Address max = wasm->memory.max == Memory::kMaxSize ? Address(0) : wasm->memory.max; + writeResizableLimits(wasm->memory.initial, max); finishSection(start); } - void writeSignatures() { + void writeTypes() { if (wasm->functionTypes.size() == 0) return; - if (debug) std::cerr << "== writeSignatures" << std::endl; - auto start = startSection(BinaryConsts::Section::Signatures); + if (debug) std::cerr << "== writeTypes" << std::endl; + auto start = startSection(BinaryConsts::Section::Type); o << U32LEB(wasm->functionTypes.size()); for (auto& type : wasm->functionTypes) { if (debug) std::cerr << "write one" << std::endl; @@ -567,20 +585,31 @@ public: void writeImports() { if (wasm->imports.size() == 0) return; if (debug) std::cerr << "== writeImports" << std::endl; - auto start = startSection(BinaryConsts::Section::ImportTable); + auto start = startSection(BinaryConsts::Section::Import); o << U32LEB(wasm->imports.size()); for (auto& import : wasm->imports) { if (debug) std::cerr << "write one" << std::endl; + writeInlineString(import->module.str); + writeInlineString(import->base.str); o << U32LEB(import->kind); switch (import->kind) { - case Export::Function: o << U32LEB(getFunctionTypeIndex(import->functionType->name)); - case Export::Table: break; - case Export::Memory: break; - case Export::Global: o << binaryWasmType(import->globalType);break; + case Export::Function: o << U32LEB(getFunctionTypeIndex(import->functionType->name)); break; + case Export::Table: { + o << U32LEB(BinaryConsts::ElementType::AnyFunc); + auto max = wasm->table.max == Table::kMaxSize ? Address(0) : wasm->table.max; + writeResizableLimits(wasm->table.initial, max); + break; + } + case Export::Memory: { + auto max = wasm->memory.max == Memory::kMaxSize ? Address(0) : wasm->memory.max; + writeResizableLimits(wasm->memory.initial, max); break; + } + case Export::Global: + o << binaryWasmType(import->globalType); + o << U32LEB(0); // Mutable global's can't be imported for now. + break; default: WASM_UNREACHABLE(); } - writeInlineString(import->module.str); - writeInlineString(import->base.str); } finishSection(start); } @@ -627,7 +656,7 @@ public: void writeFunctionSignatures() { if (wasm->functions.size() == 0) return; if (debug) std::cerr << "== writeFunctionSignatures" << std::endl; - auto start = startSection(BinaryConsts::Section::FunctionSignatures); + auto start = startSection(BinaryConsts::Section::Function); o << U32LEB(wasm->functions.size()); for (auto& curr : wasm->functions) { if (debug) std::cerr << "write one" << std::endl; @@ -645,7 +674,7 @@ public: void writeFunctions() { if (wasm->functions.size() == 0) return; if (debug) std::cerr << "== writeFunctions" << std::endl; - auto start = startSection(BinaryConsts::Section::Functions); + auto start = startSection(BinaryConsts::Section::Code); size_t total = wasm->functions.size(); o << U32LEB(total); for (size_t i = 0; i < total; i++) { @@ -679,7 +708,7 @@ public: void writeGlobals() { if (wasm->globals.size() == 0) return; if (debug) std::cerr << "== writeglobals" << std::endl; - auto start = startSection(BinaryConsts::Section::Globals); + auto start = startSection(BinaryConsts::Section::Global); o << U32LEB(wasm->globals.size()); for (auto& curr : wasm->globals) { if (debug) std::cerr << "write one" << std::endl; @@ -693,10 +722,11 @@ public: void writeExports() { if (wasm->exports.size() == 0) return; if (debug) std::cerr << "== writeexports" << std::endl; - auto start = startSection(BinaryConsts::Section::ExportTable); + auto start = startSection(BinaryConsts::Section::Export); o << U32LEB(wasm->exports.size()); for (auto& curr : wasm->exports) { if (debug) std::cerr << "write one" << std::endl; + writeInlineString(curr->name.str); o << U32LEB(curr->kind); switch (curr->kind) { case Export::Function: o << U32LEB(getFunctionIndex(curr->value)); break; @@ -705,7 +735,7 @@ public: case Export::Global: o << U32LEB(getGlobalIndex(curr->value)); break; default: WASM_UNREACHABLE(); } - writeInlineString(curr->name.str); + } finishSection(start); } @@ -716,7 +746,7 @@ public: for (auto& segment : wasm->memory.segments) { if (segment.data.size() > 0) num++; } - auto start = startSection(BinaryConsts::Section::DataSegments); + auto start = startSection(BinaryConsts::Section::Data); o << U32LEB(num); for (auto& segment : wasm->memory.segments) { if (segment.data.size() == 0) continue; @@ -770,7 +800,7 @@ public: void writeFunctionTable() { if (wasm->table.segments.size() == 0) return; if (debug) std::cerr << "== writeFunctionTable" << std::endl; - auto start = startSection(BinaryConsts::Section::FunctionTable); + auto start = startSection(BinaryConsts::Section::Table); o << U32LEB(wasm->table.initial); o << U32LEB(wasm->table.max); o << U32LEB(wasm->table.segments.size()); @@ -788,7 +818,8 @@ public: void writeNames() { if (wasm->functions.size() == 0) return; if (debug) std::cerr << "== writeNames" << std::endl; - auto start = startSection(BinaryConsts::Section::Names); + auto start = startSection(BinaryConsts::Section::User); + writeInlineString("names"); o << U32LEB(wasm->functions.size()); for (auto& curr : wasm->functions) { writeInlineString(curr->name.str); @@ -1270,51 +1301,47 @@ public: // read sections until the end while (more()) { - auto nameSize = getU32LEB(); - uint32_t sectionSize, before; - auto match = [&](const char* name) { - for (size_t i = 0; i < nameSize; i++) { - if (pos + i >= input.size()) return false; - if (name[i] == 0) return false; - if (input[pos + i] != name[i]) return false; + uint32_t sectionCode = getU32LEB(); + uint32_t payloadLen = getU32LEB(); + if (pos + payloadLen > input.size()) throw ParseException("Section extends beyond end of input"); + + switch (sectionCode) { + case BinaryConsts::Section::Start: readStart(); break; + case BinaryConsts::Section::Memory: readMemory(); break; + case BinaryConsts::Section::Type: readSignatures(); break; + case BinaryConsts::Section::Import: readImports(); break; + case BinaryConsts::Section::Function: readFunctionSignatures(); break; + case BinaryConsts::Section::Code: readFunctions(); break; + case BinaryConsts::Section::Export: readExports(); break; + case BinaryConsts::Section::Global: { + readGlobals(); + // imports can read global imports, so we run getGlobalName and create the mapping + // but after we read globals, we need to add the internal globals too, so do that here + mappedGlobals.clear(); // wipe the mapping + getGlobalName(0); // force rebuild + break; } - if (strlen(name) != nameSize) return false; - // name matched, read section size and then section itself - pos += nameSize; - sectionSize = getU32LEB(); - before = pos; - assert(pos + sectionSize <= input.size()); - return true; - }; - if (match(BinaryConsts::Section::Start)) readStart(); - else if (match(BinaryConsts::Section::Memory)) readMemory(); - else if (match(BinaryConsts::Section::Signatures)) readSignatures(); - else if (match(BinaryConsts::Section::ImportTable)) readImports(); - else if (match(BinaryConsts::Section::FunctionSignatures)) readFunctionSignatures(); - else if (match(BinaryConsts::Section::Functions)) readFunctions(); - else if (match(BinaryConsts::Section::ExportTable)) readExports(); - else if (match(BinaryConsts::Section::Globals)) { - readGlobals(); - // imports can read global imports, so we run getGlobalName and create the mapping - // but after we read globals, we need to add the internal globals too, so do that here - mappedGlobals.clear(); // wipe the mapping - getGlobalName(0); // force rebuild - } else if (match(BinaryConsts::Section::DataSegments)) readDataSegments(); - else if (match(BinaryConsts::Section::FunctionTable)) readFunctionTable(); - else if (match(BinaryConsts::Section::Names)) readNames(); - else { - std::cerr << "unfamiliar section: "; - assert(pos + nameSize - 1 < input.size()); - for (size_t i = 0; i < nameSize; i++) std::cerr << input[pos + i]; - std::cerr << std::endl; - abort(); + case BinaryConsts::Section::Data: readDataSegments(); break; + case BinaryConsts::Section::Table: readFunctionTable(); break; + + default: + if (!readUserSection()) abort(); } - assert(pos == before + sectionSize); } processFunctions(); } + bool readUserSection() { + Name sectionName = getInlineString(); + if (sectionName.equals(BinaryConsts::UserSections::Names)) { + readNames(); + return true; + } + std::cerr << "unfamiliar section: " << sectionName << std::endl; + return false; + } + bool more() { return pos < input.size(); } @@ -1328,21 +1355,21 @@ public: if (debug) std::cerr << "<==" << std::endl; auto ret = uint16_t(getInt8()); ret |= uint16_t(getInt8()) << 8; - if (debug) std::cerr << "getInt16: " << ret << " ==>" << std::endl; + if (debug) std::cerr << "getInt16: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; return ret; } uint32_t getInt32() { if (debug) std::cerr << "<==" << std::endl; auto ret = uint32_t(getInt16()); ret |= uint32_t(getInt16()) << 16; - if (debug) std::cerr << "getInt32: " << ret << " ==>" << std::endl; + if (debug) std::cerr << "getInt32: " << ret << "/0x" << std::hex << ret << std::dec <<" ==>" << std::endl; return ret; } uint64_t getInt64() { if (debug) std::cerr << "<==" << std::endl; auto ret = uint64_t(getInt32()); ret |= uint64_t(getInt32()) << 32; - if (debug) std::cerr << "getInt64: " << ret << " ==>" << std::endl; + if (debug) std::cerr << "getInt64: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; return ret; } float getFloat32() { @@ -1469,8 +1496,10 @@ public: void readMemory() { if (debug) std::cerr << "== readMemory" << std::endl; - wasm.memory.initial = getU32LEB(); - wasm.memory.max = getU32LEB(); + auto numMemories = getU32LEB(); + if (!numMemories) return; + assert(numMemories == 1); + getResizableLimits(wasm.memory.initial, &wasm.memory.max); } void readSignatures() { @@ -1480,7 +1509,7 @@ public: for (size_t i = 0; i < numTypes; i++) { if (debug) std::cerr << "read one" << std::endl; auto curr = new FunctionType; - auto form = getInt8(); + auto form = getU32LEB(); WASM_UNUSED(form); assert(form == BinaryConsts::TypeForms::Basic); size_t numParams = getU32LEB(); @@ -1514,6 +1543,14 @@ public: } } + void getResizableLimits(Address& initial, Address* max) { + auto flags = getU32LEB(); + initial = getU32LEB(); + bool hasMax = flags & 0x1; + assert(max || !hasMax); + if (hasMax) *max = getU32LEB(); + } + void readImports() { if (debug) std::cerr << "== readImports" << std::endl; size_t num = getU32LEB(); @@ -1522,6 +1559,8 @@ public: if (debug) std::cerr << "read one" << std::endl; auto curr = new Import; curr->name = Name(std::string("import$") + std::to_string(i)); + curr->module = getInlineString(); + curr->base = getInlineString(); curr->kind = (Import::Kind)getU32LEB(); switch (curr->kind) { case Import::Function: { @@ -1532,13 +1571,23 @@ public: functionImportIndexes.push_back(curr->name); break; } - case Import::Table: break; - case Import::Memory: break; - case Import::Global: curr->globalType = getWasmType(); break; + case Import::Table: { + auto elementType = getU32LEB(); + WASM_UNUSED(elementType); + assert(elementType == BinaryConsts::ElementType::AnyFunc); + getResizableLimits(wasm.table.initial, &wasm.table.max); + break; + } + case Import::Memory: getResizableLimits(wasm.memory.initial, &wasm.memory.max); break; + case Import::Global: { + curr->globalType = getWasmType(); + auto globalMutable = getU32LEB(); + WASM_UNUSED(globalMutable); + assert(!globalMutable); + break; + } default: WASM_UNREACHABLE(); } - curr->module = getInlineString(); - curr->base = getInlineString(); wasm.addImport(curr); } } @@ -1634,9 +1683,9 @@ public: for (size_t i = 0; i < num; i++) { if (debug) std::cerr << "read one" << std::endl; auto curr = new Export; + curr->name = getInlineString(); curr->kind = (Export::Kind)getU32LEB(); auto index = getU32LEB(); - curr->name = getInlineString(); exportIndexes[curr] = index; } } diff --git a/src/wasm.cpp b/src/wasm.cpp index a910575f0..f43f4be1e 100644 --- a/src/wasm.cpp +++ b/src/wasm.cpp @@ -26,20 +26,10 @@ Name WASM("wasm"), RETURN_FLOW("*return:)*"); namespace BinaryConsts { -namespace Section { - const char* Memory = "memory"; - const char* Signatures = "type"; - const char* ImportTable = "import"; - const char* FunctionSignatures = "function"; - const char* Functions = "code"; - const char* ExportTable = "export"; - const char* Globals = "global"; - const char* DataSegments = "data"; - const char* FunctionTable = "table"; - const char* Names = "name"; - const char* Start = "start"; -}; -}; +namespace UserSections { +const char* Names = "names"; +} +} Name GROW_WASM_MEMORY("__growWasmMemory"), NEW_SIZE("newSize"), @@ -163,4 +153,3 @@ void Loop::finalize() { } } // namespace wasm - |