さらに続き。
もう少し調べてみるとリンカに渡す前のオブジェクトファイルはTypeセクションが意図したおりになっていた。
ldc2 -mtriple=wasm32-unknown-unknown \ -mattr=+multivalue \ -betterC \ -fvisibility=hidden \ --frame-pointer=none \ -Oz \ -c \ multivalue.d
Typeセクションが壊れていないことが確認できる。
$ wasm-objdump -x multivalue.o multivalue.o: file format wasm 0x1 Section Details: Type[1]: - type[0] () -> (i32, i32) Import[2]: - memory[0] pages: initial=0 <- env.__linear_memory - table[0] type=funcref initial=0 <- env.__indirect_function_table Function[1]: - func[0] sig=0 <return_two_i32> Code[1]: - func[0] size=6 <return_two_i32> Custom: - name: "linking" - symbol table [count=1] - 0: F <return_two_i32> func=0 binding=global vis=default Custom: - name: "producers" Custom: - name: "target_features"
じゃあこれがなぜ壊れるようになったのか。
リンカにverboseフラグを足してみよう。
$ ldc2 -mtriple=wasm32-unknown-unknown \ -mattr=+multivalue \ -betterC \ -fvisibility=hidden \ --frame-pointer=none \ -Oz \ -L=--no-entry \ -L=--verbose \ multivalue.d lld: Loading: multivalue.o lld: Processing: multivalue.o lld: -- createOutputSegments lld: -- createSyntheticSections lld: -- populateProducers lld: -- populateTargetFeatures lld: -- calculateImports lld: -- layoutMemory lld: mem: global base = 1024 lld: mem: static data = 0 lld: mem: stack size = 65536 lld: mem: stack base = 1024 lld: mem: stack top = 66560 lld: mem: heap base = 66560 lld: mem: total pages = 2 lld: -- scanRelocations lld: -- assignIndexes lld: -- calculateInitFunctions lld: -- calculateTypes z3lld: -- calculateExports lld: -- calculateCustomSections lld: calculateCustomSections lld: -- populateSymtab lld: -- addSections lld: addSection: TYPE lld: addSection: FUNCTION lld: addSection: TABLE lld: addSection: MEMORY lld: addSection: GLOBAL lld: addSection: EXPORT lld: addSection: CODE lld: createCustomSections lld: addSection: CUSTOM(name) lld: addSection: CUSTOM(producers) lld: addSection: CUSTOM(target_features) lld: Defined Functions: 1 lld: Defined Globals : 1 lld: Defined Events : 0 lld: Function Imports : 0 lld: Global Imports : 0 lld: Event Imports : 0 lld: info for: multivalue.o Symbols : 1 Function Imports : 0 Global Imports : 0 Event Imports : 0 lld: -- finalizeSections lld: setOffset: TYPE: 8 lld: createHeader: TYPE body=5 total=7 lld: setOffset: FUNCTION: 15 lld: createHeader: FUNCTION body=2 total=4 lld: setOffset: TABLE: 19 lld: createHeader: TABLE body=5 total=7 lld: setOffset: MEMORY: 26 lld: createHeader: MEMORY body=3 total=5 lld: setOffset: GLOBAL: 31 lld: createHeader: GLOBAL body=8 total=10 lld: setOffset: EXPORT: 41 lld: createHeader: EXPORT body=27 total=29 lld: setOffset: CODE: 70 lld: createHeader: CODE body=8 total=10 lld: setOffset: CUSTOM(name): 80 lld: createHeader: CUSTOM(name) body=24 total=26 lld: setOffset: CUSTOM(producers): 106 lld: createHeader: CUSTOM(producers) body=54 total=56 lld: setOffset: CUSTOM(target_features): 162 lld: createHeader: CUSTOM(target_features) body=29 total=31 lld: -- openFile lld: writing: multivalue.wasm lld: -- writeSections lld: writing CUSTOM(target_features) lld: writing CUSTOM(producers) lld: writing CUSTOM(name) lld: writing CODE lld: size=10 lld: headersize=2 lld: codeheadersize=1 lld: writing EXPORT lld: writing MEMORY lld: writing GLOBAL lld: writing FUNCTION lld: writing TYPE lld: writing TABLE
このあたりが怪しそう。
lld: -- finalizeSections lld: setOffset: TYPE: 8 lld: createHeader: TYPE body=5 total=7
void Writer::finalizeSections() { for (OutputSection *s : outputSections) { s->setOffset(fileSize); s->finalizeContents(); fileSize += s->getSize(); // fileSize += 7 } }
TypeSectionはSynctheticSectionを継承しているのでfinalizeContentsは以下のコードになる。
void finalizeContents() override { writeBody(); bodyOutputStream.flush(); // ここでbodyに書き込んでいる? createHeader(body.size()); // body.size()=5 }
createHeaderのコードはまたOutputSectionに戻ってくる。 デバッグ出力の値を埋め込んでいるところをみると、引数のbodySizeがおかしいようだ。
void OutputSection::createHeader(size_t bodySize) { raw_string_ostream os(header); debugWrite(os.tell(), "section type [" + getSectionName() + "]"); encodeULEB128(type, os); writeUleb128(os, bodySize, "section size"); os.flush(); log("createHeader: " + toString(*this) + " body=" + Twine(bodySize) + // bodySize=5 " total=" + Twine(getSize())); // getSize()=7 }
さかのぼると、ここでbody sizeが5になっているのがおかしいようだ。
void TypeSection::writeBody() { writeUleb128(bodyOutputStream, types.size(), "type count"); for (const WasmSignature *sig : types) writeSig(bodyOutputStream, *sig); // ここ? }
typesに入れてる箇所はここ。
uint32_t TypeSection::registerType(const WasmSignature &sig) { auto pair = typeIndices.insert(std::make_pair(sig, types.size())); if (pair.second) { LLVM_DEBUG(llvm::dbgs() << "type " << toString(sig) << "\n"); types.push_back(&sig); // sigを入れてる。 } return pair.first->second; }
registerTypeはここで呼び出されている。
void Writer::calculateTypes() { // The output type section is the union of the following sets: // 1. Any signature used in the TYPE relocation // 2. The signatures of all imported functions // 3. The signatures of all defined functions // 4. The signatures of all imported events // 5. The signatures of all defined events for (ObjFile *file : symtab->objectFiles) { ArrayRef<WasmSignature> types = file->getWasmObj()->types(); for (uint32_t i = 0; i < types.size(); i++) if (file->typeIsUsed[i]) file->typeMap[i] = out.typeSec->registerType(types[i]); } for (const Symbol *sym : out.importSec->importedSymbols) { if (auto *f = dyn_cast<FunctionSymbol>(sym)) out.typeSec->registerType(*f->signature); else if (auto *e = dyn_cast<EventSymbol>(sym)) out.typeSec->registerType(*e->signature); } for (const InputFunction *f : out.functionSec->inputFunctions) out.typeSec->registerType(f->signature); for (const InputEvent *e : out.eventSec->inputEvents) out.typeSec->registerType(e->signature); }
writeSigはここ。
void writeSig(raw_ostream &os, const WasmSignature &sig) { writeU8(os, WASM_TYPE_FUNC, "signature type"); writeUleb128(os, sig.Params.size(), "param Count"); for (ValType paramType : sig.Params) { writeValueType(os, paramType, "param type"); } writeUleb128(os, sig.Returns.size(), "result Count"); // ここは2らしい for (ValType returnType : sig.Returns) { writeValueType(os, returnType, "result type"); // ここがおかしい? } }
うーん、でも元のmultivalue.oをwasm-validateにかけると通るんだよな。わからん。