Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -96,6 +96,16 @@ } }; +// A wasm data segment. A wasm binary contains only a single data section +// but that can contain many segments, each with their own virtual location +// in memory. Each MCSection data created by llvm is modeled as its own +// wasm data segment. +struct WasmDataSegment { + MCSectionWasm *Section; + uint32_t Offset; + SmallVector Data; +}; + // A wasm import to be written into the import section. struct WasmImport { StringRef ModuleName; @@ -199,6 +209,8 @@ DenseMap FunctionTypeIndices; SmallVector FunctionTypes; + SmallVector Globals; + unsigned NumGlobalImports = 0; // TargetObjectWriter wrappers. bool is64Bit() const { return TargetObjectWriter->is64Bit(); } @@ -225,7 +237,9 @@ IndirectSymbolIndices.clear(); FunctionTypeIndices.clear(); FunctionTypes.clear(); + Globals.clear(); MCObjectWriter::reset(); + NumGlobalImports = 0; } void writeHeader(const MCAssembler &Asm); @@ -252,29 +266,28 @@ void writeImportSection(const SmallVector &Imports); void writeFunctionSection(const SmallVector &Functions); void writeTableSection(uint32_t NumElements); - void writeMemorySection(const SmallVector &DataBytes); - void writeGlobalSection(const SmallVector &Globals); + void writeMemorySection(uint32_t DataSize); + void writeGlobalSection(); void writeExportSection(const SmallVector &Exports); void writeElemSection(const SmallVector &TableElems); void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout, const SmallVector &Functions); - uint64_t - writeDataSection(const SmallVector &DataBytes); + void writeDataSection(const SmallVector &Segments); void writeNameSection(const SmallVector &Functions, const SmallVector &Imports, uint32_t NumFuncImports); void writeCodeRelocSection(); - void writeDataRelocSection(uint64_t DataSectionHeaderSize); + void writeDataRelocSection(); void writeLinkingMetaDataSection(uint32_t DataSize, uint32_t DataAlignment, ArrayRef WeakSymbols, bool HasStackPointer, uint32_t StackPointerGlobal); + uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); void applyRelocations(ArrayRef Relocations, uint64_t ContentsOffset); - void writeRelocations(ArrayRef Relocations, - uint64_t HeaderSize); + void writeRelocations(ArrayRef Relocations); uint32_t getRelocationIndexValue(const WasmRelocationEntry &RelEntry); uint32_t getFunctionType(const MCSymbolWasm& Symbol); uint32_t registerFunctionType(const MCSymbolWasm& Symbol); @@ -468,15 +481,17 @@ // by RelEntry. This value isn't used by the static linker, since // we have addends; it just serves to make the code more readable // and to make standalone wasm modules directly usable. -static uint32_t ProvisionalValue(const WasmRelocationEntry &RelEntry) { +uint32_t +WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) { const MCSymbolWasm *Sym = RelEntry.Symbol; // For undefined symbols, use a hopefully invalid value. if (!Sym->isDefined(/*SetUsed=*/false)) return UINT32_MAX; - const auto &Section = cast(RelEntry.Symbol->getSection(false)); - uint64_t Address = Section.getSectionOffset() + RelEntry.Addend; + uint32_t GlobalIndex = SymbolIndices[Sym]; + const WasmGlobal& Global = Globals[GlobalIndex - NumGlobalImports]; + uint64_t Address = Global.InitialValue + RelEntry.Addend; // Ignore overflow. LLVM allows address arithmetic to silently wrap. uint32_t Value = Address; @@ -538,17 +553,17 @@ break; } case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: { - uint32_t Value = ProvisionalValue(RelEntry); + uint32_t Value = getProvisionalValue(RelEntry); WritePatchableSLEB(Stream, Value, Offset); break; } case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: { - uint32_t Value = ProvisionalValue(RelEntry); + uint32_t Value = getProvisionalValue(RelEntry); WritePatchableLEB(Stream, Value, Offset); break; } case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: { - uint32_t Value = ProvisionalValue(RelEntry); + uint32_t Value = getProvisionalValue(RelEntry); WriteI32(Stream, Value, Offset); break; } @@ -561,12 +576,12 @@ // Write out the portions of the relocation records that the linker will // need to handle. void WasmObjectWriter::writeRelocations( - ArrayRef Relocations, uint64_t HeaderSize) { + ArrayRef Relocations) { raw_pwrite_stream &Stream = getStream(); for (const WasmRelocationEntry& RelEntry : Relocations) { uint64_t Offset = RelEntry.Offset + - RelEntry.FixupSection->getSectionOffset() + HeaderSize; + RelEntry.FixupSection->getSectionOffset(); uint32_t Index = getRelocationIndexValue(RelEntry); encodeULEB128(RelEntry.Type, Stream); @@ -664,14 +679,12 @@ endSection(Section); } -void WasmObjectWriter::writeMemorySection( - const SmallVector &DataBytes) { +void WasmObjectWriter::writeMemorySection(uint32_t DataSize) { // For now, always emit the memory section, since loads and stores are not // valid without it. In the future, we could perhaps be more clever and omit // it if there are no loads or stores. SectionBookkeeping Section; - uint32_t NumPages = - (DataBytes.size() + wasm::WasmPageSize - 1) / wasm::WasmPageSize; + uint32_t NumPages = (DataSize + wasm::WasmPageSize - 1) / wasm::WasmPageSize; startSection(Section, wasm::WASM_SEC_MEMORY); encodeULEB128(1, getStream()); // number of memory spaces @@ -682,8 +695,7 @@ endSection(Section); } -void WasmObjectWriter::writeGlobalSection( - const SmallVector &Globals) { +void WasmObjectWriter::writeGlobalSection() { if (Globals.empty()) return; @@ -782,28 +794,30 @@ endSection(Section); } -uint64_t WasmObjectWriter::writeDataSection( - const SmallVector &DataBytes) { - if (DataBytes.empty()) - return 0; +void WasmObjectWriter::writeDataSection( + const SmallVector &Segments) { + if (Segments.empty()) + return; SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_DATA); - encodeULEB128(1, getStream()); // count - encodeULEB128(0, getStream()); // memory index - write8(wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(0, getStream()); // offset - write8(wasm::WASM_OPCODE_END); - encodeULEB128(DataBytes.size(), getStream()); // size - uint32_t HeaderSize = getStream().tell() - Section.ContentsOffset; - writeBytes(DataBytes); // data + encodeULEB128(Segments.size(), getStream()); // count + + for (const WasmDataSegment & Segment : Segments) { + encodeULEB128(0, getStream()); // memory index + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Segment.Offset, getStream()); // offset + write8(wasm::WASM_OPCODE_END); + encodeULEB128(Segment.Data.size(), getStream()); // size + Segment.Section->setSectionOffset(getStream().tell() - Section.ContentsOffset); + writeBytes(Segment.Data); // data + } // Apply fixups. - applyRelocations(DataRelocations, Section.ContentsOffset + HeaderSize); + applyRelocations(DataRelocations, Section.ContentsOffset); endSection(Section); - return HeaderSize; } void WasmObjectWriter::writeNameSection( @@ -851,12 +865,12 @@ encodeULEB128(wasm::WASM_SEC_CODE, getStream()); encodeULEB128(CodeRelocations.size(), getStream()); - writeRelocations(CodeRelocations, 0); + writeRelocations(CodeRelocations); endSection(Section); } -void WasmObjectWriter::writeDataRelocSection(uint64_t DataSectionHeaderSize) { +void WasmObjectWriter::writeDataRelocSection() { // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md // for descriptions of the reloc sections. @@ -869,7 +883,7 @@ encodeULEB128(wasm::WASM_SEC_DATA, getStream()); encodeULEB128(DataRelocations.size(), getStream()); - writeRelocations(DataRelocations, DataSectionHeaderSize); + writeRelocations(DataRelocations); endSection(Section); } @@ -951,16 +965,15 @@ // Collect information from the available symbols. SmallVector Functions; SmallVector TableElems; - SmallVector Globals; SmallVector Imports; SmallVector Exports; SmallVector WeakSymbols; SmallPtrSet IsAddressTaken; unsigned NumFuncImports = 0; - unsigned NumGlobalImports = 0; - SmallVector DataBytes; + SmallVector DataSegments; uint32_t DataAlignment = 1; uint32_t StackPointerGlobal = 0; + uint32_t DataSize = 0; bool HasStackPointer = false; // Populate the IsAddressTaken set. @@ -985,7 +998,7 @@ } } - // Populate the Imports set. + // Populate FunctionTypeIndices and Imports. for (const MCSymbol &S : Asm.symbols()) { const auto &WS = static_cast(S); @@ -1171,10 +1184,28 @@ if (uint64_t(Size) != Layout.getSectionFileSize(&DataSection)) report_fatal_error("data sections must contain at most one variable"); - DataBytes.resize(alignTo(DataBytes.size(), DataSection.getAlignment())); DataAlignment = std::max(DataAlignment, DataSection.getAlignment()); - DataSection.setSectionOffset(DataBytes.size()); + DataSegments.emplace_back(); + WasmDataSegment &Segment = DataSegments.back(); + + DataSize = alignTo(DataSize, DataSection.getAlignment()); + Segment.Offset = DataSize; + Segment.Section = &DataSection; + + // For each global, prepare a corresponding wasm global holding its + // address. For externals these will also be named exports. + Index = NumGlobalImports + Globals.size(); + + WasmGlobal Global; + Global.Type = PtrType; + Global.IsMutable = false; + Global.HasImport = false; + Global.InitialValue = DataSize; + Global.ImportIndex = 0; + SymbolIndices[&WS] = Index; + DEBUG(dbgs() << " -> global index: " << Index << "\n"); + Globals.push_back(Global); for (const MCFragment &Frag : DataSection) { if (Frag.hasInstructions()) @@ -1185,34 +1216,21 @@ report_fatal_error("only byte values supported for alignment"); // If nops are requested, use zeros, as this is the data section. uint8_t Value = Align->hasEmitNops() ? 0 : Align->getValue(); - uint64_t Size = std::min(alignTo(DataBytes.size(), - Align->getAlignment()), - DataBytes.size() + - Align->getMaxBytesToEmit()); - DataBytes.resize(Size, Value); + uint64_t Size = std::min( + alignTo(Segment.Data.size(), Align->getAlignment()), + Segment.Data.size() + Align->getMaxBytesToEmit()); + Segment.Data.resize(Size, Value); } else if (auto *Fill = dyn_cast(&Frag)) { - DataBytes.insert(DataBytes.end(), Fill->getSize(), Fill->getValue()); + Segment.Data.insert(Segment.Data.end(), Fill->getSize(), Fill->getValue()); } else { const auto &DataFrag = cast(Frag); const SmallVectorImpl &Contents = DataFrag.getContents(); - DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); + Segment.Data.insert(Segment.Data.end(), Contents.begin(), + Contents.end()); } } - - // For each global, prepare a corresponding wasm global holding its - // address. For externals these will also be named exports. - Index = NumGlobalImports + Globals.size(); - - WasmGlobal Global; - Global.Type = PtrType; - Global.IsMutable = false; - Global.HasImport = false; - Global.InitialValue = DataSection.getSectionOffset(); - Global.ImportIndex = 0; - SymbolIndices[&WS] = Index; - DEBUG(dbgs() << " -> global index: " << Index << "\n"); - Globals.push_back(Global); + DataSize += Segment.Data.size(); } // If the symbol is visible outside this translation unit, export it. @@ -1273,17 +1291,17 @@ writeImportSection(Imports); writeFunctionSection(Functions); writeTableSection(TableElems.size()); - writeMemorySection(DataBytes); - writeGlobalSection(Globals); + writeMemorySection(DataSize); + writeGlobalSection(); writeExportSection(Exports); // TODO: Start Section writeElemSection(TableElems); writeCodeSection(Asm, Layout, Functions); - uint64_t DataSectionHeaderSize = writeDataSection(DataBytes); + writeDataSection(DataSegments); writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); - writeDataRelocSection(DataSectionHeaderSize); - writeLinkingMetaDataSection(DataBytes.size(), DataAlignment, WeakSymbols, HasStackPointer, StackPointerGlobal); + writeDataRelocSection(); + writeLinkingMetaDataSection(DataSize, DataAlignment, WeakSymbols, HasStackPointer, StackPointerGlobal); // TODO: Translate the .comment section to the output. // TODO: Translate debug sections to the output. Index: test/MC/WebAssembly/external-data.ll =================================================================== --- test/MC/WebAssembly/external-data.ll +++ test/MC/WebAssembly/external-data.ll @@ -7,16 +7,21 @@ @foo = global i64 7, align 4 @bar = hidden global i32* @myimport, align 4 -; CHECK: - Type: DATA -; CHECK: Relocations: -; CHECK: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 -; CHECK: Index: 0 -; CHECK: Offset: 0x0000000E -; CHECK: Segments: -; CHECK: - SectionOffset: 6 -; CHECK: MemoryIndex: 0 -; CHECK: Offset: -; CHECK: Opcode: I32_CONST -; CHECK: Value: 0 -; CHECK: Content: 0700000000000000FFFFFFFF - +; CHECK: - Type: DATA +; CHECK-NEXT: Relocations: +; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: Offset: 0x00000013 +; CHECK-NEXT: Segments: +; CHECK-NEXT: - SectionOffset: 6 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 0 +; CHECK-NEXT: Content: '0700000000000000' +; CHECK-NEXT: - SectionOffset: 19 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 8 +; CHECK-NEXT: Content: FFFFFFFF Index: test/MC/WebAssembly/reloc-data.ll =================================================================== --- test/MC/WebAssembly/reloc-data.ll +++ test/MC/WebAssembly/reloc-data.ll @@ -6,21 +6,40 @@ @bar = global i64 7, align 4 @a = global i32* getelementptr (i32, i32* @foo, i32 2), align 8 @b = global i64* getelementptr (i64, i64* @bar, i64 -2), align 8 +@c = global [3 x i32*] [i32* @foo, i32* @foo, i32* @foo], align 16 -; CHECK: Format: WASM -; CHECK: Relocations [ -; CHECK: Section (6) DATA { -; CHECK: Relocation { -; CHECK: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) -; CHECK: Offset: 0xE -; CHECK: Index: 0x0 -; CHECK: Addend: 8 -; CHECK: } -; CHECK: Relocation { -; CHECK: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) -; CHECK: Offset: 0x16 -; CHECK: Index: 0x1 -; CHECK: Addend: -16 -; CHECK: } -; CHECK: } -; CHECK: ] +; CHECK: Format: WASM +; CHECK: Relocations [ +; CHECK-NEXT: Section (6) DATA { +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) +; CHECK-NEXT: Offset: 0x13 +; CHECK-NEXT: Index: 0x0 +; CHECK-NEXT: Addend: 8 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) +; CHECK-NEXT: Offset: 0x1C +; CHECK-NEXT: Index: 0x1 +; CHECK-NEXT: Addend: -16 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) +; CHECK-NEXT: Offset: 0x25 +; CHECK-NEXT: Index: 0x0 +; CHECK-NEXT: Addend: 0 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) +; CHECK-NEXT: Offset: 0x29 +; CHECK-NEXT: Index: 0x0 +; CHECK-NEXT: Addend: 0 +; CHECK-NEXT: } +; CHECK-NEXT: Relocation { +; CHECK-NEXT: Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 (5) +; CHECK-NEXT: Offset: 0x2D +; CHECK-NEXT: Index: 0x0 +; CHECK-NEXT: Addend: 0 +; CHECK-NEXT: } +; CHECK-NEXT: } +; CHECK-NEXT: ] Index: test/MC/WebAssembly/unnamed-data.ll =================================================================== --- test/MC/WebAssembly/unnamed-data.ll +++ test/MC/WebAssembly/unnamed-data.ll @@ -41,17 +41,35 @@ ; CHECK-NEXT: Relocations: ; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 ; CHECK-NEXT: Index: 0 -; CHECK-NEXT: Offset: 0x00000016 +; CHECK-NEXT: Offset: 0x0000001C ; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: Offset: 0x0000001E +; CHECK-NEXT: Offset: 0x00000025 ; CHECK-NEXT: Segments: ; CHECK-NEXT: - SectionOffset: 6 ; CHECK-NEXT: MemoryIndex: 0 ; CHECK-NEXT: Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 0 -; CHECK-NEXT: Content: 68656C6C6F00776F726C640000000000000000000000000006000000 +; CHECK-NEXT: Content: 68656C6C6F00 +; CHECK-NEXT: - SectionOffset: 17 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 6 +; CHECK-NEXT: Content: 776F726C6400 +; CHECK-NEXT: - SectionOffset: 28 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 16 +; CHECK-NEXT: Content: '00000000' +; CHECK-NEXT: - SectionOffset: 37 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 24 +; CHECK-NEXT: Content: '06000000' ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: linking ; CHECK-NEXT: DataSize: 28 Index: test/tools/llvm-objdump/WebAssembly/relocations.test =================================================================== --- test/tools/llvm-objdump/WebAssembly/relocations.test +++ test/tools/llvm-objdump/WebAssembly/relocations.test @@ -5,4 +5,4 @@ @bar = hidden global i32* @foo2, align 4 ; CHECK: RELOCATION RECORDS FOR [DATA]: -; CHECK-NEXT: 0000000e R_WEBASSEMBLY_MEMORY_ADDR_I32 1+0 +; CHECK-NEXT: 00000018 R_WEBASSEMBLY_MEMORY_ADDR_I32 1+0